[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] added will_return_maybe for ignoring mock returns


As both parameter and function call order checking allow for ignoring
cases where they are never invoked, the mock return values are at
somewhat of a mismatch in that they must always be returned at least
once (even in the case of will_return_always()). Therefore, the ability
to set the count to -2 on will_return_count was added with a new
macro (will_return_maybe) that indicates that that the value field may
never be returned and still allow a successful test.
---
 include/cmocka.h          | 35 +++++++++++++++--
 src/cmocka.c              | 12 +++---
 test_ordering_fail.c      | 95 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/CMakeLists.txt      | 14 ++++++-
 tests/test_returns.c      | 69 ++++++++++++++++++++++++++++++++++
 tests/test_returns_fail.c | 77 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 292 insertions(+), 10 deletions(-)
 create mode 100644 test_ordering_fail.c
 create mode 100644 tests/test_returns.c
 create mode 100644 tests/test_returns_fail.c

diff --git a/include/cmocka.h b/include/cmocka.h
index 6242ff2..d210f5e 100644
--- a/include/cmocka.h
+++ b/include/cmocka.h
@@ -304,9 +304,11 @@ void will_return(#function, LargestIntegralType value);
  *
  * @param[in]  value The value to be returned by mock().
  *
- * @param[in]  count The parameter returns the number of times the value should
- *                   be returned by mock(). If count is set to -1 the value will
- *                   always be returned.
+ * @param[in]  count The parameter indicates the number of times the value should
+ *                   be returned by mock(). If count is set to -1, the value 
+ *                   will always be returned but must be returned at least once.
+ *                   If count is set to -2, the value will always be returned
+ *                   by mock(), but is not required to be returned.
  *
  * @see mock()
  */
@@ -339,6 +341,33 @@ void will_return_always(#function, LargestIntegralType value);
     will_return_count(function, (value), -1)
 #endif
 
+#ifdef DOXYGEN
+/**
+ * @brief Store a value that may be always returned by mock().
+ *
+ * This stores a value which will always be returned by mock() but is not
+ * required to be returned by at least one call to mock(). Therefore,
+ * in contrast to will_return_always() which causes a test failure if it 
+ * is not returned at least once, will_return_maybe() will never cause a test
+ * to fail if its value is not returned.
+ *
+ * @param[in]  #function  The function which should return the given value.
+ *
+ * @param[in]  #value The value to be returned by mock().
+ *
+ * This is equivalent to:
+ * @code
+ * will_return_count(function, value, -2);
+ * @endcode
+ *
+ * @see will_return_count()
+ * @see mock()
+ */
+void will_return_maybe(#function, LargestIntegralType value);
+#else
+#define will_return_maybe(function, value) \
+    will_return_count(function, (value), -2)
+#endif
 /** @} */
 
 /**
diff --git a/src/cmocka.c b/src/cmocka.c
index 310073d..c84dce1 100644
--- a/src/cmocka.c
+++ b/src/cmocka.c
@@ -685,8 +685,10 @@ static int get_symbol_value(
             assert_true(return_value);
             *output = (void*) value_node->value;
             return_value = value_node->refcount;
-            if (--value_node->refcount == 0) {
+            if (value_node->refcount - 1 == 0) {
                 list_remove_free(value_node, NULL, NULL);
+            } else if (value_node->refcount > -2) {
+                --value_node->refcount;
             }
         } else {
             return_value = get_symbol_value(
@@ -939,7 +941,7 @@ void _will_return(const char * const function_name, const char * const file,
                   const int count) {
     SymbolValue * const return_value =
         (SymbolValue*)malloc(sizeof(*return_value));
-    assert_true(count > 0 || count == -1);
+    assert_true(count != 0);
     return_value->value = value;
     set_source_location(&return_value->location, file, line);
     add_symbol_value(&global_function_result_map_head, &function_name, 1,
@@ -1401,7 +1403,7 @@ static void expect_memory_setup(
         const void * const memory, const size_t size,
         const CheckParameterValue check_function, const int count) {
     CheckMemoryData * const check_data =
-	(CheckMemoryData*)malloc(sizeof(*check_data) + size);
+    (CheckMemoryData*)malloc(sizeof(*check_data) + size);
     void * const mem = (void*)(check_data + 1);
     declare_initialize_value_pointer_pointer(check_data_pointer, check_data);
     assert_non_null(memory);
@@ -1901,7 +1903,7 @@ static int display_allocated_blocks(const ListNode * const check_point) {
 
     for (node = check_point->next; node != head; node = node->next) {
         const MallocBlockInfo * const block_info =
-	    (const MallocBlockInfo*)node->value;
+            (const MallocBlockInfo*)node->value;
         assert_non_null(block_info);
 
         if (!allocated_blocks) {
@@ -2929,7 +2931,7 @@ int _run_tests(const UnitTest * const tests, const size_t number_of_tests) {
      * when a test setup occurs and popped on tear down.
      */
     TestState* test_states =
-	(TestState*)malloc(number_of_tests * sizeof(*test_states));
+        (TestState*)malloc(number_of_tests * sizeof(*test_states));
     /* The number of test states which should be 0 at the end */
     long number_of_test_states = 0;
     /* Names of the tests that failed. */
diff --git a/test_ordering_fail.c b/test_ordering_fail.c
new file mode 100644
index 0000000..652f5ad
--- /dev/null
+++ b/test_ordering_fail.c
@@ -0,0 +1,95 @@
+#include "config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <cmocka_private.h>
+
+static void mock_test_a_called(void)
+{
+    function_called();
+}
+
+static void mock_test_b_called(void)
+{
+    function_called();
+}
+
+static void mock_test_c_called(void)
+{
+    function_called();
+}
+
+static void test_does_fail_for_unexpected_call(void **state)
+{
+    (void)state;
+    expect_function_call(mock_test_a_called);
+    expect_function_call(mock_test_a_called);
+
+    mock_test_a_called();
+    mock_test_a_called();
+    mock_test_a_called();
+}
+
+static void test_does_fail_for_unmade_expected_call(void **state)
+{
+    (void)state;
+    expect_function_call(mock_test_a_called);
+    expect_function_call(mock_test_a_called);
+
+    mock_test_a_called();
+}
+
+static void test_ordering_fails_out_of_order(void **state)
+{
+    (void)state;
+    expect_function_call(mock_test_a_called);
+    expect_function_call(mock_test_b_called);
+    expect_function_call(mock_test_a_called);
+
+    mock_test_b_called();
+}
+
+static void test_ordering_fails_out_of_order_for_at_least_once_calls(void **state)
+{
+    (void)state;
+    expect_function_call_any(mock_test_a_called);
+    ignore_function_calls(mock_test_b_called);
+
+    mock_test_b_called();
+    mock_test_c_called();
+}
+
+/* Primarily used to test error message */
+static void test_fails_out_of_order_if_no_calls_found_on_any(void **state)
+{
+    (void)state;
+    expect_function_call_any(mock_test_a_called);
+    ignore_function_calls(mock_test_b_called);
+
+    mock_test_a_called();
+    mock_test_c_called();
+}
+
+static void test_fails_if_zero_count_used(void **state)
+{
+    (void)state;
+    expect_function_calls(mock_test_a_called, 0);
+
+    mock_test_a_called();
+}
+
+int main(void) {
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test(test_does_fail_for_unexpected_call)
+        ,cmocka_unit_test(test_does_fail_for_unmade_expected_call)
+        ,cmocka_unit_test(test_does_fail_for_unmade_expected_call)
+        ,cmocka_unit_test(test_ordering_fails_out_of_order)
+        ,cmocka_unit_test(test_ordering_fails_out_of_order_for_at_least_once_calls)
+        ,cmocka_unit_test(test_fails_out_of_order_if_no_calls_found_on_any)
+        ,cmocka_unit_test(test_fails_if_zero_count_used)
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index faaa604..2a496d2 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -18,7 +18,9 @@ set(CMOCKA_TESTS
     test_skip
     test_setup_fail
     test_ordering
-    test_ordering_fail)
+    test_ordering_fail
+    test_returns
+    test_returns_fail)
 
 foreach(_CMOCKA_TEST ${CMOCKA_TESTS})
     add_cmocka_test(${_CMOCKA_TEST} ${_CMOCKA_TEST}.c ${CMOCKA_STATIC_LIBRARY})
@@ -32,7 +34,7 @@ add_cmocka_test(test_cmockery test_cmockery.c ${CMOCKA_STATIC_LIBRARY})
 
 ### Exceptions
 
-# test_assert_macros_fail
+# test_skip
 set_tests_properties(
     test_skip
         PROPERTIES
@@ -56,6 +58,14 @@ set_tests_properties(
         "\\[  FAILED  \\] 7 test"
 )
 
+# test_returns_fail ensure proper failures
+set_tests_properties(
+    test_returns_fail
+        PROPERTIES
+        PASS_REGULAR_EXPRESSION
+        "\\[  FAILED  \\] 3 test"
+)
+
 # test_exception_handler
 if (WIN32)
     set_tests_properties(
diff --git a/tests/test_returns.c b/tests/test_returns.c
new file mode 100644
index 0000000..7248d05
--- /dev/null
+++ b/tests/test_returns.c
@@ -0,0 +1,69 @@
+#include "config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <cmocka_private.h>
+
+#include <stdlib.h>
+
+int mock_function(void);
+void mock_function_call_times(size_t times, int expectedValue);
+
+int mock_function(void)
+{
+  return (int) mock();
+}
+
+void mock_function_call_times(size_t times, int expectedValue)
+{
+    size_t i;
+    for (i = 0u; i < times; ++i)
+    {
+        assert_int_equal(expectedValue, mock_function());
+    }
+}
+
+static void test_will_return_maybe_for_no_calls(void **state)
+{
+    (void) state;
+
+    will_return_maybe(mock_function, 32);
+}
+
+static void test_will_return_maybe_for_one_mock_call(void **state)
+{
+    int value;
+
+    (void) state;
+
+    value = rand();
+    will_return_maybe(mock_function, value);
+    mock_function_call_times(1u, value);
+}
+
+static void test_will_return_maybe_for_more_than_one_call(void **state)
+{
+    int value;
+    size_t numberOfCalls;
+    (void)state;
+
+    value = rand();
+    numberOfCalls = (size_t) ((rand()) % 20 + 2);
+    will_return_maybe(mock_function, value);
+    mock_function_call_times(numberOfCalls, value);
+}
+
+int main(int argc, char **argv) {
+    const struct CMUnitTest alloc_tests[] = {
+        cmocka_unit_test(test_will_return_maybe_for_no_calls)
+        ,cmocka_unit_test(test_will_return_maybe_for_one_mock_call)
+        ,cmocka_unit_test(test_will_return_maybe_for_more_than_one_call)
+    };
+
+    (void)argc; 
+    (void)argv;
+
+    return cmocka_run_group_tests(alloc_tests, NULL, NULL);
+}
diff --git a/tests/test_returns_fail.c b/tests/test_returns_fail.c
new file mode 100644
index 0000000..2c1ef6d
--- /dev/null
+++ b/tests/test_returns_fail.c
@@ -0,0 +1,77 @@
+#include "config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <cmocka_private.h>
+
+#include <stdlib.h>
+
+int mock_function(void);
+void mock_function_call_times(size_t times, int expectedValue);
+
+int mock_function(void)
+{
+  return (int) mock();
+}
+
+void mock_function_call_times(size_t times, int expectedValue)
+{
+    size_t i;
+    for (i = 0u; i < times; ++i)
+    {
+        assert_int_equal(expectedValue, mock_function());
+    }
+}
+
+static void test_will_return_fails_for_no_calls(void **state)
+{
+    (void) state;
+
+    will_return(mock_function, 32);
+}
+
+static void test_will_return_count_fails_for_unreturned_items(void **state)
+{
+    int value;
+    size_t numberOfCalls;
+
+    (void) state;
+
+    value = rand();
+    numberOfCalls = (size_t) ((rand()) % 20 + 2);
+
+    will_return_count(mock_function, value, numberOfCalls);
+    mock_function_call_times(numberOfCalls - 1u, value);
+}
+
+static void test_will_return_always_fails_for_no_calls(void **state)
+{
+    int value;
+
+    (void) state;
+
+    value = rand();
+
+    will_return_always(mock_function, value);
+}
+
+static int teardown(void **state) {
+    free(*state);
+
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    const struct CMUnitTest alloc_tests[] = {
+        cmocka_unit_test_teardown(test_will_return_fails_for_no_calls, teardown)
+        ,cmocka_unit_test_teardown(test_will_return_count_fails_for_unreturned_items, teardown)
+        ,cmocka_unit_test_teardown(test_will_return_always_fails_for_no_calls, teardown)
+    };
+
+    (void)argc; 
+    (void)argv;
+
+    return cmocka_run_group_tests(alloc_tests, NULL, NULL);
+}
-- 
2.6.4