Project: Gluster (Runtime / Cloud Native Storage)
File: unittest.md

provides a good definition for unit tests. A good unit test is: Able to be fully automated Has full control over all the pieces running (Use mocks or stubs to achieve this isolation when needed) Can be run in any order if part of many other tests Runs in memory (no DB or File access, for example) Consistently returns the same result (You always run the same test, so no random numbers, for example. save those for integration or range tests) Runs fast Tests a single logical concept in the system Readable Maintainable Trustworthy (when you see its result, you dont need to debug the code just to be sure) GlusterFS unit test framework is based on . cmocka provides developers with methods to isolate and test modules written in C language. It also provides integration with Jenkins by providing JUnit XML compliant unit test results. cmocka To execute the unit tests, all you need is to type `make check`. Here is a step-by-step example assuming you just cloned a GlusterFS tree: ``` $ ./autogen.sh $ ./configure --enable-debug $ make check ``` Sample output: ``` PASS: mempoolunittest ============================================================================ Testsuite summary for glusterfs 3git ============================================================================ ============================================================================ ``` In this example, `mempoolunittest` has multiple tests inside, but `make check` assumes that the program itself is the test, and that is why it only shows one test. Here is the output when we run `mempoolunittest` directly: ``` $ ./libglusterfs/src/mempoolunittest [==========] Running 10 test(s). [ RUN ] testgfmemacctenable_set Expected assertion data != ((void *)0) occurred [ OK ] testgfmemacctenable_set [ RUN ] testgfmemsetacctinfoasserts Expected assertion xl != ((void *)0) occurred Expected assertion size > ((4 + sizeof (sizet) + sizeof (xlatort *) + 4 + 8) + 8) occurred Expected assertion type <= xl->memacct.numtypes occurred [ OK ] testgfmemsetacctinfoasserts [ RUN ] testgfmemsetacctinfomemory [ OK ] testgfmemsetacctinfomemory [ RUN ] testgfcallocdefaultcalloc [ OK ] testgfcallocdefaultcalloc [ RUN ] testgfcallocmemacct_enabled [ OK ] testgfcallocmemacct_enabled [ RUN ] testgfmallocdefaultmalloc [ OK ] testgfmallocdefaultmalloc [ RUN ] testgfmallocmemacct_enabled [ OK ] testgfmallocmemacct_enabled [ RUN ] testgfreallocdefaultrealloc [ OK ] testgfreallocdefaultrealloc [ RUN ] testgfreallocmemacct_enabled [ OK ] testgfreallocmemacct_enabled [ RUN ] testgfrealloc_ptr Expected assertion ((void *)0) != ptr occurred [ OK ] testgfrealloc_ptr [==========] 10 test(s) run. [ PASSED ] 10 test(s). [ FAILED ] 0 test(s). [ REPORT ] Created libglusterfsmempool_xunit.xml report ``` Add the following to your C file: ```c ``` ```c /* Programming by Contract is a programming methodology which binds the caller and the function called to a contract. The contract is represented using Hoare Triple: {P} C {Q} where {P} is the precondition before executing command C, and {Q} is the postcondition. * See also: http://en.wikipedia.org/wiki/Designbycontract http://en.wikipedia.org/wiki/Hoare_logic http://dlang.org/dbc.html */ /* Checks caller responsibility against contract */ /* Checks function reponsability against contract. */ /* While REQUIRE and ENSURE apply to functions, INVARIANT applies to

It ensures that intances of the class/struct are consistent. In other words, that the instance has not been corrupted. */ ``` This is an extremely simple example: ```c int divide (int n, int d) { int ans; REQUIRE(d != 0); ans = n / d; // As code is added to this function throughout its lifetime, // ENSURE will assert that data will be returned // according to the contract. Again this is an // extremely simple example. :-D ENSURE( ans == (n / d) ); return ans; } ``` `REQUIRE`, `ENSURE`, and `INVARIANT` are only available when `DEBUG` or `UNIT_TESTING` are set in the CFLAGS. You must pass `--enable-debug` to `./configure` to enable PBC on your non-unittest builds. Cmockery2 provides its own memory allocation functions which check for buffer overrun and memory leaks. The following header file must be included last to be able to override any of the memory allocation functions: ```c ``` This file will only take effect with the `UNIT_TESTING` CFLAG is set. Once you identify the C file you would like to test, first create a `unittest` directory under the directory where the C file is located. This will isolate the unittests to a different directory. Next, you need to edit the `Makefile.am` file in the directory where your C file is located. Initialize the `Makefile.am` if it does not already have the following sections: ``` CLEANFILES += .gcda .gcno *_xunit.xml noinst_PROGRAMS = TESTS = ``` Now you can add the following for each of the unit tests that you would like to build: ``` xxxunittestCPPFLAGS = $(xxx_CPPFLAGS) xxxunittestSOURCES = xxx.c \ unittest/xxx_unittest.c xxxunittestCFLAGS = $(UNITTEST_CFLAGS) xxxunittestLDFLAGS = $(UNITTEST_LDFLAGS) noinstPROGRAMS += xxxunittest TESTS += xxx_unittest ``` Where `xxx` is the name of your C file. For example, look at `libglusterfs/src/Makefile.am`. Copy the simple unit test from the to `unittest/xxxunittest.c`. If you would like to see an example of a unit test, please refer to `libglusterfs/src/unittest/mempool_unittest.c`. You may see that the linker will complain about missing functions needed by the C file you would like to test. Identify the required functions, then place their stubs in a file called `unittest/xxxmock.c`, then include this file in `Makefile.am` in `xxxunittest_SOURCES`. This will allow you to you Cmockery2's mocking functions. You can type `make` in the directory where the C file is located. Once you built it and there are no errors, you can execute the test either by directly executing the program (in our example above it is called `xxx_unittest` ), or by running `make check`. Sometimes you may need to debug your unit test. To do that, you will have to point `gdb` to the binary which is located in the same directory as the source. For example, you can do the following from the root of the source tree to debug `mempoolunittest`: ``` $ gdb libglusterfs/src/mempoolunittest ```