1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.

Saturday, October 30, 2010

Unit Testing C code with the GoogleTest framework

In a previous article I described how to get started testing C++ code with the Google Test framework. In this article I’m going to share a few tips and tricks for testing C code.

Here are some of the challenges we face when trying to test procedural code:

■We can’t create an instance of the code under test. This means that we can’t easily get a fresh object with initialized data for every test.
■The dependencies are hardcoded. This means we can’t use dependency injection techniques to mock/fake the module dependencies.
■We can’t use Polymorphism to break the dependencies

So that only leaves us with the two dependency-breaking tools available in the language: the preprocessor and the linker.

Things to watch out for
Static initialization: You need to be able to reset your data to a known state before running each test case. It is the only way to isolate your tests from each other.
Global variables: Is you module accessing a global variable? You need to provide a fake implementation for this.
Hardware Access: In embedded systems we often have memory mapped hardware register access. You most definitely don’t want to be dereferencing from random memory addresses in your tests. A good antidote to this is to define a generic function to get the address for a given register. You can then define a version of this function for testing purposes.

An example
So how does that look in practice? Suppose we have a make-believe embedded software application for controlling a device:

#include // here stdio.h
#include // here unistd.h
#define IOMEM_BASE 0x2FF
#define VALUE_REG (IOMEM_BASE + 3)
// This must be a power of 2!
#define BUFFER_SIZE 8
#define MAX_ITEMS (BUFFER_SIZE-1)
static int my_filter[BUFFER_SIZE];
static int readIdx = 0;
static int writeIdx = 0;
int filter_len()
{
return (BUFFER_SIZE + writeIdx - readIdx) % BUFFER_SIZE;
}
void filter_add(int val) {
my_filter[writeIdx] = val;
writeIdx = (writeIdx+1) & BUFFER_SIZE-1;
if(writeIdx == readIdx) readIdx = (readIdx+1) & BUFFER_SIZE-1;
}
#ifndef TESTING
int myapp_do_dangerous_io()
{
// lets dereference an io mapped register
// - on the target it is at address IOMEM_BASE + 3
return *((int *)VALUE_REG);
}
#endif
int myapp_get_average(){
int len = filter_len();
if(0 == len)
return 0;
int sum = 0;
for(int i = 0; i < len; i++)
{
sum += my_filter[(i+readIdx)%BUFFER_SIZE];
}
return sum/len;
}
int myapp_task()
{
// get value from register
int nextval = myapp_do_dangerous_io();
// add to filter line
filter_add(nextval);
// return the average value as the next delay
return myapp_get_average();
}
int myapp_mainloop()
{
for(;;)
{
int nextloopdelay = myapp_task();
sleep(nextloopdelay);
}
}
#ifndef TESTING
int main()
{
printf("!!!Hello World!!!\n");
return myapp_mainloop();
}
#endif
How do we approach testing this nastyness?

There are some challenges to testing code of this nature, but there are also methods we can use overcome them.

Infinite loops: these guys will destroy your ability to test effectively. The best approach is to move the body of any infinite loop into it’s own function call.

Dangerous Code: what you do on hardware in production can be dangerous to do in a testing environment. In this example we have a hardware access of memory mapped IO address.

There are three ways we can deal with dilemma:
1.change the address we dereference,
2.change the function we call (at link time)
3.hide the function we call during testing using #ifdefs and provide a test fake (this is the approach I have taken here)
Incompatible Function Names: You can’t link two main functions. You need to hide one…

Static Memory: This can really hurt the independence of your tests. You really ought to re-initialize all of your static data for each test case, and thankfully there is an easy way to achieve this. All the major testing frameworks have a concept of a test fixture which allows you to call a SetUp function before execution of each test case. Use this to initialize your static data. Remember: independent tests are good tests!

The General Testing Pattern
1. Define fake functions for the dependencies you want to stub out
2. If the module depends on a global (gasp!) you need to define your fake one
3. include your module implementation (#include module.c)
4. Define a method to reset all the static data to a known state.
5. Define your tests

#include // here gtest/gtest.h
// Hide main
#define TESTING
// Hide the io function since this will segfault in testing
int fake_register;
int myapp_do_dangerous_io()
{
return fake_register;
}
#include "myapp.c"
class MyAppTestSuite : public testing::Test
{
void SetUp(){
memset(&my_filter, 0, sizeof(my_filter));
readIdx = 0;
writeIdx = 0;
}
void TearDown(){}
};
TEST_F(MyAppTestSuite, myapp_task_should_return_correct_delay_for_one_element) {
fake_register = 10;
EXPECT_EQ(10, myapp_task());
}
TEST_F(MyAppTestSuite, myapp_task_should_return_correct_delay_for_two_elements) {
fake_register = 10;
myapp_task();
fake_register = 20;
EXPECT_EQ(15, myapp_task());
}
TEST_F(MyAppTestSuite, get_average_should_return_zero_on_empty_filter) {
ASSERT_EQ(0, myapp_get_average());
}
TEST_F(MyAppTestSuite, addFirstFilterValAddsVal) {
filter_add(42);
ASSERT_EQ(42, my_filter[readIdx]);
}
TEST_F(MyAppTestSuite, addFirstReturnsCorrectAverage) {
filter_add(42);
ASSERT_EQ(42, myapp_get_average());
}
TEST_F(MyAppTestSuite, addTwoValuesReturnsCorrectAverage) {
filter_add(42);
filter_add(40);
ASSERT_EQ(41, myapp_get_average());
}
TEST_F(MyAppTestSuite, get_average_should_return_average_of_full_filter) {
for(int i = 0; i < MAX_ITEMS; i++)
{
filter_add(i); } ASSERT_EQ((0+1+2+3+4+5+6)/MAX_ITEMS, myapp_get_average());
}
TEST_F(MyAppTestSuite, get_average_should_return_average_of_wrapped_filter)
{
for(int i = 0; i < BUFFER_SIZE; i++)
{
filter_add(i);
}
ASSERT_EQ((1+2+3+4+5+6+7)/MAX_ITEMS, myapp_get_average());
}
/// ....test buffer operations.....

When talking about testing C code (especially embedded) I often hear “But what about..”

■Timing Problems. That’s right, unit testing can’t magically simulate the runtime properties of your system.
■Interrupts. This is a special case of the last point, but it is the same issue all developers come across when going multi-threaded.

■Bit-correct operations. If you are running 24-bit code on a 32-bit architecture you will not see the exact same behavior for various overflow, underflow, bit-shifting and arithmetic operations.

■I can’t possibly test this! Well, there are some classes of code that simply cannot be tested using the unit testing methodology. In my experience however, it is an extreme minority of most code bases that this applies to. The secret is to factor out impossible-to-test-code as much as possible so you don’t pollute the rest of the codebase.

Summary
Testing C code is hard. Testing legacy C code is even harder. But with the limited dependency-breaking language features we have in C (the linker and the preprocessor) we can accomplish quite a lot.

Testing C++ code with the GoogleTest framework

Parameterized Tests

Sometimes your tests can become repetitive because you want to test your code with many different inputs. The google test framework helps you by allowing you to specify value and type parameterized test suites. This allows you to run your tests multiple times with either different data types or different values. This can be a huge productivity boost when testing code for multiple inputs. Here is a test suite that performs all the tests for three different input values, “meek”, “geek”, and “freek”. Individual tests can then access the value for which the test is being run by using the GetParam() function call.

#include // here gtest/gtest.h
#include // here cstring
using namespace std;
// Function under test...
bool acceptName(const char* name)
{
char *result = strstr(name, "eek");
if(result == NULL){
return false;
}
return true;
}
// A new one of these is create for each test
class MyStringTest : public testing::TestWithParam
{
public:
virtual void SetUp(){}
virtual void TearDown(){}
};
INSTANTIATE_TEST_CASE_P(InstantiationName,
MyStringTest,
::testing::Values("meek", "geek", "freek"));
TEST_P(MyStringTest, acceptsEekyWords)
{
ASSERT_TRUE(acceptName(GetParam()));
}

In addition to value-parameterized tests you can also create type-parameterized tests.

Testing C++ code with the GoogleTest framework

There are some great surveys of the C++ testing frameworks out there, my favorite being the one at Games From Within. It was based on that article that my project started using the CxxTest framework when we got started with automated testing. CxxTest really is a great testing framework, and very well suited to embedded systems development. It requires only a limited subset of the C++ language so that makes it very portable and requires very little from the compiler.

The first objective could have been quite easily achieved if I were to develop an output writer for CxxTest that would output as JUnit xml, but together with the other objectives it seemed like a good time to try the GoogleTest framework.

Getting Started with GoogleTest
Writing your first test with GoogleTest couldn’t be simpler. Simply include the code you want to test, include the google mock framework, and write your tests.

#include // here gtest/gtest.h
TEST(MathTest, TwoPlusTwoEqualsFour) {
EXPECT_EQ(2 + 2, 4);
}

Of course this is the most basic test you can write, but this ease of use significantly reduces the friction required to get started. The most interesting thing to note from the code snippit is the complete lack of scaffolding code required. There is no need to create test classes or to specifically register your tests, this is done automagically from the googletest framework. Of course as you write more and more tests you will start to notice repetitious setup and teardown code, and that’s where fixture classes can help.

#include // here gtest/gtest.h
#include // here vector
using namespace std;
// A new one of these is created for each test
class VectorTest : public testing::Test
{
public:
vector m_vector;
virtual void SetUp()
{
m_vector.push_back(1);
m_vector.push_back(2);
}
virtual void TearDown()
{
}
};
TEST_F(VectorTest, testElementZeroIsOne)
{
EXPECT_EQ(m_vector[0], 1);
}
TEST_F(VectorTest, testElementOneIsTwo)
{
EXPECT_EQ(m_vector[1], 2);
}
TEST_F(VectorTest, testSizeIsTwo)
{
EXPECT_EQ(m_vector.size(), (unsigned int)2);
}

There are a few interesting points to note here:

■Firstly, a new instance of the fixture class is created for every test associated with the fixture. This is important because it isolates each test from the side effects of other tests.
■Secondly, the test declaration has changed from TEST to TEST_F. This lets the framework know that it shouldn’t generate the default fixture class but instead look for one you defined.
■Finally, it knows which fixture class to use by matching the first test parameter (here it is VectorTest). This allows you to have any number of fixture classes and tests in your code.

So what are the runtime steps for each test you define?

1.Instantiate a fixture class
2.Call SetUp on the fixture
3.Call the test
4.Call TearDown on the fixture
5.Destroy the fixture

When you run the test suite you should see an output like the following:

[raghuk]$ bin/hellotest
Running main() from gtest_main.cc
[==========] Running 4 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 3 tests from VectorTest
[ RUN ] VectorTest.testElementZeroIsOne
[ OK ] VectorTest.testElementZeroIsOne (0 ms)
[ RUN ] VectorTest.testElementOneIsTwo
[ OK ] VectorTest.testElementOneIsTwo (0 ms)
[ RUN ] VectorTest.testSizeIsTwo
[ OK ] VectorTest.testSizeIsTwo (0 ms)
[----------] 3 tests from VectorTest (0 ms total)

[----------] 1 test from MathTest
[ RUN ] MathTest.Zero
[ OK ] MathTest.Zero (0 ms)
[----------] 1 test from MathTest (0 ms total)

[----------] Global test environment tear-down
[==========] 4 tests from 2 test cases ran. (0 ms total)
[ PASSED ] 4 tests.
[raghuk]$

You can also specify to run only a subset of tests at the command line, either by name or by using wildcards.

Wednesday, October 27, 2010

CUnit

CUnit is a system for writing, administering, and running unit tests in C. CUnit uses a simple framework for building test structures, and provides a rich set of assertions for testing common data types. In addition, several different interfaces are provided for running tests and reporting results. These include automated interfaces for code-controlled testing and reporting, as well as interactive interfaces allowing the user to run tests and view results dynamically.

CUnit homepage :http://cunit.sourceforge.net/ Here you can find examples, docs and so on.

Download CUnit:

The source code can be downloaded from here: http://sourceforge.net/projects/cunit/

If you want to play around with it on your linux machine before you use it on Android I recommend that you do NOT download the .zip file but use the .tar.gz file instead (I could not compile the version in the zip file). Select View all files and then select CUnit-2.1-0-src.tar.gz. Download it and extract it to a nice place.

Build CUnit as an Android lib:

Under /vendor folder create a folder called libCUnit

Copy the Headers and Source folders from the extracted tarball (located in CUnit-2.1-0/CUnit) and place them in the folder libCUnit. Create a file named Android.mk in the libCUnit folder with the following content:

# Path to this module
LOCAL_PATH:= $(call my-dir)
# Clear variables so it won't be afftected from other modules
include $(CLEAR_VARS)
# the src files that will be build in the lib
LOCAL_SRC_FILES:= Sources/Basic/Basic.c \
               Sources/Framework/CUError.c \
               Sources/Framework/MyMem.c \
               Sources/Framework/TestDB.c \
               Sources/Framework/TestRun.c \
               Sources/Framework/Util.c \
               Sources/Automated/Automated.c \
               Sources/Console/Console.c
# the header files 
LOCAL_C_INCLUDES +=$(LOCAL_PATH)/Headers
# the output modul name
LOCAL_MODULE := libCUnit
# other shared libs that this lib will use
LOCAL_SHARED_LIBRARIES := libcutils libc
# No prelinking of the lib
LOCAL_PRELINK_MODULE := false
# add rules for building shared lib
include $(BUILD_SHARED_LIBRARY)

Now when everything is in place we can build the lib. This is done navigating to and type:

make libCUnit

Now you have to re-build the phone SW (i.e. phone sw) and flash the phone with the newly build SW. And now you ready to use it

Small CUnit example:

Now we are ready to build a small and simple test program that uses CUnit lib. Create a folder under /vendor called test_CUnit. In the test_CUnit folder create a file called testCU.c with the following content:

#include 
// using CUnit Basic mode
#include 
 
// a simple init function that will run once before all tests
int init_suite1(void) {        
       printf("\n\t...init_suite1()...\n");
       return 0;
}
 
// a simple clean function that will run once after all test
int clean_suite1(void) {
        printf("\n\t...clean_suite1()...\n");
        return 0;
}
 
// a simple test that should pass
void testAddingPass(void) {
        printf("\n\t...testAddingPass()...\n");
        CU_ASSERT(5 == 3+2);
}
        
// a simple test that will fail (otherwise something is really wrong ;))
void testAddingFail(void) {
        printf("\n\t...testAddingFail()...\n");
        CU_ASSERT(5 == 3+3);
}
        
int main(void) {
 
        // a test suite variable
        CU_pSuite pSuite = NULL;
 
        // initiate the CUnit registery and check that it wen well
        if (CUE_SUCCESS != CU_initialize_registry()) {
               // otherwise end and return the error code
               return CU_get_error();
        }
 
        // create the test suite and spec the init and clean functions
        pSuite = CU_add_suite("Suite_1", init_suite1, clean_suite1);
        
        // make sure that we successfully created the test suite
        if (NULL == pSuite) {
               // otherwise clean up the registry
               CU_cleanup_registry();
               // and return the error code
               return CU_get_error();
        }
 
        // add the test to the test suite and make sure it succeeds
        if((NULL == CU_add_test(pSuite, "testing addPass", testAddingPass)) ||
           (NULL == CU_add_test(pSuite, "testing addFail", testAddingFail))) {
               // otherwise clean up the registry
               CU_cleanup_registry();
               // and return the error code
               return CU_get_error(); 
        }
        
        // set the level of info that will be shown in the results
        CU_basic_set_mode(CU_BRM_NORMAL);
        // run the test suite
        CU_basic_run_tests();  
        // clean the test registry.
        CU_cleanup_registry();
        // return possible error code.
        return CU_get_error();
}

Save the file and create a new file called Android.mk with the following content:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= testCU.c
LOCAL_MODULE := test_CUnit     
LOCAL_SHARED_LIBRARIES := libcutils libc libCUnit
LOCAL_CFLAGS += -I$(TOPDIR)vendor/libCUnit/Headers/
include $(BUILD_EXECUTABLE)

Now we are ready to build the test app. This is done by navigating to folder and type:

make test_CUnit

When the building is complete you will have the test_CUnit executable located in:

out/target/product/generic/system/bin/

Push the executable to /data folder in robyn, start adb shell, navigate to /data and type:

./test_CUnit

Make sure that you have the permissions to run the app, if not type:

chmod 777 ./test_CUnit

and try again. Pretty easy, isn’t it .

Important:

If you check the example, the CU_cleanup_registry() is called if anything goes wrong and it’s also called as the last thing (except for the return statement) in main. This is highly important!!!! If CU_cleanup_registry() isn’t the last CUnit call then we will end up with memory leaks! Keep this in mind while you’re coding. See the CUnit doc.: http://cunit.sourceforge.net/doc/test_registry.html#cleanup

If you have checked the examples in the CUnit web page and the example above you might have notice a small difference. On the webpage the include looks like this:

#include 

But on the example above I only use:

#include 

The reason for this is when you install CUnit on your linux host with the distributed make file, the make file create this folder structure and we do not.

One other small thing that could be mentioned. The curses version is not supported because android do not have curses (I think…)

I hope that you will have fun using CUnit when you developing/testing your C stuff

 
# #