diff options
author | Jakub Hrozek <jhrozek@redhat.com> | 2013-06-03 16:42:20 +0200 |
---|---|---|
committer | Jakub Hrozek <jhrozek@redhat.com> | 2013-06-03 18:15:51 +0200 |
commit | 6f3fbb34d504d9951a4f405d7bbf53ae4be53f30 (patch) | |
tree | d2c99c7343a639da524eed0c057487f058e846bf /example | |
parent | 0cdc214542001dab61855ddf6fd75322e8934d0d (diff) | |
download | cmocka-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.txt | 3 | ||||
-rw-r--r-- | example/chef_wrap/CMakeLists.txt | 19 | ||||
-rw-r--r-- | example/chef_wrap/chef.c | 54 | ||||
-rw-r--r-- | example/chef_wrap/chef.h | 19 | ||||
-rw-r--r-- | example/chef_wrap/waiter_test_wrap.c | 170 | ||||
-rw-r--r-- | example/chef_wrap/waiter_test_wrap.h | 2 |
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); |