Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
a3864256
Commit
a3864256
authored
Nov 03, 2006
by
mats@romeo.(none)
Browse files
Options
Browse Files
Download
Plain Diff
Merge mkindahl@bk-internal.mysql.com:/home/bk/mysql-5.1-new-rpl
into romeo.(none):/home/bkroot/mysql-5.1-new-rpl
parents
a66daa28
d728519d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
294 additions
and
54 deletions
+294
-54
unittest/README.txt
unittest/README.txt
+4
-2
unittest/mytap/Doxyfile
unittest/mytap/Doxyfile
+24
-23
unittest/mytap/tap.c
unittest/mytap/tap.c
+245
-22
unittest/mytap/tap.h
unittest/mytap/tap.h
+21
-7
No files found.
unittest/README.txt
View file @
a3864256
...
@@ -46,6 +46,8 @@ test won't be executed by 'make test' !
...
@@ -46,6 +46,8 @@ test won't be executed by 'make test' !
Documentation
Documentation
-------------
-------------
The
re is Doxygen-generated documentation available
at:
The
generated documentation is temporarily placed
at:
https://intranet.mysql.com/~mkindahl/mytap/html/
http://www.kindahl.net/mytap/doc/
I will move it to a better place once I figure out where and how.
unittest/mytap/Doxyfile
View file @
a3864256
...
@@ -432,7 +432,7 @@ FILE_PATTERNS =
...
@@ -432,7 +432,7 @@ FILE_PATTERNS =
# subdirectories should be searched for input files as well. Possible
# subdirectories should be searched for input files as well. Possible
# values are YES and NO. If left blank NO is used.
# values are YES and NO. If left blank NO is used.
RECURSIVE =
YES
RECURSIVE =
NO
# The EXCLUDE tag can be used to specify files and/or directories that
# The EXCLUDE tag can be used to specify files and/or directories that
# should excluded from the INPUT source files. This way you can easily
# should excluded from the INPUT source files. This way you can easily
...
@@ -457,14 +457,14 @@ EXCLUDE_PATTERNS =
...
@@ -457,14 +457,14 @@ EXCLUDE_PATTERNS =
# directories that contain example code fragments that are included (see
# directories that contain example code fragments that are included (see
# the \include command).
# the \include command).
EXAMPLE_PATH =
EXAMPLE_PATH =
e
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
# blank all files are included.
EXAMPLE_PATTERNS =
EXAMPLE_PATTERNS =
*.c
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# searched for input files to be used with the \include or \dontinclude
...
@@ -926,7 +926,7 @@ MACRO_EXPANSION = YES
...
@@ -926,7 +926,7 @@ MACRO_EXPANSION = YES
# then the macro expansion is limited to the macros specified with the
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF =
NO
EXPAND_ONLY_PREDEF =
YES
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
# in the INCLUDE_PATH (see below) will be search if a #include is found.
...
@@ -939,33 +939,34 @@ SEARCH_INCLUDES = YES
...
@@ -939,33 +939,34 @@ SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more
wildcard
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more
#
patterns (like *.h and *.hpp) to filter out the header-files in the
#
wildcard patterns (like *.h and *.hpp) to filter out the
#
directories. If left blank, the patterns specified with FILE_PATTERNS will
#
header-files in the directories. If left blank, the patterns
# be used.
#
specified with FILE_PATTERNS will
be used.
INCLUDE_FILE_PATTERNS =
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names
that
# The PREDEFINED tag can be used to specify one or more macro names
#
are defined before the preprocessor is started (similar to the -D option of
#
that are defined before the preprocessor is started (similar to the
#
gcc). The argument of the tag is a list of macros of the form: name
#
-D option of gcc). The argument of the tag is a list of macros of
#
or name=definition (no spaces). If the definition and the = are
#
the form: name or name=definition (no spaces). If the definition and
# omitted =1 is assumed.
#
the = are
omitted =1 is assumed.
PREDEFINED =
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES
# this tag can be used to specify a list of macro names that should be expanded.
# then this tag can be used to specify a list of macro names that
# The macro definition that is found in the sources will be used.
# should be expanded. The macro definition that is found in the
# Use the PREDEFINED tag if you want to use a different macro definition.
# sources will be used. Use the PREDEFINED tag if you want to use a
# different macro definition.
EXPAND_AS_DEFINED =
EXPAND_AS_DEFINED =
__attribute__
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are
alone
# doxygen's preprocessor will remove all function-like macros that are
#
on a line, have an all uppercase name, and do not end with a semicolon. Such
#
alone on a line, have an all uppercase name, and do not end with a
#
function macros are typically used for boiler-plate code, and will confuse the
#
semicolon. Such function macros are typically used for boiler-plate
# parser if not removed.
#
code, and will confuse the
parser if not removed.
SKIP_FUNCTION_MACROS = YES
SKIP_FUNCTION_MACROS = YES
...
...
unittest/mytap/tap.c
View file @
a3864256
...
@@ -28,12 +28,18 @@
...
@@ -28,12 +28,18 @@
#include <string.h>
#include <string.h>
#include <signal.h>
#include <signal.h>
/**
@defgroup MyTAP_Internal MyTAP Internals
Internal functions and data structures for the MyTAP implementation.
*/
/**
/**
Test data structure.
Test data structure.
Data structure containing all information about the test suite.
Data structure containing all information about the test suite.
@ingroup MyTAP
@ingroup MyTAP
_Internal
*/
*/
static
TEST_DATA
g_test
=
{
0
,
0
,
0
,
""
};
static
TEST_DATA
g_test
=
{
0
,
0
,
0
,
""
};
...
@@ -41,6 +47,8 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
...
@@ -41,6 +47,8 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
Output stream for test report message.
Output stream for test report message.
The macro is just a temporary solution.
The macro is just a temporary solution.
@ingroup MyTAP_Internal
*/
*/
#define tapout stdout
#define tapout stdout
...
@@ -50,7 +58,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
...
@@ -50,7 +58,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
To emit the directive, use the emit_dir() function
To emit the directive, use the emit_dir() function
@ingroup MyTAP
@ingroup MyTAP
_Internal
@see emit_dir
@see emit_dir
...
@@ -59,7 +67,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
...
@@ -59,7 +67,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
@param ap Vararg list for the description string above.
@param ap Vararg list for the description string above.
*/
*/
static
void
static
void
emit_tap
(
int
pass
,
char
const
*
fmt
,
va_list
ap
)
v
emit_tap
(
int
pass
,
char
const
*
fmt
,
va_list
ap
)
{
{
fprintf
(
tapout
,
"%sok %d%s"
,
fprintf
(
tapout
,
"%sok %d%s"
,
pass
?
""
:
"not "
,
pass
?
""
:
"not "
,
...
@@ -80,18 +88,22 @@ emit_tap(int pass, char const *fmt, va_list ap)
...
@@ -80,18 +88,22 @@ emit_tap(int pass, char const *fmt, va_list ap)
not ok 2 # todo some text explaining what remains
not ok 2 # todo some text explaining what remains
@endcode
@endcode
@ingroup MyTAP_Internal
@param dir Directive as a string
@param dir Directive as a string
@param
exp
Explanation string
@param
why
Explanation string
*/
*/
static
void
static
void
emit_dir
(
const
char
*
dir
,
const
char
*
exp
)
emit_dir
(
const
char
*
dir
,
const
char
*
why
)
{
{
fprintf
(
tapout
,
" # %s %s"
,
dir
,
exp
);
fprintf
(
tapout
,
" # %s %s"
,
dir
,
why
);
}
}
/**
/**
Emit a newline to the TAP output stream.
Emit a newline to the TAP output stream.
@ingroup MyTAP_Internal
*/
*/
static
void
static
void
emit_endl
()
emit_endl
()
...
@@ -190,7 +202,7 @@ ok(int const pass, char const *fmt, ...)
...
@@ -190,7 +202,7 @@ ok(int const pass, char const *fmt, ...)
if
(
!
pass
&&
*
g_test
.
todo
==
'\0'
)
if
(
!
pass
&&
*
g_test
.
todo
==
'\0'
)
++
g_test
.
failed
;
++
g_test
.
failed
;
emit_tap
(
pass
,
fmt
,
ap
);
v
emit_tap
(
pass
,
fmt
,
ap
);
va_end
(
ap
);
va_end
(
ap
);
if
(
*
g_test
.
todo
!=
'\0'
)
if
(
*
g_test
.
todo
!=
'\0'
)
emit_dir
(
"todo"
,
g_test
.
todo
);
emit_dir
(
"todo"
,
g_test
.
todo
);
...
@@ -215,7 +227,7 @@ skip(int how_many, char const *const fmt, ...)
...
@@ -215,7 +227,7 @@ skip(int how_many, char const *const fmt, ...)
while
(
how_many
--
>
0
)
while
(
how_many
--
>
0
)
{
{
va_list
ap
;
va_list
ap
;
emit_tap
(
1
,
NULL
,
ap
);
v
emit_tap
(
1
,
NULL
,
ap
);
emit_dir
(
"skip"
,
reason
);
emit_dir
(
"skip"
,
reason
);
emit_endl
();
emit_endl
();
}
}
...
@@ -308,7 +320,7 @@ int exit_status() {
...
@@ -308,7 +320,7 @@ int exit_status() {
@section UnitTest Writing unit tests
@section UnitTest Writing unit tests
The purpose of writing unit tests is to use them to drive component
The purpose of writing unit tests is to use them to drive component
development towards a solution that the tests. This means that the
development towards a solution that
passes
the tests. This means that the
unit tests has to be as complete as possible, testing at least:
unit tests has to be as complete as possible, testing at least:
- Normal input
- Normal input
...
@@ -317,29 +329,240 @@ int exit_status() {
...
@@ -317,29 +329,240 @@ int exit_status() {
- Error handling
- Error handling
- Bad environment
- Bad environment
We will go over each case and explain it in more detail.
@subsection NormalSubSec Normal input
This is to test that the component have the expected behaviour.
This is just plain simple: test that it works. For example, test
that you can unpack what you packed, adding gives the sum, pincing
the duck makes it quack.
This is what everybody does when they write tests.
@subsection BorderlineTests Borderline cases
If you have a size anywhere for your component, does it work for
size 1? Size 0? Sizes close to <code>UINT_MAX</code>?
It might not be sensible to have a size 0, so in this case it is
not a borderline case, but rather a faulty input (see @ref
FaultyInputTests).
@subsection FaultyInputTests Faulty input
Does your bitmap handle 0 bits size? Well, it might not be designed
for it, but is should <em>not</em> crash the application, but
rather produce an error. This is called defensive programming.
@subsection NormalSSec Normal input
Unfortunately, adding checks for values that should just not be
entered at all is not always practical: the checks cost cycles and
might cost more than it's worth. For example, some functions are
designed so that you may not give it a null pointer. In those
cases it's not sensible to pass it <code>NULL</code> just to see it
crash.
@subsection BorderlineSSec Borderline cases
Since every experienced programmer add an <code>assert()</code> to
ensure that you get a proper failure for the debug builds when a
null pointer passed (you add asserts too, right?), you will in this
case instead have a controlled (early) crash in the debug build.
@subsection FaultySSec Faulty input
@subsection Error
SSec
Error handling
@subsection Error
HandlingTests
Error handling
@subsection EnvironmentSSec Environment
This is testing that the errors your component is designed to give
actually are produced. For example, testing that trying to open a
non-existing file produces a sensible error code.
@subsection BadEnvironmentTests Environment
Sometimes, modules has to behave well even when the environment
Sometimes, modules has to behave well even when the environment
fails to work correctly. Typical examples are: out of dynamic
fails to work correctly. Typical examples are when the computer is
memory, disk is full,
out of dynamic memory or when the disk is full. You can emulate
this by replacing, e.g., <code>malloc()</code> with your own
version that will work for a while, but then fail. Some things are
worth to keep in mind here:
- Make sure to make the function fail deterministically, so that
you really can repeat the test.
- Make sure that it doesn't just fail immediately. The unit might
have checks for the first case, but might actually fail some time
in the near future.
@section UnitTestSec How to structure a unit test
@section UnitTest How to structure a unit test
In this section we will give some advice on how to structure the
In this section we will give some advice on how to structure the
unit tests to make the development run smoothly.
unit tests to make the development run smoothly. The basic
structure of a test is:
- Plan
- Test
- Report
@subsection TestPlanning Plan the test
Planning the test means telling how many tests there are. In the
event that one of the tests causes a crash, it is then possible to
see that there are fewer tests than expected, and print a proper
error message.
To plan a test, use the @c plan() function in the following manner:
@code
int main(int argc, char *argv[])
{
plan(5);
.
.
.
}
@endcode
If you don't call the @c plan() function, the number of tests
executed will be printed at the end. This is intended to be used
while developing the unit and you are constantly adding tests. It
is not indented to be used after the unit has been released.
@subsection PieceSec Test each piece separately
@subsection TestRunning Execute the test
To report the status of a test, the @c ok() function is used in the
following manner:
@code
int main(int argc, char *argv[])
{
plan(5);
ok(ducks == paddling_ducks,
"%d ducks did not paddle", ducks - paddling_ducks);
.
.
.
}
@endcode
This will print a test result line on the standard output in TAP
format, which allows TAP handling frameworks (like Test::Harness)
to parse the status of the test.
@subsection TestReport Report the result of the test
At the end, a complete test report should be written, with some
statistics. If the test returns EXIT_SUCCESS, all tests were
successfull, otherwise at least one test failed.
To get a TAP complient output and exit status, report the exit
status in the following manner:
@code
int main(int argc, char *argv[])
{
plan(5);
ok(ducks == paddling_ducks,
"%d ducks did not paddle", ducks - paddling_ducks);
.
.
.
return exit_status();
}
@endcode
@section DontDoThis Ways to not do unit testing
In this section, we'll go through some quite common ways to write
tests that are <em>not</em> a good idea.
@subsection BreadthFirstTests Doing breadth-first testing
If you're writing a library with several functions, don't test all
functions using size 1, then all functions using size 2, etc. If a
test for size 42 fails, you have no easy way of tracking down why
it failed.
It is better to concentrate on getting one function to work at a
time, which means that you test each function for all sizes that
you think is reasonable. Then you continue with the next function,
doing the same. This is usually also the way that a library is
developed (one function at a time) so stick to testing that is
appropriate for now the unit is developed.
@subsection JustToBeSafeTest Writing unnecessarily large tests
Don't write tests that use parameters in the range 1-1024 unless
you have a very good reason to belive that the component will
succeed for 562 but fail for 564 (the numbers picked are just
examples).
It is very common to write extensive tests "just to be safe."
Having a test suite with a lot of values might give you a warm
fuzzy feeling, but it doesn't really help you find the bugs. Good
tests fail; seriously, if you write a test that you expect to
succeed, you don't need to write it. If you think that it
<em>might</em> fail, <em>then</em> you should write it.
Don't take this as an excuse to avoid writing any tests at all
"since I make no mistakes" (when it comes to this, there are two
kinds of people: those who admit they make mistakes, and those who
don't); rather, this means that there is no reason to test that
using a buffer with size 100 works when you have a test for buffer
size 96.
The drawback is that the test suite takes longer to run, for little
or no benefit. It is acceptable to do a exhaustive test if it
doesn't take too long to run and it is quite common to do an
exhaustive test of a function for a small set of values.
Use your judgment to decide what is excessive: your milage may
vary.
*/
/**
@example simple.t.c
This is an simple example of how to write a test using the
library. The output of this program is:
@code
1..1
# Testing basic functions
ok 1 - Testing gcs()
@endcode
The basic structure is: plan the number of test points using the
plan() function, perform the test and write out the result of each
test point using the ok() function, print out a diagnostics message
using diag(), and report the result of the test by calling the
exit_status() function. Observe that this test does excessive
testing (see @ref JustToBeSafeTest), but the test point doesn't
take very long time.
*/
/**
@example todo.t.c
This example demonstrates how to use the <code>todo_start()</code>
and <code>todo_end()</code> function to mark a sequence of tests to
be done. Observe that the tests are assumed to fail: if any test
succeeds, it is considered a "bonus".
*/
/**
@example skip.t.c
This is an example of how the <code>SKIP_BLOCK_IF</code> can be
used to skip a predetermined number of tests. Observe that the
macro actually skips the following statement, but it's not sensible
to use anything than a block.
*/
/**
@example skip_all.t.c
Don't test all functions using size 1, then all functions using
Sometimes, you skip an entire test because it's testing a feature
size 2, etc.
that doesn't exist on the system that you're testing. To skip an
entire test, use the <code>skip_all()</code> function according to
this example.
*/
*/
unittest/mytap/tap.h
View file @
a3864256
...
@@ -33,6 +33,8 @@
...
@@ -33,6 +33,8 @@
/**
/**
Data about test plan.
Data about test plan.
@ingroup MyTAP_Internal
@internal We are using the "typedef struct X { ... } X" idiom to
@internal We are using the "typedef struct X { ... } X" idiom to
create class/struct X both in C and C++.
create class/struct X both in C and C++.
*/
*/
...
@@ -60,6 +62,14 @@ typedef struct TEST_DATA {
...
@@ -60,6 +62,14 @@ typedef struct TEST_DATA {
extern
"C"
{
extern
"C"
{
#endif
#endif
/**
@defgroup MyTAP_API MyTAP API
MySQL support for performing unit tests according to TAP.
@{
*/
/**
/**
Set number of tests that is planned to execute.
Set number of tests that is planned to execute.
...
@@ -101,11 +111,14 @@ void ok(int pass, char const *fmt, ...)
...
@@ -101,11 +111,14 @@ void ok(int pass, char const *fmt, ...)
/**
/**
Skip a determined number of tests.
Skip a determined number of tests.
Function to print that <em>how_many</em> tests have been
Function to print that <em>how_many</em> tests have been skipped.
skipped. The reason is printed for each skipped test. Observe
The reason is printed for each skipped test. Observe that this
that this function does not do the actual skipping for you, it just
function does not do the actual skipping for you, it just prints
prints information that tests have been skipped. It shall be used
information that tests have been skipped. This function is not
in the following manner:
usually used, but rather the macro @c SKIP_BLOCK_IF, which does the
skipping for you.
It shall be used in the following manner:
@code
@code
if (ducks == 0) {
if (ducks == 0) {
...
@@ -192,8 +205,8 @@ void BAIL_OUT(char const *fmt, ...)
...
@@ -192,8 +205,8 @@ void BAIL_OUT(char const *fmt, ...)
return exit_status();
return exit_status();
@endcode
@endcode
@returns
EXIT_SUCCESS if all tests passed, EXIT_FAILURE if one or
@returns
@c EXIT_SUCCESS if all tests passed, @c EXIT_FAILURE if
more tests failed.
one or
more tests failed.
*/
*/
int
exit_status
(
void
);
int
exit_status
(
void
);
...
@@ -242,6 +255,7 @@ void todo_start(char const *message, ...)
...
@@ -242,6 +255,7 @@ void todo_start(char const *message, ...)
void
todo_end
();
void
todo_end
();
/** @} */
#ifdef __cplusplus
#ifdef __cplusplus
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment