diff options
author | unknown <mats@mysql.com> | 2006-04-10 17:06:12 +0200 |
---|---|---|
committer | unknown <mats@mysql.com> | 2006-04-10 17:06:12 +0200 |
commit | 4f99f11e9c798225fd5906c23dc26da01ae4f20e (patch) | |
tree | a0aa9d8a9cbbeb9f6e3b36fb64e93e0cf337725b /unittest | |
parent | 3f9e35d3f350768a8d237bfd369adf63f565f81c (diff) | |
download | mariadb-git-4f99f11e9c798225fd5906c23dc26da01ae4f20e.tar.gz |
WL#3206 (Adding unit tests):
Added 'test' target to build and run tests. Added documentation.
Added README.txt files. Fixing problem with initialization of
the Test::Harness::Straps replacement. Added code to filter out non-
test directories.
unittest/Makefile.am:
Adding 'test' target to build and run tests.
unittest/examples/skip.t.c:
Changing text for skip reason.
unittest/examples/skip_all.t.c:
Changing text for skip reason.
unittest/mytap/tap.c:
Adding copyright. Adding documentation. Minor code changes.
unittest/mytap/tap.h:
Adding copyright. Adding documentation.
unittest/unit.pl:
Initializing replacement Test::Harness::Straps properly.
Adding code to filter non-test directories from default directories
to use.
unittest/README.txt:
New BitKeeper file ``unittest/README.txt''
Diffstat (limited to 'unittest')
-rw-r--r-- | unittest/Makefile.am | 10 | ||||
-rw-r--r-- | unittest/README.txt | 33 | ||||
-rw-r--r-- | unittest/examples/skip.t.c | 2 | ||||
-rw-r--r-- | unittest/examples/skip_all.t.c | 2 | ||||
-rw-r--r-- | unittest/mytap/tap.c | 147 | ||||
-rw-r--r-- | unittest/mytap/tap.h | 36 | ||||
-rw-r--r-- | unittest/unit.pl | 47 |
7 files changed, 245 insertions, 32 deletions
diff --git a/unittest/Makefile.am b/unittest/Makefile.am index 3e7fc37e4f6..8943e58170d 100644 --- a/unittest/Makefile.am +++ b/unittest/Makefile.am @@ -1,11 +1,13 @@ SUBDIRS = mytap . mysys examples -.PHONY: mytap mysys examples test - noinst_SCRIPTS = unit -test: mytap mysys examples - ./unit run $^ +unittests = mysys examples + +.PHONY: all mytap mysys examples test + +test: unit all + @./unit run $(unittests) mytap: cd mytap && $(MAKE) diff --git a/unittest/README.txt b/unittest/README.txt new file mode 100644 index 00000000000..35e930a7951 --- /dev/null +++ b/unittest/README.txt @@ -0,0 +1,33 @@ + + +Unit test structure +------------------- + +This is the current structure of the unit tests. All directories does +not currently exist, and more directories will be added over time. + ++ mysys Tests for mysys components ++ examples Example unit tests ++ sql Unit tests for server code + + rpl Unit tests for replication code + + log Unit tests for logging + +Executing unit tests +-------------------- + +To make and execute all unit tests in the directory: + + make test + + +Adding unit tests +----------------- + +Add a file with a name of the format "foo.t.c" to the appropriate +directory and add the following to the Makefile.am in that directory +(where ... denotes stuff already there): + + noinst_PROGRAMS = ... foo.t + foo_t_c_SOURCES = foo.t.c + + diff --git a/unittest/examples/skip.t.c b/unittest/examples/skip.t.c index dd8f96c9541..ef717691700 100644 --- a/unittest/examples/skip.t.c +++ b/unittest/examples/skip.t.c @@ -6,7 +6,7 @@ int main() { plan(4); ok(1, NULL); ok(1, NULL); - SKIP_BLOCK_IF(1, 2, "No point") { + SKIP_BLOCK_IF(1, 2, "Example of skipping a few test points in a test") { ok(1, NULL); ok(1, NULL); } diff --git a/unittest/examples/skip_all.t.c b/unittest/examples/skip_all.t.c index 7590bdaeb0b..bbde6b63f1c 100644 --- a/unittest/examples/skip_all.t.c +++ b/unittest/examples/skip_all.t.c @@ -13,7 +13,7 @@ int has_feature() { */ int main() { if (!has_feature()) - skip_all("Missing feature"); + skip_all("Example of skipping an entire test"); plan(4); ok(1, NULL); ok(1, NULL); diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 9540c0322d0..182210dda4f 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -1,3 +1,22 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Library for providing TAP support for testing C and C++ was written + by Mats Kindahl <mats@mysql.com>. +*/ #include "tap.h" @@ -10,8 +29,10 @@ Test data structure. Data structure containing all information about the test suite. + + @ingroup MyTAP */ -static TEST_DATA g_test = { 0 }; +static TEST_DATA g_test = { 0, 0, 0, "" }; /** Output stream for test report message. @@ -21,13 +42,20 @@ static TEST_DATA g_test = { 0 }; #define tapout stdout /** - Emit a TAP result and optionally a description. + Emit the beginning of a test line, that is: "(not) ok", test number, + and description. + + To emit the directive, use the emit_dir() function + + @ingroup MyTAP + + @see emit_dir @param pass 'true' if test passed, 'false' otherwise @param fmt Description of test in printf() format. @param ap Vararg list for the description string above. */ -static int +static void emit_tap(int pass, char const *fmt, va_list ap) { fprintf(tapout, "%sok %d%s", @@ -39,14 +67,30 @@ emit_tap(int pass, char const *fmt, va_list ap) } -static int +/** + Emit a TAP directive. + + TAP directives are comments after a have the form + + @code + ok 1 # skip reason for skipping + not ok 2 # todo some text explaining what remains + @endcode + + @param dir Directive as a string + @param exp Explanation string + */ +static void emit_dir(const char *dir, const char *exp) { fprintf(tapout, " # %s %s", dir, exp); } -static int +/** + Emit a newline to the TAP output stream. + */ +static void emit_endl() { fprintf(tapout, "\n"); @@ -71,10 +115,9 @@ plan(int const count) switch (count) { case NO_PLAN: - case SKIP_ALL: break; default: - if (plan > 0) + if (count > 0) fprintf(tapout, "1..%d\n", count); break; } @@ -103,13 +146,13 @@ ok(int const pass, char const *fmt, ...) emit_tap(pass, fmt, ap); va_end(ap); if (*g_test.todo != '\0') - emit_dir("TODO", g_test.todo); + emit_dir("todo", g_test.todo); emit_endl(); } void -skip(int how_many, char const *fmt, ...) +skip(int how_many, char const *const fmt, ...) { char reason[80]; if (fmt && *fmt) @@ -126,7 +169,7 @@ skip(int how_many, char const *fmt, ...) { va_list ap; emit_tap(1, NULL, ap); - emit_dir("SKIP", reason); + emit_dir("skip", reason); emit_endl(); } } @@ -169,3 +212,87 @@ int exit_status() { return EXIT_SUCCESS; } +/** + @mainpage Testing C and C++ using MyTAP + + @section IntroSec Introduction + + Unit tests are used to test individual components of a system. In + contrast, functional tests usually test the entire system. The + rationale is that each component should be correct if the system is + to be correct. Unit tests are usually small pieces of code that + tests an individual function, class, a module, or other unit of the + code. + + Observe that a correctly functioning system can be built from + "faulty" components. The problem with this approach is that as the + system evolves, the bugs surface in unexpected ways, making + maintenance harder. + + The advantages of using unit tests to test components of the system + are several: + + - The unit tests can make a more thorough testing than the + functional tests by testing correctness even for pathological use + (which shouldn't be present in the system). This increases the + overall robustness of the system and makes maintenance easier. + + - It is easier and faster to find problems with a malfunctioning + component than to find problems in a malfunctioning system. This + shortens the compile-run-edit cycle and therefore improves the + overall performance of development. + + - The component has to support at least two uses: in the system and + in a unit test. This leads to more generic and stable interfaces + and in addition promotes the development of reusable components. + + For example, the following are typical functional tests: + - Does transactions work according to specifications? + - Can we connect a client to the server and execute statements? + + In contrast, the following are typical unit tests: + + - Can the 'String' class handle a specified list of character sets? + - Does all operations for 'my_bitmap' produce the correct result? + - Does all the NIST test vectors for the AES implementation encrypt + correctly? + + + @section UnitTest Writing unit tests + + The purpose of writing unit tests is to use them to drive component + development towards a solution that the tests. This means that the + unit tests has to be as complete as possible, testing at least: + + - Normal input + - Borderline cases + - Faulty input + - Error handling + - Bad environment + + We will go over each case and explain it in more detail. + + @subsection NormalSSec Normal input + + @subsection BorderlineSSec Borderline cases + + @subsection FaultySSec Faulty input + + @subsection ErrorSSec Error handling + + @subsection EnvironmentSSec Environment + + Sometimes, modules has to behave well even when the environment + fails to work correctly. Typical examples are: out of dynamic + memory, disk is full, + + @section UnitTestSec How to structure a unit test + + In this section we will give some advice on how to structure the + unit tests to make the development run smoothly. + + @subsection PieceSec Test each piece separately + + Don't test all functions using size 1, then all functions using + size 2, etc. + */ diff --git a/unittest/mytap/tap.h b/unittest/mytap/tap.h index 3e9bd4d6e5d..bfd7764ce17 100644 --- a/unittest/mytap/tap.h +++ b/unittest/mytap/tap.h @@ -1,12 +1,32 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Library for providing TAP support for testing C and C++ was written + by Mats Kindahl <mats@mysql.com>. +*/ + #ifndef TAP_H #define TAP_H /* - - */ + @defgroup MyTAP MySQL support for performing unit tests according to TAP. + +*/ #define NO_PLAN (0) -#define SKIP_ALL (-1) /** Data about test plan. @@ -40,12 +60,12 @@ extern "C" { /** Set number of tests that is planned to execute. - The function also accepts the predefined constants SKIP_ALL and - NO_PLAN. + The function also accepts the predefined constant + <code>NO_PLAN</code>. If the function is not called, it is as if + it was called with <code>NO_PLAN</code>, i.e., the test plan will + be printed after all the test lines. - @param count - The planned number of tests to run. Alternatively, the SKIP_ALL - and NO_PLAN can be supplied. + @param count The planned number of tests to run. */ void plan(int count); diff --git a/unittest/unit.pl b/unittest/unit.pl index bc20938309a..5c45c15a5b1 100644 --- a/unittest/unit.pl +++ b/unittest/unit.pl @@ -3,15 +3,22 @@ # Override _command_line in the standard Perl test harness to prevent # it from using "perl" to run the test scripts. package MySQL::Straps; + use base qw(Test::Harness::Straps); -sub _command_line { return $_[1] } + +use strict; + +sub _command_line { + return $_[1] +} package main; -use strict; -use Test::Harness; +use Test::Harness qw(&runtests $verbose); use File::Find; +use strict; + sub run_cmd (@); my %dispatch = ( @@ -30,6 +37,9 @@ unit - Run unit tests in directory my $cmd = shift; +# $Test::Harness::Verbose = 1; +# $Test::Harness::Debug = 1; + if (defined $cmd && exists $dispatch{$cmd}) { $dispatch{$cmd}->(@ARGV); } else { @@ -57,20 +67,41 @@ sub _find_test_files (@) { sub run_cmd (@) { my @files; - push(@_, '.') if @_ == 0; + # If no directories were supplied, we add all directories in the + # current directory except 'mytap' since it is not part of the + # test suite. + if (@_ == 0) { + # Ignore these directories + my @ignore = qw(mytap SCCS); + + # Build an expression from the directories above that tests if a + # directory should be included in the list or not. + my $ignore = join(' && ', map { '$_ ne ' . "'$_'"} @ignore); + + # Open and read the directory. Filter out all files, hidden + # directories, and directories named above. + opendir(DIR, ".") or die "Cannot open '.': $!\n"; + @_ = grep { -d $_ && $_ !~ /^\..*/ && eval $ignore } readdir(DIR); + closedir(DIR); + } + + print "Running tests: @_\n"; foreach my $name (@_) { push(@files, _find_test_files $name) if -d $name; push(@files, $name) if -f $name; } - + if (@files > 0) { # Removing the first './' from the file names foreach (@files) { s!^\./!! } - - # Install the strap above instead of the default strap + + # Install the strap above instead of the default strap. Since + # we are replacing the straps under the feet of Test::Harness, + # we need to do some basic initializations in the new straps. $Test::Harness::Strap = MySQL::Straps->new; - + $Test::Harness::Strap->{callback} = \&Test::Harness::strap_callback; + runtests @files; } } |