From 574a9c0456c182831b3d01a4d7ee0c737b91b107 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Jun 2009 14:39:39 -0700 Subject: Remove Subversion Id strings --- tests/client/basic-t.in | 1 - tests/client/full-t.in | 1 - tests/client/pod-t.in | 1 - tests/client/prompt-t.in | 1 - tests/data/basic.conf | 1 - tests/data/cmd-fake | 1 - tests/data/cmd-wrapper.in | 1 - tests/data/fake-kadmin | 1 - tests/data/full.conf.in | 1 - tests/data/wallet.conf | 1 - tests/kasetkey/basic-t.in | 1 - tests/libtest.c | 3 +-- tests/libtest.h | 3 +-- tests/libtest.sh | 2 -- tests/portable/asprintf-t.c | 3 +-- tests/portable/snprintf-t.c | 3 +-- tests/portable/strlcat-t.c | 3 +-- tests/portable/strlcpy-t.c | 3 +-- tests/runtests.c | 3 +-- tests/server/admin-t.in | 1 - tests/server/backend-t.in | 1 - tests/server/pod-t.in | 1 - tests/util/concat-t.c | 3 +-- tests/util/messages-t.c | 3 +-- tests/util/xmalloc-t.in | 1 - tests/util/xmalloc.c | 3 +-- 26 files changed, 10 insertions(+), 37 deletions(-) (limited to 'tests') diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index f18c28e..05a7abe 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -1,5 +1,4 @@ #! /bin/sh -# $Id$ # # Test suite for the wallet command-line client. # diff --git a/tests/client/full-t.in b/tests/client/full-t.in index f4ef1d3..3240563 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # tests/client/full-t -- End-to-end tests for the wallet client. # diff --git a/tests/client/pod-t.in b/tests/client/pod-t.in index 98c34c7..db995f7 100644 --- a/tests/client/pod-t.in +++ b/tests/client/pod-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -# $Id$ # # Test POD formatting for client documentation. # diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index 2d6097d..7988fc9 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # tests/client/prompt-t -- Password prompting tests for the wallet client. # diff --git a/tests/data/basic.conf b/tests/data/basic.conf index 7ad998f..3280ce9 100644 --- a/tests/data/basic.conf +++ b/tests/data/basic.conf @@ -1,4 +1,3 @@ # remctl configuration for wallet client tests. -# $Id$ fake-wallet ALL data/cmd-fake ANYUSER diff --git a/tests/data/cmd-fake b/tests/data/cmd-fake index 3ffd9cc..9c9e38c 100755 --- a/tests/data/cmd-fake +++ b/tests/data/cmd-fake @@ -1,5 +1,4 @@ #!/bin/sh -# $Id$ # # This is a fake wallet backend that returns bogus data for verification by # the client test suite. It doesn't test any of the wallet server code. diff --git a/tests/data/cmd-wrapper.in b/tests/data/cmd-wrapper.in index e119002..7c7b342 100644 --- a/tests/data/cmd-wrapper.in +++ b/tests/data/cmd-wrapper.in @@ -1,5 +1,4 @@ #!/bin/sh -# $Id$ # # Wrapper around the standard wallet-backend script that sets the Perl INC # path and the WALLET_CONFIG environment variable appropriately. diff --git a/tests/data/fake-kadmin b/tests/data/fake-kadmin index 81dc999..61906a4 100755 --- a/tests/data/fake-kadmin +++ b/tests/data/fake-kadmin @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # fake-kadmin -- Fake kadmin.local used to test the keytab backend. # diff --git a/tests/data/full.conf.in b/tests/data/full.conf.in index b97a4bc..25aef9e 100644 --- a/tests/data/full.conf.in +++ b/tests/data/full.conf.in @@ -1,4 +1,3 @@ # remctl configuration for full wallet client tests. -# $Id$ wallet ALL @abs_top_builddir@/tests/data/cmd-wrapper ANYUSER diff --git a/tests/data/wallet.conf b/tests/data/wallet.conf index b864e5e..0a232dd 100644 --- a/tests/data/wallet.conf +++ b/tests/data/wallet.conf @@ -1,5 +1,4 @@ # wallet.conf -- Test wallet server configuration. -*- perl -*- -# $Id$ # Always test with SQLite. $DB_DRIVER = 'SQLite'; diff --git a/tests/kasetkey/basic-t.in b/tests/kasetkey/basic-t.in index afc6747..bb086d6 100644 --- a/tests/kasetkey/basic-t.in +++ b/tests/kasetkey/basic-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # Tests for basic kasetkey functionality. # diff --git a/tests/libtest.c b/tests/libtest.c index 76d5207..bddaf91 100644 --- a/tests/libtest.c +++ b/tests/libtest.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Some utility routines for writing tests. * * Herein are a variety of utility routines for writing tests. All routines diff --git a/tests/libtest.h b/tests/libtest.h index ac2b083..ad4f591 100644 --- a/tests/libtest.h +++ b/tests/libtest.h @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Some utility routines for writing tests. * * Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University diff --git a/tests/libtest.sh b/tests/libtest.sh index ed46d0e..74f5ee6 100644 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -1,5 +1,3 @@ -# $Id$ -# # Shell function library for test cases. # # Written by Russ Allbery diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index d42e740..689e7c7 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * asprintf and vasprintf test suite. * * Written by Russ Allbery diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index c33e0e7..18c2326 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * snprintf test suite. * * Copyright (c) 2004, 2005, 2006 diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index c860803..2f39925 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * strlcat test suite. * * Copyright (c) 2004, 2005, 2006 diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index 8fb1f9c..74c9ecd 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * strlcpy test suite. * * Copyright (c) 2004, 2005, 2006 diff --git a/tests/runtests.c b/tests/runtests.c index abad3b6..060c8ad 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Run a set of tests, reporting results. * * Usage: diff --git a/tests/server/admin-t.in b/tests/server/admin-t.in index be40880..44ea1fe 100644 --- a/tests/server/admin-t.in +++ b/tests/server/admin-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # Tests for the wallet-admin dispatch code. # diff --git a/tests/server/backend-t.in b/tests/server/backend-t.in index e1518d8..773a002 100644 --- a/tests/server/backend-t.in +++ b/tests/server/backend-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # Tests for the wallet-backend dispatch code. # diff --git a/tests/server/pod-t.in b/tests/server/pod-t.in index fd939a5..4973d23 100644 --- a/tests/server/pod-t.in +++ b/tests/server/pod-t.in @@ -1,5 +1,4 @@ #!/usr/bin/perl -# $Id$ # # tests/server/pod-t -- Test POD formatting for client documentation. # diff --git a/tests/util/concat-t.c b/tests/util/concat-t.c index 2428d71..81824c8 100644 --- a/tests/util/concat-t.c +++ b/tests/util/concat-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * concat test suite. * * Copyright 2004, 2005, 2006 diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index 434ef56..3f7860e 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Test suite for error handling routines. * * Copyright 2004, 2005, 2006 diff --git a/tests/util/xmalloc-t.in b/tests/util/xmalloc-t.in index f721822..5c18512 100644 --- a/tests/util/xmalloc-t.in +++ b/tests/util/xmalloc-t.in @@ -1,5 +1,4 @@ #! /bin/sh -# $Id$ # # Test suite for xmalloc and friends. # diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index 699d0c4..bd0ab62 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Test suite for xmalloc and family. * * Copyright 2004, 2005, 2006 -- cgit v1.2.3 From e455057f2fe19dd27ee1b03083454eceb07d3043 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Jun 2009 16:37:52 -0700 Subject: Update tests to reflect suppression of store data in logging --- tests/server/backend-t.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/server/backend-t.in b/tests/server/backend-t.in index 773a002..0c6ac60 100644 --- a/tests/server/backend-t.in +++ b/tests/server/backend-t.in @@ -3,7 +3,8 @@ # Tests for the wallet-backend dispatch code. # # Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2008, 2009 +# Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -338,7 +339,11 @@ for my $command (qw/autocreate create destroy setacl setattr store/) { is ($out, "$new\n$method type name$extra\n", ' and ran the right method'); ($out, $err) = run_backend ($command, 'error', 'name', @extra); - $ran = "$command error name" . (@extra ? " @extra" : ''); + if ($command eq 'store') { + $ran = "$command error name"; + } else { + $ran = "$command error name" . (@extra ? " @extra" : ''); + } is ($err, "error count $error\n", "Command $command ran with errors"); is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" . " $error\n", ' and syslog correct'); -- cgit v1.2.3 From c2cde5918af1882ee63324fd9e09f07c8e6e5cc9 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Jun 2009 16:39:08 -0700 Subject: Add owners report Add a new report owners command to wallet-admin and corresponding report_owners() method to Wallet::Admin, which returns all ACL lines on owner ACLs for matching objects. --- NEWS | 4 ++++ perl/Wallet/Admin.pm | 47 ++++++++++++++++++++++++++++++++++++++++-- perl/t/admin.t | 55 +++++++++++++++++++++++++++++++++++++++++++++++-- server/wallet-admin | 39 ++++++++++++++++++++++++++++++++++- tests/server/admin-t.in | 45 +++++++++++++++++++++++++++++++--------- 5 files changed, 175 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/NEWS b/NEWS index e16c630..ab0828b 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ wallet 0.10 (unreleased) Fix logging in wallet-backend and the remctl configuration to not log the data passed to store. + Add a new report owners command to wallet-admin and corresponding + report_owners() method to Wallet::Admin, which returns all ACL lines + on owner ACLs for matching objects. + wallet 0.9 (2008-04-24) The wallet command-line client now reads the data for store from a diff --git a/perl/Wallet/Admin.pm b/perl/Wallet/Admin.pm index 3a2f687..c11c3d4 100644 --- a/perl/Wallet/Admin.pm +++ b/perl/Wallet/Admin.pm @@ -1,7 +1,7 @@ # Wallet::Admin -- Wallet system administrative interface. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -22,7 +22,7 @@ use Wallet::Schema; # This version should be increased on any code change to this module. Always # use two digits for the minor version with a leading zero if necessary so # that it will sort properly. -$VERSION = '0.02'; +$VERSION = '0.03'; ############################################################################## # Constructor, destructor, and accessors @@ -171,6 +171,38 @@ sub list_acls { } } +# Returns a report of all ACL lines contained in owner ACLs for matching +# objects. Objects are specified by type and name, which may be SQL wildcard +# expressions. Each list member will be a pair of ACL scheme and ACL +# identifier, with duplicates removed. On error and for no matching entries, +# the empty list will be returned. To distinguish between an empty return and +# an error, call error(), which will return undef if there was no error. +sub report_owners { + my ($self, $type, $name) = @_; + undef $self->{error}; + my @lines; + eval { + my $sql = 'select distinct ae_scheme, ae_identifier from acl_entries, + acls, objects where ae_id = ac_id and ac_id = ob_owner and + ob_type like ? and ob_name like ? order by ae_scheme, + ae_identifier'; + my $sth = $self->{dbh}->prepare ($sql); + $sth->execute ($type, $name); + my $object; + while (defined ($object = $sth->fetchrow_arrayref)) { + push (@lines, [ @$object ]); + } + $self->{dbh}->commit; + }; + if ($@) { + $self->error ("cannot report on owners: $@"); + $self->{dbh}->rollback; + return; + } else { + return @lines; + } +} + ############################################################################## # Object registration ############################################################################## @@ -335,6 +367,17 @@ be deleted and a fresh set of wallet database tables will be created. This method is equivalent to calling destroy() followed by initialize(). Returns true on success and false on failure. +=item report_owners(TYPE, NAME) + +Returns a list of all ACL lines contained in owner ACLs for objects +matching TYPE and NAME, which are interpreted as SQL patterns using C<%> +as a wildcard. The return value is a list of references to pairs of +schema and identifier, with duplicates removed. + +Returns the empty list on failure. To distinguish between this and no +matches, the caller should call error(). error() is guaranteed to return +the error message if there was an error and undef if there was no error. + =back =head1 SEE ALSO diff --git a/perl/t/admin.t b/perl/t/admin.t index 7a8b8ae..8804f34 100755 --- a/perl/t/admin.t +++ b/perl/t/admin.t @@ -3,11 +3,11 @@ # t/admin.t -- Tests for wallet administrative interface. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. -use Test::More tests => 29; +use Test::More tests => 57; use Wallet::Admin; use Wallet::Schema; @@ -73,6 +73,57 @@ is ($acls[0][1], 'ADMIN', ' and the first name is still the same'); is ($acls[1][0], 3, ' but the second ID has changed'); is ($acls[1][1], 'second', ' and the second name is correct'); +# Currently, we have no owners, so we should get an empty owner report. +my @lines = $admin->report_owners ('%', '%'); +is (scalar (@lines), 0, 'Owner report is currently empty'); +is ($admin->error, undef, ' and there is no error'); + +# Set an owner and make sure we now see something in the report. +is ($server->owner ('base', 'service/admin', 'ADMIN'), 1, + 'Setting an owner works'); +@lines = $admin->report_owners ('%', '%'); +is (scalar (@lines), 1, ' and now there is one owner in the report'); +is ($lines[0][0], 'krb5', ' with the right scheme'); +is ($lines[0][1], 'admin@EXAMPLE.COM', ' and the right identifier'); +@lines = $admin->report_owners ('keytab', '%'); +is (scalar (@lines), 0, 'Owners of keytabs is empty'); +is ($admin->error, undef, ' with no error'); +@lines = $admin->report_owners ('base', 'foo/%'); +is (scalar (@lines), 0, 'Owners of base foo/* objects is empty'); +is ($admin->error, undef, ' with no error'); + +# Create a second object with the same owner. +is ($server->create ('base', 'service/foo'), 1, + 'Creating base:service/foo succeeds'); +is ($server->owner ('base', 'service/foo', 'ADMIN'), 1, + ' and setting the owner to the same value works'); +@lines = $admin->report_owners ('base', 'service/%'); +is (scalar (@lines), 1, ' and there is still owner in the report'); +is ($lines[0][0], 'krb5', ' with the right scheme'); +is ($lines[0][1], 'admin@EXAMPLE.COM', ' and the right identifier'); + +# Change the owner of the second object to an empty ACL. +is ($server->owner ('base', 'service/foo', 'second'), 1, + ' and changing the owner to an empty ACL works'); +@lines = $admin->report_owners ('base', '%'); +is (scalar (@lines), 1, ' and there is still owner in the report'); +is ($lines[0][0], 'krb5', ' with the right scheme'); +is ($lines[0][1], 'admin@EXAMPLE.COM', ' and the right identifier'); + +# Add a few things to the second ACL to see what happens. +is ($server->acl_add ('second', 'base', 'foo'), 1, + 'Adding an ACL line to the new ACL works'); +is ($server->acl_add ('second', 'base', 'bar'), 1, + ' and adding another ACL line to the new ACL works'); +@lines = $admin->report_owners ('base', '%'); +is (scalar (@lines), 3, ' and now there are three owners in the report'); +is ($lines[0][0], 'base', ' first has the right scheme'); +is ($lines[0][1], 'bar', ' and the right identifier'); +is ($lines[1][0], 'base', ' second has the right scheme'); +is ($lines[1][1], 'foo', ' and the right identifier'); +is ($lines[2][0], 'krb5', ' third has the right scheme'); +is ($lines[2][1], 'admin@EXAMPLE.COM', ' and the right identifier'); + # Clean up. is ($admin->destroy, 1, 'Destruction succeeds'); unlink 'wallet-db'; diff --git a/server/wallet-admin b/server/wallet-admin index 0daa986..b5674c5 100755 --- a/server/wallet-admin +++ b/server/wallet-admin @@ -3,7 +3,7 @@ # wallet-admin -- Wallet server administrative commands. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -64,6 +64,22 @@ sub command { } else { die "only objects or acls are supported for list\n"; } + } elsif ($command eq 'report') { + die "too few arguments to report\n" if @args < 1; + my $report = shift @args; + if ($report eq 'owners') { + die "too many arguments to report owners\n" if @args > 2; + die "too few arguments to report owners\n" if @args < 2; + my @lines = $admin->report_owners (@args); + if (!@lines and $admin->error) { + die $admin->error, "\n"; + } + for my $line (@lines) { + print join (' ', @$line), "\n"; + } + } else { + die "unknown report type $report\n"; + } } elsif ($command eq 'register') { die "too many arguments to register\n" if @args > 3; die "too few arguments to register\n" if @args < 3; @@ -168,6 +184,27 @@ default as part of database initialization, so this command is used primarily to register local implementations of additional object types or ACL schemes. +=item report [ ... ] + +Runs a wallet report. The currently supported report types are: + +=over 4 + +=item report owners + +Returns a list of all ACL lines in owner ACLs for all objects matching +both and . These can be the type or name of +objects or they can be patterns using C<%> as the wildcard character +following the normal rules of SQL patterns. + +The output will be one line per ACL line in the form: + + + +with duplicates suppressed. + +=back + =back =head1 SEE ALSO diff --git a/tests/server/admin-t.in b/tests/server/admin-t.in index 44ea1fe..3e84022 100644 --- a/tests/server/admin-t.in +++ b/tests/server/admin-t.in @@ -3,12 +3,12 @@ # Tests for the wallet-admin dispatch code. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. use strict; -use Test::More tests => 54; +use Test::More tests => 64; # Create a dummy class for Wallet::Admin that prints what method was called # with its arguments and returns data for testing. @@ -71,6 +71,13 @@ sub register_verifier { return 1; } +sub report_owners { + shift; + print "report_owners @_\n"; + return if ($error or $empty); + return ([ krb5 => 'admin@EXAMPLE.COM' ]); +} + # Back to the main package and the actual test suite. Lie about whether the # Wallet::Admin package has already been loaded. package main; @@ -98,10 +105,11 @@ is ($err, "unknown command foo\n", 'Unknown command'); is ($out, "new\n", ' and nothing ran'); # Check too few and too many arguments for every command. -my %commands = (destroy => [0, 0], - initialize => [1, 1], - list => [1, 1], - register => [3, 3]); +my %commands = (destroy => [0, 0], + initialize => [1, 1], + list => [1, 1], + register => [3, 3], + report => [1, -1]); for my $command (sort keys %commands) { my ($min, $max) = @{ $commands{$command} }; if ($min > 0) { @@ -110,10 +118,12 @@ for my $command (sort keys %commands) { "Too few arguments for $command"); is ($out, "new\n", ' and nothing ran'); } - ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); - is ($err, "too many arguments to $command\n", - "Too many arguments for $command"); - is ($out, "new\n", ' and nothing ran'); + if ($max >= 0) { + ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments to $command\n", + "Too many arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } } # Test destroy. @@ -179,6 +189,15 @@ is ($err, '', 'Register succeeds for verifier'); is ($out, "new\nregister_verifier foo Foo::Verifier\n", ' and returns the right outout'); +# Test report. +($out, $err) = run_admin ('report', 'foo'); +is ($err, "unknown report type foo\n", 'Report requires a known report'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('report', 'owners', '%', '%'); +is ($err, '', 'Report succeeds for owners'); +is ($out, "new\nreport_owners % %\nkrb5 admin\@EXAMPLE.COM\n", + ' and returns the right output'); + # Test error handling. $Wallet::Admin::error = 1; ($out, $err) = run_admin ('destroy'); @@ -204,6 +223,9 @@ is ($out, "new\nregister_object foo Foo::Object\n", is ($err, "some error\n", 'Error handling succeeds for register verifier'); is ($out, "new\nregister_verifier foo Foo::Verifier\n", ' and calls the right methods'); +($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); +is ($err, "some error\n", 'Error handling succeeds for report owners'); +is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); # Test empty lists. $Wallet::Admin::error = 0; @@ -214,3 +236,6 @@ is ($out, "new\nlist_objects\n", ' and calls the right methods'); ($out, $err) = run_admin ('list', 'acls'); is ($err, '', 'list acls runs with an empty list and no errors'); is ($out, "new\nlist_acls\n", ' and calls the right methods'); +($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); +is ($err, '', 'report owners runs with an empty list and no errors'); +is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); -- cgit v1.2.3 From 865a91bebe112076965b823e32a853d9b0b20181 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 19 Jan 2010 22:48:48 -0800 Subject: Adjust server/admin test for the new list arguments --- tests/server/admin-t.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/server/admin-t.in b/tests/server/admin-t.in index 3e84022..11d2883 100644 --- a/tests/server/admin-t.in +++ b/tests/server/admin-t.in @@ -107,7 +107,7 @@ is ($out, "new\n", ' and nothing ran'); # Check too few and too many arguments for every command. my %commands = (destroy => [0, 0], initialize => [1, 1], - list => [1, 1], + list => [1, 4], register => [3, 3], report => [1, -1]); for my $command (sort keys %commands) { -- cgit v1.2.3 From 04b875599b1d4559dbcd356726035416081c6b48 Mon Sep 17 00:00:00 2001 From: Jon Robertson Date: Thu, 28 Jan 2010 00:07:16 -0800 Subject: Improved and fixed tests related to Pod and KDC type Added a fix to the Pod tests to change the order of the arguments in a skip statement to the correct order. Also added tests for the KEYTAB_KRBTYPE value in the keytab tests, and changed the Wallet::Kadmin module to standardize the errors returned with no keytab set and add new error for keytab set but not a valid value. --- perl/Wallet/Kadmin.pm | 5 ++++- perl/t/keytab.t | 23 ++++++++++++++++++++--- tests/server/pod-t.in | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/perl/Wallet/Kadmin.pm b/perl/Wallet/Kadmin.pm index 95859a9..501bc37 100644 --- a/perl/Wallet/Kadmin.pm +++ b/perl/Wallet/Kadmin.pm @@ -32,7 +32,10 @@ $VERSION = '0.03'; sub new { my ($class) = @_; my ($kadmin); - if ($Wallet::Config::KEYTAB_KRBTYPE eq 'MIT') { + if (!defined $Wallet::Config::KEYTAB_KRBTYPE + || !$Wallet::Config::KEYTAB_KRBTYPE) { + die "keytab object implementation not configured\n"; + } elsif ($Wallet::Config::KEYTAB_KRBTYPE eq 'MIT') { require Wallet::Kadmin::MIT; $kadmin = Wallet::Kadmin::MIT->new (); } elsif ($Wallet::Config::KEYTAB_KRBTYPE eq 'Heimdal') { diff --git a/perl/t/keytab.t b/perl/t/keytab.t index 7745290..ab5b19d 100755 --- a/perl/t/keytab.t +++ b/perl/t/keytab.t @@ -8,7 +8,7 @@ # See LICENSE for licensing terms. use POSIX qw(strftime); -use Test::More tests => 208 +use Test::More tests => 212 ; use Wallet::Admin; @@ -387,6 +387,21 @@ EOO is ($@, "keytab object implementation not configured\n", ' with the right error'); $Wallet::Config::KEYTAB_REALM = contents ('t/data/test.realm'); + undef $Wallet::Config::KEYTAB_KRBTYPE; + $object = eval { + Wallet::Object::Keytab->create ('keytab', 'wallet/one', $dbh, @trace) + }; + is ($object, undef, ' and another'); + is ($@, "keytab object implementation not configured\n", + ' with the right error'); + $Wallet::Config::KEYTAB_KRBTYPE = 'Active Directory'; + $object = eval { + Wallet::Object::Keytab->create ('keytab', 'wallet/one', $dbh, @trace) + }; + is ($object, undef, ' and one set to an invalid value'); + is ($@, "keytab krb server type not set to a valid value\n", + ' with the right error'); + $Wallet::Config::KEYTAB_KRBTYPE = contents ('t/data/test.krbtype'); } # Tests for unchanging support. Skip these if we don't have a keytab or if we @@ -403,6 +418,7 @@ SKIP: { $Wallet::Config::KEYTAB_FILE = 't/data/test.keytab'; $Wallet::Config::KEYTAB_PRINCIPAL = contents ('t/data/test.principal'); $Wallet::Config::KEYTAB_REALM = contents ('t/data/test.realm'); + $Wallet::Config::KEYTAB_KRBTYPE = contents ('t/data/test.krbtype'); $Wallet::Config::KEYTAB_TMP = '.'; my $realm = $Wallet::Config::KEYTAB_REALM; my $principal = $Wallet::Config::KEYTAB_PRINCIPAL; @@ -581,6 +597,7 @@ EOO $Wallet::Config::KEYTAB_FILE = 't/data/test.keytab'; $Wallet::Config::KEYTAB_PRINCIPAL = contents ('t/data/test.principal'); $Wallet::Config::KEYTAB_REALM = contents ('t/data/test.realm'); + $Wallet::Config::KEYTAB_KRBTYPE = contents ('t/data/test.krbtype'); $Wallet::Config::KEYTAB_TMP = '.'; $Wallet::Config::KEYTAB_AFS_KASETKEY = '../kasetkey/kasetkey'; my $realm = $Wallet::Config::KEYTAB_REALM; @@ -707,8 +724,7 @@ EOO # Tests for enctype restriction. SKIP: { - unless (-f 't/data/test.keytab' - && $Wallet::Config::KEYTAB_KRBTYPE eq 'MIT') { + unless (-f 't/data/test.keytab') { skip 'no keytab configuration', 36; } @@ -716,6 +732,7 @@ SKIP: { $Wallet::Config::KEYTAB_FILE = 't/data/test.keytab'; $Wallet::Config::KEYTAB_PRINCIPAL = contents ('t/data/test.principal'); $Wallet::Config::KEYTAB_REALM = contents ('t/data/test.realm'); + $Wallet::Config::KEYTAB_KRBTYPE = contents ('t/data/test.krbtype'); $Wallet::Config::KEYTAB_TMP = '.'; my $realm = $Wallet::Config::KEYTAB_REALM; my $principal = $Wallet::Config::KEYTAB_PRINCIPAL; diff --git a/tests/server/pod-t.in b/tests/server/pod-t.in index 4973d23..4575ecb 100644 --- a/tests/server/pod-t.in +++ b/tests/server/pod-t.in @@ -15,7 +15,7 @@ plan tests => $total; eval 'use Test::Pod 1.00'; SKIP: { - skip $total, 'Test::Pod 1.00 required for testing POD' if $@; + skip 'Test::Pod 1.00 required for testing POD', $total if $@; for my $file (@files) { pod_file_ok ("@abs_top_srcdir@/server/$file", "server/$file"); } -- cgit v1.2.3 From 2d33440272200cad20a5a4c58e5d8aa0dfad9a1f Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 13:37:58 -0800 Subject: Remove kaserver synchronization support from the wallet client The wallet client no longer enables kaserver synchronization when a srvtab is requested with -S. Instead, it just extracts the DES key from the keytab and writes it to a srvtab. It no longer forces the kvno of the srvtab to 0 (a Stanford-specific action) and instead preserves the kvno from the key in the keytab. This should now do the right thing for sites that use a KDC that serves both Kerberos v4 and Kerberos v5 from the same database. --- NEWS | 8 ++++++++ TODO | 10 ---------- client/keytab.c | 38 +----------------------------------- client/srvtab.c | 8 ++------ client/wallet.pod | 44 ++++++++++++----------------------------- tests/client/basic-t.in | 38 ++++++++---------------------------- tests/data/cmd-fake | 51 +----------------------------------------------- tests/data/fake-srvtab | Bin 47 -> 50 bytes 8 files changed, 33 insertions(+), 164 deletions(-) (limited to 'tests') diff --git a/NEWS b/NEWS index 60c0945..f8bc57b 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,14 @@ wallet 0.10 (unreleased) Remove the kasetkey client for setting keys in an AFS kaserver. + The wallet client no longer enables kaserver synchronization when a + srvtab is requested with -S. Instead, it just extracts the DES key + from the keytab and writes it to a srvtab. It no longer forces the + kvno of the srvtab to 0 (a Stanford-specific action) and instead + preserves the kvno from the key in the keytab. This should now do the + right thing for sites that use a KDC that serves both Kerberos v4 and + Kerberos v5 from the same database. + Correctly handle storing of data that begins with a dash and don't parse it as an argument to wallet-backend. diff --git a/TODO b/TODO index 7448019..1b1bd78 100644 --- a/TODO +++ b/TODO @@ -67,16 +67,6 @@ Release 1.0: an ACL without having to write it into the database. Redo default ACL creation using that functionality. -* The wallet client currently sets sync kaserver whenever writing a keytab - to a srvtab. This is correct for sites using kaserver and wrong for - everyone else. Remove or rethink this once Stanford's kaserver - migration is over. - -* The wallet client currently hard-codes a kvno of 0 in srvtabs, which is - correct for how kasetkey works but probably isn't correct for people - using Heimdal or MIT to serve both K4 and K5 from the same KDC. Rethink - once Stanford's kaserver migration is over. - * Add a hook to enforce ACL naming standards. Future work: diff --git a/client/keytab.c b/client/keytab.c index bdd0134..393ce3c 100644 --- a/client/keytab.c +++ b/client/keytab.c @@ -2,7 +2,7 @@ * Implementation of keytab handling for the wallet client. * * Written by Russ Allbery - * Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -63,39 +63,6 @@ merge_keytab(krb5_context ctx, const char *newfile, const char *file) } -/* - * Configure a given keytab to be synchronized with an AFS kaserver if it - * isn't already. Returns true on success, false on failure. - */ -static int -set_sync(struct remctl *r, const char *type, const char *name) -{ - const char *command[7]; - char *data = NULL; - size_t length = 0; - int status; - - command[0] = type; - command[1] = "getattr"; - command[2] = "keytab"; - command[3] = name; - command[4] = "sync"; - command[5] = NULL; - status = run_command(r, command, &data, &length); - if (status != 0) - return 0; - if (data == NULL || strstr(data, "kaserver\n") == NULL) { - command[1] = "setattr"; - command[5] = "kaserver"; - command[6] = NULL; - status = run_command(r, command, NULL, NULL); - if (status != 0) - return 0; - } - return 1; -} - - /* * Given a remctl object, the Kerberos context, the name of a keytab object, * and a file name, call the correct wallet commands to download a keytab and @@ -111,9 +78,6 @@ get_keytab(struct remctl *r, krb5_context ctx, const char *type, size_t length = 0; int status; - if (srvtab != NULL) - if (!set_sync(r, type, name)) - return 255; command[0] = type; command[1] = "get"; command[2] = "keytab"; diff --git a/client/srvtab.c b/client/srvtab.c index a01026e..5b52955 100644 --- a/client/srvtab.c +++ b/client/srvtab.c @@ -2,7 +2,7 @@ * Implementation of srvtab handling for the wallet client. * * Written by Russ Allbery - * Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -28,10 +28,6 @@ * keytab and write it to the newly created srvtab file as a srvtab. Convert * the principal from Kerberos v5 form to Kerberos v4 form. * - * We always force the kvno to 0 for the srvtab. This works with how the - * wallet synchronizes keys with kasetkey, even though it's not particularly - * correct. - * * On any failure, print an error message to standard error and then exit. */ void @@ -84,7 +80,7 @@ write_srvtab(krb5_context ctx, const char *srvtab, const char *principal, strcpy(data + length, realm); length += strlen(realm); data[length++] = '\0'; - data[length++] = '\0'; + data[length++] = (unsigned char) entry.vno; #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK memcpy(data + length, entry.keyblock.keyvalue.data, 8); #else diff --git a/client/wallet.pod b/client/wallet.pod index 657929b..6451e72 100644 --- a/client/wallet.pod +++ b/client/wallet.pod @@ -114,9 +114,19 @@ C object, and must be used in conjunction with the B<-f> flag. After the keytab is saved to the file specified by B<-f>, the DES key for that principal will be extracted and written as a Kerberos v4 srvtab to the file I. Any existing contents of I will be -destroyed. For more information on how the principal is converted to -Kerberos v4, see the description of the B attribute under -L. +destroyed. + +The Kerberos v4 principal name will be generated from the Kerberos v5 +principal name using the krb5_524_conv_principal() function of the +Kerberos libraries. See its documentation for more information, but +briefly (and in the absence of special configuration), the Kerberos v4 +principal name will be the same as the Kerberos v5 principal name except +that the components are separated by C<.> instead of C; the second +component is truncated after the first C<.> if the first component is one +of the recognized host-based principals (generally C, C, +C, or C); and the first component is C if the Kerberos v5 +principal component is C. The principal name must not contain more +than two components. =item B<-s> I @@ -377,34 +387,6 @@ Keytabs retrieved with C set will contain all keys present in the KDC for that Kerberos principal and therefore may contain different enctypes than those requested by this attribute. -=item sync - -Sets the external systems to which the key of a given principal is -synchronized. The only supported value for this attribute is C, -which says to synchronize the key with an AFS Kerberos v4 kaserver. - -If this attribute is set on a keytab, whenever the C command is run -for that keytab, the DES key will be extracted from that keytab and set in -the configured AFS kaserver. If the B<-S> option is given to the -B client, the srvtab corresponding to the keytab will be written -to the file specified with that option. The Kerberos v4 principal name -will be the same as the Kerberos v5 principal name except that the -components are separated by C<.> instead of C; the second component is -truncated after the first C<.> if the first component is one of C, -C, C, C, or C; and the first component is C -if the Kerberos v5 principal component is C. The principal name -must not contain more than two components. - -If this attribute is set, calling C will also destroy the -principal from the AFS kaserver, with a principal mapping determined as -above. - -The realm of the srvtab defaults to the same realm as the keytab. You can -change this by setting the v4_realm configuration option in the [realms] -section of krb5.conf for the local realm. The keytab must be for a -principal in the default local realm for the B<-S> option to work -correctly. - =back =head1 CONFIGURATION diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 05a7abe..752e5d9 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -3,7 +3,8 @@ # Test suite for the wallet command-line client. # # Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2008, 2010 +# Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -46,10 +47,10 @@ if [ ! -f data/pid ] ; then exit 1 fi -# We need a modified krb5.conf file for the srvtab test to work, since we need -# to add a v4_realm setting for the test-k5.stanford.edu realm that the keytab -# is for. Despite all the Stanford hard-coding, this test isn't -# Stanford-specific. It just matches the data files shipped with the package. +# We need a modified krb5.conf file to test wallet configuration settings in +# krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't +# Stanford-specific; it just matches the files that are distributed with the +# package. krb5conf= for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do if [ -r "$p" ] ; then @@ -63,7 +64,7 @@ for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do [realms] test-k5.stanford.edu = { - v4_realm = TEST.STANFORD.EDU + v4_realm = test-k5.stanford.edu } EOF KRB5_CONFIG="./krb5.conf" @@ -77,8 +78,7 @@ if [ -z "$krb5conf" ] ; then fi # Make sure everything's clean. -rm -f output output.bak keytab keytab.bak srvtab srvtab.bak sync-kaserver \ - autocreated +rm -f output output.bak keytab keytab.bak srvtab srvtab.bak autocreated # Now, we can finally run our tests. First, basic operations. runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ @@ -139,11 +139,6 @@ if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then else printcount "not ok" fi -if [ ! -f sync-kaserver ] ; then - printcount "ok" -else - printcount "not ok" -fi # Test srvtab support. runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab @@ -153,23 +148,12 @@ else printcount "not ok" fi rm keytab -if [ -f sync-kaserver ] ; then - printcount "ok" -else - printcount "not ok" -fi runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then printcount "ok" else printcount "not ok" fi -if [ -f sync-kaserver ] ; then - printcount "ok" - rm sync-kaserver -else - printcount "not ok" -fi if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then printcount "ok" else @@ -196,12 +180,6 @@ fi # Test srvtab download into a merged keytab with an older version. cp data/fake-keytab-old keytab runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if [ -f sync-kaserver ] ; then - printcount "ok" - rm sync-kaserver -else - printcount "not ok" -fi if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then printcount "ok" else diff --git a/tests/data/cmd-fake b/tests/data/cmd-fake index 9c9e38c..199bd57 100755 --- a/tests/data/cmd-fake +++ b/tests/data/cmd-fake @@ -4,7 +4,7 @@ # the client test suite. It doesn't test any of the wallet server code. # # Written by Russ Allbery -# Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University # See LICENSE for licensing terms. command="$1" @@ -17,55 +17,6 @@ if [ "$type" != "keytab" ] && [ "$type" != "file" ] ; then fi case "$command" in -getattr) - if [ -n "$3" ] ; then - echo "Too many arguments" >&2 - exit 1 - fi - if [ "$type" != "keytab" ] || [ "$2" != sync ] ; then - echo "Unknown attribute $2" >&2 - exit 1 - fi - case "$1" in - service/fake-srvtab) - if [ -f sync-kaserver ] ; then - echo "kaserver" - fi - ;; - *) - echo "Looking at sync attribute of wrong keytab" >&2 - exit 1 - ;; - esac - ;; -setattr) - if [ -n "$4" ] ; then - echo "Too many arguments" >&2 - exit 1 - fi - if [ "$type" != "keytab" ] || [ "$2" != sync ] ; then - echo "Unknown attribute $2" >&2 - exit 1 - fi - case "$1" in - service/fake-srvtab) - if [ "$3" = "kaserver" ] ; then - touch sync-kaserver - else - if [ "$3" = "" ] ; then - rm sync-kaserver - else - echo "Invalid attribute value $3" >&2 - exit 1 - fi - fi - ;; - *) - echo "Looking at sync attribute of wrong keytab" >&2 - exit 1 - ;; - esac - ;; check) if [ -n "$2" ] ; then echo "Too many arguments" >&2 diff --git a/tests/data/fake-srvtab b/tests/data/fake-srvtab index 3c0ec65..f454af2 100644 Binary files a/tests/data/fake-srvtab and b/tests/data/fake-srvtab differ -- cgit v1.2.3 From fecab76139894250a57fa3761b3b90944c8cfa9d Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 13:43:18 -0800 Subject: Remove the test suite for kasetkey --- configure.ac | 1 - tests/TESTS | 1 - tests/kasetkey/basic-t.in | 127 ---------------------------------------------- 3 files changed, 129 deletions(-) delete mode 100644 tests/kasetkey/basic-t.in (limited to 'tests') diff --git a/configure.ac b/configure.ac index de998c0..8d00229 100644 --- a/configure.ac +++ b/configure.ac @@ -63,7 +63,6 @@ AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) AC_CONFIG_FILES([tests/client/pod-t], [chmod +x tests/client/pod-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) AC_CONFIG_FILES([tests/data/cmd-wrapper], [chmod +x tests/data/cmd-wrapper]) -AC_CONFIG_FILES([tests/kasetkey/basic-t], [chmod +x tests/kasetkey/basic-t]) AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) diff --git a/tests/TESTS b/tests/TESTS index c94cce0..a446643 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -2,7 +2,6 @@ client/basic client/full client/pod client/prompt -kasetkey/basic portable/asprintf portable/snprintf portable/strlcat diff --git a/tests/kasetkey/basic-t.in b/tests/kasetkey/basic-t.in deleted file mode 100644 index bb086d6..0000000 --- a/tests/kasetkey/basic-t.in +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for basic kasetkey functionality. -# -# We only test creation (with a random key), deletion, enable, disable, and -# examine. That's enough to verify that kasetkey is basically working, and -# since AFS kaservers are becoming scarce, it's probably not worth the effort -# to do anything more comprehensive. -# -# We do test creation of a principal with a known key given a srvtab from -# inside the wallet server test suite already. -# -# Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -BEGIN { our $total = 27 } -use Test::More tests => $total; - -use lib '@abs_top_builddir@/perl/blib/lib'; -use lib '@abs_top_srcdir@/perl/t/lib'; -use Util; - -# Global variables used for the kasetkey configuration. -our $ADMIN; -our $SRVTAB; - -# Make a call to the kasetkey client and returns the standard output, the -# standard error, and the exit status as a list. -sub kasetkey { - my @command = @_; - my $pid = fork; - if (not defined $pid) { - die "cannot fork: $!\n"; - } elsif ($pid == 0) { - open (STDOUT, '>', 'kasetkey.out') - or die "cannot create kasetkey.out: $!\n"; - open (STDERR, '>', 'kasetkey.err') - or die "cannot create kasetkey.err: $!\n"; - exec ('@abs_top_builddir@/kasetkey/kasetkey', '-a', $ADMIN, - '-k', $SRVTAB, @command) - or die "cannot run @abs_top_builddir@/kasetkey/kasetky: $!\n"; - } else { - waitpid ($pid, 0); - } - my $status = ($? >> 8); - local $/; - open (OUT, '<', 'kasetkey.out') or die "cannot open kasetkey.out: $!\n"; - my $output = ; - close OUT; - open (ERR, '<', 'kasetkey.err') or die "cannot open kasetkey.err: $!\n"; - my $error = ; - close ERR; - unlink ('kasetkey.out', 'kasetkey.err'); - return ($output, $error, $status); -} - -SKIP: { - skip 'no AFS kaserver configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.srvtab'; - skip 'no AFS kaserver support', $total, - unless -x '@abs_top_builddir@/kasetkey/kasetkey'; - - # Set up the configuration. - $ADMIN = contents ('@abs_top_builddir@/tests/data/test.admin'); - $SRVTAB = '@abs_top_builddir@/tests/data/test.srvtab'; - my $realm = $ADMIN; - $realm =~ s/^[^\@]+\@//; - my $principal = "wallet.one\@$realm"; - - # Now we can start manipulating principals. Test examine and create. - my ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 1, 'Examining a non-existent principal fails'); - is ($out, '', ' with no output'); - is ($err, "no such entry in the database\n", ' and the right error'); - ($out, $err, $status) = kasetkey ('-s', $principal, '-r'); - is ($status, 0, 'Creating a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, 'Examining a principal succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - my $shortadmin = $ADMIN; - $shortadmin =~ s/\@.*//; - my $enabled = <<"EOE"; -status: enabled -account expiration: never -password last changed: DATE -modification time: DATE -modified by: $shortadmin -EOE - is ($out, $enabled, ' with the right output'); - is ($err, '', ' and no error'); - - # Test enable and disable. - ($out, $err, $status) = kasetkey ('-s', $principal, '-n'); - is ($status, 0, 'Disabling a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, ' and examining it still succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - my $disabled = $enabled; - $disabled =~ s/enabled/disabled/; - is ($out, $disabled, ' with the right output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-s', $principal, '-t'); - is ($status, 0, 'Enabling a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, ' and examining it still succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - is ($out, $enabled, ' with the right output'); - is ($err, '', ' and no error'); - - # Test deletion. - ($out, $err, $status) = kasetkey ('-D', $principal); - is ($status, 0, 'Deleting the principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 1, ' and now examining it fails'); - is ($out, '', ' with no output'); - is ($err, "no such entry in the database\n", ' and the right error'); -} -- cgit v1.2.3 From b7af0beced6e891a652d4caf36a2ec498090a955 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 13:51:59 -0800 Subject: Update test count for tests/client/basic --- tests/client/basic-t.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 752e5d9..96b165e 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -12,7 +12,7 @@ . "@abs_top_srcdir@/tests/libtest.sh" # Print the number of tests. -total=35 +total=31 count=1 echo "$total" -- cgit v1.2.3 From c02942ddc12408f0e5b9d828cddf240519d1fe93 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 18:40:22 -0800 Subject: Update to C TAP Harness 1.1 and rra-c-util 3.0 tests * Update portable and util tests for C TAP Harness 1.1. * Remove the need for Autoconf substitution in test programs. * Support running a single test program with runtests -o. * Properly handle test cases that are skipped in their entirety. * Much improved C TAP library more closely matching Test::More. Rewrite client/basic-t to use the new test library functions and my current test case coding style. --- .gitignore | 4 +- Makefile.am | 52 ++++--- NEWS | 8 + README | 8 +- configure.ac | 1 - tests/TESTS | 3 + tests/client/basic-t.in | 220 +++++++++----------------- tests/libtest.c | 203 ------------------------ tests/libtest.h | 69 --------- tests/libtest.sh | 80 ---------- tests/portable/asprintf-t.c | 34 +++-- tests/portable/mkstemp-t.c | 73 +++++++++ tests/portable/mkstemp.c | 2 + tests/portable/setenv-t.c | 46 ++++++ tests/portable/setenv.c | 2 + tests/portable/snprintf-t.c | 123 +++++++-------- tests/portable/strlcat-t.c | 84 +++++----- tests/portable/strlcpy-t.c | 73 +++++---- tests/runtests.c | 327 ++++++++++++++++++++++++++++++--------- tests/tap/basic.c | 356 +++++++++++++++++++++++++++++++++++++++++++ tests/tap/basic.h | 98 ++++++++++++ tests/tap/kerberos.c | 164 ++++++++++++++++++++ tests/tap/kerberos.h | 32 ++++ tests/tap/kerberos.sh | 48 ++++++ tests/tap/libtap.sh | 148 ++++++++++++++++++ tests/tap/messages.c | 80 ++++++++++ tests/tap/messages.h | 35 +++++ tests/tap/process.c | 100 ++++++++++++ tests/tap/process.h | 37 +++++ tests/tap/remctl.sh | 46 ++++++ tests/util/concat-t.c | 60 +++----- tests/util/messages-krb5-t.c | 99 ++++++++++++ tests/util/messages-t.c | 201 +++++++----------------- tests/util/xmalloc-t | 127 +++++++++++++++ tests/util/xmalloc-t.in | 126 --------------- tests/util/xmalloc.c | 85 ++++++----- 36 files changed, 2138 insertions(+), 1116 deletions(-) delete mode 100644 tests/libtest.c delete mode 100644 tests/libtest.h delete mode 100644 tests/libtest.sh create mode 100644 tests/portable/mkstemp-t.c create mode 100644 tests/portable/mkstemp.c create mode 100644 tests/portable/setenv-t.c create mode 100644 tests/portable/setenv.c create mode 100644 tests/tap/basic.c create mode 100644 tests/tap/basic.h create mode 100644 tests/tap/kerberos.c create mode 100644 tests/tap/kerberos.h create mode 100644 tests/tap/kerberos.sh create mode 100644 tests/tap/libtap.sh create mode 100644 tests/tap/messages.c create mode 100644 tests/tap/messages.h create mode 100644 tests/tap/process.c create mode 100644 tests/tap/process.h create mode 100644 tests/tap/remctl.sh create mode 100644 tests/util/messages-krb5-t.c create mode 100755 tests/util/xmalloc-t delete mode 100644 tests/util/xmalloc-t.in (limited to 'tests') diff --git a/.gitignore b/.gitignore index 4599484..09ae109 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ /tests/data/test.krbtype /tests/kasetkey/basic-t /tests/portable/asprintf-t +/tests/portable/mkstemp-t +/tests/portable/setenv-t /tests/portable/snprintf-t /tests/portable/strlcat-t /tests/portable/strlcpy-t @@ -37,9 +39,9 @@ /tests/server/keytab-t /tests/server/pod-t /tests/util/concat-t +/tests/util/messages-krb5-t /tests/util/messages-t /tests/util/xmalloc -/tests/util/xmalloc-t /wallet-*.tar.gz /stamp-h1 .deps diff --git a/Makefile.am b/Makefile.am index 27a6e39..056229b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -117,33 +117,45 @@ distclean-local: fi # The bits below are for the test suite, not for the main package. -check_PROGRAMS = tests/runtests tests/portable/asprintf-t \ - tests/portable/snprintf-t tests/portable/strlcat-t \ - tests/portable/strlcpy-t tests/util/concat-t tests/util/messages-t \ - tests/util/xmalloc -check_LIBRARIES = tests/libtest.a -tests_libtest_a_SOURCES = tests/libtest.c tests/libtest.h +check_PROGRAMS = tests/runtests tests/portable/asprintf-t \ + tests/portable/mkstemp-t tests/portable/setenv-t \ + tests/portable/snprintf-t tests/portable/strlcat-t \ + tests/portable/strlcpy-t tests/util/concat-t \ + tests/util/messages-krb5-t tests/util/messages-t tests/util/xmalloc +tests_runtests_CPPFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \ + -DBUILD='"$(abs_top_builddir)/tests"' +check_LIBRARIES = tests/tap/libtap.a +tests_tap_libtap_a_CPPFLAGS = -I$(abs_top_srcdir)/tests $(KRB5_CPPFLAGS) +tests_tap_libtap_a_SOURCES = tests/tap/basic.c tests/tap/basic.h \ + tests/tap/kerberos.c tests/tap/kerberos.h tests/tap/messages.c \ + tests/tap/messages.h tests/tap/process.c tests/tap/process.h # All of the test programs. tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \ - tests/portable/asprintf.c -tests_portable_asprintf_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/asprintf.c +tests_portable_asprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_portable_mkstemp_t_SOURCES = tests/portable/mkstemp-t.c \ + tests/portable/mkstemp.c +tests_portable_mkstemp_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_portable_setenv_t_SOURCES = tests/portable/setenv-t.c \ + tests/portable/setenv.c +tests_portable_setenv_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \ - tests/portable/snprintf.c -tests_portable_snprintf_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/snprintf.c +tests_portable_snprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_strlcat_t_SOURCES = tests/portable/strlcat-t.c \ - tests/portable/strlcat.c -tests_portable_strlcat_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/strlcat.c +tests_portable_strlcat_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_strlcpy_t_SOURCES = tests/portable/strlcpy-t.c \ - tests/portable/strlcpy.c -tests_portable_strlcpy_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a -tests_util_concat_t_LDADD = tests/libtest.a util/libutil.a \ + tests/portable/strlcpy.c +tests_portable_strlcpy_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_util_concat_t_LDADD = tests/tap/libtap.a util/libutil.a \ portable/libportable.a -tests_util_messages_t_LDADD = tests/libtest.a util/libutil.a \ +tests_util_messages_krb5_t_CPPFLAGS = $(KRB5_CPPFLAGS) +tests_util_messages_krb5_t_LDFLAGS = $(KRB5_LDFLAGS) +tests_util_messages_krb5_t_LDADD = tests/tap/libtap.a util/libutil.a \ + portable/libportable.a $(KRB5_LIBS) +tests_util_messages_t_LDADD = tests/tap/libtap.a util/libutil.a \ portable/libportable.a tests_util_xmalloc_LDADD = util/libutil.a portable/libportable.a diff --git a/NEWS b/NEWS index 1d3a5e3..96962f8 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,14 @@ wallet 0.10 (unreleased) * Use AC_TYPE_LONG_LONG_INT instead of AC_CHECK_TYPES([long long]). * Provide a proper bool type with Sun Studio 12 on Solaris 10. * Break util/util.h into separate header files per module. + * Update portable and util tests for C TAP Harness 1.1. + + Update to C TAP Harness 1.1: + + * Remove the need for Autoconf substitution in test programs. + * Support running a single test program with runtests -o. + * Properly handle test cases that are skipped in their entirety. + * Much improved C TAP library more closely matching Test::More. wallet 0.9 (2008-04-24) diff --git a/README b/README index 7302c06..eb9b39c 100644 --- a/README +++ b/README @@ -233,8 +233,12 @@ TESTING not available, but this has not yet been fully tested in all of its possible permutations. - If a test case fails, please run that individual test program directly - and send me the output when reporting the problem. + If a test fails, you can run a single test with verbose output via: + + tests/runtests -o + + Do this instead of running the test program directly since it will + ensure that necessary environment variables are set up. CONFIGURATION diff --git a/configure.ac b/configure.ac index f66a682..0330aa9 100644 --- a/configure.ac +++ b/configure.ac @@ -70,5 +70,4 @@ AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) AC_CONFIG_FILES([tests/server/pod-t], [chmod +x tests/server/pod-t]) -AC_CONFIG_FILES([tests/util/xmalloc-t], [chmod +x tests/util/xmalloc-t]) AC_OUTPUT diff --git a/tests/TESTS b/tests/TESTS index a446643..ac6fd82 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -3,6 +3,8 @@ client/full client/pod client/prompt portable/asprintf +portable/mkstemp +portable/setenv portable/snprintf portable/strlcat portable/strlcpy @@ -12,4 +14,5 @@ server/keytab server/pod util/concat util/messages +util/messages-krb5 util/xmalloc diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 96b165e..1dbc0b9 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -9,43 +9,10 @@ # See LICENSE for licensing terms. # Load the test library. -. "@abs_top_srcdir@/tests/libtest.sh" - -# Print the number of tests. -total=31 -count=1 -echo "$total" - -# Find the client program. -chdir_data '../client/wallet' -if [ ! -f 'data/test.keytab' ] || [ -z '@REMCTLD@' ] ; then - skip 1 "$total" 'no Kerberos configuration' - exit 0 -fi -wallet='../client/wallet' - -# Start the remctld daemon and wait for it to start. -principal=`cat data/test.principal` -rm -f data/pid -( @REMCTLD@ -m -p 14373 -s "$principal" -P data/pid -f data/basic.conf \ - -S -F -k data/test.keytab &) -KRB5CCNAME=data/test.cache; export KRB5CCNAME -kinit -k -t data/test.keytab "$principal" > /dev/null 2>&1 -if [ $? != 0 ] ; then - kinit -t data/test.keytab "$principal" > /dev/null 2>&1 -fi -if [ $? != 0 ] ; then - kinit -T /bin/true -k -K data/test.keytab "$principal" > /dev/null 2>&1 -fi -if [ $? != 0 ] ; then - echo 'Unable to obtain Kerberos tickets' >&2 - exit 1 -fi -[ -f data/pid ] || sleep 1 -if [ ! -f data/pid ] ; then - echo 'remctld did not start' >&2 - exit 1 -fi +. "$SOURCE/tap/libtap.sh" +. "$SOURCE/tap/kerberos.sh" +. "$SOURCE/tap/remctl.sh" +cd "$BUILD" # We need a modified krb5.conf file to test wallet configuration settings in # krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't @@ -73,43 +40,39 @@ EOF fi done if [ -z "$krb5conf" ] ; then - echo 'No krb5.conf found -- put one in tests/data/krb5.conf' >&2 - exit 1 + skip_all 'no krb5.conf found, put one in tests/data/krb5.conf' +fi + +# Test setup. +kerberos_setup +if [ $? != 0 ] ; then + skip_all 'Kerberos tests not configured' +elif [ -z '@REMCTLD@' ] ; then + skip_all 'No remctld found' +else + plan 34 fi +remctld_start '@REMCTLD@' "$SOURCE/data/basic.conf" +wallet="$BUILD/../client/wallet" # Make sure everything's clean. rm -f output output.bak keytab keytab.bak srvtab srvtab.bak autocreated # Now, we can finally run our tests. First, basic operations. -runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ - -f output get file fake-test -if cmp output data/fake-data >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if [ -f output.bak ] || [ -f output.new ] ; then - printcount "not ok" -else - printcount "ok" -fi -if [ -f autocreated ] ; then - printcount "ok" -else - printcount "not ok" -fi -runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ - -f output get file fake-test -if cmp output data/fake-data >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if [ -f output.new ] || [ ! -f output.bak ] ; then - printcount "not ok" -else - printcount "ok" -fi +ok_program 'get file' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet -f output \ + get file fake-test +ok '...and file is correct' cmp output data/fake-data +ok '...and no backup files' [ ! -f output.bak ] +ok '...and no new files' [ ! -f output.new ] +ok '...and we tried autocreation' [ -f autocreated ] +ok_program 'get file again' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet -f output \ + get file fake-test +ok '...and file is correct' cmp output data/fake-data +ok '...and now there is a backup file' [ -f output.bak ] +ok '...which has the right contents' cmp output.bak data/fake-data +ok '...but there is no new file' [ ! -f output.new ] # Now, append configuration to krb5.conf and test getting configuration from # there. @@ -123,116 +86,79 @@ cat >> krb5.conf </dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'get file with configuration' 0 '' \ + "$wallet" -f output get file fake-test +ok '...and file is correct' cmp output data/fake-data rm -f output output.bak # Test keytab support. -runsuccess "" "$wallet" -f keytab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" - rm keytab -else - printcount "not ok" -fi +ok_program 'get keytab' 0 '' \ + "$wallet" -f keytab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +rm -f keytab # Test srvtab support. -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -rm keytab -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if cmp srvtab.bak data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'get srvtab' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +rm -f keytab +ok_program 'get srvtab again' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +ok '...and srvtab is correct' cmp srvtab data/fake-srvtab +ok '...and srvtab backup is correct' cmp srvtab.bak data/fake-srvtab rm -f srvtab srvtab.bak # Test keytab merging. -runsuccess "" "$wallet" -f keytab get keytab service/fake-keytab +ok_program 'keytab merging' 0 '' \ + "$wallet" -f keytab get keytab service/fake-keytab (klist -keK keytab 2>&1) | sed '/Keytab name:/d' > klist-seen (klist -keK data/fake-keytab-merge 2>&1) | sed '/Keytab name:/d' > klist-good -if cmp klist-seen klist-good >/dev/null 2>&1 ; then - printcount "ok" - rm -f keytab klist-seen klist-good -else - printcount "not ok" -fi +ok '...and the merged keytab is correct' cmp klist-seen klist-good +rm -f keytab klist-seen klist-good # Test srvtab download into a merged keytab with an older version. cp data/fake-keytab-old keytab -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'keytab merging with srvtab creation' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and the srvtab is correct' cmp srvtab data/fake-srvtab rm -f keytab srvtab # Test store from standard input. -echo "This is a test of store" | runsuccess "" "$wallet" store file fake-test -count=`expr $count + 1` +echo "This is a test of store" > input +ok_program 'store from stdin' 0 '' "$wallet" store file fake-test < input +rm -f input echo "file fake-test" > store-correct echo "This is a test of store" >> store-correct -if cmp store-output store-correct >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" - echo == store-output == - cat store-output - echo == store-correct == - cat store-correct -fi +ok '...and the correct data was stored' diff store-output store-correct rm -f store-output store-correct # Test store with -f. echo "This is more store input" > store-input echo "file fake-test" > store-correct cat store-input >> store-correct -runsuccess "" "$wallet" -f store-input store file fake-test -if cmp store-output store-correct >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'store from a file' 0 '' \ + "$wallet" -f store-input store file fake-test +ok '...and the correct data was stored' cmp store-output store-correct rm -f store-input store-output store-correct # Test various other client functions and errors. -runsuccess "This is a fake keytab." "$wallet" get keytab service/fake-output -runsuccess "Some stuff about file fake-test" \ +ok_program 'get output to stdout' 0 'This is a fake keytab.' \ + "$wallet" get keytab service/fake-output +ok_program 'show output' 0 'Some stuff about file fake-test' \ "$wallet" show file fake-test -runfailure 1 "wallet: Unknown object type srvtab" \ +ok_program 'unknown object type' 1 'wallet: Unknown object type srvtab' \ "$wallet" get srvtab service/fake-test -runfailure 1 "wallet: Unknown keytab service/unknown" \ +ok_program 'unknown keytab name in show' 1 \ + 'wallet: Unknown keytab service/unknown' \ "$wallet" show keytab service/unknown -runfailure 1 "wallet: Unknown keytab service/unknown" \ +ok_program 'unknown keytab name in get' 1 \ + 'wallet: Unknown keytab service/unknown' \ "$wallet" get keytab service/unknown -runsuccess "Expiration date of keytab service/fake-test" \ +ok_program 'expiration date' 0 'Expiration date of keytab service/fake-test' \ "$wallet" expires keytab service/fake-test # Clean up. -KRB5_CONFIG= -rm krb5.conf -rm -f autocreated data/test.cache -if [ -f data/pid ] ; then - kill `cat data/pid` - rm -f data/pid -fi +rm -f autocreated krb5.conf +remctld_stop +kerberos_cleanup diff --git a/tests/libtest.c b/tests/libtest.c deleted file mode 100644 index bddaf91..0000000 --- a/tests/libtest.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Some utility routines for writing tests. - * - * Herein are a variety of utility routines for writing tests. All routines - * of the form ok*() take a test number and some number of appropriate - * arguments, check to be sure the results match the expected output using the - * arguments, and print out something appropriate for that test number. Other - * utility routines help in constructing more complex tests. - * - * Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz - * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include - -#include -#include - -#include -#include - -/* A global buffer into which message_log_buffer stores error messages. */ -char *errors = NULL; - - -/* - * Initialize things. Turns on line buffering on stdout and then prints out - * the number of tests in the test suite. - */ -void -test_init(int count) -{ - if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) - syswarn("cannot set stdout to line buffered"); - printf("%d\n", count); -} - - -/* - * Takes a boolean success value and assumes the test passes if that value - * is true and fails if that value is false. - */ -void -ok(int n, int success) -{ - printf("%sok %d\n", success ? "" : "not ", n); -} - - -/* - * Takes an expected integer and a seen integer and assumes the test passes - * if those two numbers match. - */ -void -ok_int(int n, int wanted, int seen) -{ - if (wanted == seen) - printf("ok %d\n", n); - else - printf("not ok %d\n wanted: %d\n seen: %d\n", n, wanted, seen); -} - - -/* - * Takes a string and what the string should be, and assumes the test passes - * if those strings match (using strcmp). - */ -void -ok_string(int n, const char *wanted, const char *seen) -{ - if (wanted == NULL) - wanted = "(null)"; - if (seen == NULL) - seen = "(null)"; - if (strcmp(wanted, seen) != 0) - printf("not ok %d\n wanted: %s\n seen: %s\n", n, wanted, seen); - else - printf("ok %d\n", n); -} - - -/* - * Takes an expected integer and a seen integer and assumes the test passes if - * those two numbers match. - */ -void -ok_double(int n, double wanted, double seen) -{ - if (wanted == seen) - printf("ok %d\n", n); - else - printf("not ok %d\n wanted: %g\n seen: %g\n", n, wanted, seen); -} - - -/* - * Skip a test. - */ -void -skip(int n, const char *reason) -{ - printf("ok %d # skip", n); - if (reason != NULL) - printf(" - %s", reason); - putchar('\n'); -} - - -/* - * Report the same status on the next count tests. - */ -void -ok_block(int n, int count, int status) -{ - int i; - - for (i = 0; i < count; i++) - ok(n++, status); -} - - -/* - * Skip the next count tests. - */ -void -skip_block(int n, int count, const char *reason) -{ - int i; - - for (i = 0; i < count; i++) - skip(n++, reason); -} - - -/* - * An error handler that appends all errors to the errors global. Used by - * error_capture. - */ -static void -message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) -{ - char *message; - - message = xmalloc(len + 1); - vsnprintf(message, len + 1, fmt, args); - if (errors == NULL) { - errors = concat(message, "\n", (char *) 0); - } else { - char *new_errors; - - new_errors = concat(errors, message, "\n", (char *) 0); - free(errors); - errors = new_errors; - } - free(message); -} - - -/* - * Turn on the capturing of errors. Errors will be stored in the global - * errors variable where they can be checked by the test suite. Capturing is - * turned off with errors_uncapture. - */ -void -errors_capture(void) -{ - if (errors != NULL) { - free(errors); - errors = NULL; - } - message_handlers_warn(1, message_log_buffer); - message_handlers_notice(1, message_log_buffer); -} - - -/* - * Turn off the capturing of errors again. - */ -void -errors_uncapture(void) -{ - message_handlers_warn(1, message_log_stderr); - message_handlers_notice(1, message_log_stdout); -} diff --git a/tests/libtest.h b/tests/libtest.h deleted file mode 100644 index ad4f591..0000000 --- a/tests/libtest.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Some utility routines for writing tests. - * - * Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - * by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - * 2002, 2003 by The Internet Software Consortium and Rich Salz - * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBTEST_H -#define LIBTEST_H 1 - -#include -#include - -/* - * Used for iterating through arrays. ARRAY_SIZE returns the number of - * elements in the array (useful for a < upper bound in a for loop) and - * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it - * legal to refer to such a pointer as long as it's never dereferenced). - */ -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) -#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) - -/* A global buffer into which errors_capture stores errors. */ -extern char *errors; - -BEGIN_DECLS - -void ok(int n, int success); -void ok_int(int n, int wanted, int seen); -void ok_double(int n, double wanted, double seen); -void ok_string(int n, const char *wanted, const char *seen); -void skip(int n, const char *reason); - -/* Report the same status on, or skip, the next count tests. */ -void ok_block(int n, int count, int success); -void skip_block(int n, int count, const char *reason); - -/* Print out the number of tests and set standard output to line buffered. */ -void test_init(int count); - -/* - * Turn on capturing of errors with errors_capture. Errors reported by warn - * will be stored in the global errors variable. Turn this off again with - * errors_uncapture. Caller is responsible for freeing errors when done. - */ -void errors_capture(void); -void errors_uncapture(void); - -END_DECLS - -#endif /* LIBTEST_H */ diff --git a/tests/libtest.sh b/tests/libtest.sh deleted file mode 100644 index 74f5ee6..0000000 --- a/tests/libtest.sh +++ /dev/null @@ -1,80 +0,0 @@ -# Shell function library for test cases. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -# The count starts at 1 and is updated each time ok is printed. printcount -# takes "ok" or "not ok". -count=1 -printcount () { - echo "$1 $count $2" - count=`expr $count + 1` -} - -# Run a program expected to succeed, and print ok if it does and produces -# the correct output. Takes the output as the first argument, the command to -# run as the second argument, and then all subsequent arguments are arguments -# to the command. -runsuccess () { - w_output="$1" - shift - output=`"$@" 2>&1` - status=$? - if [ $status = 0 ] && [ x"$output" = x"$w_output" ] ; then - printcount 'ok' - else - printcount 'not ok' - echo " saw: $output" - echo " not: $w_output" - fi -} - -# Run a program expected to fail and make sure it fails with the correct exit -# status and the correct failure message. Takes the expected status, the -# expected output, and then everything else is the command and arguments. -# Strip the second colon and everything after it off the error message since -# it's system-specific. -runfailure () { - w_status="$1" - shift - w_output="$1" - shift - output=`"$@" 2>&1` - status=$? - output=`echo "$output" | sed 's/\(:[^:]*\):.*/\1/'` - if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then - printcount 'ok' - else - printcount 'not ok' - echo " saw: ($status) $output" - echo " not: ($w_status) $w_output" - fi -} - -# Skip tests from $1 to $2 inclusive with reason $3. -skip () { - n="$1" - while [ "$n" -le "$2" ] ; do - echo ok "$n # skip $3" - n=`expr "$n" + 1` - done -} - -# Given a file name or relative file path, try to cd to the correct directory -# so that the relative file path is valid. Exits with an error if that isn't -# possible. -chdir_data () { - if [ -f "../$1" ] ; then - cd .. - else - if [ -f "tests/$1" ] ; then - cd tests - fi - fi - if [ ! -f "$1" ] ; then - echo "Cannot locate $1" >&2 - exit 1 - fi -} diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index 689e7c7..04fbd1b 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -2,7 +2,8 @@ * asprintf and vasprintf test suite. * * Written by Russ Allbery - * Copyright 2006, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2006, 2008, 2009 + * Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -10,9 +11,10 @@ #include #include -#include +#include -int test_asprintf(char **, const char *, ...); +int test_asprintf(char **, const char *, ...) + __attribute__((__format__(printf, 2, 3))); int test_vasprintf(char **, const char *, va_list); static int @@ -32,25 +34,25 @@ main(void) { char *result = NULL; - test_init(12); + plan(12); - ok_int(1, 7, test_asprintf(&result, "%s", "testing")); - ok_string(2, "testing", result); + is_int(7, test_asprintf(&result, "%s", "testing"), "asprintf length"); + is_string("testing", result, "asprintf result"); free(result); - ok(3, 1); - ok_int(4, 0, test_asprintf(&result, "%s", "")); - ok_string(5, "", result); + ok(3, "free asprintf"); + is_int(0, test_asprintf(&result, "%s", ""), "asprintf empty length"); + is_string("", result, "asprintf empty string"); free(result); - ok(6, 1); + ok(6, "free asprintf of empty string"); - ok_int(7, 6, vatest(&result, "%d %s", 2, "test")); - ok_string(8, "2 test", result); + is_int(6, vatest(&result, "%d %s", 2, "test"), "vasprintf length"); + is_string("2 test", result, "vasprintf result"); free(result); - ok(9, 1); - ok_int(10, 0, vatest(&result, "%s", "")); - ok_string(11, "", result); + ok(9, "free vasprintf"); + is_int(0, vatest(&result, "%s", ""), "vasprintf empty length"); + is_string("", result, "vasprintf empty string"); free(result); - ok(12, 1); + ok(12, "free vasprintf of empty string"); return 0; } diff --git a/tests/portable/mkstemp-t.c b/tests/portable/mkstemp-t.c new file mode 100644 index 0000000..54701f7 --- /dev/null +++ b/tests/portable/mkstemp-t.c @@ -0,0 +1,73 @@ +/* + * mkstemp test suite. + * + * Written by Russ Allbery + * Copyright 2002, 2004, 2008, 2009 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include +#include + +#include + +int test_mkstemp(char *template); + +int +main(void) +{ + int fd; + char template[] = "tsXXXXXXX"; + char tooshort[] = "XXXXX"; + char bad1[] = "/foo/barXXXXX"; + char bad2[] = "/foo/barXXXXXX.out"; + char buffer[256]; + struct stat st1, st2; + ssize_t length; + + plan(20); + + /* First, test a few error messages. */ + errno = 0; + is_int(-1, test_mkstemp(tooshort), "too short of template"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("XXXXX", tooshort, "...and template didn't change"); + errno = 0; + is_int(-1, test_mkstemp(bad1), "bad template"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("/foo/barXXXXX", bad1, "...and template didn't change"); + errno = 0; + is_int(-1, test_mkstemp(bad2), "template doesn't end in XXXXXX"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("/foo/barXXXXXX.out", bad2, "...and template didn't change"); + errno = 0; + + /* Now try creating a real file. */ + fd = test_mkstemp(template); + ok(fd >= 0, "mkstemp works with valid template"); + ok(strcmp(template, "tsXXXXXXX") != 0, "...and template changed"); + ok(strncmp(template, "tsX", 3) == 0, "...and didn't touch first X"); + ok(access(template, F_OK) == 0, "...and the file exists"); + + /* Make sure that it's the same file as template refers to now. */ + ok(stat(template, &st1) == 0, "...and stat of template works"); + ok(fstat(fd, &st2) == 0, "...and stat of open file descriptor works"); + ok(st1.st_ino == st2.st_ino, "...and they're the same file"); + unlink(template); + + /* Make sure the open mode is correct. */ + length = strlen(template); + is_int(length, write(fd, template, length), "write to open file works"); + ok(lseek(fd, 0, SEEK_SET) == 0, "...and rewind works"); + is_int(length, read(fd, buffer, length), "...and the data is there"); + buffer[length] = '\0'; + is_string(template, buffer, "...and matches what we wrote"); + close(fd); + + return 0; +} diff --git a/tests/portable/mkstemp.c b/tests/portable/mkstemp.c new file mode 100644 index 0000000..4632d3d --- /dev/null +++ b/tests/portable/mkstemp.c @@ -0,0 +1,2 @@ +#define TESTING 1 +#include diff --git a/tests/portable/setenv-t.c b/tests/portable/setenv-t.c new file mode 100644 index 0000000..5bc59ce --- /dev/null +++ b/tests/portable/setenv-t.c @@ -0,0 +1,46 @@ +/* + * setenv test suite. + * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include + +#include + +int test_setenv(const char *name, const char *value, int overwrite); + +static const char test_var[] = "SETENV_TEST"; +static const char test_value1[] = "Do not taunt Happy Fun Ball."; +static const char test_value2[] = "Do not use Happy Fun Ball on concrete."; + + +int +main(void) +{ + plan(8); + + if (getenv(test_var)) + bail("%s already in the environment!", test_var); + + ok(test_setenv(test_var, test_value1, 0) == 0, "set string 1"); + is_string(test_value1, getenv(test_var), "...and getenv correct"); + ok(test_setenv(test_var, test_value2, 0) == 0, "set string 2"); + is_string(test_value1, getenv(test_var), "...and getenv unchanged"); + ok(test_setenv(test_var, test_value2, 1) == 0, "overwrite string 2"); + is_string(test_value2, getenv(test_var), "...and getenv changed"); + ok(test_setenv(test_var, "", 1) == 0, "overwrite with empty string"); + is_string("", getenv(test_var), "...and getenv correct"); + + return 0; +} diff --git a/tests/portable/setenv.c b/tests/portable/setenv.c new file mode 100644 index 0000000..79a7efd --- /dev/null +++ b/tests/portable/setenv.c @@ -0,0 +1,2 @@ +#define TESTING 1 +#include diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 18c2326..ca6ae61 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,32 +1,25 @@ /* * snprintf test suite. * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include +/* + * Intentionally don't add the printf attribute here since we pass a + * zero-length printf format during testing and don't want warnings. + */ int test_snprintf(char *str, size_t count, const char *fmt, ...); int test_vsnprintf(char *str, size_t count, const char *fmt, va_list args); @@ -93,7 +86,7 @@ static unsigned long long ullong_nums[] = { static void -test_format(int n, int truncate, const char *expected, int count, +test_format(bool truncate, const char *expected, int count, const char *format, ...) { char buf[128]; @@ -103,16 +96,8 @@ test_format(int n, int truncate, const char *expected, int count, va_start(args, format); result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args); va_end(args); - if (!strcmp(buf, expected) && result == count) { - printf("ok %d\n", n); - } else { - printf("not ok %d\n", n); - printf(" format: %s\n", format); - if (strcmp(buf, expected)) - printf(" saw: %s\n want: %s\n", buf, expected); - if (result != count) - printf(" %d != %d\n", result, count); - } + is_string(expected, buf, "format %s, wanted %s", format, expected); + is_int(count, result, "...and output length correct"); } @@ -124,75 +109,69 @@ main(void) long lcount; char lgbuf[128]; - test_init((26 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums) - + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums) - + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums) - + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums) - + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums))); - - ok(1, test_snprintf(NULL, 0, "%s", "abcd") == 4); - ok(2, test_snprintf(NULL, 0, "%d", 20) == 2); - ok(3, test_snprintf(NULL, 0, "Test %.2s", "abcd") == 7); - ok(4, test_snprintf(NULL, 0, "%c", 'a') == 1); - ok(5, test_snprintf(NULL, 0, "") == 0); - - test_format(6, 1, "abcd", 4, "%s", "abcd"); - test_format(7, 1, "20", 2, "%d", 20); - test_format(8, 1, "Test ab", 7, "Test %.2s", "abcd"); - test_format(9, 1, "a", 1, "%c", 'a'); - test_format(10, 1, "", 0, ""); - test_format(11, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s", - string); - test_format(12, 1, "abcdefghij", 10, "%.10s", string); - test_format(13, 1, " abcdefghij", 12, "%12.10s", string); - test_format(14, 1, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s", - string); - test_format(15, 1, "abcdefghij ", 14, "%-14.10s", string); - test_format(16, 1, " abcdefghijklmnopq", 50, "%50s", - string); - test_format(17, 1, "%abcd%", 6, "%%%0s%%", "abcd"); - test_format(18, 1, "", 0, "%.0s", string); - test_format(19, 1, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d", + plan(8 + + (18 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums) + + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums) + + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums) + + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums) + + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums)) * 2); + + is_int(4, test_snprintf(NULL, 0, "%s", "abcd"), "simple string length"); + is_int(2, test_snprintf(NULL, 0, "%d", 20), "number length"); + is_int(7, test_snprintf(NULL, 0, "Test %.2s", "abcd"), "limited string"); + is_int(1, test_snprintf(NULL, 0, "%c", 'a'), "character length"); + is_int(0, test_snprintf(NULL, 0, ""), "empty format length"); + + test_format(true, "abcd", 4, "%s", "abcd"); + test_format(true, "20", 2, "%d", 20); + test_format(true, "Test ab", 7, "Test %.2s", "abcd"); + test_format(true, "a", 1, "%c", 'a'); + test_format(true, "", 0, ""); + test_format(true, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s", string); + test_format(true, "abcdefghij", 10, "%.10s", string); + test_format(true, " abcdefghij", 12, "%12.10s", string); + test_format(true, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s", string); + test_format(true, "abcdefghij ", 14, "%-14.10s", string); + test_format(true, " abcdefghijklmnopq", 50, "%50s", string); + test_format(true, "%abcd%", 6, "%%%0s%%", "abcd"); + test_format(true, "", 0, "%.0s", string); + test_format(true, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d", string, 4444); - test_format(20, 1, "abcdefghijklmnopqrstuvwxyz -2.", 32, - "%.26s %.1f", string, -2.5); - test_format(21, 1, "abcdefghij4444", 14, "%.10s%n%d", string, &count, - 4444); - ok(22, count == 10); - test_format(23, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln", + test_format(true, "abcdefghijklmnopqrstuvwxyz -2.", 32, "%.26s %.1f", + string, -2.5); + test_format(true, "abcdefghij4444", 14, "%.10s%n%d", string, &count, 4444); + is_int(10, count, "correct output from %%n"); + test_format(true, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln", &count, string, &lcount); - ok(24, count == 0); - ok(25, lcount == 31); - test_format(26, 1, "(null)", 6, "%s", NULL); + is_int(0, count, "correct output from two %%n"); + is_int(31, lcount, "correct output from long %%ln"); + test_format(true, "(null)", 6, "%s", NULL); n = 26; for (i = 0; fp_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(fp_nums); j++) { count = sprintf(lgbuf, fp_formats[i], fp_nums[j]); - test_format(++n, 0, lgbuf, count, fp_formats[i], fp_nums[j]); + test_format(false, lgbuf, count, fp_formats[i], fp_nums[j]); } for (i = 0; int_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(int_nums); j++) { count = sprintf(lgbuf, int_formats[i], int_nums[j]); - test_format(++n, 0, lgbuf, count, int_formats[i], - int_nums[j]); + test_format(false, lgbuf, count, int_formats[i], int_nums[j]); } for (i = 0; uint_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(uint_nums); j++) { count = sprintf(lgbuf, uint_formats[i], uint_nums[j]); - test_format(++n, 0, lgbuf, count, uint_formats[i], - uint_nums[j]); + test_format(false, lgbuf, count, uint_formats[i], uint_nums[j]); } for (i = 0; llong_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(llong_nums); j++) { count = sprintf(lgbuf, llong_formats[i], llong_nums[j]); - test_format(++n, 0, lgbuf, count, llong_formats[i], - llong_nums[j]); + test_format(false, lgbuf, count, llong_formats[i], llong_nums[j]); } for (i = 0; ullong_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(ullong_nums); j++) { count = sprintf(lgbuf, ullong_formats[i], ullong_nums[j]); - test_format(++n, 0, lgbuf, count, ullong_formats[i], + test_format(false, lgbuf, count, ullong_formats[i], ullong_nums[j]); } diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index 2f39925..e02c277 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,31 +1,20 @@ /* * strlcat test suite. * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include size_t test_strlcat(char *, const char *, size_t); @@ -35,42 +24,51 @@ main(void) { char buffer[10] = ""; - test_init(27); + plan(27); - ok_int(1, 3, test_strlcat(buffer, "foo", sizeof(buffer))); - ok_string(2, "foo", buffer); - ok_int(3, 7, test_strlcat(buffer, " bar", sizeof(buffer))); - ok_string(4, "foo bar", buffer); - ok_int(5, 9, test_strlcat(buffer, "!!", sizeof(buffer))); - ok_string(6, "foo bar!!", buffer); - ok_int(7, 10, test_strlcat(buffer, "!", sizeof(buffer))); - ok_string(8, "foo bar!!", buffer); - ok(9, buffer[9] == '\0'); + is_int(3, test_strlcat(buffer, "foo", sizeof(buffer)), + "strlcat into empty buffer"); + is_string("foo", buffer, "...with right output"); + is_int(7, test_strlcat(buffer, " bar", sizeof(buffer)), + "...and append more"); + is_string("foo bar", buffer, "...and output is still correct"); + is_int(9, test_strlcat(buffer, "!!", sizeof(buffer)), + "...and append to buffer limit"); + is_string("foo bar!!", buffer, "...output is still correct"); + is_int(10, test_strlcat(buffer, "!", sizeof(buffer)), + "...append one more character"); + is_string("foo bar!!", buffer, "...and output didn't change"); + ok(buffer[9] == '\0', "...buffer still nul-terminated"); buffer[0] = '\0'; - ok_int(10, 11, test_strlcat(buffer, "hello world", sizeof(buffer))); - ok_string(11, "hello wor", buffer); - ok(12, buffer[9] == '\0'); + is_int(11, test_strlcat(buffer, "hello world", sizeof(buffer)), + "append single long string"); + is_string("hello wor", buffer, "...string truncates properly"); + ok(buffer[9] == '\0', "...buffer still nul-terminated"); buffer[0] = '\0'; - ok_int(13, 7, test_strlcat(buffer, "sausage", 5)); - ok_string(14, "saus", buffer); - ok_int(15, 14, test_strlcat(buffer, "bacon eggs", sizeof(buffer))); - ok_string(16, "sausbacon", buffer); + is_int(7, test_strlcat(buffer, "sausage", 5), "lie about buffer length"); + is_string("saus", buffer, "...contents are correct"); + is_int(14, test_strlcat(buffer, "bacon eggs", sizeof(buffer)), + "...add more up to real size"); + is_string("sausbacon", buffer, "...and result is correct"); /* Make sure that with a size of 0, the destination isn't changed. */ - ok_int(17, 11, test_strlcat(buffer, "!!", 0)); - ok_string(18, "sausbacon", buffer); + is_int(11, test_strlcat(buffer, "!!", 0), "no change with size of 0"); + is_string("sausbacon", buffer, "...and content is the same"); /* Now play with empty strings. */ - ok_int(19, 9, test_strlcat(buffer, "", 0)); - ok_string(20, "sausbacon", buffer); + is_int(9, test_strlcat(buffer, "", 0), + "correct count when appending empty string"); + is_string("sausbacon", buffer, "...and contents are unchanged"); buffer[0] = '\0'; - ok_int(21, 0, test_strlcat(buffer, "", sizeof(buffer))); - ok_string(22, "", buffer); - ok_int(23, 3, test_strlcat(buffer, "foo", 2)); - ok_string(24, "f", buffer); - ok(25, buffer[1] == '\0'); - ok_int(26, 1, test_strlcat(buffer, "", sizeof(buffer))); - ok(27, buffer[1] == '\0'); + is_int(0, test_strlcat(buffer, "", sizeof(buffer)), + "correct count when appending empty string to empty buffer"); + is_string("", buffer, "...and buffer content is correct"); + is_int(3, test_strlcat(buffer, "foo", 2), "append to length 2 buffer"); + is_string("f", buffer, "...and got only a single character"); + ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); + is_int(1, test_strlcat(buffer, "", sizeof(buffer)), + "append an empty string"); + ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); return 0; } diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index 74c9ecd..ba224ba 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,31 +1,20 @@ /* * strlcpy test suite. * + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include size_t test_strlcpy(char *, const char *, size_t); @@ -35,37 +24,43 @@ main(void) { char buffer[10]; - test_init(23); + plan(23); - ok_int(1, 3, test_strlcpy(buffer, "foo", sizeof(buffer))); - ok_string(2, "foo", buffer); - ok_int(3, 9, test_strlcpy(buffer, "hello wor", sizeof(buffer))); - ok_string(4, "hello wor", buffer); - ok_int(5, 10, test_strlcpy(buffer, "world hell", sizeof(buffer))); - ok_string(6, "world hel", buffer); - ok(7, buffer[9] == '\0'); - ok_int(8, 11, test_strlcpy(buffer, "hello world", sizeof(buffer))); - ok_string(9, "hello wor", buffer); - ok(10, buffer[9] == '\0'); + is_int(3, test_strlcpy(buffer, "foo", sizeof(buffer)), "simple strlcpy"); + is_string("foo", buffer, "...result is correct"); + is_int(9, test_strlcpy(buffer, "hello wor", sizeof(buffer)), + "strlcpy exact length of buffer"); + is_string("hello wor", buffer, "...result is correct"); + is_int(10, test_strlcpy(buffer, "world hell", sizeof(buffer)), + "strlcpy one more than buffer length"); + is_string("world hel", buffer, "...result is correct"); + ok(buffer[9] == '\0', "...buffer is nul-terminated"); + is_int(11, test_strlcpy(buffer, "hello world", sizeof(buffer)), + "strlcpy more than buffer length"); + is_string("hello wor", buffer, "...result is correct"); + ok(buffer[9] == '\0', "...buffer is nul-terminated"); /* Make sure that with a size of 0, the destination isn't changed. */ - ok_int(11, 3, test_strlcpy(buffer, "foo", 0)); - ok_string(12, "hello wor", buffer); + is_int(3, test_strlcpy(buffer, "foo", 0), "buffer unchanged if size 0"); + is_string("hello wor", buffer, "...contents still the same"); /* Now play with empty strings. */ - ok_int(13, 0, test_strlcpy(buffer, "", 0)); - ok_string(14, "hello wor", buffer); - ok_int(15, 0, test_strlcpy(buffer, "", sizeof(buffer))); - ok_string(16, "", buffer); - ok_int(17, 3, test_strlcpy(buffer, "foo", 2)); - ok_string(18, "f", buffer); - ok(19, buffer[1] == '\0'); - ok_int(20, 0, test_strlcpy(buffer, "", 1)); - ok(21, buffer[0] == '\0'); + is_int(0, test_strlcpy(buffer, "", 0), "copy empty string with size 0"); + is_string("hello wor", buffer, "...buffer unchanged"); + is_int(0, test_strlcpy(buffer, "", sizeof(buffer)), + "copy empty string into full buffer"); + is_string("", buffer, "...buffer now empty string"); + is_int(3, test_strlcpy(buffer, "foo", 2), + "copy string into buffer of size 2"); + is_string("f", buffer, "...got one character"); + ok(buffer[1] == '\0', "...buffer is nul-terminated"); + is_int(0, test_strlcpy(buffer, "", 1), + "copy empty string into buffer of size 1"); + ok(buffer[0] == '\0', "...buffer is empty string"); /* Finally, check using strlcpy as strlen. */ - ok_int(22, 3, test_strlcpy(NULL, "foo", 0)); - ok_int(23, 11, test_strlcpy(NULL, "hello world", 0)); + is_int(3, test_strlcpy(NULL, "foo", 0), "use strlcpy as strlen"); + is_int(11, test_strlcpy(NULL, "hello world", 0), "...again"); return 0; } diff --git a/tests/runtests.c b/tests/runtests.c index 060c8ad..1670012 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -17,12 +17,13 @@ * * where is the number of the test. ok indicates success, not ok * indicates failure, and "# skip" indicates the test was skipped for some - * reason (maybe because it doesn't apply to this platform). + * reason (maybe because it doesn't apply to this platform). This is a subset + * of TAP as documented in Test::Harness::TAP, which comes with Perl. * * Any bug reports, bug fixes, and improvements are very much welcome and * should be sent to the e-mail address below. * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009 * Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a @@ -44,16 +45,19 @@ * DEALINGS IN THE SOFTWARE. */ -#include -#include - #include #include #include +#include +#include +#include +#include #include #include +#include #include #include +#include /* sys/time.h must be included before sys/resource.h on some platforms. */ #include @@ -63,6 +67,19 @@ # define WCOREDUMP(status) ((unsigned)(status) & 0x80) #endif +/* + * The source and build versions of the tests directory. This is used to set + * the SOURCE and BUILD environment variables and find test programs, if set. + * Normally, this should be set as part of the build process to the test + * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively. + */ +#ifndef SOURCE +# define SOURCE NULL +#endif +#ifndef BUILD +# define BUILD NULL +#endif + /* Test status codes. */ enum test_status { TEST_FAIL, @@ -78,7 +95,8 @@ enum test_status { /* Structure to hold data for a set of tests. */ struct testset { - const char *file; /* The file name of the test. */ + char *file; /* The file name of the test. */ + char *path; /* The path to the test program. */ int count; /* Expected count of tests. */ int current; /* The last seen test number. */ int length; /* The length of the last status message. */ @@ -89,6 +107,8 @@ struct testset { int aborted; /* Whether the set as aborted. */ int reported; /* Whether the results were reported. */ int status; /* The exit status of the test. */ + int all_skipped; /* Whether all tests were skipped. */ + char *reason; /* Why all tests were skipped. */ }; /* Structure to hold a linked list of test sets. */ @@ -103,8 +123,7 @@ struct testlist { */ static const char banner[] = "\n\ Running all tests listed in %s. If any tests fail, run the failing\n\ -test program by hand to see more details. The test program will have the\n\ -same name as the test set but with \"-t\" appended.\n\n"; +test program with runtests -o to see more details.\n\n"; /* Header for reports of failed tests. */ static const char header[] = "\n\ @@ -115,22 +134,6 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ #define xmalloc(size) x_malloc((size), __FILE__, __LINE__) #define xstrdup(p) x_strdup((p), __FILE__, __LINE__) -/* Internal prototypes. */ -static void sysdie(const char *format, ...); -static void *x_malloc(size_t, const char *file, int line); -static char *x_strdup(const char *, const char *file, int line); -static int test_analyze(struct testset *); -static int test_batch(const char *testlist); -static void test_checkline(const char *line, struct testset *); -static void test_fail_summary(const struct testlist *); -static int test_init(const char *line, struct testset *); -static int test_print_range(int first, int last, int chars, int limit); -static void test_summarize(struct testset *, int status); -static pid_t test_start(const char *path, int *fd); -static double tv_diff(const struct timeval *, const struct timeval *); -static double tv_seconds(const struct timeval *); -static double tv_sum(const struct timeval *, const struct timeval *); - /* * Report a fatal error, including the results of strerror, and exit. @@ -218,6 +221,19 @@ tv_sum(const struct timeval *tv1, const struct timeval *tv2) } +/* + * Given a pointer to a string, skip any leading whitespace and return a + * pointer to the first non-whitespace character. + */ +static const char * +skip_whitespace(const char *p) +{ + while (isspace((unsigned char)(*p))) + p++; + return p; +} + + /* * Read the first line of test output, which should contain the range of * test numbers, and initialize the testset structure. Assume it was zeroed @@ -234,15 +250,34 @@ test_init(const char *line, struct testset *ts) * such as 1..10, accept that too for compatibility with Perl's * Test::Harness. */ - while (isspace((unsigned char)(*line))) - line++; + line = skip_whitespace(line); if (strncmp(line, "1..", 3) == 0) line += 3; - /* Get the count, check it for validity, and initialize the struct. */ - i = atoi(line); + /* + * Get the count, check it for validity, and initialize the struct. If we + * have something of the form "1..0 # skip foo", the whole file was + * skipped; record that. + */ + i = strtol(line, (char **) &line, 10); + if (i == 0) { + line = skip_whitespace(line); + if (*line == '#') { + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) { + line = skip_whitespace(line + 4); + if (*line != '\0') { + ts->reason = xstrdup(line); + ts->reason[strlen(ts->reason) - 1] = '\0'; + } + ts->all_skipped = 1; + ts->aborted = 1; + return 0; + } + } + } if (i <= 0) { - puts("invalid test count"); + puts("ABORTED (invalid test count)"); ts->aborted = 1; ts->reported = 1; return 0; @@ -329,8 +364,28 @@ static void test_checkline(const char *line, struct testset *ts) { enum test_status status = TEST_PASS; + const char *bail; + char *end; int current; + /* Before anything, check for a test abort. */ + bail = strstr(line, "Bail out!"); + if (bail != NULL) { + bail = skip_whitespace(bail + strlen("Bail out!")); + if (*bail != '\0') { + int length; + + length = strlen(bail); + if (bail[length - 1] == '\n') + length--; + test_backspace(ts); + printf("ABORTED (%.*s)\n", length, bail); + ts->reported = 1; + } + ts->aborted = 1; + return; + } + /* * If the given line isn't newline-terminated, it was too big for an * fgets(), which means ignore it. @@ -343,37 +398,40 @@ test_checkline(const char *line, struct testset *ts) status = TEST_FAIL; line += 4; } - if (strncmp(line, "ok ", 3) != 0) + if (strncmp(line, "ok", 2) != 0) return; - line += 3; - current = atoi(line); - if (current == 0) - return; - if (current < 0 || current > ts->count) { + line = skip_whitespace(line + 2); + errno = 0; + current = strtol(line, &end, 10); + if (errno != 0 || end == line) + current = ts->current + 1; + if (current <= 0 || current > ts->count) { test_backspace(ts); - printf("invalid test number %d\n", current); + printf("ABORTED (invalid test number %d)\n", current); ts->aborted = 1; ts->reported = 1; return; } - while (isspace((unsigned char)(*line))) - line++; + + /* + * Handle directives. We should probably do something more interesting + * with unexpected passes of todo tests. + */ while (isdigit((unsigned char)(*line))) line++; - while (isspace((unsigned char)(*line))) - line++; + line = skip_whitespace(line); if (*line == '#') { - line++; - while (isspace((unsigned char)(*line))) - line++; - if (strncmp(line, "skip", 4) == 0) + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) status = TEST_SKIP; + if (strncasecmp(line, "todo", 4) == 0) + status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL; } /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { test_backspace(ts); - printf("duplicate test number %d\n", current); + printf("ABORTED (duplicate test number %d)\n", current); ts->aborted = 1; ts->reported = 1; return; @@ -449,9 +507,9 @@ test_summarize(struct testset *ts, int status) int last = 0; if (ts->aborted) { - fputs("aborted", stdout); + fputs("ABORTED", stdout); if (ts->count > 0) - printf(", passed %d/%d", ts->passed, ts->count - ts->skipped); + printf(" (passed %d/%d)", ts->passed, ts->count - ts->skipped); } else { for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_INVALID) { @@ -520,19 +578,25 @@ test_analyze(struct testset *ts) { if (ts->reported) return 0; - if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) { + if (ts->all_skipped) { + if (ts->reason == NULL) + puts("skipped"); + else + printf("skipped (%s)\n", ts->reason); + return 1; + } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) { switch (WEXITSTATUS(ts->status)) { case CHILDERR_DUP: if (!ts->reported) - puts("can't dup file descriptors"); + puts("ABORTED (can't dup file descriptors)"); break; case CHILDERR_EXEC: if (!ts->reported) - puts("execution failed (not found?)"); + puts("ABORTED (execution failed -- not found?)"); break; case CHILDERR_STDERR: if (!ts->reported) - puts("can't open /dev/null"); + puts("ABORTED (can't open /dev/null)"); break; default: test_summarize(ts, WEXITSTATUS(ts->status)); @@ -561,17 +625,12 @@ test_run(struct testset *ts) int outfd, i, status; FILE *output; char buffer[BUFSIZ]; - char *file; /* * Initialize the test and our data structures, flagging this set in error * if the initialization fails. */ - file = xmalloc(strlen(ts->file) + 3); - strcpy(file, ts->file); - strcat(file, "-t"); - testpid = test_start(file, &outfd); - free(file); + testpid = test_start(ts->path, &outfd); output = fdopen(outfd, "r"); if (!output) { puts("ABORTED"); @@ -580,11 +639,8 @@ test_run(struct testset *ts) } if (!fgets(buffer, sizeof(buffer), output)) ts->aborted = 1; - if (!ts->aborted && !test_init(buffer, ts)) { - while (fgets(buffer, sizeof(buffer), output)) - ; + if (!ts->aborted && !test_init(buffer, ts)) ts->aborted = 1; - } /* Pass each line of output to test_checkline(). */ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) @@ -594,16 +650,23 @@ test_run(struct testset *ts) test_backspace(ts); /* - * Close the output descriptor, retrieve the exit status, and pass that - * information to test_analyze() for eventual output. + * Consume the rest of the test output, close the output descriptor, + * retrieve the exit status, and pass that information to test_analyze() + * for eventual output. */ + while (fgets(buffer, sizeof(buffer), output)) + ; fclose(output); child = waitpid(testpid, &ts->status, 0); if (child == (pid_t) -1) { - puts("ABORTED"); - fflush(stdout); + if (!ts->reported) { + puts("ABORTED"); + fflush(stdout); + } sysdie("waitpid for %u failed", (unsigned int) testpid); } + if (ts->all_skipped) + ts->aborted = 0; status = test_analyze(ts); /* Convert missing tests to failed tests. */ @@ -665,13 +728,54 @@ test_fail_summary(const struct testlist *fails) } +/* + * Given the name of a test, a pointer to the testset struct, and the source + * and build directories, find the test. We try first relative to the current + * directory, then in the build directory (if not NULL), then in the source + * directory. In each of those directories, we first try a "-t" extension and + * then a ".t" extension. When we find an executable program, we fill in the + * path member of the testset struct. If none of those paths are executable, + * just fill in the name of the test with "-t" appended. + * + * The caller is responsible for freeing the path member of the testset + * struct. + */ +static void +find_test(const char *name, struct testset *ts, const char *source, + const char *build) +{ + char *path; + const char *bases[] = { ".", build, source, NULL }; + int i; + + for (i = 0; bases[i] != NULL; i++) { + path = xmalloc(strlen(bases[i]) + strlen(name) + 4); + sprintf(path, "%s/%s-t", bases[i], name); + if (access(path, X_OK) != 0) + path[strlen(path) - 2] = '.'; + if (access(path, X_OK) == 0) + break; + free(path); + path = NULL; + } + if (path == NULL) { + path = xmalloc(strlen(name) + 3); + sprintf(path, "%s-t", name); + } + ts->path = path; +} + + /* * Run a batch of tests from a given file listing each test on a line by - * itself. The file must be rewindable. Returns true iff all tests + * itself. Takes two additional parameters: the root of the source directory + * and the root of the build directory. Test programs will be first searched + * for in the current directory, then the build directory, then the source + * directory. The file must be rewindable. Returns true iff all tests * passed. */ static int -test_batch(const char *testlist) +test_batch(const char *testlist, const char *source, const char *build) { FILE *tests; size_t length, i; @@ -741,7 +845,14 @@ test_batch(const char *testlist) fflush(stdout); memset(&ts, 0, sizeof(ts)); ts.file = xstrdup(buffer); - if (!test_run(&ts)) { + find_test(buffer, &ts, source, build); + ts.reason = NULL; + if (test_run(&ts)) { + free(ts.file); + free(ts.path); + if (ts.reason != NULL) + free(ts.reason); + } else { tmp = xmalloc(sizeof(struct testset)); memcpy(tmp, &ts, sizeof(struct testset)); if (!failhead) { @@ -757,9 +868,9 @@ test_batch(const char *testlist) } } aborted += ts.aborted; - total += ts.count; + total += ts.count + ts.all_skipped; passed += ts.passed; - skipped += ts.skipped; + skipped += ts.skipped + ts.all_skipped; failed += ts.failed; } total -= skipped; @@ -769,7 +880,8 @@ test_batch(const char *testlist) getrusage(RUSAGE_CHILDREN, &stats); /* Print out our final results. */ - if (failhead) test_fail_summary(failhead); + if (failhead) + test_fail_summary(failhead); putchar('\n'); if (aborted != 0) { if (aborted == 1) @@ -800,15 +912,80 @@ test_batch(const char *testlist) /* - * Main routine. Given a file listing tests, run each test listed. + * Run a single test case. This involves just running the test program after + * having done the environment setup and finding the test program. + */ +static void +test_single(const char *program, const char *source, const char *build) +{ + struct testset ts; + + memset(&ts, 0, sizeof(ts)); + find_test(program, &ts, source, build); + if (execl(ts.path, ts.path, (char *) 0) == -1) + sysdie("cannot exec %s", ts.path); +} + + +/* + * Main routine. Set the SOURCE and BUILD environment variables and then, + * given a file listing tests, run each test listed. */ int main(int argc, char *argv[]) { - if (argc != 2) { + int option; + int single = 0; + char *setting; + const char *list; + const char *source = SOURCE; + const char *build = BUILD; + + while ((option = getopt(argc, argv, "b:os:")) != EOF) { + switch (option) { + case 'b': + build = optarg; + break; + case 'o': + single = 1; + break; + case 's': + source = optarg; + break; + default: + exit(1); + } + } + argc -= optind; + argv += optind; + if (argc != 1) { fprintf(stderr, "Usage: runtests \n"); exit(1); } - printf(banner, argv[1]); - exit(test_batch(argv[1]) ? 0 : 1); + + if (source != NULL) { + setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1); + sprintf(setting, "SOURCE=%s", source); + if (putenv(setting) != 0) + sysdie("cannot set SOURCE in the environment"); + } + if (build != NULL) { + setting = xmalloc(strlen("BUILD=") + strlen(build) + 1); + sprintf(setting, "BUILD=%s", build); + if (putenv(setting) != 0) + sysdie("cannot set BUILD in the environment"); + } + + if (single) { + test_single(argv[0], source, build); + exit(0); + } else { + list = strrchr(argv[0], '/'); + if (list == NULL) + list = argv[0]; + else + list++; + printf(banner, list); + exit(test_batch(argv[0], source, build) ? 0 : 1); + } } diff --git a/tests/tap/basic.c b/tests/tap/basic.c new file mode 100644 index 0000000..5ca9ff4 --- /dev/null +++ b/tests/tap/basic.c @@ -0,0 +1,356 @@ +/* + * Some utility routines for writing tests. + * + * Herein are a variety of utility routines for writing tests. All routines + * of the form ok*() take a test number and some number of appropriate + * arguments, check to be sure the results match the expected output using the + * arguments, and print out something appropriate for that test number. Other + * utility routines help in constructing more complex tests. + * + * Copyright 2009 Russ Allbery + * Copyright 2006, 2007, 2008 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * The test count. Always contains the number that will be used for the next + * test status. + */ +int testnum = 1; + +/* + * Status information stored so that we can give a test summary at the end of + * the test case. We store the planned final test and the count of failures. + * We can get the highest test count from testnum. + * + * We also store the PID of the process that called plan() and only summarize + * results when that process exits, so as to not misreport results in forked + * processes. + */ +static int _planned = 0; +static int _failed = 0; +static pid_t _process = 0; + + +/* + * Our exit handler. Called on completion of the test to report a summary of + * results provided we're still in the original process. + */ +static void +finish(void) +{ + int highest = testnum - 1; + + if (_process != 0 && getpid() == _process && _planned > 0) { + if (_planned > highest) + printf("# Looks like you planned %d test%s but only ran %d\n", + _planned, (_planned > 1 ? "s" : ""), highest); + else if (_planned < highest) + printf("# Looks like you planned %d test%s but ran %d extra\n", + _planned, (_planned > 1 ? "s" : ""), highest - _planned); + else if (_failed > 0) + printf("# Looks like you failed %d test%s of %d\n", _failed, + (_failed > 1 ? "s" : ""), _planned); + else if (_planned > 1) + printf("# All %d tests successful or skipped\n", _planned); + else + printf("# %d test successful or skipped\n", _planned); + } +} + + +/* + * Initialize things. Turns on line buffering on stdout and then prints out + * the number of tests in the test suite. + */ +void +plan(int count) +{ + if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) + fprintf(stderr, "# cannot set stdout to line buffered: %s\n", + strerror(errno)); + printf("1..%d\n", count); + testnum = 1; + _planned = count; + _process = getpid(); + atexit(finish); +} + + +/* + * Skip the entire test suite and exits. Should be called instead of plan(), + * not after it, since it prints out a special plan line. + */ +void +skip_all(const char *format, ...) +{ + printf("1..0 # skip"); + if (format != NULL) { + va_list args; + + putchar(' '); + va_start(args, format); + vprintf(format, args); + va_end(args); + } + putchar('\n'); + exit(0); +} + + +/* + * Print the test description. + */ +static void +print_desc(const char *format, va_list args) +{ + printf(" - "); + vprintf(format, args); +} + + +/* + * Takes a boolean success value and assumes the test passes if that value + * is true and fails if that value is false. + */ +void +ok(int success, const char *format, ...) +{ + printf("%sok %d", success ? "" : "not ", testnum++); + if (!success) + _failed++; + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Skip a test. + */ +void +skip(const char *reason, ...) +{ + printf("ok %d # skip", testnum++); + if (reason != NULL) { + va_list args; + + va_start(args, reason); + putchar(' '); + vprintf(reason, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Report the same status on the next count tests. + */ +void +ok_block(int count, int status, const char *format, ...) +{ + int i; + + for (i = 0; i < count; i++) { + printf("%sok %d", status ? "" : "not ", testnum++); + if (!status) + _failed++; + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); + } +} + + +/* + * Skip the next count tests. + */ +void +skip_block(int count, const char *reason, ...) +{ + int i; + + for (i = 0; i < count; i++) { + printf("ok %d # skip", testnum++); + if (reason != NULL) { + va_list args; + + va_start(args, reason); + putchar(' '); + vprintf(reason, args); + va_end(args); + } + putchar('\n'); + } +} + + +/* + * Takes an expected integer and a seen integer and assumes the test passes + * if those two numbers match. + */ +void +is_int(int wanted, int seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %d\n# seen: %d\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes a string and what the string should be, and assumes the test passes + * if those strings match (using strcmp). + */ +void +is_string(const char *wanted, const char *seen, const char *format, ...) +{ + if (wanted == NULL) + wanted = "(null)"; + if (seen == NULL) + seen = "(null)"; + if (strcmp(wanted, seen) == 0) + printf("ok %d", testnum++); + else { + printf("# wanted: %s\n# seen: %s\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes an expected double and a seen double and assumes the test passes if + * those two numbers match. + */ +void +is_double(double wanted, double seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %g\n# seen: %g\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes an expected unsigned long and a seen unsigned long and assumes the + * test passes if the two numbers match. Otherwise, reports them in hex. + */ +void +is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted, + (unsigned long) seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Bail out with an error. + */ +void +bail(const char *format, ...) +{ + va_list args; + + fflush(stdout); + printf("Bail out! "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + exit(1); +} + + +/* + * Bail out with an error, appending strerror(errno). + */ +void +sysbail(const char *format, ...) +{ + va_list args; + int oerrno = errno; + + fflush(stdout); + printf("Bail out! "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf(": %s\n", strerror(oerrno)); + exit(1); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h new file mode 100644 index 0000000..efe94ba --- /dev/null +++ b/tests/tap/basic.h @@ -0,0 +1,98 @@ +/* + * Basic utility routines for the TAP protocol. + * + * Copyright 2006, 2007, 2008 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_BASIC_H +#define TAP_BASIC_H 1 + +#include /* pid_t */ + +/* + * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + * could you use the __format__ form of the attributes, which is what we use + * (to avoid confusion with other macros). + */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(spec) /* empty */ +# endif +#endif + +/* + * BEGIN_DECLS is used at the beginning of declarations so that C++ + * compilers don't mangle their names. END_DECLS is used at the end. + */ +#undef BEGIN_DECLS +#undef END_DECLS +#ifdef __cplusplus +# define BEGIN_DECLS extern "C" { +# define END_DECLS } +#else +# define BEGIN_DECLS /* empty */ +# define END_DECLS /* empty */ +#endif + +/* + * Used for iterating through arrays. ARRAY_SIZE returns the number of + * elements in the array (useful for a < upper bound in a for loop) and + * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it + * legal to refer to such a pointer as long as it's never dereferenced). + */ +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) +#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) + +BEGIN_DECLS + +/* + * The test count. Always contains the number that will be used for the next + * test status. + */ +extern int testnum; + +/* Print out the number of tests and set standard output to line buffered. */ +void plan(int count); + +/* Skip the entire test suite. Call instead of plan. */ +void skip_all(const char *format, ...) + __attribute__((__noreturn__, __format__(printf, 1, 2))); + +/* Basic reporting functions. */ +void ok(int success, const char *format, ...) + __attribute__((__format__(printf, 2, 3))); +void skip(const char *reason, ...) + __attribute__((__format__(printf, 1, 2))); + +/* Report the same status on, or skip, the next count tests. */ +void ok_block(int count, int success, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void skip_block(int count, const char *reason, ...) + __attribute__((__format__(printf, 2, 3))); + +/* Check an expected value against a seen value. */ +void is_int(int wanted, int seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void is_double(double wanted, double seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void is_string(const char *wanted, const char *seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); + +/* Bail out with an error. sysbail appends strerror(errno). */ +void bail(const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); +void sysbail(const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); + +END_DECLS + +#endif /* LIBTEST_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c new file mode 100644 index 0000000..700212e --- /dev/null +++ b/tests/tap/kerberos.c @@ -0,0 +1,164 @@ +/* + * Utility functions for tests that use Kerberos. + * + * Currently only provides kerberos_setup(), which assumes a particular set of + * data files in either the SOURCE or BUILD directories and, using those, + * obtains Kerberos credentials, sets up a ticket cache, and sets the + * environment variable pointing to the Kerberos keytab to use for testing. + * + * Copyright 2006, 2007, 2009, 2010 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include + +#include +#include +#include +#include + + +/* + * Given the partial path to a file, look under BUILD and then SOURCE for the + * file and return the full path to the file in newly-allocated memory. + * Returns NULL if the file doesn't exist. + */ +static char * +find_file(const char *file) +{ + char *base; + char *path = NULL; + const char *envs[] = { "BUILD", "SOURCE", NULL }; + int i; + + for (i = 0; envs[i] != NULL; i++) { + base = getenv(envs[i]); + if (base == NULL) + continue; + path = concatpath(base, file); + if (access(path, R_OK) == 0) + break; + free(path); + path = NULL; + } + return path; +} + + +/* + * Obtain Kerberos tickets for the principal specified in test.principal using + * the keytab specified in test.keytab, both of which are presumed to be in + * tests/data in either the build or the source tree. + * + * Returns the contents of test.principal in newly allocated memory or NULL if + * Kerberos tests are apparently not configured. If Kerberos tests are + * configured but something else fails, calls bail(). + * + * The error handling here is not great. We should have a bail_krb5 that uses + * the same logic as messages-krb5.c, which hasn't yet been imported into + * rra-c-util. + */ +char * +kerberos_setup(void) +{ + char *path, *krbtgt; + const char *build, *realm; + FILE *file; + char principal[BUFSIZ]; + krb5_error_code code; + krb5_context ctx; + krb5_ccache ccache; + krb5_principal kprinc; + krb5_keytab keytab; + krb5_get_init_creds_opt *opts; + krb5_creds creds; + + /* Read the principal name and find the keytab file. */ + path = find_file("data/test.principal"); + if (path == NULL) + return NULL; + file = fopen(path, "r"); + if (file == NULL) { + free(path); + return NULL; + } + if (fgets(principal, sizeof(principal), file) == NULL) { + fclose(file); + bail("cannot read %s", path); + } + fclose(file); + if (principal[strlen(principal) - 1] != '\n') + bail("no newline in %s", path); + free(path); + principal[strlen(principal) - 1] = '\0'; + path = find_file("data/test.keytab"); + if (path == NULL) + return NULL; + + /* Set the KRB5CCNAME and KRB5_KTNAME environment variables. */ + build = getenv("BUILD"); + if (build == NULL) + build = "."; + putenv(concat("KRB5CCNAME=", build, "/data/test.cache", (char *) 0)); + putenv(concat("KRB5_KTNAME=", path, (char *) 0)); + + /* Now do the Kerberos initialization. */ + code = krb5_init_context(&ctx); + if (code != 0) + bail("error initializing Kerberos"); + code = krb5_cc_default(ctx, &ccache); + if (code != 0) + bail("error setting ticket cache"); + code = krb5_parse_name(ctx, principal, &kprinc); + if (code != 0) + bail("error parsing principal %s", principal); + realm = krb5_principal_get_realm(ctx, kprinc); + krbtgt = concat("krbtgt/", realm, "@", realm, (char *) 0); + code = krb5_kt_resolve(ctx, path, &keytab); + if (code != 0) + bail("cannot open keytab %s", path); + code = krb5_get_init_creds_opt_alloc(ctx, &opts); + if (code != 0) + bail("cannot allocate credential options"); + krb5_get_init_creds_opt_set_default_flags(ctx, NULL, realm, opts); + krb5_get_init_creds_opt_set_forwardable(opts, 0); + krb5_get_init_creds_opt_set_proxiable(opts, 0); + code = krb5_get_init_creds_keytab(ctx, &creds, kprinc, keytab, 0, krbtgt, + opts); + if (code != 0) + bail("cannot get Kerberos tickets"); + code = krb5_cc_initialize(ctx, ccache, kprinc); + if (code != 0) + bail("error initializing ticket cache"); + code = krb5_cc_store_cred(ctx, ccache, &creds); + if (code != 0) + bail("error storing credentials"); + krb5_cc_close(ctx, ccache); + krb5_free_cred_contents(ctx, &creds); + krb5_kt_close(ctx, keytab); + krb5_free_principal(ctx, kprinc); + krb5_free_context(ctx); + free(krbtgt); + free(path); + + return xstrdup(principal); +} + + +/* + * Clean up at the end of a test. Currently, all this does is remove the + * ticket cache. + */ +void +kerberos_cleanup(void) +{ + char *path; + + path = concatpath(getenv("BUILD"), "data/test.cache"); + unlink(path); + free(path); +} diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h new file mode 100644 index 0000000..1c64f70 --- /dev/null +++ b/tests/tap/kerberos.h @@ -0,0 +1,32 @@ +/* + * Utility functions for tests that use Kerberos. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_KERBEROS_H +#define TAP_KERBEROS_H 1 + +#include +#include + +BEGIN_DECLS + +/* + * Set up Kerberos, returning the test principal in newly allocated memory if + * we were successful. If there is no principal in tests/data/test.principal + * or no keytab in tests/data/test.keytab, return NULL. Otherwise, on + * failure, calls bail(). + */ +char *kerberos_setup(void) + __attribute__((__malloc__)); + +/* Clean up at the end of a test. */ +void kerberos_cleanup(void); + +END_DECLS + +#endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh new file mode 100644 index 0000000..da07e66 --- /dev/null +++ b/tests/tap/kerberos.sh @@ -0,0 +1,48 @@ +# Shell function library to initialize Kerberos credentials +# +# Written by Russ Allbery +# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Set up Kerberos, including the ticket cache environment variable. Bail out +# if not successful, return 0 if successful, and return 1 if Kerberos is not +# configured. Sets the global principal variable to the principal to use. +kerberos_setup () { + local keytab + keytab='' + for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do + if [ -r "$f" ] ; then + keytab="$f" + fi + done + principal='' + for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do + if [ -r "$f" ] ; then + principal=`cat "$BUILD/data/test.principal"` + fi + done + if [ -z "$keytab" ] || [ -z "$principal" ] ; then + return 1 + fi + KRB5CCNAME="$BUILD/data/test.cache"; export KRB5CCNAME + kinit -k -t "$keytab" "$principal" >/dev/null /dev/null /dev/null +# Copyright 2009 Russ Allbery +# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Print out the number of test cases we expect to run. +plan () { + count=1 + planned="$1" + failed=0 + echo "1..$1" + trap finish 0 +} + +# Report the test status on exit. +finish () { + local highest looks + highest=`expr "$count" - 1` + looks='# Looks like you' + if [ "$planned" -gt 0 ] ; then + if [ "$planned" -gt "$highest" ] ; then + if [ "$planned" -gt 1 ] ; then + echo "$looks planned $planned tests but only ran $highest" + else + echo "$looks planned $planned test but only ran $highest" + fi + elif [ "$planned" -lt "$highest" ] ; then + local extra + extra=`expr "$highest" - "$planned"` + if [ "$planned" -gt 1 ] ; then + echo "$looks planned $planned tests but ran $extra extra" + else + echo "$looks planned $planned test but ran $extra extra" + fi + elif [ "$failed" -gt 0 ] ; then + if [ "$failed" -gt 1 ] ; then + echo "$looks failed $failed tests of $planned" + else + echo "$looks failed $failed test of $planned" + fi + elif [ "$planned" -gt 1 ] ; then + echo "# All $planned tests successful or skipped" + else + echo "# $planned test successful or skipped" + fi + fi +} + +# Skip the entire test suite. Should be run instead of plan. +skip_all () { + local desc + desc="$1" + if [ -n "$desc" ] ; then + echo "1..0 # skip $desc" + else + echo "1..0 # skip" + fi + exit 0 +} + +# ok takes a test description and a command to run and prints success if that +# command is successful, false otherwise. The count starts at 1 and is +# updated each time ok is printed. +ok () { + local desc + desc="$1" + if [ -n "$desc" ] ; then + desc=" - $desc" + fi + shift + if "$@" ; then + echo ok $count$desc + else + echo not ok $count$desc + failed=`expr $failed + 1` + fi + count=`expr $count + 1` +} + +# Skip the next test. Takes the reason why the test is skipped. +skip () { + echo "ok $count # skip $*" + count=`expr $count + 1` +} + +# Report the same status on a whole set of tests. Takes the count of tests, +# the description, and then the command to run to determine the status. +ok_block () { + local end i desc + i=$count + end=`expr $count + $1` + shift + desc="$1" + shift + while [ "$i" -lt "$end" ] ; do + ok "$desc" "$@" + i=`expr $i + 1` + done +} + +# Skip a whole set of tests. Takes the count and then the reason for skipping +# the test. +skip_block () { + local i end + i=$count + end=`expr $count + $1` + shift + while [ "$i" -lt "$end" ] ; do + skip "$@" + i=`expr $i + 1` + done +} + +# Run a program expected to succeed, and print ok if it does and produces the +# correct output. Takes the description, expected exit status, the expected +# output, the command to run, and then any arguments for that command. Strip +# a colon and everything after it off the output if the expected status is +# non-zero, since this is probably a system-specific error message. +ok_program () { + local desc w_status w_output output status + desc="$1" + shift + w_status="$1" + shift + w_output="$1" + shift + output=`"$@" 2>&1` + status=$? + if [ "$w_status" -ne 0 ] ; then + output=`echo "$output" | sed 's/^\([^:]* [^:]*\):.*/\1/'` + fi + if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then + ok "$desc" true + else + echo "# saw: ($status) $output" + echo "# not: ($w_status) $w_output" + ok "$desc" false + fi +} + +# Bail out with an error message. +bail () { + echo 'Bail out!' "$@" + exit 1 +} diff --git a/tests/tap/messages.c b/tests/tap/messages.c new file mode 100644 index 0000000..3bb9a1a --- /dev/null +++ b/tests/tap/messages.c @@ -0,0 +1,80 @@ +/* + * Utility functions to test message handling. + * + * These functions set up a message handler to trap warn and notice output + * into a buffer that can be inspected later, allowing testing of error + * handling. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include +#include +#include +#include +#include + +/* A global buffer into which message_log_buffer stores error messages. */ +char *errors = NULL; + + +/* + * An error handler that appends all errors to the errors global. Used by + * error_capture. + */ +static void +message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) +{ + char *message; + + message = xmalloc(len + 1); + vsnprintf(message, len + 1, fmt, args); + if (errors == NULL) { + errors = concat(message, "\n", (char *) 0); + } else { + char *new_errors; + + new_errors = concat(errors, message, "\n", (char *) 0); + free(errors); + errors = new_errors; + } + free(message); +} + + +/* + * Turn on the capturing of errors. Errors will be stored in the global + * errors variable where they can be checked by the test suite. Capturing is + * turned off with errors_uncapture. + */ +void +errors_capture(void) +{ + if (errors != NULL) { + free(errors); + errors = NULL; + } + message_handlers_warn(1, message_log_buffer); + message_handlers_notice(1, message_log_buffer); +} + + +/* + * Turn off the capturing of errors again. + */ +void +errors_uncapture(void) +{ + message_handlers_warn(1, message_log_stderr); + message_handlers_notice(1, message_log_stdout); +} diff --git a/tests/tap/messages.h b/tests/tap/messages.h new file mode 100644 index 0000000..2b9a7db --- /dev/null +++ b/tests/tap/messages.h @@ -0,0 +1,35 @@ +/* + * Utility functions to test message handling. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_MESSAGES_H +#define TAP_MESSAGES_H 1 + +#include +#include + +/* A global buffer into which errors_capture stores errors. */ +extern char *errors; + +BEGIN_DECLS + +/* + * Turn on capturing of errors with errors_capture. Errors reported by warn + * will be stored in the global errors variable. Turn this off again with + * errors_uncapture. Caller is responsible for freeing errors when done. + */ +void errors_capture(void); +void errors_uncapture(void); + +END_DECLS + +#endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/process.c b/tests/tap/process.c new file mode 100644 index 0000000..16154c7 --- /dev/null +++ b/tests/tap/process.c @@ -0,0 +1,100 @@ +/* + * Utility functions for tests that use subprocesses. + * + * Provides utility functions for subprocess manipulation. Currently, only + * one utility function is provided: is_function_output, which runs a function + * in a subprocess and checks its output and exit status against expected + * values. + * + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include + +#include +#include +#include + + +/* + * Given a function, an expected exit status, and expected output, runs that + * function in a subprocess, capturing stdout and stderr via a pipe, and + * compare the combination of stdout and stderr with the expected output and + * the exit status with the expected status. Expects the function to always + * exit (not die from a signal). + */ +void +is_function_output(test_function_type function, int status, const char *output, + const char *format, ...) +{ + int fds[2]; + pid_t child; + char *buf, *msg; + ssize_t count, ret, buflen; + int rval; + va_list args; + + /* Flush stdout before we start to avoid odd forking issues. */ + fflush(stdout); + + /* Set up the pipe and call the function, collecting its output. */ + if (pipe(fds) == -1) + sysbail("can't create pipe"); + child = fork(); + if (child == (pid_t) -1) { + sysbail("can't fork"); + } else if (child == 0) { + /* In child. Set up our stdout and stderr. */ + close(fds[0]); + if (dup2(fds[1], 1) == -1) + _exit(255); + if (dup2(fds[1], 2) == -1) + _exit(255); + + /* Now, run the function and exit successfully if it returns. */ + (*function)(); + fflush(stdout); + _exit(0); + } else { + /* + * In the parent; close the extra file descriptor, read the output if + * any, and then collect the exit status. + */ + close(fds[1]); + buflen = BUFSIZ; + buf = xmalloc(buflen); + count = 0; + do { + ret = read(fds[0], buf + count, buflen - count - 1); + if (ret > 0) + count += ret; + if (count >= buflen - 1) { + buflen += BUFSIZ; + buf = xrealloc(buf, buflen); + } + } while (ret > 0); + buf[count < 0 ? 0 : count] = '\0'; + if (waitpid(child, &rval, 0) == (pid_t) -1) + sysbail("waitpid failed"); + } + + /* Now, check the results against what we expected. */ + va_start(args, format); + if (xvasprintf(&msg, format, args) < 0) + bail("cannot format test description"); + va_end(args); + ok(WIFEXITED(rval), "%s (exited)", msg); + is_int(status, WEXITSTATUS(rval), "%s (status)", msg); + is_string(output, buf, "%s (output)", msg); + free(buf); + free(msg); +} diff --git a/tests/tap/process.h b/tests/tap/process.h new file mode 100644 index 0000000..b7d3b11 --- /dev/null +++ b/tests/tap/process.h @@ -0,0 +1,37 @@ +/* + * Utility functions for tests that use subprocesses. + * + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 + * by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_PROCESS_H +#define TAP_PROCESS_H 1 + +#include +#include + +BEGIN_DECLS + +/* + * Run a function in a subprocess and check the exit status and expected + * output (stdout and stderr combined) against the provided values. Expects + * the function to always exit (not die from a signal). + * + * This reports as three separate tests: whether the function exited rather + * than was killed, whether the exit status was correct, and whether the + * output was correct. + */ +typedef void (*test_function_type)(void); +void is_function_output(test_function_type, int status, const char *output, + const char *format, ...) + __attribute__((__format__(printf, 4, 5))); + +END_DECLS + +#endif /* TAP_PROCESS_H */ diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh new file mode 100644 index 0000000..b9667ef --- /dev/null +++ b/tests/tap/remctl.sh @@ -0,0 +1,46 @@ +# Shell function library to start and stop remctld +# +# Written by Russ Allbery +# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Start remctld. Takes the path to remctld, which may be found via configure, +# and the path to the configuration file. +remctld_start () { + local keytab principal + rm -f "$BUILD/data/remctld.pid" + keytab='' + for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do + if [ -r "$f" ] ; then + keytab="$f" + fi + done + principal='' + for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do + if [ -r "$f" ] ; then + principal=`cat "$BUILD/data/test.principal"` + fi + done + if [ -n "$VALGRIND" ] ; then + ( "$VALGRIND" --log-file=valgrind.%p --leak-check=full "$1" -m \ + -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" -f "$2" -d \ + -S -F -k "$keytab" &) + [ -f "$BUILD/data/remctld.pid" ] || sleep 5 + else + ( "$1" -m -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" \ + -f "$2" -d -S -F -k "$keytab" &) + fi + [ -f "$BUILD/data/remctld.pid" ] || sleep 1 + if [ ! -f "$BUILD/data/remctld.pid" ] ; then + bail 'remctld did not start' + fi +} + +# Stop remctld and clean up. +remctld_stop () { + if [ -f "$BUILD/data/remctld.pid" ] ; then + kill -TERM `cat "$BUILD/data/remctld.pid"` + rm -f "$BUILD/data/remctld.pid" + fi +} diff --git a/tests/util/concat-t.c b/tests/util/concat-t.c index 81824c8..ca7de2c 100644 --- a/tests/util/concat-t.c +++ b/tests/util/concat-t.c @@ -1,58 +1,46 @@ /* * concat test suite. * - * Copyright 2004, 2005, 2006 + * Written by Russ Allbery + * Copyright 2009 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include -#include +#include +#include #define END (char *) 0 - /* * Memory leaks everywhere! Whoo-hoo! */ int main(void) { - test_init(13); - - ok_string( 1, "a", concat("a", END)); - ok_string( 2, "ab", concat("a", "b", END)); - ok_string( 3, "ab", concat("ab", "", END)); - ok_string( 4, "ab", concat("", "ab", END)); - ok_string( 5, "", concat("", END)); - ok_string( 6, "abcde", concat("ab", "c", "", "de", END)); - ok_string( 7, "abcde", concat("abc", "de", END, "f", END)); - - ok_string( 8, "/foo", concatpath("/bar", "/foo")); - ok_string( 9, "/foo/bar", concatpath("/foo", "bar")); - ok_string(10, "./bar", concatpath("/foo", "./bar")); - ok_string(11, "/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar")); - ok_string(12, "./foo", concatpath(NULL, "foo")); - ok_string(13, "/foo/bar", concatpath(NULL, "/foo/bar")); + plan(13); + + is_string("a", concat("a", END), "concat 1"); + is_string("ab", concat("a", "b", END), "concat 2"); + is_string("ab", concat("ab", "", END), "concat 3"); + is_string("ab", concat("", "ab", END), "concat 4"); + is_string("", concat("", END), "concat 5"); + is_string("abcde", concat("ab", "c", "", "de", END), "concat 6"); + is_string("abcde", concat("abc", "de", END, "f", END), "concat 7"); + + is_string("/foo", concatpath("/bar", "/foo"), "path 1"); + is_string("/foo/bar", concatpath("/foo", "bar"), "path 2"); + is_string("./bar", concatpath("/foo", "./bar"), "path 3"); + is_string("/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar"), "path 4"); + is_string("./foo", concatpath(NULL, "foo"), "path 5"); + is_string("/foo/bar", concatpath(NULL, "/foo/bar"), "path 6"); return 0; } diff --git a/tests/util/messages-krb5-t.c b/tests/util/messages-krb5-t.c new file mode 100644 index 0000000..02d8f92 --- /dev/null +++ b/tests/util/messages-krb5-t.c @@ -0,0 +1,99 @@ +/* + * Test suite for Kerberos error handling routines. + * + * Written by Russ Allbery + * Copyright 2010 Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Test functions. + */ +static void +test_warn(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + + code = krb5_init_context(&ctx); + if (code < 0) + die_krb5(ctx, code, "cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + if (code < 0) + warn_krb5(ctx, code, "principal parse failed"); + else + die("unexpected success parsing principal"); + exit(0); +} + +static void +test_die(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + + code = krb5_init_context(&ctx); + if (code < 0) + die_krb5(ctx, code, "cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + if (code < 0) + die_krb5(ctx, code, "principal parse failed"); + else + die("unexpected success parsing principal"); + exit(0); +} + + +/* + * Run the tests. + */ +int +main(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + const char *message; + char *wanted; + + plan(6 * 3); + + /* First, we have to get what the correct error message is. */ + code = krb5_init_context(&ctx); + if (code < 0) + bail("cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + message = krb5_get_error_message(ctx, code); + + xasprintf(&wanted, "principal parse failed: %s\n", message); + is_function_output(test_warn, 0, wanted, "warn_krb5"); + is_function_output(test_die, 1, wanted, "die_krb5"); + free(wanted); + + message_program_name = "msg-test"; + xasprintf(&wanted, "msg-test: principal parse failed: %s\n", message); + is_function_output(test_warn, 0, wanted, "warn_krb5 with name"); + is_function_output(test_die, 1, wanted, "die_krb5 with name"); + free(wanted); + + message_handlers_warn(0); + is_function_output(test_warn, 0, "", "warn_krb5 with no handlers"); + message_handlers_die(0); + is_function_output(test_die, 1, "", "warn_krb5 with no handlers"); + + return 0; +} diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index 3f7860e..fb82a42 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -1,25 +1,14 @@ /* * Test suite for error handling routines. * - * Copyright 2004, 2005, 2006 + * Written by Russ Allbery + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") - * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include @@ -30,67 +19,11 @@ #include #include -#include -#include - -#define END (char *) 0 - -/* Test function type. */ -typedef void (*test_function_t)(void); - - -/* - * Fork and execute the provided function, connecting stdout and stderr to a - * pipe. Captures the output into the provided buffer and returns the exit - * status as a waitpid status value. - */ -static int -run_test(test_function_t function, char *buf, size_t buflen) -{ - int fds[2]; - pid_t child; - ssize_t count, status; - int rval; - - /* Flush stdout before we start to avoid odd forking issues. */ - fflush(stdout); - - /* Set up the pipe and call the function, collecting its output. */ - if (pipe(fds) == -1) - sysdie("can't create pipe"); - child = fork(); - if (child == (pid_t) -1) { - sysdie("can't fork"); - } else if (child == 0) { - /* In child. Set up our stdout and stderr. */ - close(fds[0]); - if (dup2(fds[1], 1) == -1) - _exit(255); - if (dup2(fds[1], 2) == -1) - _exit(255); - - /* Now, run the function and exit successfully if it returns. */ - (*function)(); - fflush(stdout); - _exit(0); - } else { - /* - * In the parent; close the extra file descriptor, read the output if - * any, and then collect the exit status. - */ - close(fds[1]); - count = 0; - do { - status = read(fds[0], buf + count, buflen - count - 1); - if (status > 0) - count += status; - } while (status > 0); - buf[count < 0 ? 0 : count] = '\0'; - if (waitpid(child, &rval, 0) == (pid_t) -1) - sysdie("waitpid failed"); - } - return rval; -} +#include +#include +#include +#include +#include /* @@ -203,43 +136,20 @@ static void test24(void) { /* - * Given the test number, intended exit status and message, and the function - * to run, print ok or not ok. - */ -static void -test_error(int n, int status, const char *output, test_function_t function) -{ - int real_status; - char buf[256]; - int succeeded = 1; - - real_status = run_test(function, buf, sizeof(buf)); - if (!WIFEXITED(real_status) || status != WEXITSTATUS(real_status)) { - printf(" unexpected exit status %d\n", real_status); - succeeded = 0; - } - if (strcmp(output, buf)) { - printf(" unexpected output: %s", buf); - printf(" expected output: %s", output); - succeeded = 0; - } - printf("%sok %d\n", succeeded ? "" : "not ", n); -} - - -/* - * Given the test number, intended status, intended message sans the appended - * strerror output, errno, and the function to run, print ok or not ok. + * Given the intended status, intended message sans the appended strerror + * output, errno, and the function to run, check the output. */ static void -test_strerror(int n, int status, const char *output, int error, - test_function_t function) +test_strerror(int status, const char *output, int error, + test_function_type function) { - char *full_output; + char *full_output, *name; - full_output = concat(output, ": ", strerror(error), "\n", END); - test_error(n, status, full_output, function); + full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL); + xasprintf(&name, "strerror %d", testnum / 3 + 1); + is_function_output(function, status, full_output, name); free(full_output); + free(name); } @@ -250,46 +160,47 @@ int main(void) { char buff[32]; - - test_init(24); - - test_error(1, 0, "warning\n", test1); - test_error(2, 1, "fatal\n", test2); - test_strerror(3, 0, "permissions", EPERM, test3); - test_strerror(4, 1, "fatal access", EACCES, test4); - test_error(5, 0, "test5: warning\n", test5); - test_error(6, 1, "test6: fatal\n", test6); - test_strerror(7, 0, "test7: perms 7", EPERM, test7); - test_strerror(8, 1, "test8: fatal", EACCES, test8); - test_error(9, 10, "fatal\n", test9); - test_strerror(10, 10, "fatal perm", EPERM, test10); - test_strerror(11, 10, "1st test11: fatal", EPERM, test11); - test_error(12, 0, "7 0 warning\n", test12); - test_error(13, 1, "5 0 fatal\n", test13); + char *output; + + plan(24 * 3); + + is_function_output(test1, 0, "warning\n", "test1"); + is_function_output(test2, 1, "fatal\n", "test2"); + test_strerror(0, "permissions", EPERM, test3); + test_strerror(1, "fatal access", EACCES, test4); + is_function_output(test5, 0, "test5: warning\n", "test5"); + is_function_output(test6, 1, "test6: fatal\n", "test6"); + test_strerror(0, "test7: perms 7", EPERM, test7); + test_strerror(1, "test8: fatal", EACCES, test8); + is_function_output(test9, 10, "fatal\n", "test9"); + test_strerror(10, "fatal perm", EPERM, test10); + test_strerror(10, "1st test11: fatal", EPERM, test11); + is_function_output(test12, 0, "7 0 warning\n", "test12"); + is_function_output(test13, 1, "5 0 fatal\n", "test13"); sprintf(buff, "%d", EPERM); - test_error(14, 0, - concat("7 ", buff, " warning\n7 ", buff, " warning\n", END), - test14); - test_error(15, 10, - concat("5 ", buff, " fatal\n5 ", buff, " fatal\n", END), - test15); - test_error(16, 0, - concat("test16: warning: ", strerror(EPERM), "\n7 ", buff, - " warning\n", END), - test16); - - test_error(17, 0, "notice\n", test17); - test_error(18, 0, "test18: notice\n", test18); - test_error(19, 0, "", test19); - test_error(20, 0, "3 0 foo\n", test20); - test_error(21, 0, "test23: baz\n", test21); + xasprintf(&output, "7 %d warning\n7 %d warning\n", EPERM, EPERM); + is_function_output(test14, 0, output, "test14"); + free(output); + xasprintf(&output, "5 %d fatal\n5 %d fatal\n", EPERM, EPERM); + is_function_output(test15, 10, output, "test15"); + free(output); + xasprintf(&output, "test16: warning: %s\n7 %d warning\n", strerror(EPERM), + EPERM); + is_function_output(test16, 0, output, "test16"); + free(output); + + is_function_output(test17, 0, "notice\n", "test17"); + is_function_output(test18, 0, "test18: notice\n", "test18"); + is_function_output(test19, 0, "", "test19"); + is_function_output(test20, 0, "3 0 foo\n", "test20"); + is_function_output(test21, 0, "test23: baz\n", "test21"); /* Make sure that it's possible to turn off a message type entirely. */ - test_error(22, 1, "", test22); - test_error(23, 0, "", test23); - test_error(24, 0, "first\nthird\n", test24); + is_function_output(test22, 1, "", "test22"); + is_function_output(test23, 0, "", "test23"); + is_function_output(test24, 0, "first\nthird\n", "test24"); return 0; } diff --git a/tests/util/xmalloc-t b/tests/util/xmalloc-t new file mode 100755 index 0000000..02f54b5 --- /dev/null +++ b/tests/util/xmalloc-t @@ -0,0 +1,127 @@ +#! /bin/sh +# +# Test suite for xmalloc and friends. +# +# Written by Russ Allbery +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2004, 2005, 2006 +# by Internet Systems Consortium, Inc. ("ISC") +# Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003 by The Internet Software Consortium and Rich Salz +# +# See LICENSE for licensing terms. + +. "$SOURCE/tap/libtap.sh" +cd "$BUILD/util" + +# Run an xmalloc test. Takes the description, the expectd exit status, the +# output, and the arguments. +ok_xmalloc () { + local desc w_status w_output output status + desc="$1" + shift + w_status="$1" + shift + w_output="$1" + shift + output=`./xmalloc "$@" 2>&1` + status=$? + if [ "$w_status" -ne 0 ] ; then + output=`echo "$output" | sed 's/:.*//'` + fi + if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then + ok "$desc" true + elif [ $status = 2 ] ; then + skip "no data limit support" + else + echo "# saw: ($status) $output" + echo "# not: ($w_status) $w_output" + ok "$desc" false + fi +} + +# Skip this test suite unless maintainer-mode tests are enabled. All of the +# failures in automated testing have been problems with the assumptions around +# memory allocation or problems with the test suite, not problems with the +# underlying xmalloc code. +if [ -z "$RRA_MAINTAINER_TESTS" ] ; then + skip_all 'xmalloc tests only run for maintainer' +fi + +# Total tests. +plan 36 + +# First run the tests expected to succeed. +ok_xmalloc "malloc small" 0 "" "m" "21" "0" +ok_xmalloc "malloc large" 0 "" "m" "3500000" "0" +ok_xmalloc "malloc zero" 0 "" "m" "0" "0" +ok_xmalloc "realloc small" 0 "" "r" "21" "0" +ok_xmalloc "realloc large" 0 "" "r" "3500000" "0" +ok_xmalloc "strdup small" 0 "" "s" "21" "0" +ok_xmalloc "strdup large" 0 "" "s" "3500000" "0" +ok_xmalloc "strndup small" 0 "" "n" "21" "0" +ok_xmalloc "strndup large" 0 "" "n" "3500000" "0" +ok_xmalloc "calloc small" 0 "" "c" "24" "0" +ok_xmalloc "calloc large" 0 "" "c" "3500000" "0" +ok_xmalloc "asprintf small" 0 "" "a" "24" "0" +ok_xmalloc "asprintf large" 0 "" "a" "3500000" "0" +ok_xmalloc "vasprintf small" 0 "" "v" "24" "0" +ok_xmalloc "vasprintf large" 0 "" "v" "3500000" "0" + +# Now limit our memory to 3.5MB and then try the large ones again, all of +# which should fail. +# +# The exact memory limits used here are essentially black magic. They need to +# be large enough to allow the program to be loaded and do small allocations, +# but not so large that we can't reasonably expect to allocate that much +# memory normally. 3.5MB seems to work reasonably well on both Solaris and +# Linux. +# +# We assume that there are enough miscellaneous allocations that an allocation +# exactly as large as the limit will always fail. +ok_xmalloc "malloc fail" 1 \ + "failed to malloc 3500000 bytes at xmalloc.c line 38" \ + "m" "3500000" "3500000" +ok_xmalloc "realloc fail" 1 \ + "failed to realloc 3500000 bytes at xmalloc.c line 66" \ + "r" "3500000" "3500000" +ok_xmalloc "strdup fail" 1 \ + "failed to strdup 3500000 bytes at xmalloc.c line 97" \ + "s" "3500000" "3500000" +ok_xmalloc "strndup fail" 1 \ + "failed to strndup 3500000 bytes at xmalloc.c line 124" \ + "n" "3500000" "3500000" +ok_xmalloc "calloc fail" 1 \ + "failed to calloc 3500000 bytes at xmalloc.c line 148" \ + "c" "3500000" "3500000" +ok_xmalloc "asprintf fail" 1 \ + "failed to asprintf 3500000 bytes at xmalloc.c line 173" \ + "a" "3500000" "3500000" +ok_xmalloc "vasprintf fail" 1 \ + "failed to vasprintf 3500000 bytes at xmalloc.c line 193" \ + "v" "3500000" "3500000" + +# Check our custom error handler. +ok_xmalloc "malloc custom" 1 "malloc 3500000 xmalloc.c 38" \ + "M" "3500000" "3500000" +ok_xmalloc "realloc custom" 1 "realloc 3500000 xmalloc.c 66" \ + "R" "3500000" "3500000" +ok_xmalloc "strdup custom" 1 "strdup 3500000 xmalloc.c 97" \ + "S" "3500000" "3500000" +ok_xmalloc "strndup custom" 1 "strndup 3500000 xmalloc.c 124" \ + "N" "3500000" "3500000" +ok_xmalloc "calloc custom" 1 "calloc 3500000 xmalloc.c 148" \ + "C" "3500000" "3500000" +ok_xmalloc "asprintf custom" 1 "asprintf 3500000 xmalloc.c 173" \ + "A" "3500000" "3500000" +ok_xmalloc "vasprintf custom" 1 "vasprintf 3500000 xmalloc.c 193" \ + "V" "3500000" "3500000" + +# Check the smaller ones again just for grins. +ok_xmalloc "malloc retry" 0 "" "m" "21" "3500000" +ok_xmalloc "realloc retry" 0 "" "r" "32" "3500000" +ok_xmalloc "strdup retry" 0 "" "s" "64" "3500000" +ok_xmalloc "strndup retry" 0 "" "n" "20" "3500000" +ok_xmalloc "calloc retry" 0 "" "c" "24" "3500000" +ok_xmalloc "asprintf retry" 0 "" "a" "30" "3500000" +ok_xmalloc "vasprintf retry" 0 "" "v" "35" "3500000" diff --git a/tests/util/xmalloc-t.in b/tests/util/xmalloc-t.in deleted file mode 100644 index 5c18512..0000000 --- a/tests/util/xmalloc-t.in +++ /dev/null @@ -1,126 +0,0 @@ -#! /bin/sh -# -# Test suite for xmalloc and friends. -# -# Copyright 2004, 2005, 2006 -# by Internet Systems Consortium, Inc. ("ISC") -# Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003 by The Internet Software Consortium and Rich Salz -# -# This code is derived from software contributed to the Internet Software -# Consortium by Rich Salz. -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. - -# The count starts at 1 and is updated each time ok is printed. printcount -# takes "ok" or "not ok". -count=1 -printcount () { - echo "$1 $count $2" - count=`expr $count + 1` -} - -# Run a program expected to succeed, and print ok if it does. -runsuccess () { - output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null` - status=$? - if test $status = 0 && test -z "$output" ; then - printcount "ok" - else - if test $status = 2 ; then - printcount "ok" "# skip - no data limit support" - else - printcount "not ok" - echo " $output" - fi - fi -} - -# Run a program expected to fail and make sure it fails with an exit status -# of 2 and the right failure message. Strip the colon and everything after -# it off the error message since it's system-specific. -runfailure () { - output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null` - status=$? - output=`echo "$output" | sed 's/:.*//' \ - | sed 's% [^ ]*/xmalloc.c% xmalloc.c%'` - if test $status = 1 && test x"$output" = x"$4" ; then - printcount "ok" - else - if test $status = 2 ; then - printcount "ok" "# skip - no data limit support" - else - printcount "not ok" - echo " saw: $output" - echo " not: $4" - fi - fi -} - -# Find where the helper program is. -xmalloc="@abs_top_builddir@/tests/util/xmalloc" - -# Total tests. -echo 36 - -# First run the tests expected to succeed. -runsuccess "m" "21" "0" -runsuccess "m" "128000" "0" -runsuccess "m" "0" "0" -runsuccess "r" "21" "0" -runsuccess "r" "128000" "0" -runsuccess "s" "21" "0" -runsuccess "s" "128000" "0" -runsuccess "n" "21" "0" -runsuccess "n" "128000" "0" -runsuccess "c" "24" "0" -runsuccess "c" "128000" "0" -runsuccess "a" "24" "0" -runsuccess "a" "128000" "0" -runsuccess "v" "24" "0" -runsuccess "v" "128000" "0" - -# Now limit our memory to 120KB and then try the large ones again, all of -# which should fail. -runfailure "m" "128000" "120000" \ - "failed to malloc 128000 bytes at xmalloc.c line 61" -runfailure "r" "128000" "120000" \ - "failed to realloc 128000 bytes at xmalloc.c line 90" -runfailure "s" "64000" "120000" \ - "failed to strdup 64000 bytes at xmalloc.c line 121" -runfailure "n" "64000" "120000" \ - "failed to strndup 64000 bytes at xmalloc.c line 148" -runfailure "c" "128000" "120000" \ - "failed to calloc 128000 bytes at xmalloc.c line 172" -runfailure "a" "64000" "120000" \ - "failed to asprintf 64000 bytes at xmalloc.c line 241" -runfailure "v" "64000" "120000" \ - "failed to vasprintf 64000 bytes at xmalloc.c line 217" - -# Check our custom error handler. -runfailure "M" "128000" "120000" "malloc 128000 xmalloc.c 61" -runfailure "R" "128000" "120000" "realloc 128000 xmalloc.c 90" -runfailure "S" "64000" "120000" "strdup 64000 xmalloc.c 121" -runfailure "N" "64000" "120000" "strndup 64000 xmalloc.c 148" -runfailure "C" "128000" "120000" "calloc 128000 xmalloc.c 172" -runfailure "A" "64000" "120000" "asprintf 64000 xmalloc.c 241" -runfailure "V" "64000" "120000" "vasprintf 64000 xmalloc.c 217" - -# Check the smaller ones again just for grins. -runsuccess "m" "21" "96000" -runsuccess "r" "32" "96000" -runsuccess "s" "64" "96000" -runsuccess "n" "20" "96000" -runsuccess "c" "24" "96000" -runsuccess "a" "30" "96000" -runsuccess "v" "35" "96000" diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index bd0ab62..3bd5588 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -1,27 +1,17 @@ /* * Test suite for xmalloc and family. * + * Copyright 2008 Board of Trustees, Leland Stanford Jr. University * Copyright 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, * 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ +#line 1 "xmalloc.c" + #include #include @@ -32,7 +22,8 @@ /* Linux requires sys/time.h be included before sys/resource.h. */ #include -#include +#include +#include /* @@ -81,20 +72,19 @@ test_realloc(size_t size) char *buffer; size_t i; - buffer = xmalloc(size / 2); + buffer = xmalloc(10); if (buffer == NULL) return 0; - if (size / 2 > 0) - memset(buffer, 1, size / 2); + memset(buffer, 1, 10); buffer = xrealloc(buffer, size); if (buffer == NULL) return 0; if (size > 0) - memset(buffer + size / 2, 2, size - size / 2); - for (i = 0; i < size / 2; i++) + memset(buffer + 10, 2, size - 10); + for (i = 0; i < 10; i++) if (buffer[i] != 1) return 0; - for (i = size / 2; i < size; i++) + for (i = 10; i < size; i++) if (buffer[i] != 2) return 0; free(buffer); @@ -257,6 +247,7 @@ main(int argc, char *argv[]) int willfail = 0; unsigned char code; struct rlimit rl; + void *tmp; if (argc < 3) die("Usage error. Type, size, and limit must be given."); @@ -269,6 +260,27 @@ main(int argc, char *argv[]) if (limit == 0 && errno != 0) sysdie("Invalid limit"); + /* If the code is capitalized, install our customized error handler. */ + code = argv[1][0]; + if (isupper(code)) { + xmalloc_error_handler = test_handler; + code = tolower(code); + } + + /* + * Decide if the allocation should fail. If it should, set willfail to 2, + * so that if it unexpectedly succeeds, we exit with a status indicating + * that the test should be skipped. + */ + max = size; + if (code == 's' || code == 'n' || code == 'a' || code == 'v') { + max += size; + if (limit > 0) + limit += size; + } + if (limit > 0 && max > limit) + willfail = 2; + /* * If a memory limit was given and we can set memory limits, set it. * Otherwise, exit 2, signalling to the driver that the test should be @@ -277,37 +289,28 @@ main(int argc, char *argv[]) * the shell to die). */ if (limit > 0) { -#if HAVE_SETRLIMIT && defined(RLIMIT_DATA) +#if HAVE_SETRLIMIT && defined(RLIMIT_AS) rl.rlim_cur = limit; rl.rlim_max = limit; - if (setrlimit(RLIMIT_DATA, &rl) < 0) { + if (setrlimit(RLIMIT_AS, &rl) < 0) { syswarn("Can't set data limit to %lu", (unsigned long) limit); exit(2); } + if (size < limit || code == 'r') { + tmp = malloc(code == 'r' ? 10 : size); + if (tmp == NULL) { + syswarn("Can't allocate initial memory of %lu", + (unsigned long) size); + exit(2); + } + free(tmp); + } #else warn("Data limits aren't supported."); exit(2); #endif } - /* If the code is capitalized, install our customized error handler. */ - code = argv[1][0]; - if (isupper(code)) { - xmalloc_error_handler = test_handler; - code = tolower(code); - } - - /* - * Decide if the allocation should fail. If it should, set willfail to 2, - * so that if it unexpectedly succeeds, we exit with a status indicating - * that the test should be skipped. - */ - max = size; - if (code == 's' || code == 'n' || code == 'a' || code == 'v') - max *= 2; - if (limit > 0 && max > limit) - willfail = 2; - switch (code) { case 'c': exit(test_calloc(size) ? willfail : 1); case 'm': exit(test_malloc(size) ? willfail : 1); -- cgit v1.2.3 From ff2d5ac3c63af9833d884d4840c772e60e45da7d Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 19:55:12 -0800 Subject: Use the $SOURCE and $BUILD test suite variables Now that runtests has been updated to a version that sets $SOURCE and $BUILD, use that in the test cases rather than Autoconf substitutions. --- tests/client/full-t.in | 20 ++++++++++---------- tests/client/pod-t.in | 4 ++-- tests/client/prompt-t.in | 28 +++++++++++++--------------- tests/server/admin-t.in | 4 ++-- tests/server/backend-t.in | 4 ++-- tests/server/keytab-t.in | 9 ++++----- tests/server/pod-t.in | 6 +++--- 7 files changed, 36 insertions(+), 39 deletions(-) (limited to 'tests') diff --git a/tests/client/full-t.in b/tests/client/full-t.in index 3240563..a4ca19d 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -1,23 +1,23 @@ #!/usr/bin/perl -w # -# tests/client/full-t -- End-to-end tests for the wallet client. +# End-to-end tests for the wallet client. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. # Point to our server configuration. This must be done before Wallet::Config # is loaded, and it's pulled in as a prerequisite for Wallet::Admin. -BEGIN { $ENV{WALLET_CONFIG} = '@abs_top_srcdir@/tests/data/wallet.conf' } +BEGIN { $ENV{WALLET_CONFIG} = "$ENV{SOURCE}/data/wallet.conf" } BEGIN { our $total = 53 } use Test::More tests => $total; -use lib '@abs_top_srcdir@/perl'; +use lib "$ENV{SOURCE}/../perl"; use Wallet::Admin; -use lib '@abs_top_srcdir@/perl/t/lib'; +use lib "$ENV{SOURCE}/../perl/t/lib"; use Util; # Make a call to the wallet client. Takes the principal used by the server @@ -33,9 +33,9 @@ sub wallet { or die "cannot create wallet.out: $!\n"; open (STDERR, '>', 'wallet.err') or die "cannot create wallet.err: $!\n"; - exec ('@abs_top_builddir@/client/wallet', '-k', $principal, '-p', + exec ("$ENV{BUILD}/../client/wallet", '-k', $principal, '-p', '14373', '-s', 'localhost', @command) - or die "cannot run @abs_top_builddir@/client/wallet: $!\n"; + or die "cannot run $ENV{BUILD}/client/wallet: $!\n"; } else { waitpid ($pid, 0); } @@ -53,19 +53,19 @@ sub wallet { SKIP: { skip 'no keytab configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.keytab'; + unless -f "$ENV{BUILD}/data/test.keytab"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; # Spawn remctld and get local tickets. Don't destroy the user's Kerberos # ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ('@abs_top_builddir@/tests/data/test.principal'); + my $principal = contents ("$ENV{BUILD}/data/test.principal"); remctld_spawn ($remctld, $principal, '@abs_top_builddir@/tests/data/test.keytab', '@abs_top_builddir@/tests/data/full.conf'); $ENV{KRB5CCNAME} = 'krb5cc_test'; - getcreds ('@abs_top_builddir@/tests/data/test.keytab', $principal); + getcreds ("$ENV{BUILD}/data/test.keytab", $principal); # Use Wallet::Admin to set up the database. db_setup; diff --git a/tests/client/pod-t.in b/tests/client/pod-t.in index db995f7..9963567 100644 --- a/tests/client/pod-t.in +++ b/tests/client/pod-t.in @@ -3,7 +3,7 @@ # Test POD formatting for client documentation. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -17,6 +17,6 @@ eval 'use Test::Pod 1.00'; SKIP: { skip $total, 'Test::Pod 1.00 required for testing POD' if $@; for my $file (@files) { - pod_file_ok ("@abs_top_srcdir@/client/$file", "client/$file"); + pod_file_ok ("$ENV{SOURCE}/../client/$file", "client/$file"); } } diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index 7988fc9..e037b3f 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -1,28 +1,27 @@ #!/usr/bin/perl -w # -# tests/client/prompt-t -- Password prompting tests for the wallet client. +# Password prompting tests for the wallet client. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. BEGIN { our $total = 5 } use Test::More tests => $total; -use lib '@abs_top_srcdir@/perl'; +use lib "$ENV{SOURCE}/..//perl"; use Wallet::Admin; -use lib '@abs_top_srcdir@/perl/t/lib'; +use lib "$ENV{SOURCE}/../perl/t/lib"; use Util; # cd to the correct directory. -chdir '@abs_top_srcdir@/tests' - or die "Cannot chdir to @abs_top_srcdir@/tests: $!\n"; +chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; SKIP: { skip 'no password configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.password'; + unless -f "$ENV{BUILD}/data/test.password"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; eval { require Expect }; @@ -35,22 +34,21 @@ SKIP: { # Spawn remctld and set up with a different ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ('@abs_top_builddir@/tests/data/test.principal'); - remctld_spawn ($remctld, $principal, - '@abs_top_builddir@/tests/data/test.keytab', - '@abs_top_builddir@/tests/data/basic.conf'); + my $principal = contents ("$ENV{BUILD}/data/test.principal"); + remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", + "$ENV{BUILD}/data/basic.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; # Read in the principal and password. - open (PASS, '<', '@abs_top_builddir@/tests/data/test.password') - or die "Cannot open @abs_top_builddir@/tests/data/test.password: $!\n"; + open (PASS, '<', "$ENV{BUILD}/data/test.password") + or die "Cannot open $ENV{BUILD}/data/test.password: $!\n"; my $user = ; my $password = ; close PASS; chomp ($user, $password); # Spawn wallet and check an invalid password. - my $wallet = Expect->spawn ('@abs_top_builddir@/client/wallet', '-k', + my $wallet = Expect->spawn ("$ENV{BUILD}/../client/wallet", '-k', $principal, '-p', 14373, '-s', 'localhost', '-c', 'fake-wallet', '-u', $user, 'get', 'keytab', 'service/fake-output'); @@ -61,7 +59,7 @@ SKIP: { $wallet->soft_close; # Now check a valid password. - $wallet = Expect->spawn ('@abs_top_builddir@/client/wallet', '-k', + $wallet = Expect->spawn ("$ENV{BUILD}/../client/wallet", '-k', $principal, '-p', 14373, '-s', 'localhost', '-c', 'fake-wallet', '-u', $user, 'get', 'keytab', 'service/fake-output'); diff --git a/tests/server/admin-t.in b/tests/server/admin-t.in index 11d2883..570dc52 100644 --- a/tests/server/admin-t.in +++ b/tests/server/admin-t.in @@ -3,7 +3,7 @@ # Tests for the wallet-admin dispatch code. # # Written by Russ Allbery -# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -82,7 +82,7 @@ sub report_owners { # Wallet::Admin package has already been loaded. package main; $INC{'Wallet/Admin.pm'} = 'FAKE'; -eval { do '@abs_top_srcdir@/server/wallet-admin' }; +eval { do "$ENV{SOURCE}/../server/wallet-admin" }; # Run the wallet admin client. This fun hack takes advantage of the fact that # the wallet admin client is written in Perl so that we can substitute our own diff --git a/tests/server/backend-t.in b/tests/server/backend-t.in index 0c6ac60..2fc6a53 100644 --- a/tests/server/backend-t.in +++ b/tests/server/backend-t.in @@ -3,7 +3,7 @@ # Tests for the wallet-backend dispatch code. # # Written by Russ Allbery -# Copyright 2006, 2007, 2008, 2009 +# Copyright 2006, 2007, 2008, 2009, 2010 # Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -163,7 +163,7 @@ package main; $INC{'Wallet/Server.pm'} = 'FAKE'; my $OUTPUT; our $SYSLOG = \$OUTPUT; -eval { do '@abs_top_srcdir@/server/wallet-backend' }; +eval { do "$ENV{SOURCE}/../server/wallet-backend" }; # Run the wallet backend. This fun hack takes advantage of the fact that the # wallet backend is written in Perl so that we can substitute our own diff --git a/tests/server/keytab-t.in b/tests/server/keytab-t.in index f74267d..2a0ceed 100644 --- a/tests/server/keytab-t.in +++ b/tests/server/keytab-t.in @@ -1,10 +1,9 @@ #!/usr/bin/perl -w -# $Id: backend-t.in 3547 2007-09-14 23:18:48Z rra $ # # Tests for the keytab-backend dispatch code. # # Written by Russ Allbery -# Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -16,9 +15,9 @@ use Test::More tests => 63; # Load the keytab-backend code and override various settings. my $OUTPUT; $SYSLOG = \$OUTPUT; -eval { do '@abs_top_srcdir@/server/keytab-backend' }; -$CONFIG = '@abs_top_srcdir@/tests/data/allow-extract'; -$KADMIN = '@abs_top_srcdir@/tests/data/fake-kadmin'; +eval { do "$ENV{SOURCE}/../server/keytab-backend" }; +$CONFIG = "$ENV{SOURCE}/data/allow-extract"; +$KADMIN = "$ENV{SOURCE}/data/fake-kadmin"; $TMP = '.'; # Run the keytab backend. diff --git a/tests/server/pod-t.in b/tests/server/pod-t.in index 4575ecb..52d81eb 100644 --- a/tests/server/pod-t.in +++ b/tests/server/pod-t.in @@ -1,9 +1,9 @@ #!/usr/bin/perl # -# tests/server/pod-t -- Test POD formatting for client documentation. +# Test POD formatting for client documentation. # # Written by Russ Allbery -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -17,6 +17,6 @@ eval 'use Test::Pod 1.00'; SKIP: { skip 'Test::Pod 1.00 required for testing POD', $total if $@; for my $file (@files) { - pod_file_ok ("@abs_top_srcdir@/server/$file", "server/$file"); + pod_file_ok ("$ENV{SOURCE}/../server/$file", "server/$file"); } } -- cgit v1.2.3 From a556c732806da87d06bb787565e12240ea39b553 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 21:01:33 -0800 Subject: Stop doing Autoconf substitution on some test suite code Anything that only was using substitution for the paths to the build tree now uses $SOURCE and $BUILD instead. Stop doing substitution. Also fix tests/data/cmd-wrapper to use the environment variables. --- .gitignore | 7 - configure.ac | 6 - tests/client/pod-t | 22 ++ tests/client/pod-t.in | 22 -- tests/data/cmd-wrapper | 8 + tests/data/cmd-wrapper.in | 9 - tests/server/admin-t | 241 ++++++++++++++++++++++ tests/server/admin-t.in | 241 ---------------------- tests/server/backend-t | 502 ++++++++++++++++++++++++++++++++++++++++++++++ tests/server/backend-t.in | 502 ---------------------------------------------- tests/server/keytab-t | 88 ++++++++ tests/server/keytab-t.in | 88 -------- tests/server/pod-t | 22 ++ tests/server/pod-t.in | 22 -- 14 files changed, 883 insertions(+), 897 deletions(-) create mode 100755 tests/client/pod-t delete mode 100644 tests/client/pod-t.in create mode 100755 tests/data/cmd-wrapper delete mode 100644 tests/data/cmd-wrapper.in create mode 100755 tests/server/admin-t delete mode 100644 tests/server/admin-t.in create mode 100755 tests/server/backend-t delete mode 100644 tests/server/backend-t.in create mode 100755 tests/server/keytab-t delete mode 100644 tests/server/keytab-t.in create mode 100755 tests/server/pod-t delete mode 100644 tests/server/pod-t.in (limited to 'tests') diff --git a/.gitignore b/.gitignore index 09ae109..b0a49df 100644 --- a/.gitignore +++ b/.gitignore @@ -18,15 +18,12 @@ /perl/t/data/test.krbtype /tests/client/basic-t /tests/client/full-t -/tests/client/pod-t /tests/client/prompt-t -/tests/data/cmd-wrapper /tests/data/full.conf /tests/data/test.keytab /tests/data/test.password /tests/data/test.principal /tests/data/test.krbtype -/tests/kasetkey/basic-t /tests/portable/asprintf-t /tests/portable/mkstemp-t /tests/portable/setenv-t @@ -34,10 +31,6 @@ /tests/portable/strlcat-t /tests/portable/strlcpy-t /tests/runtests -/tests/server/admin-t -/tests/server/backend-t -/tests/server/keytab-t -/tests/server/pod-t /tests/util/concat-t /tests/util/messages-krb5-t /tests/util/messages-t diff --git a/configure.ac b/configure.ac index c897775..664c6f7 100644 --- a/configure.ac +++ b/configure.ac @@ -64,11 +64,5 @@ AC_CONFIG_HEADER([config.h]) AC_CONFIG_FILES([Makefile perl/Makefile.PL tests/data/full.conf]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) -AC_CONFIG_FILES([tests/client/pod-t], [chmod +x tests/client/pod-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) -AC_CONFIG_FILES([tests/data/cmd-wrapper], [chmod +x tests/data/cmd-wrapper]) -AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) -AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) -AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) -AC_CONFIG_FILES([tests/server/pod-t], [chmod +x tests/server/pod-t]) AC_OUTPUT diff --git a/tests/client/pod-t b/tests/client/pod-t new file mode 100755 index 0000000..9963567 --- /dev/null +++ b/tests/client/pod-t @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# +# Test POD formatting for client documentation. +# +# Written by Russ Allbery +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use Test::More; + +my @files = qw(wallet.pod); +my $total = scalar (@files); +plan tests => $total; + +eval 'use Test::Pod 1.00'; +SKIP: { + skip $total, 'Test::Pod 1.00 required for testing POD' if $@; + for my $file (@files) { + pod_file_ok ("$ENV{SOURCE}/../client/$file", "client/$file"); + } +} diff --git a/tests/client/pod-t.in b/tests/client/pod-t.in deleted file mode 100644 index 9963567..0000000 --- a/tests/client/pod-t.in +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -# -# Test POD formatting for client documentation. -# -# Written by Russ Allbery -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use Test::More; - -my @files = qw(wallet.pod); -my $total = scalar (@files); -plan tests => $total; - -eval 'use Test::Pod 1.00'; -SKIP: { - skip $total, 'Test::Pod 1.00 required for testing POD' if $@; - for my $file (@files) { - pod_file_ok ("$ENV{SOURCE}/../client/$file", "client/$file"); - } -} diff --git a/tests/data/cmd-wrapper b/tests/data/cmd-wrapper new file mode 100755 index 0000000..79b1943 --- /dev/null +++ b/tests/data/cmd-wrapper @@ -0,0 +1,8 @@ +#!/bin/sh +# +# Wrapper around the standard wallet-backend script that sets the Perl INC +# path and the WALLET_CONFIG environment variable appropriately. + +WALLET_CONFIG="$SOURCE/data/wallet.conf" +export WALLET_CONFIG +exec perl -I"$SOURCE/../perl" "$SOURCE/../server/wallet-backend" -q "$@" diff --git a/tests/data/cmd-wrapper.in b/tests/data/cmd-wrapper.in deleted file mode 100644 index 7c7b342..0000000 --- a/tests/data/cmd-wrapper.in +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# -# Wrapper around the standard wallet-backend script that sets the Perl INC -# path and the WALLET_CONFIG environment variable appropriately. - -WALLET_CONFIG='@abs_top_srcdir@/tests/data/wallet.conf' -export WALLET_CONFIG -exec perl -I'@abs_top_srcdir@/perl' '@abs_top_srcdir@/server/wallet-backend' \ - -q "$@" diff --git a/tests/server/admin-t b/tests/server/admin-t new file mode 100755 index 0000000..570dc52 --- /dev/null +++ b/tests/server/admin-t @@ -0,0 +1,241 @@ +#!/usr/bin/perl -w +# +# Tests for the wallet-admin dispatch code. +# +# Written by Russ Allbery +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use Test::More tests => 64; + +# Create a dummy class for Wallet::Admin that prints what method was called +# with its arguments and returns data for testing. +package Wallet::Admin; + +use vars qw($empty $error); +$error = 0; +$empty = 0; + +sub error { + if ($error) { + return "some error"; + } else { + return; + } +} + +sub new { + print "new\n"; + return bless ({}, 'Wallet::Admin'); +} + +sub destroy { + print "destroy\n"; + return if $error; + return 1; +} + +sub initialize { + shift; + print "initialize @_\n"; + return if $error; + return 1; +} + +sub list_objects { + print "list_objects\n"; + return if ($error or $empty); + return ([ keytab => 'host/windlord.stanford.edu' ], + [ file => 'unix-wallet-password' ]); +} + +sub list_acls { + print "list_acls\n"; + return if ($error or $empty); + return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]); +} + +sub register_object { + shift; + print "register_object @_\n"; + return if $error; + return 1; +} + +sub register_verifier { + shift; + print "register_verifier @_\n"; + return if $error; + return 1; +} + +sub report_owners { + shift; + print "report_owners @_\n"; + return if ($error or $empty); + return ([ krb5 => 'admin@EXAMPLE.COM' ]); +} + +# Back to the main package and the actual test suite. Lie about whether the +# Wallet::Admin package has already been loaded. +package main; +$INC{'Wallet/Admin.pm'} = 'FAKE'; +eval { do "$ENV{SOURCE}/../server/wallet-admin" }; + +# Run the wallet admin client. This fun hack takes advantage of the fact that +# the wallet admin client is written in Perl so that we can substitute our own +# Wallet::Admin class. +sub run_admin { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { command (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# Now for the actual tests. First check for unknown commands. +my ($out, $err) = run_admin ('foo'); +is ($err, "unknown command foo\n", 'Unknown command'); +is ($out, "new\n", ' and nothing ran'); + +# Check too few and too many arguments for every command. +my %commands = (destroy => [0, 0], + initialize => [1, 1], + list => [1, 4], + register => [3, 3], + report => [1, -1]); +for my $command (sort keys %commands) { + my ($min, $max) = @{ $commands{$command} }; + if ($min > 0) { + ($out, $err) = run_admin ($command, ('foo') x ($min - 1)); + is ($err, "too few arguments to $command\n", + "Too few arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } + if ($max >= 0) { + ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments to $command\n", + "Too many arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } +} + +# Test destroy. +my $answer = ''; +close STDIN; +open (STDIN, '<', \$answer) or die "cannot reopen standard input: $!\n"; +($out, $err) = run_admin ('destroy'); +is ($err, "Aborted\n", 'Destroy with no answer aborts'); +is ($out, "new\n" . + 'This will delete all data in the wallet database. Are you sure (N/y)? ', + ' and prints the right prompt'); +seek (STDIN, 0, 0); +$answer = 'n'; +($out, $err) = run_admin ('destroy'); +is ($err, "Aborted\n", 'Destroy with negative answer answer aborts'); +is ($out, "new\n" . + 'This will delete all data in the wallet database. Are you sure (N/y)? ', + ' and prints the right prompt'); +seek (STDIN, 0, 0); +$answer = 'y'; +($out, $err) = run_admin ('destroy'); +is ($err, '', 'Destroy succeeds with a positive answer'); +is ($out, "new\n" + . 'This will delete all data in the wallet database.' + . ' Are you sure (N/y)? ' . "destroy\n", ' and destroy was run'); +seek (STDIN, 0, 0); + +# Test initialize. +($out, $err) = run_admin ('initialize', 'rra'); +is ($err, "invalid admin principal rra\n", 'Initialize requires a principal'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); +is ($err, '', 'Initialize succeeds with a principal'); +is ($out, "new\ninitialize rra\@stanford.edu\n", ' and runs the right code'); + +# Test list. +($out, $err) = run_admin ('list', 'foo'); +is ($err, "only objects or acls are supported for list\n", + 'List requires a known object'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('list', 'objects'); +is ($err, '', 'List succeeds for objects'); +is ($out, "new\nlist_objects\n" + . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", + ' and returns the right output'); +($out, $err) = run_admin ('list', 'acls'); +is ($err, '', 'List succeeds for ACLs'); +is ($out, "new\nlist_acls\n" + . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", + ' and returns the right output'); + +# Test register. +($out, $err) = run_admin ('register', 'foo', 'foo', 'Foo::Bar'); +is ($err, "only object or verifier is supported for register\n", + 'Register requires object or verifier'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); +is ($err, '', 'Register succeeds for object'); +is ($out, "new\nregister_object foo Foo::Object\n", + ' and returns the right outout'); +($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); +is ($err, '', 'Register succeeds for verifier'); +is ($out, "new\nregister_verifier foo Foo::Verifier\n", + ' and returns the right outout'); + +# Test report. +($out, $err) = run_admin ('report', 'foo'); +is ($err, "unknown report type foo\n", 'Report requires a known report'); +is ($out, "new\n", ' and nothing was run'); +($out, $err) = run_admin ('report', 'owners', '%', '%'); +is ($err, '', 'Report succeeds for owners'); +is ($out, "new\nreport_owners % %\nkrb5 admin\@EXAMPLE.COM\n", + ' and returns the right output'); + +# Test error handling. +$Wallet::Admin::error = 1; +($out, $err) = run_admin ('destroy'); +is ($err, "some error\n", 'Error handling succeeds for destroy'); +is ($out, "new\n" + . 'This will delete all data in the wallet database.' + . ' Are you sure (N/y)? ' . "destroy\n", ' and calls the right methods'); +($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); +is ($err, "some error\n", 'Error handling succeeds for initialize'); +is ($out, "new\ninitialize rra\@stanford.edu\n", + ' and calls the right methods'); +($out, $err) = run_admin ('list', 'objects'); +is ($err, "some error\n", 'Error handling succeeds for list objects'); +is ($out, "new\nlist_objects\n", ' and calls the right methods'); +($out, $err) = run_admin ('list', 'acls'); +is ($err, "some error\n", 'Error handling succeeds for list acls'); +is ($out, "new\nlist_acls\n", ' and calls the right methods'); +($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); +is ($err, "some error\n", 'Error handling succeeds for register object'); +is ($out, "new\nregister_object foo Foo::Object\n", + ' and calls the right methods'); +($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); +is ($err, "some error\n", 'Error handling succeeds for register verifier'); +is ($out, "new\nregister_verifier foo Foo::Verifier\n", + ' and calls the right methods'); +($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); +is ($err, "some error\n", 'Error handling succeeds for report owners'); +is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); + +# Test empty lists. +$Wallet::Admin::error = 0; +$Wallet::Admin::empty = 1; +($out, $err) = run_admin ('list', 'objects'); +is ($err, '', 'list objects runs with an empty list with no errors'); +is ($out, "new\nlist_objects\n", ' and calls the right methods'); +($out, $err) = run_admin ('list', 'acls'); +is ($err, '', 'list acls runs with an empty list and no errors'); +is ($out, "new\nlist_acls\n", ' and calls the right methods'); +($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); +is ($err, '', 'report owners runs with an empty list and no errors'); +is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); diff --git a/tests/server/admin-t.in b/tests/server/admin-t.in deleted file mode 100644 index 570dc52..0000000 --- a/tests/server/admin-t.in +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for the wallet-admin dispatch code. -# -# Written by Russ Allbery -# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use strict; -use Test::More tests => 64; - -# Create a dummy class for Wallet::Admin that prints what method was called -# with its arguments and returns data for testing. -package Wallet::Admin; - -use vars qw($empty $error); -$error = 0; -$empty = 0; - -sub error { - if ($error) { - return "some error"; - } else { - return; - } -} - -sub new { - print "new\n"; - return bless ({}, 'Wallet::Admin'); -} - -sub destroy { - print "destroy\n"; - return if $error; - return 1; -} - -sub initialize { - shift; - print "initialize @_\n"; - return if $error; - return 1; -} - -sub list_objects { - print "list_objects\n"; - return if ($error or $empty); - return ([ keytab => 'host/windlord.stanford.edu' ], - [ file => 'unix-wallet-password' ]); -} - -sub list_acls { - print "list_acls\n"; - return if ($error or $empty); - return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]); -} - -sub register_object { - shift; - print "register_object @_\n"; - return if $error; - return 1; -} - -sub register_verifier { - shift; - print "register_verifier @_\n"; - return if $error; - return 1; -} - -sub report_owners { - shift; - print "report_owners @_\n"; - return if ($error or $empty); - return ([ krb5 => 'admin@EXAMPLE.COM' ]); -} - -# Back to the main package and the actual test suite. Lie about whether the -# Wallet::Admin package has already been loaded. -package main; -$INC{'Wallet/Admin.pm'} = 'FAKE'; -eval { do "$ENV{SOURCE}/../server/wallet-admin" }; - -# Run the wallet admin client. This fun hack takes advantage of the fact that -# the wallet admin client is written in Perl so that we can substitute our own -# Wallet::Admin class. -sub run_admin { - my (@args) = @_; - my $result = ''; - open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; - select OUTPUT; - local $| = 1; - eval { command (@args) }; - my $error = $@; - select STDOUT; - return ($result, $error); -} - -# Now for the actual tests. First check for unknown commands. -my ($out, $err) = run_admin ('foo'); -is ($err, "unknown command foo\n", 'Unknown command'); -is ($out, "new\n", ' and nothing ran'); - -# Check too few and too many arguments for every command. -my %commands = (destroy => [0, 0], - initialize => [1, 1], - list => [1, 4], - register => [3, 3], - report => [1, -1]); -for my $command (sort keys %commands) { - my ($min, $max) = @{ $commands{$command} }; - if ($min > 0) { - ($out, $err) = run_admin ($command, ('foo') x ($min - 1)); - is ($err, "too few arguments to $command\n", - "Too few arguments for $command"); - is ($out, "new\n", ' and nothing ran'); - } - if ($max >= 0) { - ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); - is ($err, "too many arguments to $command\n", - "Too many arguments for $command"); - is ($out, "new\n", ' and nothing ran'); - } -} - -# Test destroy. -my $answer = ''; -close STDIN; -open (STDIN, '<', \$answer) or die "cannot reopen standard input: $!\n"; -($out, $err) = run_admin ('destroy'); -is ($err, "Aborted\n", 'Destroy with no answer aborts'); -is ($out, "new\n" . - 'This will delete all data in the wallet database. Are you sure (N/y)? ', - ' and prints the right prompt'); -seek (STDIN, 0, 0); -$answer = 'n'; -($out, $err) = run_admin ('destroy'); -is ($err, "Aborted\n", 'Destroy with negative answer answer aborts'); -is ($out, "new\n" . - 'This will delete all data in the wallet database. Are you sure (N/y)? ', - ' and prints the right prompt'); -seek (STDIN, 0, 0); -$answer = 'y'; -($out, $err) = run_admin ('destroy'); -is ($err, '', 'Destroy succeeds with a positive answer'); -is ($out, "new\n" - . 'This will delete all data in the wallet database.' - . ' Are you sure (N/y)? ' . "destroy\n", ' and destroy was run'); -seek (STDIN, 0, 0); - -# Test initialize. -($out, $err) = run_admin ('initialize', 'rra'); -is ($err, "invalid admin principal rra\n", 'Initialize requires a principal'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); -is ($err, '', 'Initialize succeeds with a principal'); -is ($out, "new\ninitialize rra\@stanford.edu\n", ' and runs the right code'); - -# Test list. -($out, $err) = run_admin ('list', 'foo'); -is ($err, "only objects or acls are supported for list\n", - 'List requires a known object'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('list', 'objects'); -is ($err, '', 'List succeeds for objects'); -is ($out, "new\nlist_objects\n" - . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", - ' and returns the right output'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, '', 'List succeeds for ACLs'); -is ($out, "new\nlist_acls\n" - . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", - ' and returns the right output'); - -# Test register. -($out, $err) = run_admin ('register', 'foo', 'foo', 'Foo::Bar'); -is ($err, "only object or verifier is supported for register\n", - 'Register requires object or verifier'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); -is ($err, '', 'Register succeeds for object'); -is ($out, "new\nregister_object foo Foo::Object\n", - ' and returns the right outout'); -($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); -is ($err, '', 'Register succeeds for verifier'); -is ($out, "new\nregister_verifier foo Foo::Verifier\n", - ' and returns the right outout'); - -# Test report. -($out, $err) = run_admin ('report', 'foo'); -is ($err, "unknown report type foo\n", 'Report requires a known report'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('report', 'owners', '%', '%'); -is ($err, '', 'Report succeeds for owners'); -is ($out, "new\nreport_owners % %\nkrb5 admin\@EXAMPLE.COM\n", - ' and returns the right output'); - -# Test error handling. -$Wallet::Admin::error = 1; -($out, $err) = run_admin ('destroy'); -is ($err, "some error\n", 'Error handling succeeds for destroy'); -is ($out, "new\n" - . 'This will delete all data in the wallet database.' - . ' Are you sure (N/y)? ' . "destroy\n", ' and calls the right methods'); -($out, $err) = run_admin ('initialize', 'rra@stanford.edu'); -is ($err, "some error\n", 'Error handling succeeds for initialize'); -is ($out, "new\ninitialize rra\@stanford.edu\n", - ' and calls the right methods'); -($out, $err) = run_admin ('list', 'objects'); -is ($err, "some error\n", 'Error handling succeeds for list objects'); -is ($out, "new\nlist_objects\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, "some error\n", 'Error handling succeeds for list acls'); -is ($out, "new\nlist_acls\n", ' and calls the right methods'); -($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); -is ($err, "some error\n", 'Error handling succeeds for register object'); -is ($out, "new\nregister_object foo Foo::Object\n", - ' and calls the right methods'); -($out, $err) = run_admin ('register', 'verifier', 'foo', 'Foo::Verifier'); -is ($err, "some error\n", 'Error handling succeeds for register verifier'); -is ($out, "new\nregister_verifier foo Foo::Verifier\n", - ' and calls the right methods'); -($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); -is ($err, "some error\n", 'Error handling succeeds for report owners'); -is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); - -# Test empty lists. -$Wallet::Admin::error = 0; -$Wallet::Admin::empty = 1; -($out, $err) = run_admin ('list', 'objects'); -is ($err, '', 'list objects runs with an empty list with no errors'); -is ($out, "new\nlist_objects\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, '', 'list acls runs with an empty list and no errors'); -is ($out, "new\nlist_acls\n", ' and calls the right methods'); -($out, $err) = run_admin ('report', 'owners', 'foo', 'bar'); -is ($err, '', 'report owners runs with an empty list and no errors'); -is ($out, "new\nreport_owners foo bar\n", ' and calls the right methods'); diff --git a/tests/server/backend-t b/tests/server/backend-t new file mode 100755 index 0000000..2fc6a53 --- /dev/null +++ b/tests/server/backend-t @@ -0,0 +1,502 @@ +#!/usr/bin/perl -w +# +# Tests for the wallet-backend dispatch code. +# +# Written by Russ Allbery +# Copyright 2006, 2007, 2008, 2009, 2010 +# Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use Test::More tests => 1263; + +# Create a dummy class for Wallet::Server that prints what method was called +# with its arguments and returns data for testing. +package Wallet::Server; + +use vars qw($error $okay); +$error = 0; +$okay = 0; + +sub error { + if ($okay) { + $okay = 0; + return; + } else { + $error++; + return "error count $error"; + } +} + +sub new { shift; print "new @_\n"; return bless ({}, 'Wallet::Server') } +sub create { shift; print "create @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub destroy { shift; print "destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub store { shift; print "store @_\n"; ($_[0] eq 'error') ? undef : 1 } + +sub acl_add + { shift; print "acl_add @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_create + { shift; print "acl_create @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_destroy + { shift; print "acl_destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_remove + { shift; print "acl_remove @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub acl_rename + { shift; print "acl_rename @_\n"; ($_[0] eq 'error') ? undef : 1 } + +sub acl_history { + shift; + print "acl_history @_\n"; + return if $_[0] eq 'error'; + return 'acl_history'; +} + +sub acl_show { + shift; + print "acl_show @_\n"; + return if $_[0] eq 'error'; + return 'acl_show'; +} + +sub flag_clear + { shift; print "flag_clear @_\n"; ($_[0] eq 'error') ? undef : 1 } +sub flag_set + { shift; print "flag_set @_\n"; ($_[0] eq 'error') ? undef : 1 } + +sub acl { + shift; + print "acl @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } else { + return 'acl'; + } +} + +sub attr { + shift; + print "attr @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } elsif (@_ == 3) { + return ('attr1', 'attr2'); + } else { + return 'attr'; + } +} + +sub autocreate { + shift; + print "autocreate @_\n"; + return ($_[0] eq 'error') ? undef : 1 +} + +sub check { + shift; + print "check @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + return 0; + } else { + return 1; + } +} + +sub expires { + shift; + print "expires @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } else { + return 'expires'; + } +} + +sub get { + shift; + print "get @_\n"; + return if $_[0] eq 'error'; + return 'get'; +} + +sub history { + shift; + print "history @_\n"; + return if $_[0] eq 'error'; + return 'history'; +} + +sub owner { + shift; + print "owner @_\n"; + if ($_[0] eq 'error') { + return; + } elsif ($_[1] eq 'empty') { + $okay = 1; + return; + } else { + return 'owner'; + } +} + +sub show { + shift; + print "show @_\n"; + return if $_[0] eq 'error'; + return 'show'; +} + +# Back to the main package and the actual test suite. Lie about whether the +# Wallet::Server package has already been loaded. +package main; +$INC{'Wallet/Server.pm'} = 'FAKE'; +my $OUTPUT; +our $SYSLOG = \$OUTPUT; +eval { do "$ENV{SOURCE}/../server/wallet-backend" }; + +# Run the wallet backend. This fun hack takes advantage of the fact that the +# wallet backend is written in Perl so that we can substitute our own +# Wallet::Server class. +sub run_backend { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { command (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# Now for the actual tests. First, check for lack of trace information. +my ($out, $err) = run_backend; +is ($err, "REMOTE_USER not set\n", 'REMOTE_USER required'); +is ($OUTPUT, "error: REMOTE_USER not set\n", ' and syslog correct'); +$ENV{REMOTE_USER} = 'admin'; +($out, $err) = run_backend; +is ($err, "neither REMOTE_HOST nor REMOTE_ADDR set\n", + 'REMOTE_HOST or _ADDR required'); +is ($OUTPUT, "error for admin: neither REMOTE_HOST nor REMOTE_ADDR set\n", + ' and syslog correct'); +$ENV{REMOTE_ADDR} = '1.2.3.4'; +my $new = 'new admin 1.2.3.4'; + +# Check unknown commands. +($out, $err) = run_backend ('foo'); +is ($err, "unknown command foo\n", 'Unknown command'); +is ($OUTPUT, "error for admin (1.2.3.4): unknown command foo\n", + ' and syslog correct'); +is ($out, "$new\n", ' and nothing ran'); +($out, $err) = run_backend ('acl', 'foo'); +is ($err, "unknown command acl foo\n", 'Unknown ACL command'); +is ($OUTPUT, "error for admin (1.2.3.4): unknown command acl foo\n", + ' and syslog correct'); +is ($out, "$new\n", ' and nothing ran'); +($out, $err) = run_backend ('flag', 'foo', 'service', 'foo', 'foo'); +is ($err, "unknown command flag foo\n", 'Unknown flag command'); +is ($OUTPUT, "error for admin (1.2.3.4): unknown command flag foo\n", + ' and syslog correct'); +is ($out, "$new\n", ' and nothing ran'); + +# Check too few, too many, and bad arguments for every command. +my %commands = (autocreate => [2, 2], + check => [2, 2], + create => [2, 2], + destroy => [2, 2], + expires => [2, 4], + get => [2, 2], + getacl => [3, 3], + getattr => [3, 3], + history => [2, 2], + owner => [2, 3], + setacl => [4, 4], + setattr => [4, 9], + show => [2, 2], + store => [3, 3]); +my %acl_commands = (add => [3, 3], + create => [1, 1], + destroy => [1, 1], + history => [1, 1], + remove => [3, 3], + rename => [2, 2], + show => [1, 1]); +my %flag_commands = (clear => [3, 3], + set => [3, 3]); +for my $command (sort keys %commands) { + my ($min, $max) = @{ $commands{$command} }; + ($out, $err) = run_backend ($command, ('foo') x ($min - 1)); + is ($err, "insufficient arguments\n", "Too few arguments for $command"); + is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + unless ($max >= 9) { + ($out, $err) = run_backend ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments\n", "Too many arguments for $command"); + is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } + my @base = ('foobar') x $max; + for my $arg (0 .. ($max - 1)) { + my @args = @base; + $args[$arg] = 'foo;bar'; + ($out, $err) = run_backend ($command, @args); + if ($command eq 'store' and $arg == 2) { + is ($err, '', 'Store allows any characters'); + is ($OUTPUT, "command $command @args[0,1] from admin (1.2.3.4)" + . " succeeded\n", ' and success logged'); + is ($out, "$new\nstore foobar foobar foo;bar\n", + ' and calls the right method'); + } else { + is ($err, "invalid characters in argument: foo;bar\n", + "Invalid arguments for $command $arg"); + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: foo;bar\n", ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } + } +} +for my $command (sort keys %acl_commands) { + my ($min, $max) = @{ $acl_commands{$command} }; + ($out, $err) = run_backend ('acl', $command, ('foo') x ($min - 1)); + is ($err, "insufficient arguments\n", + "Too few arguments for acl $command"); + is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + ($out, $err) = run_backend ('acl', $command, ('foo') x ($max + 1)); + is ($err, "too many arguments\n", "Too many arguments for acl $command"); + is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + my @base = ('foobar') x $max; + for my $arg (0 .. ($max - 1)) { + my @args = @base; + $args[$arg] = 'foo;bar'; + ($out, $err) = run_backend ('acl', $command, @args); + is ($err, "invalid characters in argument: foo;bar\n", + "Invalid arguments for acl $command $arg"); + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: foo;bar\n", ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } +} +for my $command (sort keys %flag_commands) { + my ($min, $max) = @{ $flag_commands{$command} }; + ($out, $err) = run_backend ('flag', $command, ('foo') x ($min - 1)); + is ($err, "insufficient arguments\n", + "Too few arguments for flag $command"); + is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + ($out, $err) = run_backend ('flag', $command, ('foo') x ($max + 1)); + is ($err, "too many arguments\n", "Too many arguments for flag $command"); + is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", + ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + my @base = ('foobar') x $max; + for my $arg (0 .. ($max - 1)) { + my @args = @base; + $args[$arg] = 'foo;bar'; + ($out, $err) = run_backend ('flag', $command, @args); + is ($err, "invalid characters in argument: foo;bar\n", + "Invalid arguments for flag $command $arg"); + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: foo;bar\n", ' and syslog correct'); + is ($out, "$new\n", ' and nothing ran'); + } +} + +# Now, test that we ran the right functions and passed the correct arguments. +my $error = 1; +for my $command (qw/autocreate create destroy setacl setattr store/) { + my $method = { setacl => 'acl', setattr => 'attr' }->{$command}; + $method ||= $command; + my @extra = ('foo') x ($commands{$command}[0] - 2); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ($command, 'type', 'name', @extra); + my $ran; + if ($command eq 'store') { + $ran = "$command type name"; + } else { + $ran = "$command type name" . (@extra ? " @extra" : ''); + } + is ($err, '', "Command $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type name$extra\n", + ' and ran the right method'); + ($out, $err) = run_backend ($command, 'error', 'name', @extra); + if ($command eq 'store') { + $ran = "$command error name"; + } else { + $ran = "$command error name" . (@extra ? " @extra" : ''); + } + is ($err, "error count $error\n", "Command $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\n$method error name$extra\n", + ' and ran the right method'); + $error++; +} +for my $command (qw/check expires get getacl getattr history owner show/) { + my $method = { getacl => 'acl', getattr => 'attr' }->{$command}; + $method ||= $command; + my @extra = ('foo') x ($commands{$command}[0] - 2); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ($command, 'type', 'name', @extra); + my $ran = "$command type name" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + if ($command eq 'getattr') { + is ($out, "$new\n$method type name$extra\nattr1\nattr2\n", + ' and ran the right method with output'); + } elsif ($command eq 'check') { + is ($out, "$new\n$method type name$extra\nyes\n", + ' and ran the right method with output'); + } else { + my $newline = ($command =~ /^(get|history|show)\z/) ? '' : "\n"; + is ($out, "$new\n$method type name$extra\n$method$newline", + ' and ran the right method with output'); + } + if ($command eq 'expires' or $command eq 'owner') { + ($out, $err) = run_backend ($command, 'type', 'name', @extra, 'foo'); + my $ran = "$command type name" . (@extra ? " @extra" : '') . ' foo'; + is ($err, '', "Command $command ran with no errors (setting)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type name$extra foo\n", + ' and ran the right method'); + } + if ($command eq 'expires' or $command eq 'getacl' or $command eq 'owner') { + ($out, $err) = run_backend ($command, 'type', 'empty', @extra); + my $ran = "$command type empty" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors (empty)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + my $desc; + if ($command eq 'expires') { $desc = 'expiration' } + elsif ($command eq 'getacl') { $desc = 'ACL' } + elsif ($command eq 'owner') { $desc = 'owner' } + is ($out, "$new\n$method type empty$extra\nNo $desc set\n", + ' and ran the right method with output'); + $error++; + } elsif ($command eq 'getattr') { + ($out, $err) = run_backend ($command, 'type', 'empty', @extra); + my $ran = "$command type empty" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors (empty)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type empty$extra\n", + ' and ran the right method with output'); + $error++; + } elsif ($command eq 'check') { + ($out, $err) = run_backend ($command, 'type', 'empty', @extra); + my $ran = "$command type empty" . (@extra ? " @extra" : ''); + is ($err, '', "Command $command ran with no errors (empty)"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\n$method type empty$extra\nno\n", + ' and ran the right method with output'); + } + ($out, $err) = run_backend ($command, 'error', 'name', @extra); + my $ran = "$command error name" . (@extra ? " @extra" : ''); + is ($err, "error count $error\n", "Command $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\n$method error name$extra\n", + ' and ran the right method'); + $error++; +} +for my $command (sort keys %acl_commands) { + my @extra = ('foo') x ($acl_commands{$command}[0] - 1); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ('acl', $command, 'name', @extra); + my $ran = "acl $command name" . (@extra ? " @extra" : ''); + is ($err, '', "Command acl $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + my $expected; + if ($command eq 'show') { + $expected = "$new\nacl_$command name$extra\nacl_show"; + } elsif ($command eq 'history') { + $expected = "$new\nacl_$command name$extra\nacl_history"; + } else { + $expected = "$new\nacl_$command name$extra\n"; + } + is ($out, $expected, ' and ran the right method'); + ($out, $err) = run_backend ('acl', $command, 'error', @extra); + $ran = "acl $command error" . (@extra ? " @extra" : ''); + is ($err, "error count $error\n", "Command acl $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\nacl_$command error$extra\n", + ' and ran the right method'); + $error++; +} +for my $command (sort keys %flag_commands) { + my @extra = ('foo') x ($flag_commands{$command}[0] - 2); + my $extra = @extra ? join (' ', '', @extra) : ''; + ($out, $err) = run_backend ('flag', $command, 'type', 'name', @extra); + my $ran = "flag $command type name" . (@extra ? " @extra" : ''); + is ($err, '', "Command flag $command ran with no errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", + ' and success logged'); + is ($out, "$new\nflag_$command type name$extra\n", + ' and ran the right method'); + ($out, $err) = run_backend ('flag', $command, 'error', 'name', @extra); + $ran = "flag $command error name" . (@extra ? " @extra" : ''); + is ($err, "error count $error\n", + "Command flag $command ran with errors"); + is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" + . " $error\n", ' and syslog correct'); + is ($out, "$new\nflag_$command error name$extra\n", + ' and ran the right method'); + $error++; +} + +# Almost done. All that remains is to test the robustness of the bad +# character checks against every possible character and test permitting the +# empty argument. +($out, $err) = run_backend ('show', 'type', ''); +is ($err, '', 'Allowed the empty argument'); +is ($OUTPUT, "command show type from admin (1.2.3.4) succeeded\n", + ' and success logged'); +my $ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/.@-'; +($out, $err) = run_backend ('show', 'type', $ok); +is ($err, '', 'Allowed all valid characters'); +is ($OUTPUT, "command show type $ok from admin (1.2.3.4) succeeded\n", + ' and success logged'); +is ($out, "$new\nshow type $ok\nshow", ' and returned the right output'); +for my $n (0 .. 255) { + my $c = chr ($n); + my $name = $ok . $c; + ($out, $err) = run_backend ('show', 'type', $name); + if (index ($ok, $c) == -1) { + is ($err, "invalid characters in argument: $name\n", + "Rejected invalid character $n"); + my $stripped = $name; + $stripped =~ s/[^\x20-\x7e]/_/g; + is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" + . " argument: $stripped\n", ' and syslog correct'); + is ($out, "$new\n", ' and did nothing'); + } else { + is ($err, '', "Accepted valid character $n"); + is ($OUTPUT, "command show type $name from admin (1.2.3.4)" + . " succeeded\n", ' and success logged'); + is ($out, "$new\nshow type $name\nshow", ' and ran the method'); + } +} diff --git a/tests/server/backend-t.in b/tests/server/backend-t.in deleted file mode 100644 index 2fc6a53..0000000 --- a/tests/server/backend-t.in +++ /dev/null @@ -1,502 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for the wallet-backend dispatch code. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2008, 2009, 2010 -# Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use strict; -use Test::More tests => 1263; - -# Create a dummy class for Wallet::Server that prints what method was called -# with its arguments and returns data for testing. -package Wallet::Server; - -use vars qw($error $okay); -$error = 0; -$okay = 0; - -sub error { - if ($okay) { - $okay = 0; - return; - } else { - $error++; - return "error count $error"; - } -} - -sub new { shift; print "new @_\n"; return bless ({}, 'Wallet::Server') } -sub create { shift; print "create @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub destroy { shift; print "destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub store { shift; print "store @_\n"; ($_[0] eq 'error') ? undef : 1 } - -sub acl_add - { shift; print "acl_add @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_create - { shift; print "acl_create @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_destroy - { shift; print "acl_destroy @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_remove - { shift; print "acl_remove @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub acl_rename - { shift; print "acl_rename @_\n"; ($_[0] eq 'error') ? undef : 1 } - -sub acl_history { - shift; - print "acl_history @_\n"; - return if $_[0] eq 'error'; - return 'acl_history'; -} - -sub acl_show { - shift; - print "acl_show @_\n"; - return if $_[0] eq 'error'; - return 'acl_show'; -} - -sub flag_clear - { shift; print "flag_clear @_\n"; ($_[0] eq 'error') ? undef : 1 } -sub flag_set - { shift; print "flag_set @_\n"; ($_[0] eq 'error') ? undef : 1 } - -sub acl { - shift; - print "acl @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } else { - return 'acl'; - } -} - -sub attr { - shift; - print "attr @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } elsif (@_ == 3) { - return ('attr1', 'attr2'); - } else { - return 'attr'; - } -} - -sub autocreate { - shift; - print "autocreate @_\n"; - return ($_[0] eq 'error') ? undef : 1 -} - -sub check { - shift; - print "check @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - return 0; - } else { - return 1; - } -} - -sub expires { - shift; - print "expires @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } else { - return 'expires'; - } -} - -sub get { - shift; - print "get @_\n"; - return if $_[0] eq 'error'; - return 'get'; -} - -sub history { - shift; - print "history @_\n"; - return if $_[0] eq 'error'; - return 'history'; -} - -sub owner { - shift; - print "owner @_\n"; - if ($_[0] eq 'error') { - return; - } elsif ($_[1] eq 'empty') { - $okay = 1; - return; - } else { - return 'owner'; - } -} - -sub show { - shift; - print "show @_\n"; - return if $_[0] eq 'error'; - return 'show'; -} - -# Back to the main package and the actual test suite. Lie about whether the -# Wallet::Server package has already been loaded. -package main; -$INC{'Wallet/Server.pm'} = 'FAKE'; -my $OUTPUT; -our $SYSLOG = \$OUTPUT; -eval { do "$ENV{SOURCE}/../server/wallet-backend" }; - -# Run the wallet backend. This fun hack takes advantage of the fact that the -# wallet backend is written in Perl so that we can substitute our own -# Wallet::Server class. -sub run_backend { - my (@args) = @_; - my $result = ''; - open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; - select OUTPUT; - local $| = 1; - eval { command (@args) }; - my $error = $@; - select STDOUT; - return ($result, $error); -} - -# Now for the actual tests. First, check for lack of trace information. -my ($out, $err) = run_backend; -is ($err, "REMOTE_USER not set\n", 'REMOTE_USER required'); -is ($OUTPUT, "error: REMOTE_USER not set\n", ' and syslog correct'); -$ENV{REMOTE_USER} = 'admin'; -($out, $err) = run_backend; -is ($err, "neither REMOTE_HOST nor REMOTE_ADDR set\n", - 'REMOTE_HOST or _ADDR required'); -is ($OUTPUT, "error for admin: neither REMOTE_HOST nor REMOTE_ADDR set\n", - ' and syslog correct'); -$ENV{REMOTE_ADDR} = '1.2.3.4'; -my $new = 'new admin 1.2.3.4'; - -# Check unknown commands. -($out, $err) = run_backend ('foo'); -is ($err, "unknown command foo\n", 'Unknown command'); -is ($OUTPUT, "error for admin (1.2.3.4): unknown command foo\n", - ' and syslog correct'); -is ($out, "$new\n", ' and nothing ran'); -($out, $err) = run_backend ('acl', 'foo'); -is ($err, "unknown command acl foo\n", 'Unknown ACL command'); -is ($OUTPUT, "error for admin (1.2.3.4): unknown command acl foo\n", - ' and syslog correct'); -is ($out, "$new\n", ' and nothing ran'); -($out, $err) = run_backend ('flag', 'foo', 'service', 'foo', 'foo'); -is ($err, "unknown command flag foo\n", 'Unknown flag command'); -is ($OUTPUT, "error for admin (1.2.3.4): unknown command flag foo\n", - ' and syslog correct'); -is ($out, "$new\n", ' and nothing ran'); - -# Check too few, too many, and bad arguments for every command. -my %commands = (autocreate => [2, 2], - check => [2, 2], - create => [2, 2], - destroy => [2, 2], - expires => [2, 4], - get => [2, 2], - getacl => [3, 3], - getattr => [3, 3], - history => [2, 2], - owner => [2, 3], - setacl => [4, 4], - setattr => [4, 9], - show => [2, 2], - store => [3, 3]); -my %acl_commands = (add => [3, 3], - create => [1, 1], - destroy => [1, 1], - history => [1, 1], - remove => [3, 3], - rename => [2, 2], - show => [1, 1]); -my %flag_commands = (clear => [3, 3], - set => [3, 3]); -for my $command (sort keys %commands) { - my ($min, $max) = @{ $commands{$command} }; - ($out, $err) = run_backend ($command, ('foo') x ($min - 1)); - is ($err, "insufficient arguments\n", "Too few arguments for $command"); - is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - unless ($max >= 9) { - ($out, $err) = run_backend ($command, ('foo') x ($max + 1)); - is ($err, "too many arguments\n", "Too many arguments for $command"); - is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } - my @base = ('foobar') x $max; - for my $arg (0 .. ($max - 1)) { - my @args = @base; - $args[$arg] = 'foo;bar'; - ($out, $err) = run_backend ($command, @args); - if ($command eq 'store' and $arg == 2) { - is ($err, '', 'Store allows any characters'); - is ($OUTPUT, "command $command @args[0,1] from admin (1.2.3.4)" - . " succeeded\n", ' and success logged'); - is ($out, "$new\nstore foobar foobar foo;bar\n", - ' and calls the right method'); - } else { - is ($err, "invalid characters in argument: foo;bar\n", - "Invalid arguments for $command $arg"); - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: foo;bar\n", ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } - } -} -for my $command (sort keys %acl_commands) { - my ($min, $max) = @{ $acl_commands{$command} }; - ($out, $err) = run_backend ('acl', $command, ('foo') x ($min - 1)); - is ($err, "insufficient arguments\n", - "Too few arguments for acl $command"); - is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - ($out, $err) = run_backend ('acl', $command, ('foo') x ($max + 1)); - is ($err, "too many arguments\n", "Too many arguments for acl $command"); - is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - my @base = ('foobar') x $max; - for my $arg (0 .. ($max - 1)) { - my @args = @base; - $args[$arg] = 'foo;bar'; - ($out, $err) = run_backend ('acl', $command, @args); - is ($err, "invalid characters in argument: foo;bar\n", - "Invalid arguments for acl $command $arg"); - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: foo;bar\n", ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } -} -for my $command (sort keys %flag_commands) { - my ($min, $max) = @{ $flag_commands{$command} }; - ($out, $err) = run_backend ('flag', $command, ('foo') x ($min - 1)); - is ($err, "insufficient arguments\n", - "Too few arguments for flag $command"); - is ($OUTPUT, "error for admin (1.2.3.4): insufficient arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - ($out, $err) = run_backend ('flag', $command, ('foo') x ($max + 1)); - is ($err, "too many arguments\n", "Too many arguments for flag $command"); - is ($OUTPUT, "error for admin (1.2.3.4): too many arguments\n", - ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - my @base = ('foobar') x $max; - for my $arg (0 .. ($max - 1)) { - my @args = @base; - $args[$arg] = 'foo;bar'; - ($out, $err) = run_backend ('flag', $command, @args); - is ($err, "invalid characters in argument: foo;bar\n", - "Invalid arguments for flag $command $arg"); - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: foo;bar\n", ' and syslog correct'); - is ($out, "$new\n", ' and nothing ran'); - } -} - -# Now, test that we ran the right functions and passed the correct arguments. -my $error = 1; -for my $command (qw/autocreate create destroy setacl setattr store/) { - my $method = { setacl => 'acl', setattr => 'attr' }->{$command}; - $method ||= $command; - my @extra = ('foo') x ($commands{$command}[0] - 2); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ($command, 'type', 'name', @extra); - my $ran; - if ($command eq 'store') { - $ran = "$command type name"; - } else { - $ran = "$command type name" . (@extra ? " @extra" : ''); - } - is ($err, '', "Command $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type name$extra\n", - ' and ran the right method'); - ($out, $err) = run_backend ($command, 'error', 'name', @extra); - if ($command eq 'store') { - $ran = "$command error name"; - } else { - $ran = "$command error name" . (@extra ? " @extra" : ''); - } - is ($err, "error count $error\n", "Command $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\n$method error name$extra\n", - ' and ran the right method'); - $error++; -} -for my $command (qw/check expires get getacl getattr history owner show/) { - my $method = { getacl => 'acl', getattr => 'attr' }->{$command}; - $method ||= $command; - my @extra = ('foo') x ($commands{$command}[0] - 2); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ($command, 'type', 'name', @extra); - my $ran = "$command type name" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - if ($command eq 'getattr') { - is ($out, "$new\n$method type name$extra\nattr1\nattr2\n", - ' and ran the right method with output'); - } elsif ($command eq 'check') { - is ($out, "$new\n$method type name$extra\nyes\n", - ' and ran the right method with output'); - } else { - my $newline = ($command =~ /^(get|history|show)\z/) ? '' : "\n"; - is ($out, "$new\n$method type name$extra\n$method$newline", - ' and ran the right method with output'); - } - if ($command eq 'expires' or $command eq 'owner') { - ($out, $err) = run_backend ($command, 'type', 'name', @extra, 'foo'); - my $ran = "$command type name" . (@extra ? " @extra" : '') . ' foo'; - is ($err, '', "Command $command ran with no errors (setting)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type name$extra foo\n", - ' and ran the right method'); - } - if ($command eq 'expires' or $command eq 'getacl' or $command eq 'owner') { - ($out, $err) = run_backend ($command, 'type', 'empty', @extra); - my $ran = "$command type empty" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors (empty)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - my $desc; - if ($command eq 'expires') { $desc = 'expiration' } - elsif ($command eq 'getacl') { $desc = 'ACL' } - elsif ($command eq 'owner') { $desc = 'owner' } - is ($out, "$new\n$method type empty$extra\nNo $desc set\n", - ' and ran the right method with output'); - $error++; - } elsif ($command eq 'getattr') { - ($out, $err) = run_backend ($command, 'type', 'empty', @extra); - my $ran = "$command type empty" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors (empty)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type empty$extra\n", - ' and ran the right method with output'); - $error++; - } elsif ($command eq 'check') { - ($out, $err) = run_backend ($command, 'type', 'empty', @extra); - my $ran = "$command type empty" . (@extra ? " @extra" : ''); - is ($err, '', "Command $command ran with no errors (empty)"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\n$method type empty$extra\nno\n", - ' and ran the right method with output'); - } - ($out, $err) = run_backend ($command, 'error', 'name', @extra); - my $ran = "$command error name" . (@extra ? " @extra" : ''); - is ($err, "error count $error\n", "Command $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\n$method error name$extra\n", - ' and ran the right method'); - $error++; -} -for my $command (sort keys %acl_commands) { - my @extra = ('foo') x ($acl_commands{$command}[0] - 1); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ('acl', $command, 'name', @extra); - my $ran = "acl $command name" . (@extra ? " @extra" : ''); - is ($err, '', "Command acl $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - my $expected; - if ($command eq 'show') { - $expected = "$new\nacl_$command name$extra\nacl_show"; - } elsif ($command eq 'history') { - $expected = "$new\nacl_$command name$extra\nacl_history"; - } else { - $expected = "$new\nacl_$command name$extra\n"; - } - is ($out, $expected, ' and ran the right method'); - ($out, $err) = run_backend ('acl', $command, 'error', @extra); - $ran = "acl $command error" . (@extra ? " @extra" : ''); - is ($err, "error count $error\n", "Command acl $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\nacl_$command error$extra\n", - ' and ran the right method'); - $error++; -} -for my $command (sort keys %flag_commands) { - my @extra = ('foo') x ($flag_commands{$command}[0] - 2); - my $extra = @extra ? join (' ', '', @extra) : ''; - ($out, $err) = run_backend ('flag', $command, 'type', 'name', @extra); - my $ran = "flag $command type name" . (@extra ? " @extra" : ''); - is ($err, '', "Command flag $command ran with no errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n", - ' and success logged'); - is ($out, "$new\nflag_$command type name$extra\n", - ' and ran the right method'); - ($out, $err) = run_backend ('flag', $command, 'error', 'name', @extra); - $ran = "flag $command error name" . (@extra ? " @extra" : ''); - is ($err, "error count $error\n", - "Command flag $command ran with errors"); - is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" - . " $error\n", ' and syslog correct'); - is ($out, "$new\nflag_$command error name$extra\n", - ' and ran the right method'); - $error++; -} - -# Almost done. All that remains is to test the robustness of the bad -# character checks against every possible character and test permitting the -# empty argument. -($out, $err) = run_backend ('show', 'type', ''); -is ($err, '', 'Allowed the empty argument'); -is ($OUTPUT, "command show type from admin (1.2.3.4) succeeded\n", - ' and success logged'); -my $ok = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/.@-'; -($out, $err) = run_backend ('show', 'type', $ok); -is ($err, '', 'Allowed all valid characters'); -is ($OUTPUT, "command show type $ok from admin (1.2.3.4) succeeded\n", - ' and success logged'); -is ($out, "$new\nshow type $ok\nshow", ' and returned the right output'); -for my $n (0 .. 255) { - my $c = chr ($n); - my $name = $ok . $c; - ($out, $err) = run_backend ('show', 'type', $name); - if (index ($ok, $c) == -1) { - is ($err, "invalid characters in argument: $name\n", - "Rejected invalid character $n"); - my $stripped = $name; - $stripped =~ s/[^\x20-\x7e]/_/g; - is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in" - . " argument: $stripped\n", ' and syslog correct'); - is ($out, "$new\n", ' and did nothing'); - } else { - is ($err, '', "Accepted valid character $n"); - is ($OUTPUT, "command show type $name from admin (1.2.3.4)" - . " succeeded\n", ' and success logged'); - is ($out, "$new\nshow type $name\nshow", ' and ran the method'); - } -} diff --git a/tests/server/keytab-t b/tests/server/keytab-t new file mode 100755 index 0000000..2a0ceed --- /dev/null +++ b/tests/server/keytab-t @@ -0,0 +1,88 @@ +#!/usr/bin/perl -w +# +# Tests for the keytab-backend dispatch code. +# +# Written by Russ Allbery +# Copyright 2006, 2007, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use vars qw($CONFIG $KADMIN $SYSLOG $TMP); + +use Test::More tests => 63; + +# Load the keytab-backend code and override various settings. +my $OUTPUT; +$SYSLOG = \$OUTPUT; +eval { do "$ENV{SOURCE}/../server/keytab-backend" }; +$CONFIG = "$ENV{SOURCE}/data/allow-extract"; +$KADMIN = "$ENV{SOURCE}/data/fake-kadmin"; +$TMP = '.'; + +# Run the keytab backend. +sub run_backend { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { download (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# The actual tests. +$ENV{REMOTE_USER} = 'admin'; +my ($out, $err) = run_backend (); +is ($err, "keytab-backend: invalid arguments: \n", 'Fails with no arguments'); +is ($OUTPUT, "invalid arguments: \n", ' and syslog matches'); +is ($out, '', ' and produces no output'); +($out, $err) = run_backend ('foo', 'bar', 'baz'); +is ($err, "keytab-backend: invalid arguments: foo bar baz\n", + 'Fails with three arguments'); +is ($OUTPUT, "invalid arguments: foo bar baz\n", ' and syslog matches'); +is ($out, '', ' and produces no output'); +for my $bad (qw{service service\*@example =@example host/foo+bar@example + rcmd.foo@EXAMPLE host/foo/bar@EXAMPLE /bar@EXAMPLE.NET + bar/@EXAMPLE.NET bar/bar@}) { + ($out, $err) = run_backend ('keytab', $bad); + is ($err, "keytab-backend: bad principal name $bad\n", + "Invalid principal $bad"); + is ($OUTPUT, "bad principal name $bad\n", ' and syslog matches'); + is ($out, '', ' and produces no output'); +} +for my $bad (qw{service/foo@EXAMPLE.ORGA bar@EXAMPLE.NET + host/example.net@EXAMPLE.ORG aservice/foo@EXAMPLE.ORG}) { + ($out, $err) = run_backend ('keytab', $bad); + is ($err, + "keytab-backend: permission denied: admin may not retrieve $bad\n", + "Permission denied for $bad"); + is ($OUTPUT, "permission denied: admin may not retrieve $bad\n", + ' and syslog matches'); + is ($out, '', ' and produces no output'); +} +for my $good (qw{service/foo@EXAMPLE.ORG foo/bar@EXAMPLE.NET + host/example.org@EXAMPLE.ORG}) { + ($out, $err) = run_backend ($good); + is ($err, '', "Success for good keytab $good"); + is ($out, "$good\n", ' and the right output'); + is ($OUTPUT, "keytab $good retrieved by admin\n", ' and syslog is right'); + ok (! -f "$TMP/keytab$$", ' and the file is gone'); +} +($out, $err) = run_backend ('keytab', 'error@EXAMPLE.ORG'); +is ($err, "keytab-backend: retrieve of error\@EXAMPLE.ORG failed for" + . " admin: kadmin.local exited with status 1\n", + 'Good error on kadmin failure'); +is ($OUTPUT, "retrieve of error\@EXAMPLE.ORG failed for admin: kadmin.local" + . " exited with status 1\n", ' and syslog matches'); +is ($out, '', ' and no output'); + +# Test a configuration failure. +$CONFIG = '/path/to/bad/file'; +($out, $err) = run_backend ('get', 'service/foo@EXAMPLE.ORG'); +like ($err, qr{^keytab-backend: cannot open /path/to/bad/file: }, + 'Fails with bad configuration file'); +like ($OUTPUT, qr{^cannot open /path/to/bad/file: }, ' and syslog matches'); +is ($out, '', ' and produces no output'); diff --git a/tests/server/keytab-t.in b/tests/server/keytab-t.in deleted file mode 100644 index 2a0ceed..0000000 --- a/tests/server/keytab-t.in +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/perl -w -# -# Tests for the keytab-backend dispatch code. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use strict; -use vars qw($CONFIG $KADMIN $SYSLOG $TMP); - -use Test::More tests => 63; - -# Load the keytab-backend code and override various settings. -my $OUTPUT; -$SYSLOG = \$OUTPUT; -eval { do "$ENV{SOURCE}/../server/keytab-backend" }; -$CONFIG = "$ENV{SOURCE}/data/allow-extract"; -$KADMIN = "$ENV{SOURCE}/data/fake-kadmin"; -$TMP = '.'; - -# Run the keytab backend. -sub run_backend { - my (@args) = @_; - my $result = ''; - open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; - select OUTPUT; - local $| = 1; - eval { download (@args) }; - my $error = $@; - select STDOUT; - return ($result, $error); -} - -# The actual tests. -$ENV{REMOTE_USER} = 'admin'; -my ($out, $err) = run_backend (); -is ($err, "keytab-backend: invalid arguments: \n", 'Fails with no arguments'); -is ($OUTPUT, "invalid arguments: \n", ' and syslog matches'); -is ($out, '', ' and produces no output'); -($out, $err) = run_backend ('foo', 'bar', 'baz'); -is ($err, "keytab-backend: invalid arguments: foo bar baz\n", - 'Fails with three arguments'); -is ($OUTPUT, "invalid arguments: foo bar baz\n", ' and syslog matches'); -is ($out, '', ' and produces no output'); -for my $bad (qw{service service\*@example =@example host/foo+bar@example - rcmd.foo@EXAMPLE host/foo/bar@EXAMPLE /bar@EXAMPLE.NET - bar/@EXAMPLE.NET bar/bar@}) { - ($out, $err) = run_backend ('keytab', $bad); - is ($err, "keytab-backend: bad principal name $bad\n", - "Invalid principal $bad"); - is ($OUTPUT, "bad principal name $bad\n", ' and syslog matches'); - is ($out, '', ' and produces no output'); -} -for my $bad (qw{service/foo@EXAMPLE.ORGA bar@EXAMPLE.NET - host/example.net@EXAMPLE.ORG aservice/foo@EXAMPLE.ORG}) { - ($out, $err) = run_backend ('keytab', $bad); - is ($err, - "keytab-backend: permission denied: admin may not retrieve $bad\n", - "Permission denied for $bad"); - is ($OUTPUT, "permission denied: admin may not retrieve $bad\n", - ' and syslog matches'); - is ($out, '', ' and produces no output'); -} -for my $good (qw{service/foo@EXAMPLE.ORG foo/bar@EXAMPLE.NET - host/example.org@EXAMPLE.ORG}) { - ($out, $err) = run_backend ($good); - is ($err, '', "Success for good keytab $good"); - is ($out, "$good\n", ' and the right output'); - is ($OUTPUT, "keytab $good retrieved by admin\n", ' and syslog is right'); - ok (! -f "$TMP/keytab$$", ' and the file is gone'); -} -($out, $err) = run_backend ('keytab', 'error@EXAMPLE.ORG'); -is ($err, "keytab-backend: retrieve of error\@EXAMPLE.ORG failed for" - . " admin: kadmin.local exited with status 1\n", - 'Good error on kadmin failure'); -is ($OUTPUT, "retrieve of error\@EXAMPLE.ORG failed for admin: kadmin.local" - . " exited with status 1\n", ' and syslog matches'); -is ($out, '', ' and no output'); - -# Test a configuration failure. -$CONFIG = '/path/to/bad/file'; -($out, $err) = run_backend ('get', 'service/foo@EXAMPLE.ORG'); -like ($err, qr{^keytab-backend: cannot open /path/to/bad/file: }, - 'Fails with bad configuration file'); -like ($OUTPUT, qr{^cannot open /path/to/bad/file: }, ' and syslog matches'); -is ($out, '', ' and produces no output'); diff --git a/tests/server/pod-t b/tests/server/pod-t new file mode 100755 index 0000000..52d81eb --- /dev/null +++ b/tests/server/pod-t @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# +# Test POD formatting for client documentation. +# +# Written by Russ Allbery +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use Test::More; + +my @files = qw(keytab-backend wallet-admin wallet-backend); +my $total = scalar (@files); +plan tests => $total; + +eval 'use Test::Pod 1.00'; +SKIP: { + skip 'Test::Pod 1.00 required for testing POD', $total if $@; + for my $file (@files) { + pod_file_ok ("$ENV{SOURCE}/../server/$file", "server/$file"); + } +} diff --git a/tests/server/pod-t.in b/tests/server/pod-t.in deleted file mode 100644 index 52d81eb..0000000 --- a/tests/server/pod-t.in +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -# -# Test POD formatting for client documentation. -# -# Written by Russ Allbery -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use Test::More; - -my @files = qw(keytab-backend wallet-admin wallet-backend); -my $total = scalar (@files); -plan tests => $total; - -eval 'use Test::Pod 1.00'; -SKIP: { - skip 'Test::Pod 1.00 required for testing POD', $total if $@; - for my $file (@files) { - pod_file_ok ("$ENV{SOURCE}/../server/$file", "server/$file"); - } -} -- cgit v1.2.3 From 0f81ba24e021a63d42c51ee9bec6e521fc540251 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 22:13:53 -0800 Subject: Fix multiple builddir != srcdir issues with test suite Simplify the build rules for the test suite to take advantage of the improved runtests support for builddir != srcdir. Stop doing Autoconf substitution on full.conf now that we have that support. --- .gitignore | 1 - Makefile.am | 34 +++++++++++++--------------------- configure.ac | 5 ++++- tests/client/basic-t.in | 2 +- tests/client/full-t.in | 7 +++++-- tests/client/prompt-t.in | 2 +- tests/data/full.conf | 3 +++ tests/data/full.conf.in | 3 --- 8 files changed, 27 insertions(+), 30 deletions(-) create mode 100644 tests/data/full.conf delete mode 100644 tests/data/full.conf.in (limited to 'tests') diff --git a/.gitignore b/.gitignore index b0a49df..3778ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ /tests/client/basic-t /tests/client/full-t /tests/client/prompt-t -/tests/data/full.conf /tests/data/test.keytab /tests/data/test.password /tests/data/test.principal diff --git a/Makefile.am b/Makefile.am index 77514a7..d4dc8a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,20 +23,19 @@ PERL_FILES = perl/Wallet/ACL.pm perl/Wallet/ACL/Base.pm \ perl/t/keytab.t perl/t/lib/Util.pm perl/t/object.t perl/t/pod.t \ perl/t/schema.t perl/t/server.t perl/t/verifier-netdb.t \ perl/t/verifier.t -TEST_FILES = tests/TESTS tests/data/README tests/data/allow-extract \ - tests/data/basic.conf tests/data/cmd-fake tests/data/fake-data \ - tests/data/fake-kadmin tests/data/fake-keytab \ - tests/data/fake-keytab-2 tests/data/fake-keytab-merge \ - tests/data/fake-keytab-old tests/data/fake-srvtab \ - tests/data/wallet.conf tests/libtest.sh AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST = LICENSE autogen client/wallet.pod config/allow-extract \ - config/keytab config/keytab.acl config/wallet docs/design \ - contrib/README contrib/wallet-report contrib/wallet-report.8 \ - docs/design-acl docs/design-api docs/netdb-role-api docs/notes \ - docs/setup examples/stanford.conf $(PERL_FILES) $(TEST_FILES) +EXTRA_DIST = LICENSE autogen client/wallet.pod config/allow-extract \ + config/keytab config/keytab.acl config/wallet docs/design \ + contrib/README contrib/wallet-report contrib/wallet-report.8 \ + docs/design-acl docs/design-api docs/netdb-role-api docs/notes \ + docs/setup examples/stanford.conf tests/TESTS tests/data/README \ + tests/data/allow-extract tests/data/basic.conf tests/data/cmd-fake \ + tests/data/cmd-wrapper tests/data/fake-data tests/data/fake-kadmin \ + tests/data/fake-keytab tests/data/fake-keytab-2 \ + tests/data/fake-keytab-merge tests/data/fake-keytab-old \ + tests/data/fake-srvtab tests/data/wallet.conf $(PERL_FILES) noinst_LIBRARIES = portable/libportable.a util/libutil.a portable_libportable_a_SOURCES = portable/dummy.c portable/krb5-extra.c \ @@ -89,8 +88,8 @@ all-local: perl/blib/lib/Wallet/Config.pm perl/blib/lib/Wallet/Config.pm: set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \ mkdir perl/Wallet perl/Wallet/ACL perl/Wallet/ACL/NetDB \ - perl/Wallet/Object perl/t perl/t/data perl/t/lib \ - 2>/dev/null || true ; \ + perl/Wallet/Kadmin perl/Wallet/Object perl/t perl/t/data \ + perl/t/lib 2>/dev/null || true ; \ for f in $(PERL_FILES) ; do \ cp "$(srcdir)/$$f" "$(builddir)/$$f" ; \ done \ @@ -160,14 +159,7 @@ tests_util_messages_t_LDADD = tests/tap/libtap.a util/libutil.a \ tests_util_xmalloc_LDADD = util/libutil.a portable/libportable.a check-local: $(check_PROGRAMS) - set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \ - mkdir tests/data/acls 2>/dev/null || true ; \ - for f in $(TEST_FILES) ; do \ - cp "$(srcdir)/$$f" "$(builddir)/$$f" ; \ - done \ - fi - cd tests && ./runtests TESTS - @echo '' + cd tests && ./runtests $(abs_top_srcdir)/tests/TESTS @echo '' cd perl && $(MAKE) test diff --git a/configure.ac b/configure.ac index 664c6f7..78201c1 100644 --- a/configure.ac +++ b/configure.ac @@ -60,8 +60,11 @@ AS_IF([test x"$REMCTLD" != x], [AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"], [Define to the full path to remctld to run remctl tests.])]) +dnl Create the tests/data directory for builds outside the source directory. +AC_CONFIG_COMMANDS([tests/data/.placeholder], [touch tests/data/.placeholder]) + AC_CONFIG_HEADER([config.h]) -AC_CONFIG_FILES([Makefile perl/Makefile.PL tests/data/full.conf]) +AC_CONFIG_FILES([Makefile perl/Makefile.PL]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) AC_CONFIG_FILES([tests/client/full-t], [chmod +x tests/client/full-t]) AC_CONFIG_FILES([tests/client/prompt-t], [chmod +x tests/client/prompt-t]) diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 1dbc0b9..30bc004 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -12,7 +12,7 @@ . "$SOURCE/tap/libtap.sh" . "$SOURCE/tap/kerberos.sh" . "$SOURCE/tap/remctl.sh" -cd "$BUILD" +cd "$SOURCE" # We need a modified krb5.conf file to test wallet configuration settings in # krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't diff --git a/tests/client/full-t.in b/tests/client/full-t.in index a4ca19d..8acc360 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -51,6 +51,9 @@ sub wallet { return ($output, $error, $status); } +# cd to the correct directory. +chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; + SKIP: { skip 'no keytab configuration', $total unless -f "$ENV{BUILD}/data/test.keytab"; @@ -62,8 +65,8 @@ SKIP: { unlink ('krb5cc_test', 'test-pid'); my $principal = contents ("$ENV{BUILD}/data/test.principal"); remctld_spawn ($remctld, $principal, - '@abs_top_builddir@/tests/data/test.keytab', - '@abs_top_builddir@/tests/data/full.conf'); + "$ENV{BUILD}/data/test.keytab", + "$ENV{SOURCE}/data/full.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; getcreds ("$ENV{BUILD}/data/test.keytab", $principal); diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index e037b3f..1d8b079 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -36,7 +36,7 @@ SKIP: { unlink ('krb5cc_test', 'test-pid'); my $principal = contents ("$ENV{BUILD}/data/test.principal"); remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", - "$ENV{BUILD}/data/basic.conf"); + "$ENV{SOURCE}/data/basic.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; # Read in the principal and password. diff --git a/tests/data/full.conf b/tests/data/full.conf new file mode 100644 index 0000000..4c0f435 --- /dev/null +++ b/tests/data/full.conf @@ -0,0 +1,3 @@ +# remctl configuration for full wallet client tests. + +wallet ALL data/cmd-wrapper ANYUSER diff --git a/tests/data/full.conf.in b/tests/data/full.conf.in deleted file mode 100644 index 25aef9e..0000000 --- a/tests/data/full.conf.in +++ /dev/null @@ -1,3 +0,0 @@ -# remctl configuration for full wallet client tests. - -wallet ALL @abs_top_builddir@/tests/data/cmd-wrapper ANYUSER -- cgit v1.2.3 From 5d7f614e88bac459a693f1dcc91aad36ed3d00dd Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 23:57:10 -0800 Subject: Reorganize main POD tests and add a spelling check Add a POD spelling test to the non-Perl-module part of the code and move the documentation tests into a separate directory. Merge the POD syntax tests between client and server into one test. Reformat all of the POD documentation to use 74 columns. Fix a few revealed spelling errors or weird wordings. --- client/wallet.pod | 11 ++++--- server/keytab-backend | 64 +++++++++++++++++++----------------- server/wallet-admin | 17 ++++++---- server/wallet-backend | 83 +++++++++++++++++++++++++---------------------- tests/TESTS | 4 +-- tests/client/pod-t | 22 ------------- tests/docs/pod-spelling-t | 80 +++++++++++++++++++++++++++++++++++++++++++++ tests/docs/pod-t | 21 ++++++++++++ tests/server/pod-t | 22 ------------- 9 files changed, 200 insertions(+), 124 deletions(-) delete mode 100755 tests/client/pod-t create mode 100755 tests/docs/pod-spelling-t create mode 100755 tests/docs/pod-t delete mode 100755 tests/server/pod-t (limited to 'tests') diff --git a/client/wallet.pod b/client/wallet.pod index 9908bb1..09fb571 100644 --- a/client/wallet.pod +++ b/client/wallet.pod @@ -2,6 +2,11 @@ wallet - Client for retrieving secure data from a central server +=for stopwords +-hv srvtab arg keytabs metadata keytab ACL PTS kinit klist remctl PKINIT +acl timestamp autocreate backend-specific setacl enctypes enctype ktadd +KDC appdefaults remctld Allbery nul uuencode getacl backend + =head1 SYNOPSIS B [B<-hv>] [B<-c> I] [B<-f> I] @@ -44,9 +49,7 @@ entries, each of which is a scheme and an identifier. A scheme specifies a way of checking whether a user is authorized. An identifier is some data specific to the scheme that specifies which users are authorized. For example, for the C scheme, the identifier is a principal name -and only that principal is authorized by that ACL entry. For the C -scheme, the identifier is a PTS group name, and all members of that PTS -group are authorized by that ACL entry. +and only that principal is authorized by that ACL entry. To run the wallet command-line client, you must already have a Kerberos ticket. You can obtain a Kerberos ticket with B and see your @@ -201,7 +204,7 @@ Display the history of the ACL . Each change to the ACL (not including changes to the name of the ACL) will be represented by two lines. The first line will have a timestamp of the change followed by a description of the change, and the second line will give the user who made -the change and the host from which the change was mde. +the change and the host from which the change was made. =item acl remove diff --git a/server/keytab-backend b/server/keytab-backend index b37fb3a..7b6adb4 100755 --- a/server/keytab-backend +++ b/server/keytab-backend @@ -17,7 +17,8 @@ # The keytab for the extracted principal will be printed to standard output. # # Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2008, 2010 +# Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -155,6 +156,10 @@ __END__ # Documentation ############################################################################## +=for stopwords +keytab-backend keytabs KDC keytab kadmin.local -norandkey ktadd remctld +auth Allbery rekeying + =head1 NAME keytab-backend - Extract keytabs from the KDC without changing the key @@ -165,27 +170,28 @@ B retrieve I =head1 DESCRIPTION -B retrieves a keytab for an existing principal from the KDC -database without changing the current key. It allows generation of a keytab -for a service without rekeying that service. It requires a B -patched to support the B<-norandkey> option to B. +B retrieves a keytab for an existing principal from the +KDC database without changing the current key. It allows generation of a +keytab for a service without rekeying that service. It requires a +B patched to support the B<-norandkey> option to B. -This script is intended to run under B. On success, it prints the -keytab to standard output, logs a success message to syslog (facility auth, -priority info), and exits with status 0. On failure, it prints out an error -message, logs an error to syslog (facility auth, priority err), and exits -with a non-zero status. +This script is intended to run under B. On success, it prints +the keytab to standard output, logs a success message to syslog (facility +auth, priority info), and exits with status 0. On failure, it prints out +an error message, logs an error to syslog (facility auth, priority err), +and exits with a non-zero status. The principal is checked for basic sanity (only accepting alphanumerics, -C<_>, and C<-> with an optional instance and then only alphanumerics, C<_>, -C<->, and C<.> in the realm) and then checked against a configuration file -that lists regexes of principals that can be retrieved. When deploying this -software, limit as tightly as possible which principals can be downloaded in -this fashion. Generally only shared service principals used on multiple -systems should be made available in this way. +C<_>, and C<-> with an optional instance and then only alphanumerics, +C<_>, C<->, and C<.> in the realm) and then checked against a +configuration file that lists regexes of principals that can be retrieved. +When deploying this software, limit as tightly as possible which +principals can be downloaded in this fashion. Generally only shared +service principals used on multiple systems should be made available in +this way. -B does not do any authorization checks. Those should be done -by B before it is called. +B does not do any authorization checks. Those should be +done by B before it is called. =head1 FILES @@ -193,19 +199,19 @@ by B before it is called. =item F -The configuration file that controls which principals can have their keytabs -retrieved. Blank lines and lines starting with C<#>, as well as anything -after C<#> on a line, are ignored. All other lines should be Perl regular -expressions, one per line, that match principals whose keytabs can be -retrieved by B. Any principal that does not match one of -those regular expressions cannot be retrieved. +The configuration file that controls which principals can have their +keytabs retrieved. Blank lines and lines starting with C<#>, as well as +anything after C<#> on a line, are ignored. All other lines should be +Perl regular expressions, one per line, that match principals whose +keytabs can be retrieved by B. Any principal that does +not match one of those regular expressions cannot be retrieved. =item F The temporary directory used for creating keytabs. B will -create the keytab in this directory, make sure that was successful, and then -delete the temporary file after the results have been sent to standard -output. +create the keytab in this directory, make sure that was successful, and +then delete the temporary file after the results have been sent to +standard output. =back @@ -213,8 +219,8 @@ output. kadmin.local(8), remctld(8) -This program is part of the wallet system. The current version is available -from L. +This program is part of the wallet system. The current version is +available from L. =head1 AUTHOR diff --git a/server/wallet-admin b/server/wallet-admin index cd775b6..828cfc5 100755 --- a/server/wallet-admin +++ b/server/wallet-admin @@ -1,9 +1,9 @@ #!/usr/bin/perl -w # -# wallet-admin -- Wallet server administrative commands. +# wallet-backend -- Wallet server administrative commands. # # Written by Russ Allbery -# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -110,6 +110,9 @@ __END__ wallet-admin - Wallet server administrative commands +=for stopwords +metadata ACL hostname backend acl acls wildcard SQL Allbery + =head1 SYNOPSIS B I [I ...] @@ -171,8 +174,8 @@ be listed in the form: In both cases, there will be one line per ACL or object. -If no searchtype is given, all the ACLs or objects in the database will -be returned. If a searchtype (and possible search arguments) are given, +If no search type is given, all the ACLs or objects in the database will +be returned. If a search type (and possible search arguments) are given, then the ACLs or objects will be limited to those that match the search. The currently supported object search types are: @@ -206,7 +209,7 @@ The currently supported ACL search types are: =item list acls empty Returns all ACLs which have no entries, generally so that abandoned ACLs -can be housekept. +can be destroyed. =item list acls entry @@ -256,8 +259,8 @@ with duplicates suppressed. Wallet::Admin(3), Wallet::Config(3), wallet-backend(8) -This program is part of the wallet system. The current version is available -from L. +This program is part of the wallet system. The current version is +available from L. =head1 AUTHOR diff --git a/server/wallet-backend b/server/wallet-backend index 0770f97..7780758 100755 --- a/server/wallet-backend +++ b/server/wallet-backend @@ -3,7 +3,7 @@ # wallet-backend -- Wallet server for storing and retrieving secure data. # # Written by Russ Allbery -# Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -311,6 +311,11 @@ __END__ # The commands section of this document is duplicated from the documentation # for wallet and should be kept in sync. +=for stopwords +wallet-backend backend backend-specific remctld ACL acl timestamp getacl +setacl metadata nul keytab keytabs enctypes enctype ktadd KDC Allbery +autocreate + =head1 NAME wallet-backend - Wallet server for storing and retrieving secure data @@ -321,20 +326,22 @@ B [B<-q>] I [I ...] =head1 DESCRIPTION -B implements the interface between B and the wallet -system. It is written to run under B and expects the authenticated -identity of the remote user in the REMOTE_USER environment variable. It -uses REMOTE_HOST or REMOTE_ADDR if REMOTE_HOST isn't set for additional -trace information. It accepts the command from B on the command -line, creates a Wallet::Server object, and calls the appropriate methods. - -This program is a fairly thin wrapper around Wallet::Server that translates -command strings into method calls and returns the results. It does check -all arguments except for the argument to the store command and -rejects any argument not matching C<^[\w_/.-]+\z>; in other words, only -alphanumerics, underscore (C<_>), slash (C), period (C<.>), and hyphen -(C<->) are permitted in arguments. This provides some additional security -over and above the checking already done by the rest of the wallet code. +B implements the interface between B and the +wallet system. It is written to run under B and expects the +authenticated identity of the remote user in the REMOTE_USER environment +variable. It uses REMOTE_HOST or REMOTE_ADDR if REMOTE_HOST isn't set for +additional trace information. It accepts the command from B on +the command line, creates a Wallet::Server object, and calls the +appropriate methods. + +This program is a fairly thin wrapper around Wallet::Server that +translates command strings into method calls and returns the results. It +does check all arguments except for the argument to the store +command and rejects any argument not matching C<^[\w_/.-]+\z>; in other +words, only alphanumerics, underscore (C<_>), slash (C), period (C<.>), +and hyphen (C<->) are permitted in arguments. This provides some +additional security over and above the checking already done by the rest +of the wallet code. =head1 OPTIONS @@ -400,7 +407,7 @@ Display the history of the ACL . Each change to the ACL (not including changes to the name of the ACL) will be represented by two lines. The first line will have a timestamp of the change followed by a description of the change, and the second line will give the user who made -the change and the host from which the change was mde. +the change and the host from which the change was made. =item acl remove @@ -447,8 +454,8 @@ The expiration will be displayed in seconds since epoch. If is given, sets the expiration on the object identified by and to and (if given)