| 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 testall | |
| 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. | |