aboutsummaryrefslogtreecommitdiff
path: root/example
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2013-06-03 16:42:20 +0200
committerJakub Hrozek <jhrozek@redhat.com>2013-06-03 18:15:51 +0200
commit6f3fbb34d504d9951a4f405d7bbf53ae4be53f30 (patch)
treed2c99c7343a639da524eed0c057487f058e846bf /example
parent0cdc214542001dab61855ddf6fd75322e8934d0d (diff)
downloadcmocka-6f3fbb34d504d9951a4f405d7bbf53ae4be53f30.tar.gz
cmocka-6f3fbb34d504d9951a4f405d7bbf53ae4be53f30.tar.xz
cmocka-6f3fbb34d504d9951a4f405d7bbf53ae4be53f30.zip
example: wrap trick
Adds a new example that illustrates the gcc wrap trick.
Diffstat (limited to 'example')
-rw-r--r--example/CMakeLists.txt3
-rw-r--r--example/chef_wrap/CMakeLists.txt19
-rw-r--r--example/chef_wrap/chef.c54
-rw-r--r--example/chef_wrap/chef.h19
-rw-r--r--example/chef_wrap/waiter_test_wrap.c170
-rw-r--r--example/chef_wrap/waiter_test_wrap.h2
6 files changed, 267 insertions, 0 deletions
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index a060de1..d03c40a 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -68,6 +68,9 @@ set_tests_properties(
"\\[ FAILED \\] 2 test"
)
+if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)")
+ add_subdirectory(chef_wrap)
+endif()
add_executable(run_tests run_tests.c)
target_link_libraries(run_tests ${CMOCKA_SHARED_LIBRARY})
diff --git a/example/chef_wrap/CMakeLists.txt b/example/chef_wrap/CMakeLists.txt
new file mode 100644
index 0000000..b6bd6b5
--- /dev/null
+++ b/example/chef_wrap/CMakeLists.txt
@@ -0,0 +1,19 @@
+project(cmocka-wrap-examples C CXX)
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMOCKA_PUBLIC_INCLUDE_DIRS}
+)
+
+add_definitions(-DUNIT_TESTING=1)
+
+add_executable(waiter_test_wrap waiter_test_wrap.c chef.c)
+target_link_libraries(waiter_test_wrap ${CMOCKA_SHARED_LIBRARY})
+
+add_test(waiter_test_wrap ${CMAKE_CURRENT_BINARY_DIR}/waiter_test_wrap)
+
+set_target_properties(waiter_test_wrap
+ PROPERTIES
+ LINK_FLAGS "-Wl,--wrap=chef_cook"
+)
diff --git a/example/chef_wrap/chef.c b/example/chef_wrap/chef.c
new file mode 100644
index 0000000..1429cde
--- /dev/null
+++ b/example/chef_wrap/chef.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 (c) Andreas Schneider <asn@cynapses.org>
+ * Jakub Hrozek <jakub.hrozek@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "chef.h"
+
+
+/* This is the real chef, just not implemented yet, currently it always
+ * returns ENOSYS
+ */
+int chef_cook(const char *order, char **dish_out)
+{
+ if (order == NULL || dish_out == NULL) return EINVAL;
+
+ return -ENOSYS;
+}
+
+/* Print chef return codes as string */
+const char *chef_strerror(int error)
+{
+ switch (error) {
+ case 0:
+ return "Success";
+ case -1:
+ return "Unknown dish";
+ case -2:
+ return "Not enough ingredients for the dish";
+ }
+
+ return "Unknown error!";
+}
+
diff --git a/example/chef_wrap/chef.h b/example/chef_wrap/chef.h
new file mode 100644
index 0000000..c1a01c7
--- /dev/null
+++ b/example/chef_wrap/chef.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 (c) Andreas Schneider <asn@cynapses.org>
+ * Jakub Hrozek <jakub.hrozek@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+int chef_cook(const char *order, char **dish_out);
+const char *chef_strerror(int error);
diff --git a/example/chef_wrap/waiter_test_wrap.c b/example/chef_wrap/waiter_test_wrap.c
new file mode 100644
index 0000000..01afa15
--- /dev/null
+++ b/example/chef_wrap/waiter_test_wrap.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2013 (c) Andreas Schneider <asn@cynapses.org>
+ * Jakub Hrozek <jakub.hrozek@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "waiter_test_wrap.h"
+#include "chef.h"
+
+/*
+ * This is a mocked Chef object. A real Chef would look if he knows
+ * the dish in some kind of internal database and check his storage for
+ * ingredients. This chef simply retrieves this information from the test
+ * that is calling him.
+ *
+ * This object is also wrapped - if any code links with this file and is
+ * compiled with linker option --wrap chef_cook, any calls of that code to
+ * chef_cook will end up calling __wrap_chef_cook.
+ *
+ * If for any reason the wrapped function wanted to call the real chef_cook()
+ * function, it could do so by calling the special symbol __real_chef_cook().
+ *
+ * Please note that when setting return codes for the chef_cook function, we
+ * use this wrapper as a parameter for the will_return() macro, not the
+ * real function.
+ *
+ * A chef object would return:
+ * 0 - cooking dish went fine
+ * -1 - unknown dish
+ * -2 - ran out of ingredients for the dish
+ * any other error code -- unexpected error while cooking
+ *
+ * The return codes should be consistent between the real and mocked objects.
+ */
+int __wrap_chef_cook(const char *order, char **dish_out)
+{
+ bool has_ingredients;
+ bool knows_dish;
+ char *dish;
+
+ check_expected(order);
+
+ knows_dish = (bool) mock();
+ if (knows_dish == false) {
+ return -1;
+ }
+
+ has_ingredients = (bool) mock();
+ if (has_ingredients == false) {
+ return -2;
+ }
+
+ dish = (char *) mock();
+ *dish_out = strdup(dish);
+ if (*dish_out == NULL) return ENOMEM;
+
+ return mock();
+}
+
+/* Waiter return codes:
+ * 0 - success
+ * -1 - kitchen failed
+ * -2 - kitchen succeeded, but cooked a different food
+ */
+static int waiter_process(const char *order, char **dish)
+{
+ int rv;
+
+ rv = chef_cook(order, dish);
+ if (rv != 0) {
+ fprintf(stderr, "Chef couldn't cook %s: %s\n",
+ order, chef_strerror(rv));
+ return -1;
+ }
+
+ /* Check if we received the dish we wanted from the kitchen */
+ if (strcmp(order, *dish) != 0) {
+ *dish = NULL;
+ return -2;
+ }
+
+ return 0;
+}
+
+static void test_order_hotdog(void **state)
+{
+ (void) state; /* unused */
+
+ int rv;
+ char *dish;
+
+ /* We expect the chef to receive an order for a hotdog */
+ expect_string(__wrap_chef_cook, order, "hotdog");
+ /* And we tell the test chef that ke knows how to cook a hotdog
+ * and has the ingredients
+ */
+ will_return(__wrap_chef_cook, true);
+ will_return(__wrap_chef_cook, true);
+ /* The result will be a hotdog and the cooking process will succeed */
+ will_return(__wrap_chef_cook, "hotdog");
+ will_return(__wrap_chef_cook, 0);
+
+ /* Test the waiter */
+ rv = waiter_process("hotdog", &dish);
+
+ /* We expect the cook to succeed cooking the hotdog */
+ assert_int_equal(rv, 0);
+ /* And actually receive one */
+ assert_string_equal(dish, "hotdog");
+}
+
+static void test_bad_dish(void **state)
+{
+ (void) state; /* unused */
+
+ int rv;
+ char *dish;
+
+ /* We expect the chef to receive an order for a hotdog */
+ expect_string(__wrap_chef_cook, order, "hotdog");
+ /* And we tell the test chef that ke knows how to cook a hotdog
+ * and has the ingredients
+ */
+ will_return(__wrap_chef_cook, true);
+ will_return(__wrap_chef_cook, true);
+ /* The result will be a burger and the cooking process will succeed.
+ * We expect the waiter to handle the bad dish and return an error
+ * code
+ */
+ will_return(__wrap_chef_cook, "burger");
+ will_return(__wrap_chef_cook, 0);
+
+ /* Test the waiter */
+ rv = waiter_process("hotdog", &dish);
+
+ /* According to the documentation the waiter should return -2 now */
+ assert_int_equal(rv, -2);
+ /* And do not give the bad dish to the customer */
+ assert_null(dish);
+}
+
+int main(void)
+{
+ const UnitTest tests[] = {
+ unit_test(test_order_hotdog),
+ unit_test(test_bad_dish),
+ };
+
+ return run_tests(tests);
+}
diff --git a/example/chef_wrap/waiter_test_wrap.h b/example/chef_wrap/waiter_test_wrap.h
new file mode 100644
index 0000000..9178ca2
--- /dev/null
+++ b/example/chef_wrap/waiter_test_wrap.h
@@ -0,0 +1,2 @@
+
+int __wrap_chef_cook(const char *order, char **dish_out);