ThrowTheSwitch / CMock

CMock - Mock/stub generator for C
http://throwtheswitch.org
MIT License
681 stars 273 forks source link

"ERROR: Test executable failed" when using ExpectAnyArgsAndReturn with the same value twice #481

Closed rickymohk closed 1 month ago

rickymohk commented 1 month ago

I have a function to test which calls a function from another module twice

FOO_StatusTypeDef foo()
{
  uint16_t value1, value2; 

  if (readAdc(CHANNEL1, &value1) != ADC_OK) 
  {
    //handle error
    return SOME_ERROR;  
  }
  if (readAdc(CHANNEL2, &value2) != ADC_OK)
  {
    //handle error
    return BATMGMT_ERROR;  
  }

  //Do stuff...
  return FOO_OK
}

So I write a test for this function

void test_foo_should_work(void)
{
    readAdc_ExpectAnyArgsAndReturn(ADC_OK);
    readAdc_ExpectAnyArgsAndReturn(ADC_OK);
    FOO_StatusTypeDef ret = foo();
    TEST_ASSERT_EQUAL(FOO_OK, ret);
}  

But it produces the following error:

ERROR: Test executable "test_foo.elf" failed.
> Produced no output to $stdout.
> And exited with status: [0] (count of failed tests).
> This is often a symptom of a bad memory access in source or test code.

When I only have one readAdc_ExpectAnyArgsAndReturn(ADC_OK);, it correctly executes and says

"Function readAdc.  Called more times than expected."

It also has no issue if the second call returns another value other than ADC_OK. I also tried one IgnoreAndReturn instead of two ExpectAnyArgsAndReturn call but it also produces the same issue.

Letme commented 1 month ago

That bad memory access I would say is because your value pointer is initialized based on your example you probably use that value2 (or 1) in your original function afterwards. That is why you get a bad memory access. I suggest you look at ReturnThruPtr option of CMock to fill in those pointers.

You can run your TestFile.elf with gdb to see where exactly the bad memory access happens and why. It could also be stack corruption.

rickymohk commented 1 month ago

gdb gives Segmentation fault at a usage afterwards

 float x = (((float)(6 * (*y) * value1)/(float)value2/(float)4095));

where y is a local variable defined a few lines before

uint16_t* y = (uint16_t*) 0x1FFF75AA;

So turns out the y is the culprit as it is referencing a hardcoded memory address for the MCU. Thank you for the debugging advice.