[SSSD] [PATCH] RFC: Unit test for the NSS responder based on the cmocka library

Pavel Březina pbrezina at redhat.com
Wed Mar 6 14:39:30 UTC 2013


On 03/05/2013 05:31 PM, Jakub Hrozek wrote:
> On Fri, Mar 01, 2013 at 01:39:22PM +0100, Pavel Březina wrote:
>> On 02/12/2013 06:12 PM, Jakub Hrozek wrote:
>>> Hi,
>>>
>>> Short version - attached is a patch proposal for a unit test based on
>>> cmocka. The patches are an example of a complex, yet isolated unit test
>>> and I'd like to get opinions on whether this would be a good way to go.
>>>
>>> Long version:
>>> Our check based test suite is OK for developing simple tests for APIs
>>> like sysdb but not really great for testing of more complex interfaces.
>>>
>>> In particular, it's hard to simulate certain pieces of functionality
>>> that require interaction with network or the rest of the system such as
>>> LDAP searches or logins, especially in environments like buildsystems.
>>>
>>> I think we might want to look at Cmocka to provide this missing functionality
>>> and test complex interfaces in isolation. Cmocka[1] is a fork of Google's
>>> cmockery which is not maintained upstream anymore. Cmocka is maintained
>>> by Andreas Schneider.
>>>
>>> I've been playing with cmocka lately and wrote a couple of patches that
>>> add a unit test for the NSS responder, in particular the getpwnam()
>>> function. It was quite easy to simulate the Data Provider and the client
>>> using two concepts:
>>>
>>> 1) cmocka's mocking feature
>>> Functions that provide external interface, like the dummy DP functions
>>> in my patches, can access a stack of values. The unit test can push some
>>> expected return values onto the stack before launching the test and the
>>> dummy functions would then pop the values and use them as appropriate.
>>> It's quite easy to simulate a Data Provider saving an entry to cache
>>> with call like this (pseudocode):
>>>
>>> int unit_test()
>>> {
>>>      will_return(sss_dp_get_account_recv, "testuser");
>>>      will_return(sss_dp_get_account_recv, 10);
>>>      will_return(sss_dp_get_account_recv, 11);
>>> }
>>>
>>> int sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, tevent_req *req)
>>> {
>>>      char *username = mock();
>>>      uid_t uid = mock();
>>>      gid_t gid = mock();
>>>
>>>      sysdb_store_user(username, uid, gid);
>>> }
>>>
>>> 2) the -wrap feature of ld
>>> It is possible to selectively override functions from modules linked against
>>> to intercept calls using the -wrap feature of ld. In my unit tests I used
>>> this feature to check results of responder search with defined callbacks
>>> instead of returning the results to the nss client. Here is a simple example
>>> of intercepting a call to negative cache to make sure the negative cache
>>> is hit when it's supposed to:
>>>
>>> int __wrap_sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
>>>                                   struct sss_domain_info *dom,
>>>                                   const char *name)
>>> {
>>>      int ret;
>>>
>>>      ret = __real_sss_ncache_check_user(ctx, ttl, dom, name);
>>>      if (ret == EEXIST) {
>>>          nss_test_ctx->ncache_hit = true;
>>>      }
>>>      return ret;
>>> }
>>>
>>> There's a couple of boilerplate functions but once these are in place,
>>> they can be reused to cover different pieces of functionality..the
>>> attached patches test the following requests:
>>>   * cached user including assertion that DP was not contacted
>>>   * nonexistant user including assertion that on a subsequent lookup, the
>>>     request is returned from negative cache
>>>   * search for a user that has not been cache prior to the search
>>>   * updating an expired user including assertions that the user had been
>>>     updated from DP and the updated entry is returned.
>>>
>>> The downsides of using cmocka as far as I can see are:
>>>    * writing the mocking code can be tedious. On the other hand, the NSS
>>>      responder I picked was probably the most complex code I could test
>>>      as it communicates with both DP and the client library. Even more
>>>      isolated testing (the AD sites feature comes to mind) would be much
>>>      easier I think.
>>>    * cmocka is not present in RHEL
>>>
>>> I'll be glad to hear comments about this unit test proposal. Patches
>>> are attached.
>>>
>>> [1] http://cmocka.cryptomilk.org/
>>
>> Very nice.
>>
>> I would like to increase granularity though. First of all we should
>> separate cmocka test files from check files on directory/filename
>> level. I'd say that all cmocka stuff should go into tests/cmocka. For
>> example, there are files common_check.c, common_dom.c, common.c, but
>> only common_dom.c is suitable for cmocka. When more common files and
>> tests comes, it will be hard to distinguish them.
>>
>
> OK, the files are hopefully better split now:
>
> There is common_dom.c for tests that would like some common way to
> connect to a test sysdb. In future, we should convert even the existing
> tests like sysdb-test or sysdb-ssh-test to use this common code to avoid
> code duplication. I will file a ticket about this.
>
> Then there is common_tev.c for tests that require an event loop and
> may need to issue dummy tevent requests.

Your fingers must have been really painful, so that you was unable to 
write the remaining three letters :-)

> Both files are stored in the src/tests. I think we should shuffle the
> files a bit so that only helpers that do not require neither check nor
> cmocka would be in the src/tests subdirectory directly. The check-based
> tests and their specific utils should be moved to src/tests/check. This
> would be another ticket.
>
>> Testing with cmocka is mainly about mocking structures and creating
>> wrappers around functions. We should try to create reusable mocks and
>> wrappers right from the beginning, making them independent on tests.
>> For example: mock_rctx(), mock_cctx(), sss_dp_get_account_send(),
>> sss_dp_get_account_recv(), ... I believe these and more functions can
>> be reused when testing other responders so they should be placed in
>> e.g. common_responder.c
>>
>
> Currently there are no mock-related functions that would be reusable by
> any test (one that would test provider and one that would test responder
> for example).
>
> The functions you mentioned above were moved to
> src/tests/cmocka/cmocka_common_resp.c as they would be reused by any
> cmocka-based test that tests the NSS responder.
>
>> Why did you include setjmp.h?
>
> For clarity the setjmp and other required headers are now included in
> header src/tests/cmocka/common_mock.h
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
>




More information about the sssd-devel mailing list