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/