Quick question on ReturnArrayThruPtr from a CMOCK stubbed code

293 views
Skip to first unread message

sreeram mutya

unread,
Feb 5, 2024, 1:09:15 PM2/5/24
to ThrowTheSwitch Forums
Hello Team, 

I have some function which is stubbed but, it takes a uint16* data (array uint16 data of structs casted in to uint16*) and in the real world, this data is read from an external signal say from a temperature sensor through another write API which is not currently stubbed by CMOCK. On the top of this whenever a signal is updated by the write API, I will set a flag. I am able to set the flag and reset it and based on that I am able to check the check the functionality of the flag through ASSERT_EQUAL_TRUE(); In this context, I want only the read code to be stubbed by CMOCK and write code is not stubbed and the flag is also not stubbed. This is the currently things are.

My goal is, I want to be able to pass some data and use the cmock to get this stubbed data but, whatever I am doing here is not making sense to me when I am using CMOCK generated stubs. 

Below is what I am doing:
uint16 default_values_array[size] = {1,2,3,4.....45}
uint16* test_ptr;
update_flag = 1;
test_ptr = &default_value[0];
mocked_tempsensdatareadfunc_ExpectAndReturn((uint16*)&test_ptr, 0);
mocked_tempsensdatareadfunc_IgnoreAndReturn(TRUE);
mocked_tempreadfunc_ReturnArrayThruPtr_Data((uint16*)&test_ptr, 45);
TEST_ASSERT_EQUAL_TRUE(temp_sensor_data_updated()); //returns true if
the flag is set, this is working.
TEST_ASSERT_EQUAL_PTR_ARRAY((uint16*)default_values_array, (uint16*)
&test_ptr, 45);

Now, my question is even without mocked functions, I am able to assign the data and use "TEST_ASSERT_EQUAL_PTR" and I really don't understand what use CMOCK is giving me in this context ?

All I want my CMOCK stub in this case to do is, set the data I gave it and use a simple mem copy function to be able to copy the mocked data that I gave in to my buffer. Why can't CMOCK generate this simple thing for me ? 

Can someone explain ?

David Good

unread,
Feb 5, 2024, 3:38:02 PM2/5/24
to throwth...@googlegroups.com
I'll take a stab .

Let's assume that you have two modules , module 1 and module 2 . They both define functions like this :

uint8_t mod1_getVals(uint8_t *buf); // module 1 prototype
uint8_t mod2_getVals(uint8_t *buf); // module 2 prototype

Now , let's assume that the only thing that module 1 function does is copy the values that module 2 populates into its buffer :

uint8_t mod1_getVals(uint8_t *buf) {
    return mod2_getVals(buf);
}

So , now you want to test module 1 in isolation from module 2 . How do you do it ? By only mocking module 2 and letting CMock handle what the module 2 function is doing :

void test_mod1_get_values_works(void) {
    uint8_t vals[4] = {1, 2, 3, 4};
    uint8_t expected[4] = {1, 2, 3, 4};

    uint8_t buf[5];
    uint8_t count;

    mod2_getVals_ExpectAndReturn(NULL, 4);
    mod2_getVals_IgnoreArg_buf();
    mod2_getVals_ReturnArrayThruPtr_buf(vals, 4);
    count = mod1_getVals(buf);
    TEST_ASSERT_EQUAL(4, count);
    TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, buf, 4);
}

As you can see , the only thing being mocked here is module 2 which fills a buffer with values , presumably from a hardware sensor or something . Module 1 doesn't get mocked at all , because that is what is being tested .

I tested this code and it works . Notice how I created an array of expected values rather than reusing the "default" values again . This is recommended so you can change the expected values and see that the test fails , which is the only way to prove that the test is actually working .

If I have to do a lot of this , I usually set up a file scope array and some helper functions which help me change its contents between runs of my functions rather than having tons of pre-rendered arrays , but adjust this advice to taste .

--David


--
You received this message because you are subscribed to the Google Groups "ThrowTheSwitch Forums" group.
To unsubscribe from this group and stop receiving emails from it, send an email to throwtheswitc...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/throwtheswitch/bb364fbc-6aac-404e-83ac-d5968494b53cn%40googlegroups.com.

sreeram mutya

unread,
Feb 6, 2024, 12:33:52 PM2/6/24
to ThrowTheSwitch Forums
Hello David, 

Thank you for your reply and I mocked the module 2 as you suggested but, it still didn't work for me. Here is the code that it generates. My example in the initial Email was on arrays, instead I took a less complicated scenario from one of my tests. 

Write Temperature function - example only, High level code of what it does
#define GOOD 0
uint8 Write_Temperature_Data(uint16* temperature_data)
{
  uint8 return_value = GOOD;
  some_global_value1 = *(&temperature_data);
  global_flag = 1; //update flag only when the temperature_data is available
  return return_value;
}

Mocked Write Temperature:
void Write_Temperature_Data_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, uint16 temperature_data, uint8 cmock_to_return)
{
  CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_Write_Temperature_Data_CALL_INSTANCE));
  CMOCK_Write_Temperature_Data_CALL_INSTANCE* cmock_call_instance = (CMOCK_Write_Temperature_Data_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);
  UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);
  memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));
  Mock.Write_Temperature_Data_CallInstance = CMock_Guts_MemChain(Mock.Write_Temperature_Data_CallInstance, cmock_guts_index);
  Mock.CWrite_Temperature_Data_IgnoreBool = (char)0;
  cmock_call_instance->LineNumber = cmock_line;
  cmock_call_instance->CallOrder = ++GlobalExpectCount;
  cmock_call_instance->ExpectAnyArgsBool = (char)0;
  CMockExpectParameters_Write_Temperature_Data(cmock_call_instance, temperature_data);
  memcpy((void*)(&cmock_call_instance->ReturnVal), (void*)(&cmock_to_return),
         sizeof(uint8[sizeof(cmock_to_return) == sizeof(uint8) ? 1 : -1])); /* add uint8 to :treat_as_array if this causes an error */
}

Manual Mock: This is what I want it to look like, as simple as that,

uint8 Write_Temperature_Data_CMockExpectAndReturn(uint16 temperature_data, uint8 cmock_value)
{
   cmock_value = 0;
   global_value = temperature_data;
   return (cmock_value);
}
//this test is failing and I am not sure why the cmock is not returning 0. 
write_temperature_test()
{
Write_Temperature_Data_CMockExpectAndReturn(60, 0);

David Good

unread,
Feb 6, 2024, 1:34:12 PM2/6/24
to throwth...@googlegroups.com
Hi Sreeram ,

Just scanning your reply , I'm not sure that I grasp exactly what you are trying to do , but I think I have some ideas anyhow .

1 . You should never really need to look into the code generated for a mock for anything other than learning about CMOCK . If you are , it means you are probably on the wrong track .
2 . It sounds like you might want stubs and not mocks . Mocks are rigidly defined in what they do and don't do . CMock functions just keep track of inputs and outputs . If you need a "mocked" function to set some global vars as well as pass data , you will have to write your own stub function which does exactly that .

I don't know where your global variables live , so I just used an example global var in my test file .

Expanding the previous example with stubs , you can see how simple it is to create whatever function you need :

// Test file helper vars
uint8_t global_flag;
uint8_t test_data[4];

void setUp(void) {
    global_flag = 0;
}

void tearDown(void) {
}

uint8_t mod2_getVals_callback(uint8_t *buf, int numcalls) {
    memcpy (buf, test_data, 4);
    global_flag = 1;
    return 4;
}

void test_mod1_getVals_stub_works(void) {

    uint8_t vals[4] = {1, 2, 3, 4};
    uint8_t expected[4] = {1, 2, 3, 4};

    uint8_t buf[5];
    uint8_t count;

    // Setup the callback
    mod2_getVals_StubWithCallback(mod2_getVals_callback);
    memcpy(test_data, vals, 4); // we have to setup these vals to be returned by mod2_getVals
    global_flag = 0;

    count = mod1_getVals(buf);

    // Check the results (same as previous example)

    TEST_ASSERT_EQUAL(4, count);
    TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, buf, 4);
}

Stubs are more effort to code since you have to do all of the logic yourself rather than relying on CMock's automatically generated functions , however they are infinitely flexible .

Hope this helps you get on the right track !

--David

David Good

unread,
Feb 6, 2024, 2:15:06 PM2/6/24
to throwth...@googlegroups.com
I realize that I should have commented the code a little better , so let me explain a few more things :

1 . uint8_t mod2_getVals_callback can be any name you want as long as the function return value and argument list is correct . I just like using the _callback convention to make it clear what the function is meant to do .
2 . You will notice that the argument list has an extra numcalls argument . This is added automatically by CMock when you use the _StubWithCallback version of a function and must be on the callback version of the function .
3 . I used a global variable test_data array to hold the values returned by our fake callback function . I just made this up . You can do whatever makes the most sense for your situation . I usually use helper functions to make this easier for myself rather than doing something like memcpy in all of my tests .

uint8_t helper_setup_mod2_getVals(uint8_t *vals, uint8_t count) {
    uint8_t i;
    global_flag = 0; // maybe you want this here ?
    for (i = 0; i < count; ++i) {
        // copy or setup things here
        test_data[i] = vals[i];
    }
    return i;
}

Then the test becomes :

void test_mod1_getVals_stub_works_with_helpers(void) {

    uint8_t vals[4] = {1, 2, 3, 4};
    uint8_t expected[4] = {1, 2, 3, 4};

    uint8_t buf[5];
    uint8_t count;

    // Setup the callback
    mod2_getVals_StubWithCallback(mod2_getVals_callback);
    helper_setup_mod2_getVals(vals, 4);


    count = mod1_getVals(buf);

    // Check the results (same as previous example)
    TEST_ASSERT_EQUAL(4, count);
    TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, buf, 4);
}


--David

sreeram mutya

unread,
Feb 6, 2024, 2:15:47 PM2/6/24
to throwth...@googlegroups.com
David, 

Thank you for your response. If I am stubbing everything, what is the use of CMOCK here ? Let me explain a detailed scenario as an example here. 

1. Module 1 gets data from outside (say from another sensor), Module 1 writes to Global 1 and sets a flag. 
2. Module 2 will be notified when Module 1 has updated data, it reads the data and processes the data. it does some processing and writes it to another Global2 and sets another global flag.
3. Module 3 will be notified if Module 2 is updated and then it reads the data from Module 2 and uses it. 

I mocked Module 2 and 3 and cmock literally doing nothing. Why I can't just mock and Module 3 because, Module 1 and Module 2 data is linked to outside world. 

If you don't mind, Can you tell me at a high level why I can't use CMOCK to do the above ? You said CMOCK will keep inputs and outputs, here in the above context, I have inputs and outputs too. My inputs are the sensor value coming from the outside world. 

I can follow your suggestion but, if I am thinking how to test 100 functions of this type. 







--
Thanks, 
Sreeram Mutya
Phone - 248-949-4656
 
 
 
 

David Good

unread,
Feb 6, 2024, 2:26:14 PM2/6/24
to throwth...@googlegroups.com
You are correct that if you are trying to mock everything all at once , you end up not testing any real code which defeats the point of testing !

CMock is what allows you to test each one of these things in isolation with either mocks or stubs , depending on your specific needs . Going through your list :

1 . Module1 depends on some hardware and sets a flag and copies some data . This should have its own test_module1.c file and should not include any references to module2 or module3 . Totally isolated .
2 . Module2 depends on Module1 , so Module2 should mock or stub the exact Module1 functions that it calls so that you can test that Module2 is setting the correct flags etc given some controlled outputs from the Module1 functions . Again , Module2 is totally isolated in a separate test_module2.c file .
3 . Module3 depends on Module2 , so Module3 should only mock or stub the Module2 functions it calls , but should not touch Module1 or the hardware below that . This should also have it's own test_module3.c file so it's properly isolated from the other modules being tested separately .

Hopefully that makes sense as a high level strategy for how to think about testing your modules .

--David

David Good

unread,
Feb 6, 2024, 2:29:49 PM2/6/24
to throwth...@googlegroups.com
I should add that if Module3 had to use both Module1 and Module2 functions that is OK too . You would simply have to mock / stub both of those in the test_module3.c file .

The point is that each module test file should only be testing the logic belonging to that module and the dependent modules should be mocked / stubbed away so that they only provide the correct outputs / side effects that your "module under test" is looking for / reacting to .

--David

sreeram mutya

unread,
Feb 6, 2024, 4:07:18 PM2/6/24
to throwth...@googlegroups.com
David, 

Thank you for all the information. You mentioned a couple of times that I can mock/stub, as you said CMOCK would only capture inputs and outputs. Then in that case, how can I avoid writing stubs and using mocks  for functions like below ? or can you give me some pointers to easily tell whether I am able to use MOCK or I have to stub them ? How would I know ?

int write_temp(uint16* test)
{
   global_var from module 2= &test;
   return *global_var;
}




David Good

unread,
Feb 6, 2024, 5:31:23 PM2/6/24
to throwth...@googlegroups.com
I'm not clear on which function is where . I see that you are trying to set a variable in module2 , but what module is int write_temp in ? And what module is global_var in ? Is it truly global , like in main ?

Using global variables like this is harder to do with CMock than functions , but still possible . Without specifics of your code , this is all a bit high level and theoretical .

Let's assume that you are testing module1 which calls functions from module 2 . If the function in module2 is supposed to set some truly global var in your main module , then you have some options :

1 . You could set the variable yourself in the test before you call the function . This means that you set up the mock using CMock and ReturnThruPtr instead of using stubs , and your test code just forces the global variable . This doesn't work if your module1 function is checking the global variable before and after the call to module2 .

void test_mod1_getVals_stub_works_with_helpers_setting_globals_directly(void) {

    uint8_t vals[4] = {1, 2, 3, 4};
    uint8_t expected[4] = {1, 2, 3, 4};

    uint8_t buf[5];
    uint8_t count;

    // Setup the first expectations returning only 2 values
    mod2_getVals_ExpectAndReturn(NULL, 2);
    mod2_getVals_IgnoreArg_buf();
    mod2_getVals_ReturnArrayThruPtr_buf(vals, 2);

    global_flag = 0;

    count = mod1_getVals(buf);

    // Check that mod1_getVals returned nothing because it doesn't have 4 yet
    TEST_ASSERT_EQUAL(0, count);

    // Setup the second expectations returning the remaining 2 values
    mod2_getVals_ExpectAndReturn(NULL, 2);
    mod2_getVals_IgnoreArg_buf();
    mod2_getVals_ReturnArrayThruPtr_buf(&vals[2], 2);
    global_flag = 1;

    count = mod1_getVals(buf);

    // Check that mod1_getVals finally returns all data

    TEST_ASSERT_EQUAL(4, count);
    TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, buf, 4);
}


2 . Instead of using naked global variables , you can wrap access to them in functions which can be easily mocked with _ExpectAndReturn calls .

In order to give an example of this , I would need to know a little bit more about these global flags . Does the code you are trying to test read these variables , or just write to them ? If I understand your code , it seems that each module sets the global variable , and a different module reads this as a signal to do something . If that's true , then you can set the global variable to some default state before calling the "function under test" and then do a check to ensure that the function set the variable as you expected .

--David




sreeram mutya

unread,
Feb 11, 2024, 12:46:36 PM2/11/24
to throwth...@googlegroups.com
Hi David, 

Thanks, here is a little more explanation on what I am doing but, before that when I try to do what you mentioned, I keep getting this error - ReturnThruPtr called before Expect. 

test_sw_module.c is not returning 60 and my test case is failing, can you tell me why my mock is not working? and I keep getting "called more times than expected" and I think this is OK because this is a function that repeats. 

Module 1.h:
===========
status(uint8) write_temp_value(uint16 temp_value);

Module 2.h:
============
status(uint8) read_temp_value(uint16* temp_value)


Mediator.c
==============
#include <module1.h>
#include <module2.h>
uint16 adc_value;

status(uint8) write_temp_value(uint16 temp_value)
{
  stop_interrupts();
  adc_value = *(&temp_value)
  flag = 1;
  start_interrupts();
}

status(uint8) read_temp_value(uint16* temp_value)
{
  stop_interrupts();
  uint8 status = 0; //which means OK
  *(temp_value) = adc_value; (externed global set from another module, this is also mocked)
  flag = 0;
  stop_interrupts();
  return status;
}

sw_module.c under test:
===================
static my_idx(uint16 adc_value);
uint16 temp_value;
task_1sec()
{
   uint16 adc_value;
   if(flag == 1)
   {
      read_temp_value(&temp_value);
   }
   my_idx(adc_value);
}

test_sw_module.c
=================
#include <uinty.h>
#include <Module2.h>
setup()
{

}

teardown()
{

}

test_swc_module_task_pass()
{
//setting flag manually
flag = 1;
read_temp_value_ExpectAndReturn(60, 0);
task_1sec();
}


sreeram mutya

unread,
Feb 11, 2024, 12:48:36 PM2/11/24
to throwth...@googlegroups.com
David, 

Edited my bad... I didn't mention earlier that I am mocking Module 2.

test_sw_module.c
=================
#include <uinty.h>
#include <mock_Module2.h>

setup()
{

}

teardown()
{

}

test_swc_module_task_pass()
{
//setting flag manually
flag = 1;
read_temp_value_ExpectAndReturn(60, 0);
task_1sec();
}

David Good

unread,
Feb 11, 2024, 2:31:51 PM2/11/24
to throwth...@googlegroups.com
Hi Sreeram ,

Yeah , I was going to mention that ;)

My first observation is that we can completely ignore Mediator.c . I assume that there is no Mediator.h . If there is , don't include it in your test file . This file gives the real implementation of read_temp_value(uint16* temp_value) , which we aren't using because we are telling CMock to use a mocked version instead . It is important that you really understand this point so you don't go looking in there for answers or solutions .

So , now we are getting to something that we can work with . In your test , you are just testing task_1sec() , so now we just need to make it happy . You are setting the flag , which is good , but where do you think that the value of 60 is going to come from ?

read_temp_value_ExpectAndReturn(60, 0);

I assume that you really want read_temp_value to write the value of 60 into the variable temp_value , but I would ask why ? Where does 60 go and how can you test that it went there properly ?

void test_swc_module_task_pass_improved(void)
{
    flag = 1; //setting flag manually
    read_temp_value_ExpectAndReturn(0, 0); // we say we are expecting *temp_value to be 0 but we don't really care what the actual pointer value is. You could use any number you want.
    read_temp_value_IgnoreArg_temp_value(); // don't check the value of temp_value , just use it for passing values
    read_temp_value_ReturnThruPtr_temp_value(60);
    task_1sec();

    /* How can you check that 60 was used correctly ??? */
}


You show that my_idx() is a local module function , so you can't check that externally from your test function . There must be some other output or side effect of this function which you need to check .

Regarding the "ReturnThruPtr called before Expect" error , don't ignore this . It is telling you that the code is doing stuff that your test didn't specify , which means that either your code is wrong or your test is wrong . Since I don't see any ReturnThruPtr in the code you posted , I don't know what to say about that . Notice in my example above that the _ExpectAndReturn is called before the _ReturnThruPtr , which is how you have to order those calls , and you have to have all three .

My recommendation is to create a copy of your project and strip away all of the files that are unrelated to testing this one module and just try to get that working . Also , if you had to , you could post that here so we could see what you were actually trying to do and work through the "smallest example of the problem" . Frequently , this is the best way to learn testing tools and strategies .

--David


sreeram mutya

unread,
Feb 11, 2024, 3:37:14 PM2/11/24
to throwth...@googlegroups.com
In simple examples, I am able to understand what these are - let's call this 3 statements. 

1) read_temp_value_ExpectAndReturn(0,0) - Why ? what is the significance of this to  read_temp_value_IgnoreArg_temp_value and  read_temp_value_ReturnThruPtr_temp_value ?
2) read_temp_value_IgnoreArg_temp_value, when we are saying ignore arguments, why we have above with arguments ? that part I am not getting. 
3) read_temp_value_ReturnThruPtr_temp_value(60) - My point is why can't I simply have this statement without having the above 2 ?4

With the mocked functions in place, why can't I verify the temp_value with an assert statement and see if it's matching with 60 right after the 3rd statement above ? If I have to, do I have to mock anything else ?



David Good

unread,
Feb 11, 2024, 4:07:51 PM2/11/24
to throwth...@googlegroups.com
The reason is that CMock is a tool which mockes function calls to get your code to run and also acts as a watchdog , reporting when calls are made that were not expected , with the wrong values , or out of order .

So in our example , the first thing we have to do is tell CMock that we are expecting our code under test to call the read_temp_value function one time , and when it does , that the return value of this function should be 0 (for all good status) . This is why the mocked function is called _ExpectAndReturn . Now , if you just did that , the test would run , but you might or might not get a passing test . The reason is that CMock not only needs to know to expect the function call , but also wants to verify that the arguments passed to the function are what you expect as well . That's where we start getting into _IgnoreArg_temp_value . This tells CMock not to worry about checking the value of this argument when the function is called that one time . If you just did only these 2 functions , your test would run and probably pass . Notice that we haven't even bothered with a return value yet , and that is because the code you've shown us doesn't actually do anything with this returned value . Now , if your code uses this return value to set some other global var or make some other function calls outside of the module , then we can setup tests with TEST_ASSERT_EQUAL or _Expect depending on what it does . Once we have that , only then do we need to tell CMock to pass a specific value back through the mocked function call using the _ReturnThruPtr_temp_value function .

You see , each function is "programming" CMock for exactly what you expect should happen when you run your code , and if those things don't happen , you want CMock to fail your test because your code isn't doing what you told CMock it should be doing .

This is how you should think about testing : Given the following conditions / inputs , when I call function x , I want these other things to happen , in such and such order . You have to communicate all of that to the testing tools in order for it to understand what your code should be doing and alert you when it doesn't do that .

--David

sreeram mutya

unread,
Feb 11, 2024, 4:24:14 PM2/11/24
to throwth...@googlegroups.com
David, 

my test is passing now but, I have to do this without ASSERT, 
uint16 expected_temp_data = 60;
read_temp_value_ExpectAndReturn(0,0);
read_temp_value__IgnoreAndReturn(0);
read_temp_value_ReturnThruPtr_data(expected_temp_data);

and yes, the expected_data i.e. 60 in this case is used internally inside task_1sec to make some decisions but, that function my_idx(adc_value); is a local function in Module Under Test, once this decision is made, I think I can finally test the values being sent out and there I can add the assert equal statements in the use case when the value becomes 60.
I believe this is what you were mentioning that I can't do TEST_ASSERT_EQUAL to cross check the expected_temp_data as there is no way to do it and the code to cross check this is already mocked.

The strange thing here is - I don't have "_ignoreArg_temp_value", cmock didn't generate this when I mocked the Moulde2, I don't know why, but, can you tell me if the above is correct or not ?


sreeram mutya

unread,
Feb 11, 2024, 5:10:00 PM2/11/24
to throwth...@googlegroups.com
David, 

One more observation I made was, I can take out the first statement read_temp_value_ExpectAndReturn(0,0); and I can still get the test case to pass but, when I call Task_1sec(), I keep getting this error always - Called more times than expected and results in a test failure. If I don't call  Task_1sec(), I keep getting Task_1sec() "called fewer times than expected". Now, this has become a chicken and egg problem. Without calling "Task_1sec(), I won't be testing Task_1sec() but, if I call that, I keep getting failures because, it's called more times than expected", Since it's a repeated call based on a 1 sec timer, I would expect that this is called more times but, cmock knew that it's called more times ? Now, the Mediator.c is where this function is called but, I didn't even mock that function and it's completely excluded here. 

David Good

unread,
Feb 11, 2024, 5:52:35 PM2/11/24
to throwth...@googlegroups.com
It sounds like you are on the right track !

Be sure you have the :ignore and :ignore_arg plugins enabled under :cmock: in your project.yml . Here's an example :

:cmock:
  :mock_prefix: "mock_"
  :when_no_prototypes: :warn
  :enforce_strict_ordering: FALSE
  :plugins:
    - :ignore
    - :ignore_arg
    - :callback
    - :array
    - :return_thru_ptr

Regarding your internal function , you are correct that you can't directly test that (without some extra helpers) , but you shouldn't need to . You should just test whatever effects it has . We can go through that next if you post some more details .

Next about chickens and eggs ... You are right that you have to call Task_1sec() once for your expectations to actually fire , however , if inside your Task_1sec() you call read_temp_value more than once , you have to setup those expectations all over again . For instance , if you called it from a loop or called it again from your my_idx() function , you would have to setup new expectations for each time your code called it . This is common when using the return value to know whether or not to continue processing . For a general example :

while (read_temp_value(&temp_value) == 0)
{
    // Do something with the value here
    my_idx(temp_value);
}

For this case , your test would look like this :

void test_swc_module_task_pass_improved(void)
{
    flag = 1; // set flag manually

    // First read expectations
    read_temp_value_ExpectAndReturn(0, 0); // return status 0 (OK) the first time
    read_temp_value_IgnoreArg_temp_value(); // ignore pointer arg
    read_temp_value_ReturnThruPtr_temp_value(60); // return a value because status 0

    // Second read expectations
    read_temp_value_ExpectAndReturn(0, 1); // return status 1 (BUSY) the second time
    read_temp_value_IgnoreArg_temp_value(); // still ignore pointer arg

    task_1sec(); // one call makes multiple read calls internally

    // check more results here
}

So , for troubleshooting ideas :
1 . How many tests do you have ? I'm assuming just one right now .
2 . What is the full list of #includes that you are using ? It is possible that either you are accidentally mocking Task_1sec() , some other module is getting compiled in which is calling it , or perhaps it's getting called from an init function or something like that . I would look in your code and find out every place this function is being called in order to get some ideas .

You mentioned that "it's called based on a 1 sec timer" , probably in Mediator.c , but like you said , that module shouldn't even be getting compiled or included in this test in any way .

--David

sreeram mutya

unread,
Feb 11, 2024, 7:06:33 PM2/11/24
to ThrowTheSwitch Forums
That Task_1sec() is present in Mediator.c and it's called via task scheduler and it's not mocked and it's not present in the compilation as well so, I am not sure if that's being mocked some and at the same time, I couldn't find it in the mocked functions either. So, the only way that it can be mocked is when I mock Task scheduler which I am not doing. 

Is there anyway we can block this error so that I can see results, also when I run gcov, I can't find the coverage report for my module under test, I can only find it for "test_module_under_test.c", is this because of that test_failed issue which I am getting because, I am calling my Task_1sec() more times than expected ? 

And how would I know how many times it can get exactly called while I am calling it only 1 time ? 

David Good

unread,
Feb 12, 2024, 12:27:17 PM2/12/24
to throwth...@googlegroups.com
Do not try to "block this error" . The tool is trying to tell you something , and if you block it , it's like asking your alarm clock to wake you up but then making sure that it's on mute and can't make any sounds .

If you don't understand how many times something is getting called , I would recommend putting some printf statements inside of your code under test in order to try and understand things . The output of printf will appear in the test console . I don't have to do that often , but sometimes it's the only way .

--David

David Good

unread,
Feb 12, 2024, 1:11:41 PM2/12/24
to throwth...@googlegroups.com
Also , at this point , I would encourage you to create a separate test project with just this one test and one module (or one like it) so that you can share it here . I think part of the problem is that I don't see the full version of what you are trying to test , and cannot replicate it on my side , so what I'm saying might or might not help . If you had a minimal example that I could run and see the same problem , we could get to the bottom of this very quickly .

--David

sreeram mutya

unread,
Feb 12, 2024, 10:25:07 PM2/12/24
to throwth...@googlegroups.com
David, 

Thanks alot for the pointers, it was encouraging, I finally found the culprit, the Task_v1sec() is being mocked as well because, it resides inside that the module where other functions that are to be mocked are present. 



David Good

unread,
Feb 12, 2024, 11:27:21 PM2/12/24
to throwth...@googlegroups.com
Alright !!

Yep , that will do it . With CMock , you either mock all of the functions in a header file , or none of them . It won't let you mock only some of them . This actually pushes you to structure your c files a bit differently so that you detangle things into better logical units , but sometimes it means that you have files like :

module1_task.c (or just modul1.c)
module1_internals.c
etc.

If you do that , you can have separate header files for the "public" parts of the module which are called from the application , and then "private" parts of the module which are only called by the main module . This allows you to mock the internal functions when testing the public functions and usually makes testing the internal functions very easy since they are more likely to be very single focused .

I'm glad you stuck it out !

--David

Aarush Jain

unread,
Mar 5, 2024, 1:46:06 PM3/5/24
to ThrowTheSwitch Forums
Hi,

Do you know how to configure a makefile i have in my production code with the .yml file in the ceedling project so i can run unit tests?

Reply all
Reply to author
Forward
0 new messages