From 91529a777fd3f56aeadef19dc25e196ba2e1cc51 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Mon, 7 Sep 2020 19:58:45 +0200 Subject: [HICN-636] Add gtest framework and first tests for loop. Signed-off-by: Mauro Sardara Change-Id: I133c8adda125c430aa9c4a35fb63bf1d8585afe9 Signed-off-by: Mauro Sardara --- cmake/Modules/BuildMacros.cmake | 14 - cmake/Modules/GTestImport.cmake | 32 +++ hicn-light/CMakeLists.txt | 12 +- hicn-light/src/hicn/CMakeLists.txt | 4 +- hicn-light/src/hicn/base/CMakeLists.txt | 4 + hicn-light/src/hicn/base/loop.c | 1 - hicn-light/src/hicn/base/test/CMakeLists.txt | 25 ++ hicn-light/src/hicn/base/test/test-loop.cc | 291 +++++++++++++++++++++ .../src/hicn/command_line/daemon/CMakeLists.txt | 2 +- 9 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 cmake/Modules/GTestImport.cmake create mode 100644 hicn-light/src/hicn/base/test/CMakeLists.txt create mode 100644 hicn-light/src/hicn/base/test/test-loop.cc diff --git a/cmake/Modules/BuildMacros.cmake b/cmake/Modules/BuildMacros.cmake index d360d26af..ed95259b2 100644 --- a/cmake/Modules/BuildMacros.cmake +++ b/cmake/Modules/BuildMacros.cmake @@ -217,19 +217,5 @@ macro(build_library lib) endif() endmacro() -add_custom_target(${PROJECT_NAME}_cleanup_profiling_data - "find" "." "-name" "*.gcda" "-delete" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Cleanup previous profiling data." -) - -macro(AddTest testFile) - add_executable(${ARGV0} ${ARGV0}.cc) - target_link_libraries(${ARGV0} ${TARGET_TRANSPORT_STATIC} ${GTEST_LIBRARIES}) - add_test(${ARGV0} ${ARGV0}) - set_target_properties(${ARGV0} PROPERTIES FOLDER Test) - add_dependencies(${ARGV0} ${PROJECT_NAME}_cleanup_profiling_data) -endmacro(AddTest) - include(IosMacros) include(WindowsMacros) diff --git a/cmake/Modules/GTestImport.cmake b/cmake/Modules/GTestImport.cmake new file mode 100644 index 000000000..938dec786 --- /dev/null +++ b/cmake/Modules/GTestImport.cmake @@ -0,0 +1,32 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# 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. + +################################## +# Download and install GoogleTest + +include(ExternalProject) +ExternalProject_Add(gtest + URL https://github.com/google/googletest/archive/v1.10.x.zip + PREFIX ${CMAKE_BINARY_DIR}/gtest + INSTALL_COMMAND "" +) + +ExternalProject_Get_Property(gtest source_dir binary_dir) + +message (STATUS "GTest include dir: ${source_dir}/googlemock/include ${source_dir}/googletest/include)") +message (STATUS "GTest libs: ${binary_dir}/lib/libgmock_main.a ${binary_dir}/lib/libgmock.a ${binary_dir}/lib/libgtest_main.a ${binary_dir}/lib/libgtest.a") + +set(GTEST_INCLUDE_DIRS ${source_dir}/googlemock/include ${source_dir}/googletest/include) +set(GTEST_LIBRARIES ${binary_dir}/lib/libgmock_main.a ${binary_dir}/lib/libgmock.a ${binary_dir}/lib/libgtest_main.a ${binary_dir}/lib/libgtest.a) + +enable_testing() \ No newline at end of file diff --git a/hicn-light/CMakeLists.txt b/hicn-light/CMakeLists.txt index dd7d7fcc2..5d0d59b43 100644 --- a/hicn-light/CMakeLists.txt +++ b/hicn-light/CMakeLists.txt @@ -84,9 +84,9 @@ find_package(Threads REQUIRED) set(LIBHICN_LIGHT hicn-light) set(LIBHICN_LIGHT_STATIC ${LIBHICN_LIGHT}.static) +set(LIBHICN_LIGHT_SHARED ${LIBHICN_LIGHT}.shared) set(HICN_LIGHT_LINK_LIBRARIES - ${LIBHICN_LIGHT_STATIC} ${HICN_LIBRARIES} ${LIBEVENT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} @@ -114,6 +114,16 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") message(STATUS "Set \"-undefined dynamic_lookup\" for shared libraries") endif() +if (BUILD_TESTS) + include (GTestImport) + + if(${CMAKE_VERSION} VERSION_GREATER "3.10.0") + include (GoogleTest) + else() + include (CTest) + endif() +endif() + add_subdirectory(src/hicn) # Install service file in linux systems diff --git a/hicn-light/src/hicn/CMakeLists.txt b/hicn-light/src/hicn/CMakeLists.txt index 29b76da25..4ecd5e2a0 100644 --- a/hicn-light/src/hicn/CMakeLists.txt +++ b/hicn-light/src/hicn/CMakeLists.txt @@ -65,10 +65,10 @@ else() endif() build_library(${LIBHICN_LIGHT} - STATIC ${LIB_BUILD_TYPE} + STATIC SHARED ${LIB_BUILD_TYPE} SOURCES ${SOURCE_FILES} INSTALL_HEADERS ${TO_INSTALL_HEADER_FILES} - LINK_LIBRARIES ${LIBRARIES} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} DEPENDS ${DEPENDENCIES} COMPONENT ${HICN_LIGHT} INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} diff --git a/hicn-light/src/hicn/base/CMakeLists.txt b/hicn-light/src/hicn/base/CMakeLists.txt index 5b2677ed1..6e5ed4a3e 100644 --- a/hicn-light/src/hicn/base/CMakeLists.txt +++ b/hicn-light/src/hicn/base/CMakeLists.txt @@ -36,3 +36,7 @@ set(TO_INSTALL_HEADER_FILES ${HEADER_FILES} PARENT_SCOPE ) + +if (BUILD_TESTS) + add_subdirectory(test) +endif() \ No newline at end of file diff --git a/hicn-light/src/hicn/base/loop.c b/hicn-light/src/hicn/base/loop.c index 34aa94d6e..6e477f85a 100644 --- a/hicn-light/src/hicn/base/loop.c +++ b/hicn-light/src/hicn/base/loop.c @@ -156,7 +156,6 @@ ERR_EVENT_ADD: int loop_event_unregister(event_t *event) { - assert(event->event_type == EVTYPE_FD); event_del(&event->raw_event); return 0; } diff --git a/hicn-light/src/hicn/base/test/CMakeLists.txt b/hicn-light/src/hicn/base/test/CMakeLists.txt new file mode 100644 index 000000000..03c5a89b5 --- /dev/null +++ b/hicn-light/src/hicn/base/test/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. + +include(BuildMacros) + +list(APPEND TESTS + test-loop +) + +foreach(test ${TESTS}) + build_executable(${test} + NO_INSTALL + SOURCES ${test}.cc + LINK_LIBRARIES ${LIBHICN_LIGHT_SHARED} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} + DEPENDS gtest ${LIBHICN_LIGHT_SHARED} + COMPONENT ${HICN_LIGHT} + DEFINITIONS "${COMPILER_DEFINITIONS}" + ) + + if(${CMAKE_VERSION} VERSION_GREATER "3.10.0") + gtest_discover_tests(${test}-bin TEST_PREFIX new:) + else() + add_test(NAME ${test}-bin COMMAND ${test}) + endif() +endforeach() diff --git a/hicn-light/src/hicn/base/test/test-loop.cc b/hicn-light/src/hicn/base/test/test-loop.cc new file mode 100644 index 000000000..44684ef0e --- /dev/null +++ b/hicn-light/src/hicn/base/test/test-loop.cc @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * 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 + +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +class LoopTest : public ::testing::Test { + static constexpr uint16_t BUFFER_SIZE = 1024; + protected: + LoopTest() + : server_port_(9191), + loop_(nullptr), + timer_tick_(2000), + connection_socket_(-1), + event_(nullptr), + timer_(nullptr) { + } + + virtual ~LoopTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + static int onFirstTimerExpiration(void *owner, int fd, void* arg) { + std::cout << "This function should never be called" << std::endl; + EXPECT_TRUE(false); + return -1; + } + + static int onSecondTimerExpiration(void *owner, int fd, void* arg) { + std::cout << "First timer expired. Cancel second timer." << std::endl; + LoopTest *test = (LoopTest *)(arg); + loop_event_unregister(test->timer_); + return 0; + } + + static int onTimerExpiration(void *owner, int fd, void* arg) { + // Create client socket + struct sockaddr_in addr; + int client_socket; + LoopTest *test = (LoopTest *)(arg); + + client_socket = socket(AF_INET, SOCK_STREAM, 0); + if (client_socket == -1) { + perror("socket"); + return -1; + } + + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_family = AF_INET; + addr.sin_port = htons(test->server_port_); + + if (connect(client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { + perror("connect"); + return -1; + } + + if (send(client_socket, "Hello, world!\n", 14, 0) == -1){ + perror("send"); + return -1; + } + + close(client_socket); + loop_event_unregister(test->timer_); + + return 0; + } + + static int onNewConnection(void *owner, int fd, void* arg) { + LoopTest *test = (LoopTest *)arg; + struct sockaddr_in addr; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_family = AF_INET; + + socklen_t addr_len = sizeof(struct sockaddr_in); + int ret; + + int client_fd = accept(test->connection_socket_, (struct sockaddr*)(&addr), &addr_len); + if (client_fd == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + fprintf(stderr, "accept failed"); + } + + perror("accept"); + return -1; + } + + // Read whatever data available and close connection. + ret = read(client_fd, test->buffer, BUFFER_SIZE); + if (ret < 0) { + perror("read"); + return -1; + } + + test->buffer[ret] = '\0'; + std::cout << "Received: " << (char*)test->buffer << std::endl; + + close(client_fd); + loop_event_unregister(test->event_); + + return 0; + } + + void createTcpSocketServer() { + struct sockaddr_in addr; + int ret; + + /* Create local socket. */ + + connection_socket_ = socket(AF_INET, SOCK_STREAM, 0); + if (connection_socket_ == -1) { + perror("socket"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(server_port_); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(connection_socket_, (const struct sockaddr *) &addr, + sizeof(struct sockaddr_in)); + if (ret == -1) { + perror("bind"); + return; + } + + ret = listen(connection_socket_, 20); + if (ret == -1) { + perror("listen"); + return; + } +} + + uint16_t server_port_; + loop_t *loop_; + unsigned timer_tick_; + int connection_socket_; + event_t *event_; + event_t *timer_; + char buffer[BUFFER_SIZE]; +}; + +TEST_F(LoopTest, LoopCreate) +{ + loop_ = loop_create(); + EXPECT_TRUE(loop_ != NULL); +} + +TEST_F(LoopTest, LoopFree) +{ + loop_ = loop_create(); + loop_free (loop_); + EXPECT_TRUE(loop_ != NULL); +} + +TEST_F(LoopTest, EventCreateAndFree) +{ + int ret; + + // Fake fd + int fd = 1; + loop_ = loop_create(); + + ret = loop_fd_event_create(&event_, loop_, fd, nullptr, &LoopTest::onNewConnection, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(event_); + + // Register the event + ret = loop_fd_event_register(event_); + EXPECT_TRUE(ret >= 0); + + // Unregister the event + ret = loop_event_unregister(event_); + EXPECT_TRUE(ret >= 0); + + // Free event loop + loop_free (loop_); +} + +TEST_F(LoopTest, TimerCreateAndCancel) +{ + int ret; + event_t *timer2; + loop_ = loop_create(); + + ret = loop_timer_create(&timer_, loop_, nullptr, &LoopTest::onFirstTimerExpiration, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(timer_); + + ret = loop_timer_create(&timer2, loop_, nullptr, &LoopTest::onSecondTimerExpiration, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(timer2); + + // Register the 1st timer + ret = loop_timer_register(timer_, timer_tick_); + EXPECT_TRUE(ret >= 0); + + // Register the 2nd timer + ret = loop_timer_register(timer2, timer_tick_ / 2); + EXPECT_TRUE(ret >= 0); + + loop_dispatch(loop_); + + loop_undispatch(loop_); + + // Unregister the events + ret = loop_event_unregister(timer_); + ret += loop_event_unregister(timer2); + EXPECT_TRUE(ret >= 0); + + // Free event loop + loop_free (loop_); +} + +TEST_F(LoopTest, LoopDispatch) +{ + int ret; + + // Create new unix socket + createTcpSocketServer(); + loop_ = loop_create(); + + ret = loop_fd_event_create(&event_, loop_, connection_socket_, nullptr, &LoopTest::onNewConnection, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(event_); + + ret = loop_fd_event_register(event_); + EXPECT_TRUE(ret >= 0); + + // Create timer. + ret = loop_timer_create(&timer_, loop_, nullptr, &LoopTest::onTimerExpiration, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(timer_); + + ret = loop_timer_register(timer_, timer_tick_); + EXPECT_TRUE(ret >= 0); + + // Start event dispatching + loop_dispatch(loop_); + + // Stop dispatching + loop_undispatch(loop_); + + // Free events + loop_event_free(timer_); + loop_event_free(event_); + + // Free event loop + loop_free (loop_); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/hicn-light/src/hicn/command_line/daemon/CMakeLists.txt b/hicn-light/src/hicn/command_line/daemon/CMakeLists.txt index 1ab8a4e6f..8606c8f89 100644 --- a/hicn-light/src/hicn/command_line/daemon/CMakeLists.txt +++ b/hicn-light/src/hicn/command_line/daemon/CMakeLists.txt @@ -17,7 +17,7 @@ list(APPEND DAEMON_SRC if (NOT DISABLE_EXECUTABLES) build_executable(${HICN_LIGHT_DAEMON} SOURCES ${DAEMON_SRC} - LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + LINK_LIBRARIES ${LIBHICN_LIGHT_STATIC} DEPENDS ${LIBHICN_LIGHT_STATIC} COMPONENT ${HICN_LIGHT} DEFINITIONS ${COMPILER_DEFINITIONS} -- cgit 1.2.3-korg