cmocka: Add 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.

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
diff --git a/include/cmocka.h b/include/cmocka.h
index 6242ff2..fecd873 100644
--- a/include/cmocka.h
+++ b/include/cmocka.h
@@ -304,9 +304,11 @@
  *
  * @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 @@
     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 4f13f21..376acba 100644
--- a/src/cmocka.c
+++ b/src/cmocka.c
@@ -689,8 +689,10 @@
             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(
@@ -943,7 +945,7 @@
                   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,
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 4b015ba..0422487 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -19,7 +19,9 @@
     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})
@@ -33,7 +35,7 @@
 
 ### Exceptions
 
-# test_assert_macros_fail
+# test_skip
 set_tests_properties(
     test_skip
         PROPERTIES
@@ -57,6 +59,14 @@
         "\\[  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..b9370c9
--- /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..81197d3
--- /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);
+}