Tom Willis
Tom Willis

Reputation: 378

How do I pass a double value to my C mock function with cmocka will_return()?

I am using cmocka to unit test a C project.

I want to mock a call to another modules made in my C function under test. This function in the other module deals with doubles, not ints. The will_return documentation says it passes integer values, and I can see that if I call will_return(__wrap_other_func, 42.99) then the value passed into __wrap_other_func and pulled out through double value = mock_type(double) will be 42.0, not the desired 42.99.

double __wrap_other_func(double param) {
  check_expected(param);
  double value = mock_type(double); // will_return() has converted this, effectively rounding
  fprintf(stderr, "%lf\n", value);
  return value;
}

static void test_this_func(void **state) {
  (void) state;
  expect_value(__wrap_other_func, param, 1.0);
  will_return(__wrap_other_func, 42.99);
  int result = this_func(12.34); // this_func() will call other_func()
  ...
  assert(result == 0); 
  /* the assert is failing because the double 42.99 is converted to an integer,
     then back to a double, rounding off all the decimal places. */
}

> 42.0

Has anyone figured out how to pass doubles into the mock function with will_return or some other cmocka method? I'm stuck on this.

I was expecting to be able to pass non-integer values to my mock function using cmocka.

When I tried that using will_return(), I found all double values were rounded to integer equivalents.

I have pored over the cmocka documentation and searched for cmocka examples online.

Update 2023-11-19 I have a solution.

The cmocka main repo at cryptomilk was updated by Andreas Schneider so that mocks work with real numbers (floats and doubles) as expected. Thank you Andreas!

With the 2023-11-17 head of the main branch built and installed on my system, this code now works as expected.

double __wrap_other_func(double param) {
  // verify that the expected value was passed in to the mock
  check_expected(param);
  // assign the return value
  double value = mock_float; 
// will_return_float() can deal with real numbers (doubles and floats)
  fprintf(stderr, "%lf\n", value); // this will print the real number value now
  return value;
}

static void test_this_func(void **state) {
  (void) state;
  // make sure the argument passed to the mock is correct
  expect_value(__wrap_other_func, param, 1.0);
  // tell the mock what to return
  will_return_float(__wrap_other_func, 42.99);
  // call this_func normally. The mock for other_func will get called
  int result = this_func(12.34); // this_func() will call other_func()
  ...
  assert(result == 0); 
  // the assert is now passing because the double 42.99 remains a double
}

> 42.99

Thanks to everyone who offered comments and answers.

Upvotes: 4

Views: 793

Answers (2)

Andinni
Andinni

Reputation: 1

I do not think that cmocka does natively offer a will_return_float or will_return_double macro. I was facing the same issue as you did and think I found a workaround involving to "trick" the mock() macro by using it to pass a pointer to a float. (Edit: I just realized that this is exactly the comment by https://stackoverflow.com/users/13199234/mem) Anyway, taking your example, it would be:

double __wrap_other_func(double param) {
  check_expected(param);
  double value = *(mock_type(double*)); // "value" takes the content of the double pointer! 
  fprintf(stderr, "%lf\n", value);
  return value;
}

static void test_this_func(void **state) {
  (void) state;
  double wantedResult = 42.99;  // define your wanted result
  expect_value(__wrap_other_func, param, 1.0);
  will_return(__wrap_other_func, &wantedResult);  // pass the pointer to the double value wantedResult instead of the value itself.
  int result = this_func(12.34); // this_func() will call other_func()
  ...

}

This line

double value = *(mock_type(double*));

in the definition of __wrap_other_func(double param) resolves to

double value = *((double*) &wantedResult;

which is identical to wantedResult.

Note the importance to pass the pointer to the wantedResult instead of the value in the will_return function.

Upvotes: 0

Pignotto
Pignotto

Reputation: 633

The intended way is to use will_return_float and mock_float

There are also other "assigning" macros:

  • will_return_int mock_int
  • will_return_float mock_float
  • will_return_ptr mock_ptr
  • etc.

to use instead of the "casting" macros will_return and mock_type

Side note: mock_float doesn't take macro arguments, as of now it stores the value as a double

Upvotes: 1

Related Questions