Trouble mocking pthread_create in proof of concept work

733 views
Skip to first unread message

Travis Orr

unread,
Oct 17, 2016, 3:21:04 PM10/17/16
to cpputest
I'm currently investigating CppUTest as a possible framework.

I have chosen a cross platform C library that wraps pthread_create to allow for the creation and the naming of the thread in the same function on Linux systems.

While attempting to mock pthread_create, I am having trouble getting the pthread_mock.h to correctly import the CppUTestExt/MockSuport.h

When I include <CppuTest/MockSupport.h> I get the following error when attempting to compile thr.c
#ifndef pthread_mocked
#define pthread_mocked
Remove the include, then the mock() type/class isn't recognized. I realize that I many be taking completely the wrong approach, but have been unable to find a good full example of how to do something like this with CppUTest.

Thanks in advance.

Code is as follows. Sorry if this is too much detail:

//thr.c
//Direct implementation where I want to mock pthread_create.
#define TESTING 1
#ifndef TESTING
#include <pthread.h>
#else
#include "tests/pthread_mock.h"
#endif
long th_start_thread_name(TH_THREAD_FUNC thread, void *arg, th_opts *opts, const char* name)
{
    pthread_t thr;
    int ret, sret;
    ret = pthread_create(opts ? &opts->thr : &thr, NULL, thread, arg);
    if (ret == 0 && name != NULL)
    {
       extern int pthread_setname_np(pthread_t thr, const char *name);  /* Fix warning from missing prototype. */
       
       sret = pthread_setname_np(opts ? opts->thr : thr, name);
       /* pthreads says that thread names must not exceed 16, including NULL. */
       if (sret != 0 && strlen(name) > 15)
       {
           ret = -1;
       }
    }
    return (long)ret;
}


//thr_test.c
//CppUTest file
#include <CppUTest/CommandLineTestRunner.h>
#include <CppUTest/TestHarness.h>
#include <CppUTestExt/MockSupport.h>
#include <stdio.h>

extern "C"
{
#include "../thr.h"
}

static void *TestThread(void *arg);

/**
 * The definition of a TEST_GROUP
 */
TEST_GROUP(ThreadTestGroup)
{

};


TEST(ThreadTestGroup, ThreadCreateFail)
{
// mock().expectOneCall("pthread_create").withParameter("thread", NULL)
//  .withParameter("arg", this)
//  .withParameter("opts", NULL)
//  .withParameter("name", "TestThread")
//  .andReturnValue(-1);

int4 thread_status = mock().actualCall("th_start_thread_name").returnValue().getIntValue();

CHECK_EQUAL(-1, thread_status);
}

int main(int argc, char *argv[])
{
CommandLineTestRunner::RunAllTests(argc, argv);
return 0;
}


// Attempt at creating a pthread_mock.h
#ifndef pthread_mocked
#define pthread_mocked

#include <CppUTestExt/MockSupport.h>

/**
 * Mocked version of pthread_create
 */
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
{
    return mock().actualCall("pthread_create").intReturnValue();
}
#endif


Bas Vodde

unread,
Oct 17, 2016, 9:55:40 PM10/17/16
to CppUTest

Hi Travis,

I would avoid mocking out pthreads_create (and other pthreads functions) the way you are doing. Why? I tried it years ago and discovered that pthreads functions are used in the runtime library and therefore you get very weird errors. In my case, I got a core dump the throw keyword :)

There is a better way of mocking out pthread_create and that is via function pointer stubs. Here is what you do:

In a mypthreads.h, you put:

extern int (*sys_ptheads_create)(etc…);

Then in the mypthreads.c, you put:

int (*sys_pthreads_create)(etc..) = pthreads_create;

————

What this does is it creates an additional level of indirection. It creates a function pointer that is always set to the real pthreads_create.

Then, with that in place, in your production code you call the sys_pthreads_create and NOT the pthreads_create.

In your test, you’ll need to swap it out, like this:

TEST(x, y)
{
   sys_pthreads_create = mocked_pthreads;

   … do test

   sys_ptrheads_create = original_value;
}

Now, because this is so common, CppUTest has a small util for this, so you can do it like this:

TEST(x, y)
{
   UT_PTR_SET(sys_pthreads_create, mocked_pthreads);

   … do test
}

And the UT_PTR_SET will return the original value at the end of the test.

Hope this helps1

Bas




--
You received this message because you are subscribed to the Google Groups "cpputest" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cpputest+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

James Grenning

unread,
Oct 17, 2016, 11:49:31 PM10/17/16
to cpputest

Hello Travis

I wrote most this before Bas weighed in. So I suggest you take Bas's advice and swap via function pointer or proceed with a linker fake but be on the look out for what Bas saw when he tried it.

--- Now for the problem with the code you posted --

I see several problems. For starters, it's best not to use conditional compilation and ask the question #ifdef TESTING in any production code. Additionally, you don't need to do it in this case. You should be trying to create a link time substitute instead.

You might want to start with my starter kit, get a simpler working example then work your way to pthread_mock. https://github.com/jwgrenning/cpputest-starter-project. Sometimes you have to tweak the build, depending on your setup.

I'm sitting in a airport and thought you might like a working mock of pthread_create, so here it is. From this example, you can probably mock the other pthread functions.

Usually I put all my mocks into CPP files; given that pthread.h is probably C++ aware (with #ifdef __cplusplus) it is likely to give you problems in a C file.

//pthread_mock.cpp -- link time fake
#include <pthread.h>
#include "CppUTestExt/MockSupport.h"

int pthread_create(pthread_t * __restrict pthread, const pthread_attr_t * __restrict attr,
                void *(*entry)(void *), void * __restrict parameter)
{
    return mock("pthread").actualCall("create")
            .withParameter("pthread", pthread)
            .withParameter("attr", attr)
            .withParameter("entry", entry)
            .withParameter("parameter", parameter)
            .returnValue().getIntValue();
}

The pthread_create signature is from my mac's pthread.h file.

Notice the actualCall() is in the stub function not in the test case.

Here is the associated test case that calls the link-time stub of pthread_create. Not is uses expectOneCall() to describe the expectation.

#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"
#include <pthread.h>

TEST_GROUP(pthread_mock)
{
    void setup()
    {
    }
    void teardown()
    {
        mock("pthread").checkExpectations();
        mock().clear();
    }
};

void * entry(void*)
{
    return 0;
}

TEST(pthread_mock, create)
{
    pthread_t pthread;
    pthread_attr_t attr;
    int resultYourThreadFillsIn;
    mock("pthread").expectOneCall("create")
        .withParameter("pthread", (void*)&pthread)
        .withParameter("attr", (void*)&attr)
        .withParameter("entry", (void*)&entry)
        .withParameter("parameter", (void*)&resultYourThreadFillsIn)
        .andReturnValue(0);

    //Your production code will be making calls to pthread_create() and not know it is being mocked
    int result = pthread_create(&pthread, &attr, &entry, &resultYourThreadFillsIn);
    LONGS_EQUAL(0, result);
}

Hope that helps.

If you use Bas' function pointer approach, you'll need to rename pthread_create linker stub to something like pthread_create_mock

James


James Grenning - Author of TDD for Embedded C - wingman-sw.com/tddec
wingman-sw.com
wingman-sw.com/blog
twitter.com/jwgrenning
facebook.com/wingman.sw
wingman software

Travis Orr

unread,
Oct 18, 2016, 11:17:56 AM10/18/16
to cpputest
Bas,

That does help. I am, unfortunately, just ramping up on unit testing and have been trying to find the best ways to test. 

The solution you presented will definitely work within the confines of the module I am on right now. My concern is that somewhere in the legacy code base there are going to be instances where this threads component wasn't used.

As I am just in the proof of concept stage for choosing a framework your presented solution is most helpful.

Thanks again,
Travis

Travis Orr

unread,
Oct 18, 2016, 11:20:40 AM10/18/16
to cpputest
James,

Thanks for pointing me to that github repository. Lots of good stuff in there.

I agree with your assessment of avoiding preprocessor macros for conditional compilation. After I posted this I managed to get a bit further but started running into the issues that Bas mentioned.

By the way, I discovered your wingman-sw site yesterday and have enjoyed a lot of the topics you cover there.

Thanks,

Travis

Bas Vodde

unread,
Oct 18, 2016, 9:53:49 PM10/18/16
to cppu...@googlegroups.com

Hi Travis,

The change in legacy code to change a call to the pthread_create to a call to sys_pthread_create is one of the safest you can make.

I guess it *can* go wrong, it hasn’t ever gone wrong me :)

Bas
Reply all
Reply to author
Forward
0 new messages