mirror of https://gitee.com/openkylin/apr.git
333 lines
12 KiB
Plaintext
333 lines
12 KiB
Plaintext
Writing APR tests
|
|
|
|
All APR tests should be executable in 2 ways, as an individual program, or
|
|
as a part of the full test suite. The full test suite is controlled with
|
|
the testall program. At the beginning of the testall.c file, there is an
|
|
array of functions called tests. The testall program loops through this
|
|
array calling each function. Each function returns a CuSuite variable, which
|
|
is then added to the SuiteList. Once all Suites have been added, the SuiteList
|
|
is executed, and the output is printed to the screen. All functions in the
|
|
array should follow the same basic format:
|
|
|
|
The Full Suite
|
|
--------------
|
|
|
|
/* The driver function. This must return a CuSuite variable, which will
|
|
* then be used to actually run the tests. Essentially, all Suites are a
|
|
* collection of tests. The driver will take each Suite, and put it in a
|
|
* SuiteList, which is a collection of Suites.
|
|
*/
|
|
CuSuite *testtime(void)
|
|
{
|
|
/* The actual suite, this must be created for each test program. Please
|
|
* give it a useful name, that will inform the user of the feature being
|
|
* tested.
|
|
*/
|
|
CuSuite *suite = CuSuiteNew("Test Time");
|
|
|
|
/* Each function must be added to the suite. Each function represents
|
|
* a single test. It is possible to test multiple features in a single
|
|
* function, although no tests currently do that.
|
|
*/
|
|
SUITE_ADD_TEST(suite, test_now);
|
|
SUITE_ADD_TEST(suite, test_gmtstr);
|
|
SUITE_ADD_TEST(suite, test_localstr);
|
|
SUITE_ADD_TEST(suite, test_exp_get_gmt);
|
|
SUITE_ADD_TEST(suite, test_exp_get_lt);
|
|
SUITE_ADD_TEST(suite, test_imp_gmt);
|
|
SUITE_ADD_TEST(suite, test_rfcstr);
|
|
SUITE_ADD_TEST(suite, test_ctime);
|
|
SUITE_ADD_TEST(suite, test_strftime);
|
|
SUITE_ADD_TEST(suite, test_strftimesmall);
|
|
SUITE_ADD_TEST(suite, test_exp_tz);
|
|
SUITE_ADD_TEST(suite, test_strftimeoffset);
|
|
|
|
/* You must return the suite so that the driver knows which suites to
|
|
* run.
|
|
*/
|
|
return suite;
|
|
}
|
|
|
|
Building the full driver
|
|
------------------------
|
|
|
|
All you need to do to build the full driver is run:
|
|
|
|
make
|
|
|
|
To run it, run:
|
|
|
|
./testall
|
|
|
|
Running individual tests
|
|
------------------------
|
|
|
|
It is not possible to build individual tests, however it is possible to
|
|
run individual tests. When running the test suite, specify the name of the
|
|
tests that you want to run on the command line. For example:
|
|
|
|
./testall teststr testrand
|
|
|
|
Will run the Strings and Random generator tests.
|
|
|
|
Reading the test suite output
|
|
-----------------------------
|
|
|
|
Once you run the test suite, you will get output like:
|
|
|
|
All APR Tests:
|
|
Test Strings: ....
|
|
Test Time: ............
|
|
|
|
16 tests run: 16 passed, 0 failed, 0 not implemented.
|
|
|
|
Known test failures are documented in ../STATUS.
|
|
|
|
There are a couple of things to look at with this. First, if you look at the
|
|
first function in this document, you should notice that the string passed to
|
|
the CuSuiteNew function is in the output. That is why the string should
|
|
explain the feature you are testing.
|
|
|
|
Second, this test passed completely. This is obvious in two ways. First, and
|
|
most obvious, the summary line tells you that 16 tests were run and 16 tests
|
|
passed. However, the results can also be found in the lines above. Every
|
|
'.' in the output represents a passed test.
|
|
|
|
If a test fails, the output will look like:
|
|
|
|
All APR Tests:
|
|
Test Strings: ....
|
|
Test Time: ..F.........
|
|
|
|
16 tests run: 15 passed, 1 failed, 0 not implemented.
|
|
|
|
This is not very useful, because you don't know which test failed. However,
|
|
once you know that a test failed, you can run the suite again, with the
|
|
-v option. If you do this, you will get something like:
|
|
|
|
All APR Tests:
|
|
Test Strings: ....
|
|
Test Time: ..F.........
|
|
|
|
16 tests run: 15 passed, 1 failed, 0 not implemented.
|
|
Failed tests:
|
|
1) test_localstr: assert failed
|
|
|
|
In this case, we know the test_localstr function failed, and there is an
|
|
Assert in this that failed (I modified the test to fail for this document).
|
|
Now, you can look at what that test does, and why it would have failed.
|
|
|
|
There is one other possible output for the test suite (run with -v):
|
|
|
|
All APR Tests:
|
|
Test Strings: ....
|
|
Test Time: ..N.........
|
|
|
|
16 tests run: 15 passed, 0 failed, 1 not implemented.
|
|
|
|
Not Implemented tests:
|
|
|
|
Not Implemented tests:
|
|
1) test_localstr: apr_time_exp_lt not implemented on this platform
|
|
|
|
The 'N' means that a function has returned APR_ENOTIMPL. This should be
|
|
treated as an error, and the function should be implemented as soon as
|
|
possible.
|
|
|
|
Adding New test Suites to the full driver
|
|
-------------------------------------------
|
|
|
|
To add a new Suite to the full driver, you must make a couple of modifications.
|
|
|
|
1) Edit test_apr.h, and add the prototype for the function.
|
|
2) Edit testall.c, and add the function and name to the tests array.
|
|
3) Edit Makefile.in, and add the .lo file to the testall target.
|
|
|
|
Once those four things are done, your tests will automatically be added
|
|
to the suite.
|
|
|
|
Writting an ABTS unit test
|
|
--------------------------
|
|
|
|
The aim of this quick and dirty Howto is to give a short introduction
|
|
to APR (Apache Portable Runtime) unit tests, and how to write
|
|
one. During my Google's Summer of Code 2005 project, I discovered a
|
|
small bug in the APR-Util's date parsing routines, and I needed to
|
|
write a unit test for the fixed code. I decided to write this
|
|
documentation because I did not find any. Thanks to Garrett Rooney for
|
|
his help on writing the unit test !
|
|
|
|
The APR and APR-Util libraries provide a platform independent API for
|
|
software developers. They contain a lot of modules, including network
|
|
programming, threads, string and memory management, etc. All these
|
|
functions need to be heavily tested so that developers can be sure the
|
|
library is reliable.
|
|
|
|
The ABTS give APR developers the ability to build a complete test
|
|
suite for the bunch of tests they wrote, which can then be ran under
|
|
various platforms. In this Howto, I will try teach you how to write an
|
|
ABTS unit test.
|
|
|
|
As you may probably know, a unit test is a simple routine which tests
|
|
a very specific feature of the tested software or library. To build a
|
|
unit test, you need three different things :
|
|
|
|
* the to-be-tested function,
|
|
* the input data that will be given to the function,
|
|
* the expected output data.
|
|
|
|
The principle of a unit test is very simple : for each entry in your
|
|
set of input data, we pass it to our function, fetch what the function
|
|
returned and compare it to the corresponding expected output data. Of
|
|
course, the more edge cases you can test, the better your input data
|
|
set is.
|
|
|
|
The ABTS aims to quicken the write of unit test, and make them
|
|
available to the whole test suite by providing a set of preprocessor
|
|
macros. Adding a unit test to a test suite can be easily done by the
|
|
following piece of code :
|
|
|
|
abts_suite *testdaterfc(abts_suite *suite)
|
|
{
|
|
suite = ADD_SUITE(suite);
|
|
abts_run_test(suite, test_date_rfc, NULL);
|
|
|
|
return suite;
|
|
}
|
|
|
|
Where test_date_rfc is the name of the function performing the
|
|
test. Writing such a function is, in the light of the explanation I
|
|
just gave, pretty much easy too. As I said, we need to check every
|
|
entry of our input data set. That gives us a loop. For each loop
|
|
iteration, we call our to-be-tested function, grab its result and
|
|
compare the returned value with the expected one.
|
|
|
|
Test functions must have the following prototype :
|
|
|
|
static void my_test_function(abts_case *tc, void *data);
|
|
|
|
The comparison step is performed by the ABTS, thus giving the
|
|
whole test suite the correct behavior if your unit test fails. Here
|
|
comes a list of the available test methods :
|
|
|
|
ABTS_INT_EQUAL(tc, a, b)
|
|
ABTS_INT_NEQUAL(tc, a, b)
|
|
ABTS_STR_EQUAL(tc, a, b)
|
|
ABTS_STR_NEQUAL(tc, a, b, c)
|
|
ABTS_PTR_NOTNULL(tc, b)
|
|
ABTS_PTR_EQUAL(tc, a, b)
|
|
ABTS_TRUE(tc, b)
|
|
ABTS_FAIL(tc, b)
|
|
ABTS_NOT_IMPL(tc, b)
|
|
ABTS_ASSERT(tc, a, b)
|
|
|
|
The first argument, tc is a reference to the unit test currently
|
|
processed by the test suite (passed to your test function). The other
|
|
parameters are the data to be tested. For example, the following line
|
|
will never make your unit test fail :
|
|
|
|
ABTS_INT_EQUAL(tc, 1, 1);
|
|
|
|
See, it's easy ! Let's take a look at the complete example :
|
|
testdaterfc. We want to test our date string parser. For this, we will
|
|
use some chosen date strings (from mail headers for example) written
|
|
in various formats but that should all be handled by our function, and
|
|
their equivalents in correct RFC822 format.
|
|
|
|
The function we want to test returns an apr_time_t}, which will be
|
|
directly given as input to the apr_rfc822_date() function, thus
|
|
producing the corresponding RFC822 date string. All we need to do
|
|
after this is to call the correct test method from the ABTS macros !
|
|
|
|
You can take a look at the apr-util/test/testdaterfc.c file for the
|
|
complete source code of this unit test.
|
|
|
|
Although this Howto is very small and mostly dedicated to the
|
|
testdaterfc unit test, I hope you'll find it useful. Good luck !
|
|
|
|
Writing tests for CuTest (no longer used)
|
|
-----------------------------------------
|
|
|
|
There are a couple of rules for writing good tests for the test suite.
|
|
|
|
1) All tests can determine for themselves if it passed or not. This means
|
|
that there is no reason for the person running the test suite to interpret
|
|
the results of the tests.
|
|
2) Never use printf to add to the output of the test suite. The suite
|
|
library should be able to print all of the information required to debug
|
|
a problem.
|
|
3) Functions should be tested with both positive and negative tests. This
|
|
means that you should test things that should both succeed and fail.
|
|
4) Just checking the return code does _NOT_ make a useful test. You must
|
|
check to determine that the test actually did what you expected it to do.
|
|
|
|
An example test
|
|
---------------
|
|
|
|
Finally, we will look at a quick test:
|
|
|
|
/* All tests are passed a CuTest variable. This is how the suite determines
|
|
* if the test succeeded or failed.
|
|
*/
|
|
static void test_localstr(CuTest *tc)
|
|
{
|
|
apr_status_t rv;
|
|
apr_time_exp_t xt;
|
|
time_t os_now;
|
|
|
|
rv = apr_time_exp_lt(&xt, now);
|
|
os_now = now / APR_USEC_PER_SEC;
|
|
|
|
/* If the function can return APR_ENOTIMPL, then you should check for it.
|
|
* This allows platform implementors to know if they have to implement
|
|
* the function.
|
|
*/
|
|
if (rv == APR_ENOTIMPL) {
|
|
CuNotImpl(tc, "apr_time_exp_lt");
|
|
}
|
|
|
|
/* It often helps to ensure that the return code was APR_SUCESS. If it
|
|
* wasn't, then we know the test failed.
|
|
*/
|
|
CuAssertTrue(tc, rv == APR_SUCCESS);
|
|
|
|
/* Now that we know APR thinks it worked properly, we need to check the
|
|
* output to ensure that we got what we expected.
|
|
*/
|
|
CuAssertStrEquals(tc, "2002-08-14 12:05:36.186711 -25200 [257 Sat] DST",
|
|
print_time(p, &xt));
|
|
}
|
|
|
|
Notice, the same test can fail for any of a number of reasons. The first
|
|
test to fail ends the test.
|
|
|
|
CuTest
|
|
------
|
|
|
|
CuTest is an open source test suite written by Asim Jalis. It has been
|
|
released under the zlib/libpng license. That license can be found in the
|
|
CuTest.c and CuTest.h files.
|
|
|
|
The version of CuTest that is included in the APR test suite has been modified
|
|
from the original distribution in the following ways:
|
|
|
|
1) The original distribution does not have a -v flag, the details are always
|
|
printed.
|
|
2) The NotImplemented result does not exist.
|
|
3) SuiteLists do not exist. In the original distribution, you can add suites
|
|
to suites, but it just adds the tests in the first suite to the list of tests
|
|
in the original suite. The output wasn't as detailed as I wanted, so I created
|
|
SuiteLists.
|
|
|
|
The first two modifications have been sent to the original author of CuTest,
|
|
but they have not been integrated into the base distribution. The SuiteList
|
|
changes will be sent to the original author soon.
|
|
|
|
The modified version of CuTest is not currently in any CVS or Subversion
|
|
server. In time, it will be hosted at rkbloom.net.
|
|
|
|
There are currently no docs for how to write tests, but the teststr and
|
|
testtime programs should give an idea of how it is done. In time, a document
|
|
should be written to define how tests are written.
|
|
|