| #!/usr/bin/env perl |
| |
| use Getopt::Long; |
| #use Data::Dumper; |
| use File::Basename; |
| use Cwd qw(abs_path); |
| use strict; |
| |
| ######################################## |
| # Globals and Command Line options |
| |
| my %opts = ('groups' => 'default', |
| 'master-directory' => 'fulltests', |
| 'srcdir' => dirname("$0") . "/..", |
| 'builddir' => '..', |
| 'failed-file' => 'failed_tests', |
| ); |
| |
| Getopt::Long::Configure(qw(no_ignore_case)); |
| GetOptions(\%opts, |
| "verbose", |
| "help|?", |
| "quiet|q", |
| "groups|g=s", |
| "r=s", |
| "debug", |
| "srcdir|D=s", |
| "builddir|d=s", |
| "f", |
| "F", |
| "failed-file=s", |
| "master-directory=s", |
| ) || ++$opts{'help'}; |
| |
| # Change srcdir and builddir to absolute paths |
| $opts{'srcdir'} = abs_path($opts{'srcdir'}); |
| $opts{'builddir'} = abs_path($opts{'builddir'}); |
| # Set exeext. |
| $opts{'exeext'} = join(readpipe($opts{'builddir'} . '/net-snmp-config --exeext')); |
| |
| usage() if ($opts{'help'}); |
| |
| # Build the harness object |
| my %args = ( |
| verbosity => ($opts{'verbose'} ? 1 : ($opts{'quiet'} ? -1 : 0)), |
| exec => \&decide_exec, |
| # this option is *really* weird in how it works |
| failures => ($opts{'quiet'} ? 0 : ($opts{'verbose'} ? 0 : 1)), |
| errors => ($opts{'quiet'} ? 0 : 1), |
| ); |
| |
| # list of support infrastructure components |
| my %support; |
| my %sources; |
| |
| # if the -d option was specified, pass on the source root directory to all apps |
| if (exists($opts{'master-directory'})) { |
| $ENV{'NETSNMPSRCDIR'} = $opts{'master-directory'}; |
| } else { |
| $ENV{'NETSNMPSRCDIR'} = '.'; |
| } |
| |
| # pass srcdir and builddir to all apps |
| $ENV{'srcdir'} = $opts{'srcdir'}; |
| $ENV{'builddir'} = $opts{'builddir'}; |
| |
| # set up MIBDIRS to refer to the src directory |
| if (!$ENV{'MIBDIRS'}) { |
| $ENV{'MIBDIRS'} = "$opts{srcdir}/mibs"; |
| } |
| |
| ######################################## |
| # Protection measures |
| $ENV{'SNMPCONFPATH'} = "/dev/null"; |
| |
| # create the testing harness infrastructure |
| |
| my $harness; |
| if (eval { require TAP::Harness; } ) { |
| import TAP::Harness; |
| $harness = TAP::Harness->new(\%args); |
| } else { |
| require Test::Harness; |
| import Test::Harness; |
| if ($opts{'groups'} ne 'default') { |
| print STDERR " |
| ERROR: I can not find the perl TAP::Harness module. We support the |
| more common Test::Harness module but only for the default test group. |
| |
| Either: |
| 1) run only the default tests (i.e., just \"make test\") |
| 2) install the TAP::Harness perl module |
| |
| "; |
| exit 1; |
| } |
| DIE("Test::Harness must be of version 1.21 or newer\n" . |
| "Install the TAP::Harness module or use the RUNTESTS script\n") |
| if $Test::Harness::VERSION < 1.21; |
| } |
| |
| |
| ######################################## |
| # gather the tests |
| my @tests; |
| |
| DEBUG("Gathering and building tests:\n"); |
| find_support(); |
| if ($opts{'f'}) { |
| DIE("The -f and -g options can not be both specified\n") |
| if ($opts{'groups'} ne 'default'); |
| DIE("The -f and -r options can not be both specified\n") if ($opts{'r'}); |
| DIE("No $opts{'failed-file'} file was found to read failed state from\n") |
| if (! -f $opts{'failed-file'}); |
| open(F, $opts{'failed-file'}); |
| while (<F>) { |
| chomp; |
| push @tests, build_test($_); |
| } |
| } else { |
| @tests = gather_tests($opts{'groups'}, $opts{'r'}); |
| } |
| |
| ######################################## |
| # rename them to remove parent dirs |
| @tests = rename_tests(@tests); |
| |
| ######################################## |
| # run the tests |
| |
| DEBUG("Running tests:\n"); |
| DEBUG("-" x 78, "\n"); |
| |
| my $results; |
| if ($harness) { |
| $results = $harness->runtests(@tests); |
| } else { |
| # minimal backwards compat with Test::Harness |
| run_older_perl_tests(@tests); |
| } |
| |
| my @failed = $results->failed(); |
| if (!$opts{'q'} && $#failed > -1) { |
| print "\nWe failed these ", (1 + $#failed), " tests:\n"; |
| my @lines = @failed; |
| map { if (exists($sources{$_})) { $_ = "$_ ( $sources{$_} )"} } @lines; |
| print " ", join("\n ",@lines), "\n"; |
| } |
| |
| if (!$opts{'F'}) { |
| open(F,">$opts{'failed-file'}"); |
| if ($#failed > -1) { |
| print F join("\n", get_sources(@failed)) . "\n"; |
| } |
| close(F); |
| } |
| |
| exit($results->all_passed() ? 0 : 1); |
| |
| ###################################################################### |
| # Infrastructure |
| # |
| |
| ######################################## |
| # decides how we should execute a test |
| # |
| sub decide_exec { |
| my ( $harness, $testfile ) = @_; |
| |
| # 1) Parse the $testfile argument. |
| my ($dirname, $groupname, $basename, $app_extension, $file_extension) = |
| ($testfile =~ /([^\/]+)\/([^\/]+)\/([^\/]+)_([^\/_]+)\.*([^\/\.]*)$/); |
| $app_extension =~ s/$opts{'exeext'}\$//; |
| |
| # 2) we have a RUN_TYPE file in the same directory |
| if (exists($support{'run'}{$app_extension}{$groupname})) { |
| return [$support{'run'}{$app_extension}{$groupname}, $testfile]; |
| } |
| |
| # 3) return a generic run script |
| if (exists($support{'run'}{$app_extension}{'generic'})) { |
| return [$support{'run'}{$app_extension}{'generic'}, $testfile]; |
| } |
| |
| # 4) give up and let the test harness decide itself |
| return undef; |
| } |
| |
| sub gather_tests { |
| my ( $groups, $regexp ) = @_; |
| my @groups; |
| |
| # figure out the list of groups we need to search through |
| if ($groups eq 'all') { |
| # find every group we can |
| # we exclude: |
| # - things not a directory |
| # - anything with "template" in the name |
| @groups = |
| grep { !/(template|support)/ && |
| -d $_ && s/$opts{'srcdir'}\/testing\/$opts{'master-directory'}.// } glob("$opts{'srcdir'}/testing/$opts{'master-directory'}/*"); |
| } else { |
| # they specified a comma separated list |
| @groups = split(/,\s*/, $groups); |
| } |
| DEBUG("Checking groups: ", join(", ", @groups), "\n"); |
| |
| my @tests; |
| foreach my $group (@groups) { |
| my @files; |
| |
| DEBUG("checking group $group\n"); |
| |
| if (! -d "$opts{'srcdir'}/testing/$opts{'master-directory'}/$group") { |
| ERROR("group '$group' is not a directory under '$opts{'srcdir'}/testing/$opts{'master-directory'}'; ignoring\n"); |
| next; |
| } |
| |
| # push on all files that start with T[NUM]* |
| push_or_skip(\@tests, $regexp, glob("$opts{'srcdir'}/testing/$opts{'master-directory'}/$group/T[0-9]*")); |
| } |
| |
| return @tests; |
| } |
| |
| sub push_or_skip { |
| my ($array, $regexp, @files) = @_; |
| foreach my $file (@files) { |
| next if ($file =~ /.(bak|old|orig|rej)$/); |
| next if ($file =~ /~$/); |
| next if (defined($regexp) && $file !~ /$regexp/i); |
| DEBUG(" Adding file $file\n"); |
| push @$array, build_test($file); |
| } |
| } |
| |
| # rename all the tests to remove the top subdir to help readability |
| sub rename_tests { |
| my (@tests) = @_; |
| my @newtests; |
| |
| # yes, I could have used map. But I didn't. |
| foreach my $file (@tests) { |
| my $title = "$file"; |
| my $foundheader = 0; |
| $title = $sources{$file} if (exists($sources{$file})); |
| |
| open(SRC, $title); |
| while (<SRC>) { |
| if (/(HEADER|TITLE)\s+['"]*(.*)/) { |
| $title = $2; |
| $title =~ s/\s*\*\/.*//; |
| $title =~ s/['"]$//; |
| $foundheader = 1; |
| last; |
| } |
| } |
| close(SRC); |
| |
| if (! $foundheader) { |
| $title =~ s/^$opts{'srcdir'}\/testing\///; |
| $title =~ s/$opts{'master-directory'}.//; |
| } |
| $sources{$title} = $sources{$file} || $file; |
| push @newtests, [$file, $title]; |
| } |
| return @newtests; |
| } |
| |
| # called to possibly manipulate the list of tests to run by building some |
| sub build_tests { |
| my (@tests) = @_; |
| my @newtests; |
| |
| foreach my $test (@tests) { |
| my $title; |
| |
| my $built = build_test($test); |
| if (ref($built) eq 'ARRAY') { |
| push @newtests, @$built; |
| } elsif ($built ne "") { |
| push @newtests, $built; |
| } |
| } |
| return @newtests; |
| } |
| |
| # |
| # Finds scripts that are used to build and run actual commands |
| # |
| sub find_builders { |
| $support{'build'} = {}; |
| find_scripts('build', $support{'build'}); |
| } |
| |
| sub find_runners { |
| $support{'run'} = {}; |
| find_scripts('run', $support{'run'}); |
| } |
| |
| sub find_support { |
| find_builders(); |
| find_runners(); |
| } |
| |
| sub find_scripts { |
| my ($buildname, $hashref) = @_; |
| my $count; |
| DEBUG("looking for $buildname scripts\n"); |
| foreach my $builder (glob("$opts{'srcdir'}/testing/$opts{'master-directory'}/*/*_${buildname}")) { |
| next if ($builder =~ /~$/); |
| next if ($builder =~ /.(bak|orig|rej|old)$/); |
| |
| my ($group, $type) = ($builder =~ /([^\/]+)\/([^\/]*)_${buildname}/); |
| # save this as a certain group builder |
| $hashref->{$type}{$group} = $builder; |
| |
| # save this as a generic builder if there isn't a better |
| # generic one, such as one that exists in the support |
| # directory. |
| if (!exists($hashref->{$type}{'generic'}) || $group eq 'support') { |
| $hashref->{$type}{'generic'} = $builder; |
| } |
| $count++; |
| } |
| DEBUG(" found $count\n"); |
| } |
| |
| # called to build a test from a registerd builder |
| sub build_test { |
| my ($testfile) = @_; |
| |
| my ($dirname, $groupname, $basename, $app_extension, $file_extension) = |
| ($testfile =~ /([^\/]+)\/([^\/]+)\/([^\/]+)_([^\/_]+)\.([^\/\.]+)$/); |
| |
| # is this even a buildable type recipe? |
| if (!$dirname || !$basename || !$app_extension || !$file_extension) { |
| return $testfile; |
| } |
| |
| DEBUG("found: $testfile => $dirname, $basename, $app_extension, $file_extension\n"); |
| |
| # choices: |
| # 1) we have a registered subroutine to build an extension from |
| # XXX |
| |
| # 2) we have a BUILD_TYPE file in the same directory |
| if (exists($support{'build'}{$app_extension}{$dirname})) { |
| return |
| call_build_script($support{'build'}{$app_extension}{$dirname}, $testfile); |
| } |
| |
| # 3) return a generic build script |
| if (exists($support{'build'}{$app_extension}{'generic'})) { |
| return |
| call_build_script($support{'build'}{$app_extension}{'generic'}, $testfile); |
| } |
| |
| # 4) we assume it's fine as is |
| return $testfile; |
| } |
| |
| sub call_build_script { |
| my ($scriptname, $filename) = @_; |
| |
| my $maybenewfile = $filename; |
| $maybenewfile =~ s/.[^\.]+$/$opts{'exeext'}/; |
| $maybenewfile =~ s/T([^\/]+)$/B$1/; # change prefix to B for 'Built' |
| $maybenewfile =~ s/^$opts{'srcdir'}\///; |
| |
| my $newpath = $maybenewfile; |
| $newpath =~ s/\/[^\/]*$//; |
| |
| if (! -d $newpath) { |
| DEBUG("making directory $newpath\n"); |
| system("$opts{'srcdir'}/mkinstalldirs $newpath"); |
| } |
| |
| my $lastline; |
| DEBUG("BUILDING: $scriptname $filename $maybenewfile\n"); |
| open(B,"$scriptname $filename $maybenewfile|"); |
| while (<B>) { |
| $lastline = $_; |
| } |
| chomp($lastline); |
| |
| DEBUG(" result: $lastline\n"); |
| return undef if ($lastline eq 'fail'); |
| return undef if ($lastline eq 'skip'); |
| return $filename if ($lastline eq ''); |
| $sources{$lastline} = $filename; # remember where we came from |
| return $lastline; |
| } |
| |
| sub get_sources { |
| my (@names) = @_; |
| map { if (exists($sources{$_})) { $_ = $sources{$_} } } @names; |
| return @names; |
| } |
| |
| sub run_older_perl_tests { |
| # |
| # Older versions of perl used a different test suite called Test::Harness |
| # It is much more limited than TAP::Harness; |
| # |
| # Here we massage our older tests into something that will work under |
| # Test::Harness too. |
| # |
| my @tests = @_; |
| |
| # create the temporary files |
| my @tempfiles; |
| if (! -d "$opts{'master-directory'}") { |
| mkdir("$opts{'master-directory'}", 0777); |
| } |
| if (! -d "$opts{'master-directory'}/temptests") { |
| mkdir("$opts{'master-directory'}/temptests", 0777); |
| } |
| foreach my $test (@tests) { |
| my $tempfile = "$test->[0].t"; |
| $tempfile =~ s#^$opts{'srcdir'}#$opts{'builddir'}#; |
| $tempfile =~ s#$opts{'master-directory'}/default/#$opts{'master-directory'}/temptests/#; |
| open(T, ">$tempfile") || die("$tempfile: $!"); |
| print T "# functionally perl\n\nsystem(\"$opts{'srcdir'}/testing/fulltests/support/simple_run $test->[0]\");\n"; |
| close(T); |
| chmod(0755, $tempfile); |
| push @tempfiles, $tempfile; |
| } |
| |
| $results = runtests(@tempfiles); |
| |
| unlink(@tempfiles) || die("$@ $!"); |
| exit; |
| } |
| |
| # usage output |
| sub usage { |
| print "$0 [OPTIONS]\n"; |
| |
| print "\nOPTIONS:\n"; |
| print " -v\t\t\tRuns in verbose mode; dumping all test output\n"; |
| print " --verbose\n"; |
| print " -q\t\t\tRuns in quieter mode; dumps less test output\n"; |
| print " --quiet\n"; |
| print " -g GROUP\t\tRuns the group of specified tests (default = 'default')\n"; |
| print " --group GROUP\n"; |
| print "\t\t\t(use 'all' to run all tests)\n"; |
| print " -r REGEXP\t\tOnly run test files matching this regexp\n"; |
| print " -f\t\t\tRun only the failed tests from the last run\n"; |
| print " -F\t\t\tDon't create the failed_tests file\n"; |
| print " --failed-file FILE\tThe location of the failed state file\n"; |
| print " -D PATH\t\tSource directory\n"; |
| print " --srcdir PATH\n"; |
| print " (currently '$opts{'srcdir'}')\n"; |
| print " -d PATH\t\tBuild directory to be tested\n"; |
| print " --builddir PATH\n"; |
| print " (currently '$opts{'builddir'}')\n"; |
| print " --master-directory DIRNAME\n"; |
| print " (default = 'fulltests')\n"; |
| print " -h\t\t\tThis help output\n"; |
| print " --help\n"; |
| print " --debug\t\tDebugging output\n\n"; |
| exit; |
| } |
| |
| sub DEBUG { |
| return if (!$opts{'debug'}); |
| print @_; |
| } |
| |
| sub ERROR { |
| print STDERR "Error:", @_; |
| } |
| |
| sub DIE { |
| ERROR(@_); |
| exit 1; |
| } |
| |
| =pod |
| |
| =head1 NAME |
| |
| runfulltests - the Net-SNMP test suite coordinator |
| |
| =head1 SYNOPSIS |
| |
| runfulltests [OPTIONS] |
| |
| # ./RUNFULLTESTS |
| |
| # ./RUNFULLTESTS -g all |
| |
| =head1 DESCRIPTION |
| |
| B<RUNFULLTESTS> is a TAP (see below) output aggregator and test suite |
| management program. It runs groups of tests that it can find in the |
| I<fulltests> sub-directory. It defaults to running a basic set of |
| high-level Net-SNMP tests found in the fulltests/default directory. |
| To run a different set of tests see the -g flag. |
| |
| It is able to keep state and remember which tests failed so that |
| during development you can simply re-run only the "last failed tests" |
| using the -f switch. |
| |
| =head2 Perl Requirements |
| |
| Ideally it should be run under a fairly modern version of perl (eg, |
| 5.10) but minmial support is provided for older versions of perl as |
| well. |
| |
| If no perl is available on the system, there is also a fall-back |
| "RUNTESTS" suite which merely executes the default scripts for testing |
| compliance of the high-level applications. |
| |
| =head2 Important Notes About Writing New Tests |
| |
| When designing new tests it is strongly encouraged that some |
| conventions are followed: |
| |
| - Design the test files so they can be build/run without them |
| needing to be build/run within a testing harness like this one |
| (B<RUNFULLTESTS>). IE, you should be able to run them directly by |
| hand for debugging purposes without requiring them to be invoked |
| within this or any other testing harness. |
| - Name them beginning with TNNN where NNN is a 3 digit number |
| |
| The rational behind these rules follows in the sections below |
| |
| =head1 OPTIONS |
| |
| =over |
| |
| =item -g GROUP |
| |
| =item --group GROUP |
| |
| By default the "default" group of tests is run. Which is really just |
| everything that B<RUNFULLTESTS> can find from the I<fulltests/default> |
| sub-directory. The -g switch can be used to specify other |
| sub-directories of tests to run. The argument is a comma-separated |
| list of subdirectories to use. |
| |
| The special keyword I<all> can be used to run every test in every |
| subdirectory of the I<fulltests> directory. |
| |
| =item -r REGEXP |
| |
| Only run test files that match the I<REGEXP> regular expression. |
| |
| To run only tests of a certain file name you might combine this with |
| '-g all'. E.G., -g all -r snmpv3 will run all snmpv3 (named) tests |
| that B<RUNFULLTESTS> can find. |
| |
| =item -f |
| |
| Only run the tests that failed from the last run. |
| |
| =item --failed-file FILE |
| |
| Where to store the state of which tests have failed. |
| |
| =item -F |
| |
| Don't save state to the failed-file. |
| |
| =item -D PATH |
| |
| =item --srcdir PATH |
| |
| If RUNFULLTESTS is being executed from a build directory other than |
| where the source files are located, this flag can be used to specify |
| where the Net-SNMP root source directory is found. |
| |
| =item -d PATH |
| |
| =item --builddir PATH |
| |
| Specifies the root of the build directory. |
| |
| =item --master-directory DIRNAME |
| |
| Specifies an alternate master directory. The default is "fulltests" |
| |
| =item -v |
| |
| =item --verbose |
| |
| Turns on verbose output mode. |
| |
| =item -q |
| |
| =item --quiet |
| |
| Turns on quiet output mode. |
| |
| =item --debug |
| |
| Turns on debugging output (which primarily shows how B<RUNFULLTESTS> |
| is collecting tests to run, etc). |
| |
| =item -h |
| |
| =item --help |
| |
| Command line usage help output. |
| |
| =back |
| |
| =head1 TEST ARCHITECTURE |
| |
| =head2 TAP output |
| |
| TAP stands for "Test Anything Protocol". TAP was originally |
| perl-specific but has been turning out to be a generic protocol for |
| testing just about anything. It's heavily documented at: |
| |
| http://testanything.org/wiki/index.php/Main_Page |
| |
| We're using TAP because it's highly flexible and separates the |
| invidual tests from the "collect and report" aspect. RUNFULLTESTS is |
| simply a perl-based implementation for collecting and summarizing the |
| test results. Other collection agents could be used instead of this |
| one, and any sort of test or language could be used for the individual |
| tests as well (in fact the default suite has some that are SH-based, |
| C-based, ...). |
| |
| It may be that eventually the TAP protocol actually makes it into the |
| IETF (http://testanything.org/wiki/index.php/TAP_at_IETF:_Draft_Standard). |
| |
| The syntax of TAP is very simple. See the above web page for a |
| complete description, but this will provide you a minimal "getting |
| started" example and shows the output of 5 sub-tests run from a single |
| test application. |
| |
| 1..5 |
| ok 1 - Yay |
| ok 2 - Second part succeeded |
| not ok 3 - Oh no... A problem occurred. |
| not ok 4 - The computer thought 2+2 was 5 |
| ok 5 - All is well that ends well |
| |
| That's it. Output from a test tool like that is auto-summarized by |
| this application in success/fail reports. |
| |
| =head2 Testing Phases |
| |
| The testing process goes through the following phases: |
| |
| - Test and Infrastructure Collection |
| - Test Execution |
| - Build if needed |
| - Run |
| |
| =head2 Test Collection |
| |
| B<RUNFULLTESTS> will search all the requested groups for files that |
| begin with the letter 'T'. They are executed in alphabetical order |
| within their group. Convention is to number test files with a three |
| digit (zero-padded) number after the 'T' letter to ensure ordering is |
| as expected. |
| |
| Files to be collected by B<RUNFULLTESTS> are made up of a number of |
| components to support a flexible build and execution system (discussed |
| in detail in the following sections). They are structured as follows: |
| |
| T<NNN><NAME>_<TYPE>.<SUFFIX> |
| |
| Where: |
| |
| NNN: Is the 3 digit number mentioned above. |
| NAME: The filename of the test describing what it's about |
| TYPE: The internal "type" of the test, which is used later in building |
| and execution (see below). |
| .SUFFIX: An optional file-type suffix |
| |
| Examples: |
| |
| fulltests/default/T001snmpv1get_simple |
| fulltests/snmpv3/T040keymanagetest_capp.c |
| |
| Any other files are ignored in terms of tests and may be supplimental |
| to the above build systems. (Supporting files, by convention, begin |
| with a capital 'S'). |
| |
| =head3 Full Title |
| |
| Within the file there may be a line containing "HEADER ..." that will |
| be examined for a better title of the test. Anything before "HEADER" |
| will be ignored, and the special "*/" will be replaced as well. For |
| example, these are valid header source lines: |
| |
| # HEADER A cool test |
| /* HEADER A cool test from my C-based source file */ |
| |
| =head2 Infrastructure Collection |
| |
| In addition to test files, I<infrastructure> files are searched for |
| and remembered for later use (again, see below). These files will be |
| of the form: |
| |
| <TYPE>_<USEAGE> |
| |
| Where: |
| |
| TYPE: The type name matching the file to support. |
| |
| USAGE: How this file should be used. Currently should be either |
| I<build> or I<run> as described below. |
| |
| Example files |
| |
| fulltests/support/clib_build |
| fulltests/support/simple_run |
| |
| Infrastructure files may exist in the source directory of where they're |
| expected to be run (ie, parallel to the test files) or they may exist |
| in the special "support" directory if they're expected to be |
| generically used across multilpe test group types. |
| |
| =head2 Test Execution |
| |
| Tests are run in two phases using the following pseudo-algorithm: |
| |
| + for each test file |
| + if an appropriate TYPE_build file exists for a test { |
| + run "TYPE_build TESTFILE" |
| + record the last line as the new TESTFILE to run |
| } |
| |
| + if an apporpriate TYPE_run file exists for a test { |
| + run "TYPE_run TESTFILE" |
| + collect it's output as the TAP output |
| } else { |
| + run "TESTFILE" |
| + collect it's output as the TAP output |
| } |
| |
| For example, if the following files existed: |
| |
| fulltests/examplres/T001testing_examp |
| fulltests/examplres/examp_build |
| fulltests/examplres/examp_run |
| |
| Then the following would be the rough execution: |
| |
| newfile = `fulltests/examplres/examp_build \ |
| fulltests/examplres/T001testing_examp | tail -1` |
| fulltests/examplres/examp_run $newfile |
| |
| =head1 TEST TYPES |
| |
| Net-SNMP testing comes with a number of test suite "builders" and |
| "runners" that are useful for developing new tests. These are |
| documented here: |
| |
| =over |
| |
| =item simple |
| |
| I<simple> test files are simple sh-shell-script files used to test |
| high-level functionality of Net-SNMP tools. They're easy to write and |
| should generally contain the following sort of structure: |
| |
| . ../support/simple_eval_tools.sh |
| HEADER my name |
| STARTAGENT |
| CAPTURE "snmpget..." |
| CHECK "for this string" |
| STOPAGENT |
| FINISHED |
| |
| Example file: fulltests/default/T001snmpv1get_simple |
| |
| =item capp |
| |
| I<capp> files are fundamentally full C-source-code applications that |
| are built and linked against the libnetsnmp library. Thus, a file |
| named I<T001mytest_capp.c> is compiled using the same compiler used to |
| compile Net-SNMP and linked against the required libraries for a basic |
| Net-SNMP application. It should, of course, produce TAP output after |
| it's compiled and run. |
| |
| Example file: fulltests/snmpv3/T010scapitest_capp.c |
| |
| =item clib |
| |
| I<clib> files are simple C-source-code files that are wrapped into a |
| main() application with appropriate #include files, etc. I<clib> |
| files are designed primarly to write quick unit-tests for the Net-SNMP |
| core library. |
| |
| Example file: fulltests/unit-tests/T001defaultstore_clib.c |
| |
| =item Write your own! |
| |
| This test system is designed to be flexible and expandable if the |
| basic architecture is followed. The goal is to make it easy to create |
| very simple test suites or complex unit-tests or anything in between. |
| |
| =back |
| |
| =head1 DEBUGGING BROKEN TESTS |
| |
| If the individual tests are designed well, you should be able to |
| re-run individual tests outside of the B<RUNFULLTESTS> aggregation |
| environment using the appropriate _build and _run scripts as needed. |
| Test writers are encouraged to output comments in their TAP output to |
| help users debug the results. |
| |
| =head1 Author |
| |
| Original architecture: Wes Hardaker <hardaker@users.sourceforge.net> |
| |
| =cut |
| |