PengZheng commented on a change in pull request #392:
URL: https://github.com/apache/celix/pull/392#discussion_r791780927



##########
File path: libs/framework/src/bundle_context.c
##########
@@ -76,7 +76,7 @@ celix_status_t bundleContext_create(framework_pt framework, 
celix_framework_logg
 
         }
        }
-
+    //FIXME: context == NULL?

Review comment:
       I guess you are talking about injecting errors into libc or any 3rd 
party library function call when doing unit test, right? 
   The standard mocking method in C++  is to provide mock implementation of an 
abstract interface. The C equivalent is using function pointers:
   
   So instead of 
   ```C
   int FormatOutput(const char *, ...);
   ```
   we put the following in the header file:
   ```C
   extern int (*FormatOutput)(const char *, ...);
   ```
   
   Though both of them support runtime-bound test doubles very well, careless 
use can lead to lots of unnecessary abstract classes/function pointers, which 
could harm both performance and code readability considerably.  In this 
particular case (libc/3rd party C libraries), these runtime test doubles are 
pretty awkward to use,  i.e. we have to re-exposing functions as function 
pointers instead of using header file directly.
   
   Instead, I use link-time substitution. It does not require any wierd 
wrappers in production codes. We still need wrappers, but they only appear in 
testing code. It permits testing corner cases which is almost impossible to 
cover in system test like the following:
   
   ```C
   TEST(AlarmUploader, HeartbeatTimeoutWhenSending)
   {
           NETRET_HEADER header;
        expectRecv = true;
           //enable link-time test doubles for clock_gettime and gettimeofday
           //if disabled, they will call the libc's implementation
        EnableClockMock(true);
        LONGS_EQUAL(EDA_OK, AlarmUploaderInit(conn, module, &attr));
        LONGS_EQUAL(EDA_OK, AlarmUploaderConnect(conn, NULL, NULL));
        //enable IO mock to substitute write and writev, which libuv uses for 
socket IO
           EnableIoMock(true);
        //limit each write/writev invocation can send out at most 1 byte
        //so that the transportation of tiny heartbeat packet (< 100 bytes) 
cannot be completed in one uv_run call
        SetIoMockMaxBytesPerWrite(1);
           // let 5.001 seconds pass in our fake clock to trigger 5 seconds 
timeout
        ClockAdvance(5001); 
           // actually trigger libuv timer with heartbeat pending
        uv_run(loop, UV_RUN_NOWAIT);
           // disable IO mock, so that libuv send out packet at normal speed
        EnableIoMock(false);
        ClockAdvance(5000);
        ExpectNetRetHeader(&header, NETRET_QUALIFIED);
        
mock().expectOneCall("message_received").withUnsignedIntParameter("status", 
NETRET_EXCHANGE);
        
mock().expectOneCall("message_received").withUnsignedIntParameter("status", 
NETRET_EXCHANGE);
        uv_run(loop, UV_RUN_NOWAIT);
        ObjectPolyFinish((Object)conn);
        fd = -1;
           EnableIoMock(false);
   }
   
   ```
   
   Triggering timeout when sending a tiny packet is very difficult (if not 
possible) in reality, even with the help of expensive network emulation 
hardware. But with link-time test double, it's easy to inject various errors 
into any C library call. Also the test case is extremely fast to execute, since 
we use fake clock to avoid waiting 10 seconds. High line/branch coverage (like 
sqlite) is not difficult to achieve.
   
   However, such powerful testing technique is not without its price:
   
   * 1000 LOC `AlarmUploader` takes 3300 LOC to test.
   * Such tests are not written against API, they are completely whitebox.
   * Bad readability as a consequence. API test case can be used as demo while 
the above test case cannot.
   
   We have to take extra care of the interplay between malloc/calloc/pthread 
test double and Google Address Sanitizer so that they can work together. I'll 
prepare a gist for malloc/calloc if Celix wants to take this whitebox way of 
testing. @pnoltes 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to