diff --git a/.gitignore b/.gitignore index 67e45bd1e..46ad960ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,59 @@ *.swp bazel-* compile_commands.json +*.exe +*.ilk +*.pdb +*.obj +*.tlog +*.idb +*.recipe +*.stamp +*.suo +*.db* +*.hpp +third_party/boost/ +third_party/muduo/ +third_party/protobuf/ +third_party/zlib/ +output/ +lib/ +bin/ +x64/ +.fc-cache/ +.vs/ +vs.mk +gtestd.lib +gtest.lib +external +build +lib +*.suo +*.db +*.db-shm +*.db-wal +*.pch +*.rule +*.bin +*.exe +*.tlog +*.ipch +*.log +*.list + +# CMake and FetchContent generated directories +build*/ +cmake-build-*/ + +# Fetched/downloaded third-party directories in workspace root +boost/ +googletest/ +protobuf/ +zlib/ +*-download/ +*-build/ + +# Local extracted/auxiliary directories (do not commit) +libs/ +more/ +tools/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6071a4ec8..d9c48fd7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,102 +1,162 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.5...3.31) + +if(POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() project(muduo C CXX) -enable_testing() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release") +if(MSVC) + add_compile_options(/std:c++latest) endif() -# only build examples if this is the main project -if(CMAKE_PROJECT_NAME STREQUAL "muduo") - option(MUDUO_BUILD_EXAMPLES "Build Muduo examples" ON) -endif() +# Keep FetchContent cache stable across different build directories. +set(FETCHCONTENT_BASE_DIR "${CMAKE_SOURCE_DIR}/.fc-cache" CACHE PATH "FetchContent shared cache" FORCE) +include(FetchContent) -set(CXX_FLAGS - -g - # -DVALGRIND - -DCHECK_PTHREAD_RETURN_VALUE - -D_FILE_OFFSET_BITS=64 - -Wall - -Wextra - -Werror - -Wconversion - -Wno-unused-parameter - -Wold-style-cast - -Woverloaded-virtual - -Wpointer-arith - -Wshadow - -Wwrite-strings - -march=native - # -MMD - -std=c++11 - -rdynamic - ) -if(CMAKE_BUILD_BITS EQUAL 32) - list(APPEND CXX_FLAGS "-m32") -endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - list(APPEND CXX_FLAGS "-Wno-null-dereference") - list(APPEND CXX_FLAGS "-Wno-sign-conversion") - list(APPEND CXX_FLAGS "-Wno-unused-local-typedef") - list(APPEND CXX_FLAGS "-Wthread-safety") - list(REMOVE_ITEM CXX_FLAGS "-rdynamic") -endif() -string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") +# Release tags (can be overridden with -Dxxx_TAG=...) +set(BOOST_TAG "boost-1.90.0" CACHE STRING "Boost release tag") +set(GTEST_TAG "v1.14.0" CACHE STRING "googletest release tag") +set(PROTOBUF_TAG "v3.21.12" CACHE STRING "protobuf release tag") +set(ZLIB_TAG "v1.3.1" CACHE STRING "zlib release tag") -set(CMAKE_CXX_FLAGS_DEBUG "-O0") -set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") -set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) -set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) +set(BOOST_LOCAL_DIR "${CMAKE_SOURCE_DIR}/boost") +set(GTEST_LOCAL_DIR "${CMAKE_SOURCE_DIR}/googletest") +set(PROTOBUF_LOCAL_DIR "${CMAKE_SOURCE_DIR}/protobuf") +set(ZLIB_LOCAL_DIR "${CMAKE_SOURCE_DIR}/zlib") -find_package(Boost REQUIRED) -find_package(Protobuf) -find_package(CURL) -find_package(ZLIB) -find_path(CARES_INCLUDE_DIR ares.h) -find_library(CARES_LIBRARY NAMES cares) -find_path(MHD_INCLUDE_DIR microhttpd.h) -find_library(MHD_LIBRARY NAMES microhttpd) -find_library(BOOSTTEST_LIBRARY NAMES boost_unit_test_framework) -find_library(BOOSTPO_LIBRARY NAMES boost_program_options) -find_library(BOOSTSYSTEM_LIBRARY NAMES boost_system) -find_path(TCMALLOC_INCLUDE_DIR gperftools/heap-profiler.h) -find_library(TCMALLOC_LIBRARY NAMES tcmalloc_and_profiler) -find_path(HIREDIS_INCLUDE_DIR hiredis/hiredis.h) -find_library(HIREDIS_LIBRARY NAMES hiredis) -find_path(GD_INCLUDE_DIR gd.h) -find_library(GD_LIBRARY NAMES gd) -find_program(THRIFT_COMPILER thrift) -find_path(THRIFT_INCLUDE_DIR thrift) -find_library(THRIFT_LIBRARY NAMES thrift) - -if(CARES_INCLUDE_DIR AND CARES_LIBRARY) - message(STATUS "found cares") -endif() -if(CURL_FOUND) - message(STATUS "found curl") +if(EXISTS "${BOOST_LOCAL_DIR}") + message(STATUS "Using local boost source: ${BOOST_LOCAL_DIR}") + FetchContent_Declare(boost SOURCE_DIR "${BOOST_LOCAL_DIR}") +else() + FetchContent_Declare(boost + URL https://archives.boost.io/release/1.90.0/source/boost_1_90_0.zip + ) endif() -if(PROTOBUF_FOUND) - message(STATUS "found protobuf") + +if(EXISTS "${GTEST_LOCAL_DIR}/CMakeLists.txt") + message(STATUS "Using local googletest source: ${GTEST_LOCAL_DIR}") + FetchContent_Declare(googletest SOURCE_DIR "${GTEST_LOCAL_DIR}") +else() + FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG ${GTEST_TAG} + GIT_SHALLOW TRUE + ) endif() -if(TCMALLOC_INCLUDE_DIR AND TCMALLOC_LIBRARY) - message(STATUS "found tcmalloc") + +if(EXISTS "${PROTOBUF_LOCAL_DIR}/CMakeLists.txt") + message(STATUS "Using local protobuf source: ${PROTOBUF_LOCAL_DIR}") + FetchContent_Declare(protobuf SOURCE_DIR "${PROTOBUF_LOCAL_DIR}") +else() + FetchContent_Declare(protobuf + GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git + GIT_TAG ${PROTOBUF_TAG} + GIT_SHALLOW TRUE + ) endif() -if(ZLIB_FOUND) - message(STATUS "found zlib") + +if(EXISTS "${ZLIB_LOCAL_DIR}/CMakeLists.txt") + message(STATUS "Using local zlib source: ${ZLIB_LOCAL_DIR}") + FetchContent_Declare(zlib SOURCE_DIR "${ZLIB_LOCAL_DIR}") +else() + FetchContent_Declare(zlib + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG ${ZLIB_TAG} + GIT_SHALLOW TRUE + ) endif() -if(HIREDIS_INCLUDE_DIR AND HIREDIS_LIBRARY) - message(STATUS "found hiredis") + +# Protobuf defaults bring its own gmock/gtest tests, which conflicts with +# the separately fetched googletest in this workspace. +set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE) +if(MSVC) + # Keep protobuf CRT consistent with this workspace (/MD). + set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "" FORCE) endif() -if(GD_INCLUDE_DIR AND GD_LIBRARY) - message(STATUS "found gd") + +FetchContent_MakeAvailable(boost googletest protobuf zlib) + +# Prevent GoogleTest from overriding our compiler/linker options +# when building with Visual Studio +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +enable_testing() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") endif() -if(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY) - message(STATUS "found thrift") + +if (MSVC) + + # Build with multiple processes + add_definitions(/MP) + add_definitions( /wd4068 /wd4996 /wd4244) + # Allow big object + add_definitions(/bigobj) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ") + set(CompilerFlags + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() + +else (MSVC) + + set(CXX_FLAGS + -g + # -DVALGRIND + # -DMUDUO_STD_STRING + -DCHECK_PTHREAD_RETURN_VALUE + -D_FILE_OFFSET_BITS=64 + -Wall + -Wextra + -Werror + -Wconversion + -Wno-unused-parameter + #-Wold-style-cast + -Woverloaded-virtual + -Wpointer-arith + -Wshadow + -Wwrite-strings + -march=native + -rdynamic + -pthread + -lpthread + -D_GLIBCXX_USE_DEPRECATED=0 + ) + +if(CMAKE_BUILD_BITS EQUAL 64) + list(APPEND CXX_FLAGS "-m64") endif() -include_directories(${Boost_INCLUDE_DIRS}) +string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") +set(CMAKE_CXX_COMPILER "g++") +set(CMAKE_CXX_FLAGS_DEBUG "-O0") +set(CMAKE_CXX_FLAGS_RELEASE "-O2 -finline-limit=1000 -DNDEBUG") +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) +set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) +endif (MSVC) + +include_directories(contrib/boost/boost) +include_directories(contrib/windows/include) +include_directories(${boost_SOURCE_DIR}) +include_directories(${googletest_SOURCE_DIR}/googletest/include) +include_directories(${googletest_SOURCE_DIR}/googlemock/include) +include_directories(${protobuf_SOURCE_DIR}/src) +include_directories(muduo/net/protorpc) +include_directories(${zlib_SOURCE_DIR}) +include_directories("${zlib_BINARY_DIR}") include_directories(${PROJECT_SOURCE_DIR}) @@ -105,13 +165,18 @@ message(STATUS "CXX_FLAGS = " ${CMAKE_CXX_FLAGS} " " ${CMAKE_CXX_FLAGS_${BUILD_T add_subdirectory(muduo/base) add_subdirectory(muduo/net) +add_subdirectory(examples/protobuf) -if(MUDUO_BUILD_EXAMPLES) - add_subdirectory(contrib) - add_subdirectory(examples) -else() - if(CARES_INCLUDE_DIR AND CARES_LIBRARY) - add_subdirectory(examples/cdns) - endif() +# Merge muduo_base.lib + muduo_net.lib into a single muduo.lib +if(MSVC) + set(muduo_combined_output "${CMAKE_BINARY_DIR}/lib/$/muduo.lib") + add_custom_target(muduo ALL + COMMAND "${CMAKE_AR}" /NOLOGO + "/OUT:${muduo_combined_output}" + "$" + "$" + DEPENDS muduo_base muduo_net + COMMENT "Merging muduo_base.lib and muduo_net.lib into muduo.lib" + VERBATIM + ) endif() - diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..fd45e3c5d --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +export BUILD_DIR +export CXXFLAGS +export DEFINEDS +export DEPEND_LIBS +export DEPEND_LIBS_DIR +export INCLUDES +export SOURCES + +OBJS = $(addprefix $(BUILD_DIR)/,$(patsubst %.cpp,%.o,$(notdir $(SOURCES)))) +OBJS = $(addprefix $(BUILD_DIR)/,$(patsubst %.cc,%.o,$(notdir $(SOURCES)))) + +DEPS = $(OBJS:.o=.d) + +CXXFLAGS := -pthread -lpthread --std=c++11 -Wall -rdynamic -O0 -D_GLIBCXX_USE_DEPRECATED=0 + +include vs.mk + + +$(exe):$(OBJS) + $(CXX) $(OBJS) $(CXXFLAGS) $(DEFINEDS) $(INCLUDES) -o $@ $(DEPEND_LIBS) $(DEPEND_LIBS_DIR) + +$(lib):$(OBJS) + $(AR) -rcs $@ $(OBJS) + +$(shared): + $(CXX) -shared -fPIC $(CXXFLAGS) $(DEFINEDS) $(INCLUDES) $(SOURCES) -o $@ + +clean: + @rm -rf $(OBJS) $(exec) $(lib) $(shared) $(BUILD_DIR) + +#http://stackoverflow.com/questions/1963802/makefile-pattern-rules-and-directories +define define_obj_rules +$(BUILD_DIR)/%.o: $(1)%.cc + @mkdir -p $$(@D) + $(CXX) -c $(CXXFLAGS) $(DEFINEDS) $(INCLUDES) $$< -o $$@ +$(BUILD_DIR)/%.o: $(1)%.cpp + @mkdir -p $$(@D) + $(CXX) -c $(CXXFLAGS) $(DEFINEDS) $(INCLUDES) $$< -o $$@ +endef + +$(foreach directory,$(sort $(dir $(SOURCES))),$(eval $(call define_obj_rules,$(directory)))) diff --git a/README.md b/README.md new file mode 100644 index 000000000..4a1468f72 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# muduo windows old version from chensuo's muduo \ No newline at end of file diff --git a/autogen.sh b/autogen.sh new file mode 100644 index 000000000..0420211f8 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# Run this script to generate the configure script and other files that will +# be included in the distribution. These files are not checked in because they +# are automatically generated. + +c_dir=$(pwd) + +set -e + +if [ ! -z "$@" ]; then + for argument in "$@"; do + case $argument in + # make curl silent + "-s") + curlopts="-s" + ;; + esac + done +fi + + +# Check that we're being run from the right directory. +if test ! -f muduo/base/Types.h; then + cat >&2 << __EOF__ +Could not find source code. Make sure you are running this script from the +root of the distribution tree. +__EOF__ + exit 1 +fi + +if test ! -e external; then + mkdir external +fi + +if test ! -e build; then + mkdir build +fi + +if test ! -e lib; then + mkdir lib +fi + +# Check that boost is present. Usually it is already there since the +# directory is set up as an SVN external. +if test ! -e external/boost; then + echo "external/boost not present. Fetching boost from the web..." + cd external + curl $curlopts -L -O https://dl.bintray.com/boostorg/release/1.65.0/source/boost_1_65_0.zip + unzip -q boost_1_65_0.zip + rm boost_1_65_0.zip + mv boost_1_65_0 boost + cd $c_dir +fi + +if test ! -e external/googletest; then + echo "external/googletest not present. Fetching googletest from the web..." + cd external + git clone --recursive https://github.com/google/googletest.git + g++ -isystem ./googletest/googletest/include -I./googletest/googletest -lpthread -c ./googletest/googletest/src/gtest-all.cc + ar -rv ../lib/gtest.lib gtest-all.o + rm gtest-all.o + cp ../lib/gtest.lib ../lib/gtestd.lib + cd $c_dir +fi + +if test ! -e external/zlib; then + echo "external/zlib not present. Fetching zlib from the web..." + cd external + curl $curlopts -L -O http://zlib.net/zlib-1.2.11.tar.gz + tar -xf zlib-1.2.11.tar.gz + mv zlib-1.2.11 zlib + cd zlib + ./configure + make + mv libz.a zlib.lib + cp zlib.lib zlibd.lib + cp -rf zlib.lib ../../lib/ + cp -rf zlibd.lib ../../lib/ + cd $c_dir +fi + +function gen() +{ + for d in $1/*; + do + if test -d "$d" ;then + ./make.sh -proj-dir=$d -build=../../../../build/$(basename $d) --vs-file=$(basename $d).vcxproj -tomakefile-dir=$(pwd) -tomakefile-name=vcxproj2mk.sh; + fi + done + +} + +gen ./muduo/base/tests +gen ./muduo/net/tests + +set -ex + +exit 0 diff --git a/clean.sh b/clean.sh new file mode 100644 index 000000000..346151680 --- /dev/null +++ b/clean.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +root_dir=$(pwd) + +function clean() +{ + for d in $1/*; + do [[ -d "$d" ]] && { ./make.sh -proj-dir=$d -build=../../../../build/$(basename $d) --vs-file=$(basename $d).vcxproj -tomakefile-dir=$(pwd) -tomakefile-name=vcxproj2mk.sh --make-clean=clean --make-target=lib; }; + done +} + +clean ./muduo/base/tests +clean ./muduo/net/tests + +rm -rf build/* + +cd $root_dir diff --git a/contrib/windows/include/arpa/inet.h b/contrib/windows/include/arpa/inet.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/cxxabi.h b/contrib/windows/include/cxxabi.h new file mode 100644 index 000000000..7be006c42 --- /dev/null +++ b/contrib/windows/include/cxxabi.h @@ -0,0 +1,15 @@ +#pragma once +// Windows shim for +// On Linux this header provides abi::__cxa_demangle(); on Windows we provide +// a stub that returns NULL so boost::core::demangle falls back to raw names. + +#include +#include + +namespace abi { +inline char* __cxa_demangle(const char* /*mangled*/, char* /*buf*/, + size_t* /*length*/, int* status) { + if (status) *status = -2; // not available + return nullptr; +} +} // namespace abi diff --git a/contrib/windows/include/endian.h b/contrib/windows/include/endian.h new file mode 100644 index 000000000..28be9b8c4 --- /dev/null +++ b/contrib/windows/include/endian.h @@ -0,0 +1,29 @@ +#ifndef MUDUO_CONTRIB_WINDOWS_ENDIAN_H +#define MUDUO_CONTRIB_WINDOWS_ENDIAN_H + +#if defined(_WIN32) || defined(WIN32) + +#include +#include "muduo/base/CrossPlatformAdapterFunction.h" + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif + +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#else + +#if defined(__GNUC__) || defined(__clang__) +#include_next +#endif + +#endif + +#endif diff --git a/contrib/windows/include/execinfo.h b/contrib/windows/include/execinfo.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/linux/unistd.h b/contrib/windows/include/linux/unistd.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/netdb.h b/contrib/windows/include/netdb.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/netinet/in.h b/contrib/windows/include/netinet/in.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/netinet/tcp.h b/contrib/windows/include/netinet/tcp.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/poll.h b/contrib/windows/include/poll.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/pthread.h b/contrib/windows/include/pthread.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/eventfd.h b/contrib/windows/include/sys/eventfd.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/prctl.h b/contrib/windows/include/sys/prctl.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/resource.h b/contrib/windows/include/sys/resource.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/socket.h b/contrib/windows/include/sys/socket.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/syscall.h b/contrib/windows/include/sys/syscall.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/time.h b/contrib/windows/include/sys/time.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/timerfd.h b/contrib/windows/include/sys/timerfd.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/sys/uio.h b/contrib/windows/include/sys/uio.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/windows/include/unistd.h b/contrib/windows/include/unistd.h new file mode 100644 index 000000000..e69de29bb diff --git a/examples/ace/ttcp/ttcp_asio_async.cc b/examples/ace/ttcp/ttcp_asio_async.cc index acf2a892b..ffa138bcd 100644 --- a/examples/ace/ttcp/ttcp_asio_async.cc +++ b/examples/ace/ttcp/ttcp_asio_async.cc @@ -25,7 +25,7 @@ class TtcpServerConnection : public std::enable_shared_from_this vs.mk + if test "$make_clean" ; then + make $make_clean + else + make -j20 + fi + rm -rf vs.mk + cd .. +} + +set -ex + +fbuildfile diff --git a/muduo/base/AsyncLogging.cc b/muduo/base/AsyncLogging.cc index 0f82444d0..cc9cbb9d8 100644 --- a/muduo/base/AsyncLogging.cc +++ b/muduo/base/AsyncLogging.cc @@ -21,7 +21,11 @@ AsyncLogging::AsyncLogging(const string& basename, thread_(std::bind(&AsyncLogging::threadFunc, this), "Logging"), latch_(1), mutex_(), +#ifdef __muduo_asynchronization__ cond_(mutex_), +#else + cond_(), +#endif currentBuffer_(new Buffer), nextBuffer_(new Buffer), buffers_() @@ -76,7 +80,11 @@ void AsyncLogging::threadFunc() muduo::MutexLockGuard lock(mutex_); if (buffers_.empty()) // unusual usage! { +#ifdef __muduo_asynchronization__ cond_.waitForSeconds(flushInterval_); +#else + cond_.waitForSeconds(lock, flushInterval_); +#endif } buffers_.push_back(std::move(currentBuffer_)); currentBuffer_ = std::move(newBuffer1); diff --git a/muduo/base/Atomic.h b/muduo/base/Atomic.h index 6158fac08..a7fd297fc 100644 --- a/muduo/base/Atomic.h +++ b/muduo/base/Atomic.h @@ -9,10 +9,13 @@ #include "muduo/base/noncopyable.h" #include +#include namespace muduo { + +#ifdef __muduo_asynchronization__ namespace detail { template @@ -88,7 +91,82 @@ class AtomicIntegerT : noncopyable volatile T value_; }; } // namespace detail - +#else +namespace detail +{ +template +class AtomicIntegerT : noncopyable +{ +public: + AtomicIntegerT() + : value_(0) + { + } + + // uncomment if you need copying and assignment + // + // AtomicIntegerT(const AtomicIntegerT& that) + // : value_(that.get()) + // {} + // + // AtomicIntegerT& operator=(const AtomicIntegerT& that) + // { + // getAndSet(that.get()); + // return *this; + // } + + T get() + { + return value_; + } + + T getAndAdd(T x) + { + return value_.fetch_add(x); + } + + T addAndGet(T x) + { + return getAndAdd(x); + } + + T incrementAndGet() + { + return addAndGet(1); + } + + T decrementAndGet() + { + return value_.fetch_sub(1); + } + + void add(T x) + { + getAndAdd(x); + } + + void increment() + { + incrementAndGet(); + } + + void decrement() + { + decrementAndGet(); + } + + T getAndSet(T newValue) + { + T r = value_; + value_ = newValue; + return r; + } + +private: + std::atomic value_; +}; +} // namespace detail +#endif // __muduo_asynchronization__ typedef detail::AtomicIntegerT AtomicInt32; typedef detail::AtomicIntegerT AtomicInt64; diff --git a/muduo/base/BlockingQueue.h b/muduo/base/BlockingQueue.h index adf92bcd4..341fa740a 100644 --- a/muduo/base/BlockingQueue.h +++ b/muduo/base/BlockingQueue.h @@ -23,7 +23,11 @@ class BlockingQueue : noncopyable BlockingQueue() : mutex_(), +#ifdef __muduo_asynchronization__ notEmpty_(mutex_), +#else + notEmpty_(), +#endif queue_() { } @@ -49,7 +53,11 @@ class BlockingQueue : noncopyable // always use a while-loop, due to spurious wakeup while (queue_.empty()) { +#ifdef __muduo_asynchronization__ notEmpty_.wait(); +#else + notEmpty_.wait(lock); +#endif } assert(!queue_.empty()); T front(std::move(queue_.front())); diff --git a/muduo/base/BoundedBlockingQueue.h b/muduo/base/BoundedBlockingQueue.h index cfb4d3bae..18fdec8fa 100644 --- a/muduo/base/BoundedBlockingQueue.h +++ b/muduo/base/BoundedBlockingQueue.h @@ -21,8 +21,13 @@ class BoundedBlockingQueue : noncopyable public: explicit BoundedBlockingQueue(int maxSize) : mutex_(), +#ifdef __muduo_asynchronization__ notEmpty_(mutex_), notFull_(mutex_), +#else + notEmpty_(), + notFull_(), +#endif queue_(maxSize) { } @@ -32,7 +37,11 @@ class BoundedBlockingQueue : noncopyable MutexLockGuard lock(mutex_); while (queue_.full()) { +#ifdef __muduo_asynchronization__ notFull_.wait(); +#else + notFull_.wait(lock); +#endif } assert(!queue_.full()); queue_.push_back(x); @@ -44,7 +53,11 @@ class BoundedBlockingQueue : noncopyable MutexLockGuard lock(mutex_); while (queue_.full()) { +#ifdef __muduo_asynchronization__ notFull_.wait(); +#else + notFull_.wait(lock); +#endif } assert(!queue_.full()); queue_.push_back(std::move(x)); @@ -56,7 +69,11 @@ class BoundedBlockingQueue : noncopyable MutexLockGuard lock(mutex_); while (queue_.empty()) { +#ifdef __muduo_asynchronization__ notEmpty_.wait(); +#else + notEmpty_.wait(lock); +#endif } assert(!queue_.empty()); T front(std::move(queue_.front())); diff --git a/muduo/base/CMakeLists.txt b/muduo/base/CMakeLists.txt index 96e2d029f..667507f5d 100644 --- a/muduo/base/CMakeLists.txt +++ b/muduo/base/CMakeLists.txt @@ -1,8 +1,6 @@ set(base_SRCS AsyncLogging.cc - Condition.cc CountDownLatch.cc - CurrentThread.cc Date.cc Exception.cc FileUtil.cc @@ -11,24 +9,29 @@ set(base_SRCS LogStream.cc ProcessInfo.cc Timestamp.cc - Thread.cc - ThreadPool.cc TimeZone.cc + ThreadPool.cc + CurrentThread.cc + CrossPlatformAdapterFunction.cc + Thread.cc ) - +include_directories(${PROJECT_SOURCE_DIR}/contrib/windows/include) add_library(muduo_base ${base_SRCS}) -target_link_libraries(muduo_base pthread rt) -#add_library(muduo_base_cpp11 ${base_SRCS}) -#target_link_libraries(muduo_base_cpp11 pthread rt) -#set_target_properties(muduo_base_cpp11 PROPERTIES COMPILE_FLAGS "-std=c++0x") +if (WIN32) + target_compile_options(muduo_base PUBLIC /FI"muduo/base/CrossPlatformAdapterFunction.h") +endif () + + target_link_libraries(muduo_base gtest) +if (UNIX) + target_link_libraries(muduo_base pthread rt) +endif () install(TARGETS muduo_base DESTINATION lib) -#install(TARGETS muduo_base_cpp11 DESTINATION lib) file(GLOB HEADERS "*.h") install(FILES ${HEADERS} DESTINATION include/muduo/base) -if(MUDUO_BUILD_EXAMPLES) +if(NOT CMAKE_BUILD_NO_EXAMPLES) add_subdirectory(tests) endif() diff --git a/muduo/base/Condition.cc b/muduo/base/Condition.cc index 47e9d23a9..af3086710 100644 --- a/muduo/base/Condition.cc +++ b/muduo/base/Condition.cc @@ -6,6 +6,7 @@ #include "muduo/base/Condition.h" #include +#ifdef __muduo_asynchronization__ // returns true if time out, false otherwise. bool muduo::Condition::waitForSeconds(double seconds) @@ -24,3 +25,4 @@ bool muduo::Condition::waitForSeconds(double seconds) return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime); } +#endif // __muduo_asynchronization__ \ No newline at end of file diff --git a/muduo/base/Condition.h b/muduo/base/Condition.h index 0181550b7..28cddeef7 100644 --- a/muduo/base/Condition.h +++ b/muduo/base/Condition.h @@ -9,10 +9,12 @@ #include "muduo/base/Mutex.h" #include +#include +#include namespace muduo { - +#ifdef __muduo_asynchronization__ class Condition : noncopyable { public: @@ -50,6 +52,32 @@ class Condition : noncopyable MutexLock& mutex_; pthread_cond_t pcond_; }; +#else +class Condition : public std::condition_variable +{ +public: + + // returns true if time out, false otherwise. + bool waitForSeconds(MutexLockGuard & lock, double seconds) + { + wait_for(lock, std::chrono::seconds((int32_t)seconds)); + return true; + } + + void notify() + { + notify_all(); + } + + void notifyAll() + { + notify_all(); + } + +private: + +}; +#endif//__muduo_asynchronization__ } // namespace muduo diff --git a/muduo/base/CountDownLatch.cc b/muduo/base/CountDownLatch.cc index 72d26cced..c661e5f99 100644 --- a/muduo/base/CountDownLatch.cc +++ b/muduo/base/CountDownLatch.cc @@ -9,7 +9,11 @@ using namespace muduo; CountDownLatch::CountDownLatch(int count) : mutex_(), +#ifdef __muduo_asynchronization__ condition_(mutex_), +#else + condition_(), +#endif count_(count) { } @@ -19,7 +23,11 @@ void CountDownLatch::wait() MutexLockGuard lock(mutex_); while (count_ > 0) { +#ifdef __muduo_asynchronization__ condition_.wait(); +#else + condition_.wait(lock); +#endif } } diff --git a/muduo/base/CrossPlatformAdapterFunction.cc b/muduo/base/CrossPlatformAdapterFunction.cc new file mode 100644 index 000000000..2ecdeb225 --- /dev/null +++ b/muduo/base/CrossPlatformAdapterFunction.cc @@ -0,0 +1,1609 @@ + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +//////////////////////WIN32///////////////////////////////// +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ProcessInfo.h" +#endif // __linux__ + +#ifdef __linux__ +int muduo_eventloop_poll_timeout_ms() +{ + return 10000; +} + +void muduo_ignore_sigpipe() +{ + ::signal(SIGPIPE, SIG_IGN); +} + +void muduo_close_fd_compat(int fd) +{ + ::close(fd); +} + +bool muduo_eventloop_use_timerqueue_loop() +{ + return false; +} + +bool muduo_channel_default_log_hup() +{ + return true; +} + +bool muduo_channel_return_after_close() +{ + return false; +} + +int muduo_socket_connect_compat(int sockfd, const struct sockaddr* addr) +{ + return ::connect(sockfd, addr, static_cast(sizeof(struct sockaddr_in6))); +} + +ssize_t muduo_socket_read_compat(int sockfd, void* buf, size_t count) +{ + return ::read(sockfd, buf, count); +} + +ssize_t muduo_socket_write_compat(int sockfd, const void* buf, size_t count) +{ + return ::write(sockfd, buf, count); +} + +int muduo_socket_close_compat(int sockfd) +{ + return ::close(sockfd); +} + +std::string muduo_make_loop_thread_name(const std::string& base, int index) +{ + char buf[64]; + std::snprintf(buf, sizeof buf, "%s%d", base.c_str(), index); + return std::string(buf); +} + +ssize_t muduo_eventloop_wakeup_write(int fd) +{ + uint64_t one = 1; + return ::write(fd, &one, sizeof one); +} + +ssize_t muduo_eventloop_wakeup_read(int fd) +{ + uint64_t one = 1; + return ::read(fd, &one, sizeof one); +} + +bool muduo_use_epoll_poller() +{ + return !::getenv("MUDUO_USE_POLL"); +} + +int muduo_open_idle_fd(int family) +{ + (void)family; + return ::open("/dev/null", O_RDONLY | O_CLOEXEC); +} + +void muduo_close_idle_fd(int fd) +{ + ::close(fd); +} + +void muduo_handle_emfile_idle_fd(int listenfd, int* idlefd, int family) +{ + (void)family; + muduo_close_idle_fd(*idlefd); + *idlefd = ::accept(listenfd, NULL, NULL); + muduo_close_idle_fd(*idlefd); + *idlefd = muduo_open_idle_fd(AF_INET); +} + +ssize_t muduo_read_timerfd(int timerfd, uint64_t* howmany) +{ + return ::read(timerfd, howmany, sizeof(*howmany)); +} + +void muduo_close_timerfd(int timerfd) +{ + ::close(timerfd); +} + +FILE* muduo_fopen_append(const char* filename) +{ + return ::fopen(filename, "ae"); // 'e' for O_CLOEXEC +} + +ssize_t muduo_read_file_compat(int fd, void* buf, size_t count) +{ + return ::read(fd, buf, count); +} + +int muduo_create_nonblocking_or_die(int family) +{ +#if VALGRIND + int sockfd = ::socket(family, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + return sockfd; + } + // non-block + int flags = ::fcntl(sockfd, F_GETFL, 0); + flags |= O_NONBLOCK; + ::fcntl(sockfd, F_SETFL, flags); + // close-on-exec + flags = ::fcntl(sockfd, F_GETFD, 0); + flags |= FD_CLOEXEC; + ::fcntl(sockfd, F_SETFD, flags); + return sockfd; +#else + return ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); +#endif +} + +pid_t muduo_gettid() +{ + return static_cast(::syscall(SYS_gettid)); +} + +void muduo_after_fork_in_child() +{ + extern void afterFork(); + pthread_atfork(NULL, NULL, &afterFork); +} + +muduo::string muduo_stack_trace(bool demangle) +{ + muduo::string stack; + const int max_frames = 200; + void* frame[max_frames]; + int nptrs = ::backtrace(frame, max_frames); + char** strings = ::backtrace_symbols(frame, nptrs); + if (strings) + { + size_t len = 256; + char* demangled = demangle ? static_cast(::malloc(len)) : nullptr; + for (int i = 1; i < nptrs; ++i) // skipping the 0-th, which is this function + { + if (demangle) + { + char* left_par = nullptr; + char* plus = nullptr; + for (char* p = strings[i]; *p; ++p) + { + if (*p == '(') + left_par = p; + else if (*p == '+') + plus = p; + } + + if (left_par && plus) + { + *plus = '\0'; + int status = 0; + char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status); + *plus = '+'; + if (status == 0) + { + demangled = ret; + stack.append(strings[i], left_par+1); + stack.append(demangled); + stack.append(plus); + stack.push_back('\n'); + continue; + } + } + } + stack.append(strings[i]); + stack.push_back('\n'); + } + free(demangled); + free(strings); + } + return stack; +} +#endif // __linux__ + +#ifdef WIN32 + +#include "Logging.h" + +uint32_t be32toh(uint32_t x) +{ + return __bswap_32(x); +} + +uint64_t be64toh(uint64_t x) +{ + return __bswap_64(x); +} + +uint32_t htobe32(uint32_t x) +{ + return __bswap_32(x); +} + +uint64_t htobe64(uint64_t x) +{ + return __bswap_64(x); +} + +void setbuffer(FILE *stream, char *buf, size_t size) +{ + (void*)stream; + (void*)buf; + (void)size; +} + +char * strerror_r(int errnum, char *buf, size_t buflen) +{ + strerror_s(buf, buflen, errnum); + return buf; +} + +size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, + FILE *stream) +{ + return ::fwrite(ptr, size, n, stream); +} + +struct tm *gmtime_r(const time_t *timep, struct tm *result) +{ + gmtime_s(result, timep); + return result; +} + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + (void)tz; + std::micro microratio; + uint64_t c = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + tv->tv_sec = (long)(c / microratio.den); + tv->tv_usec = (long)(c % microratio.den); + return 0; +} + +int gethostbyname_r(const char *name, + struct hostent *ret, char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + return 0; +} + +ssize_t pread(int fd, void *buf, size_t count, off_t offset) +{ + //return ::_read(fd, buf, count); + (void)fd; + (void*)buf; + (void)count; + (void)offset; + return 0; +} + +extern inline int __libc_use_alloca(size_t size) +{ + return size <= __MAX_ALLOCA_CUTOFF; +} + +ssize_t winreadsock(int fd, void *buf, size_t count) +{ + // Use plain recv() on non-blocking socket (FIONBIO=1). + // WSARecv + OVERLAPPED on a non-overlapped socket can block unexpectedly. + int ret = ::recv(fd, static_cast(buf), static_cast(count), 0); + if (ret < 0) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + { + errno = EWOULDBLOCK; + } + else if (err == WSAECONNRESET || err == WSAECONNABORTED) + { + errno = ECONNRESET; + } + else + { + _set_errno(err); + } + return -1; + } + return ret; +} + +ssize_t winwritesock(int fd, const void *buf, size_t count) +{ + // Use plain send() on non-blocking socket (FIONBIO=1). + // WSASend + OVERLAPPED on a non-overlapped socket can block unexpectedly. + int ret = ::send(fd, static_cast(buf), static_cast(count), 0); + if (ret < 0) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + { + errno = EWOULDBLOCK; + } + else if (err == WSAECONNRESET || err == WSAECONNABORTED) + { + errno = ECONNRESET; + } + else + { + LOG_ERROR << "send() failed with error: " << err; + _set_errno(err); + } + return -1; + } + return ret; +} + +ssize_t winclosesock(int fd) +{ + return closesocket(fd); +} + +int muduo_open_idle_fd(int family) +{ + int sockfd = (int)::WSASocket(family, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if (sockfd < 0) + { + _set_errno(WSAGetLastError()); + return -1; + } + u_long mode = 1; + if (ioctlsocket(sockfd, FIONBIO, &mode) != NO_ERROR) + { + _set_errno(WSAGetLastError()); + ::closesocket(sockfd); + return -1; + } + return sockfd; +} + +bool muduo_use_epoll_poller() +{ + return false; // Windows: always use PollPoller +} + +int muduo_eventloop_poll_timeout_ms() +{ + return 100; +} + +void muduo_ignore_sigpipe() +{ +} + +void muduo_close_fd_compat(int fd) +{ + (void)::winclosesock(fd); +} + +bool muduo_eventloop_use_timerqueue_loop() +{ + return true; +} + +bool muduo_channel_default_log_hup() +{ + return false; +} + +bool muduo_channel_return_after_close() +{ + return true; +} + +int muduo_socket_connect_compat(int sockfd, const struct sockaddr* addr) +{ + return ::win_connect(sockfd, addr); +} + +ssize_t muduo_socket_read_compat(int sockfd, void* buf, size_t count) +{ + return ::winreadsock(sockfd, buf, count); +} + +ssize_t muduo_socket_write_compat(int sockfd, const void* buf, size_t count) +{ + return ::winwritesock(sockfd, buf, count); +} + +int muduo_socket_close_compat(int sockfd) +{ + return static_cast(::winclosesock(sockfd)); +} + +std::string muduo_make_loop_thread_name(const std::string& base, int index) +{ + return base + std::to_string(index); +} + +ssize_t muduo_eventloop_wakeup_write(int fd) +{ + (void)fd; + return sizeof(uint64_t); +} + +ssize_t muduo_eventloop_wakeup_read(int fd) +{ + (void)fd; + return sizeof(uint64_t); +} + +void muduo_close_idle_fd(int fd) +{ + (void)::winclosesock(fd); +} + +void muduo_handle_emfile_idle_fd(int listenfd, int* idlefd, int family) +{ + muduo_close_idle_fd(*idlefd); + *idlefd = (int)::accept(listenfd, NULL, NULL); + muduo_close_idle_fd(*idlefd); + *idlefd = muduo_open_idle_fd(family); +} + +ssize_t muduo_read_timerfd(int timerfd, uint64_t* howmany) +{ + (void)timerfd; + *howmany = 1; + return sizeof(uint64_t); // pretend success, no-op on Windows +} + +void muduo_close_timerfd(int timerfd) +{ + (void)::winclosesock(timerfd); +} + +FILE* muduo_fopen_append(const char* filename) +{ + return ::fopen(filename, "a"); +} + +ssize_t muduo_read_file_compat(int fd, void* buf, size_t count) +{ + return ::_read(fd, buf, (unsigned int)count); +} + +int muduo_create_nonblocking_or_die(int family) +{ + int sockfd = (int)::WSASocket(family, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if (sockfd < 0) + { + return sockfd; + } + u_long mode = 1; + int result = ioctlsocket(sockfd, FIONBIO, &mode); + if (result != NO_ERROR) + { + ::closesocket(sockfd); + return -1; + } + return sockfd; +} + +pid_t muduo_gettid() +{ + return ::GetCurrentThreadId(); +} + +void muduo_after_fork_in_child() +{ + // no fork on Windows, no-op +} + +muduo::string muduo_stack_trace(bool demangle) +{ + (void)demangle; + muduo::string stack; + const int len = 200; + const int maxFunNameLen = 512; + void* buffer[len]; + HANDLE process = GetCurrentProcess(); + + SymInitialize(process, nullptr, true); + + int nptrs = CaptureStackBackTrace(0, len, buffer, nullptr); + + SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + maxFunNameLen * sizeof(char), 1); + symbol->MaxNameLen = maxFunNameLen; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + if (symbol) + { + for (int i = 0; i < nptrs; ++i) + { + SymFromAddr(process, (DWORD64)(buffer[i]), 0, symbol); + char funstring[maxFunNameLen] = {}; + sprintf_s(funstring, "%i: %s - 0x%I64X\n", nptrs - i - 1, symbol->Name, symbol->Address); + stack.append(funstring); + } + free(symbol); + } + return stack; +} + +//glibc +ssize_t readv(int fd, const struct iovec *vector, int count) +{ + + /* Find the total number of bytes to be read. */ + ssize_t bytes = 0; + for (int i = 0; i < count; ++i) + { + /* Check for ssize_t overflow. */ + if (SSIZE_MAX - bytes < vector[i].iov_len) + { + SetLastError(EINVAL); + return -1; + } + bytes += vector[i].iov_len; + } + + /* Allocate a temporary buffer to hold the data. We should normally + use alloca since it's faster and does not require synchronization + with other threads. But we cannot if the amount of memory + required is too large. */ + char *buffer = NULL; + char *malloced_buffer = NULL; + char stack_buffer[__MAX_ALLOCA_CUTOFF] = {}; + std::vector mb((std::size_t)bytes); + if (__libc_use_alloca((std::size_t)bytes)) + buffer = stack_buffer; + else + { + malloced_buffer = buffer = mb.data(); + } + + /* Read the data. */ + ssize_t bytes_read = ::winreadsock(fd, buffer, (std::size_t)bytes); + if (bytes_read < 0) + return -1; + + /* Copy the data from BUFFER into the memory specified by VECTOR. */ + bytes = bytes_read; + for (int i = 0; i < count; ++i) + { + ssize_t copy = std::min(vector[i].iov_len, bytes); + + (void)memcpy((void *)vector[i].iov_base, (void *)buffer, (std::size_t)copy); + + buffer += copy; + bytes -= copy; + if (bytes == 0) + break; + } + + return bytes_read; +} + +void bzero(void *s, size_t n) +{ + memset(s, '\0', n); +} + +char *basename(const char* full_path) { + static char ret[1024]; + char path_copy[1024]; + char *ptr; + + if (full_path == NULL || strcmp(full_path, "") == 0) { + strcpy_s(ret, "."); + } + else { + strcpy_s(path_copy, full_path); + char* p{ nullptr }; + ptr = strtok_s(path_copy, "/", &p); + if (ptr == NULL) + strcpy_s(ret, "/"); + + while (ptr != NULL) { + strcpy_s(ret, ptr); + ptr = strtok_s(NULL, "/", &p); + } + } + + ptr = ret; + return ptr; +} + +const int64_t kNanoSecondsPerSecond = (int64_t)1e9; +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + std::this_thread::sleep_for(std::chrono::nanoseconds(req->tv_sec * kNanoSecondsPerSecond + req->tv_nsec)); + (void)rem; + return 0; +} + +int setrlimit(int resource, const struct rlimit *rlim) +{ + (void)resource; + (void)rlim; + + return 0; +} + +class WindowsSocketInitFunction +{ +public: + WindowsSocketInitFunction() + { + WSADATA wsaData = { 0 }; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + //... + } + + } +}; + + +WindowsSocketInitFunction initSocketObj; + + + + +int timerfd_settime(int fd, int flags, + const struct itimerspec *new_value, + struct itimerspec *old_value) +{ + (void)fd; + (void)flags; + (void)new_value; + (void)old_value; + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + std::this_thread::sleep_for(std::chrono::seconds(seconds)); + + return 0; +} +int poll(struct pollfd *fds, size_t nfds, int timeout) +{ + return WSAPoll(fds, (ULONG)nfds, timeout); +} + +uid_t geteuid() +{ + return 0; +} + + +int getsockopt(int sockfd, int level, int optname, + int *optval, socklen_t *optlen) +{ + return ::getsockopt(sockfd, level, optname, (char*)(optval), optlen); +} + +void setsockopt(int sockfd, int level, int optname, + int *optval, socklen_t optlen) +{ + ::setsockopt(sockfd, level, optname, (const char*)(optval), optlen); +} + +int getsockopt(int sockfd, int level, int optname, + tcp_info *optval, socklen_t *optlen) +{ + return ::getsockopt(sockfd, level, optname, (char*)(optval), optlen); +} + +int accept4(int sockfd, struct sockaddr *addr, + socklen_t *addrlen, int flags) +{ + int connfd = (int)::accept(sockfd, addr, addrlen); + u_long mode = 0; + ioctlsocket(connfd, FIONBIO, &mode); + return connfd; +} + +int32_t win_connect(int sockfd, const struct sockaddr* addr) +{ + auto result = ::connect(sockfd, addr, static_cast(sizeof(struct sockaddr_in6))); + if (result == SOCKET_ERROR) { + int error_code = WSAGetLastError(); + if (error_code == WSAEWOULDBLOCK) { + LOG_INFO << "Connect is in progress (WSAEWOULDBLOCK). Waiting for completion..." ; + + // 使用 select 等待套接字可写 + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(sockfd, &writefds); + + timeval timeout; + timeout.tv_sec = 5; // 等待 5 秒 + timeout.tv_usec = 0; + + result = select(0, nullptr, &writefds, nullptr, &timeout); + if (result > 0 && FD_ISSET(sockfd, &writefds)) { + LOG_INFO << "Connection established!" ; + } + else if (result == 0) { + LOG_INFO << "Connection timed out."; + } + else { + LOG_ERROR << "Select failed with error: " << WSAGetLastError() ; + } + return 0; + } + } + + return result; +} + +#pragma region strptime + + +//https://github.com/res2001/strptime/blob/master/strptime.c + +/* $NetBSD: strptime.c,v 1.62 2017/08/24 01:01:09 ginsbach Exp $ */ +/* http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/lib/libc/time/strptime.c?only_with_tag=HEAD + * NetBSD implementation strptime(). + * Format description: https://netbsd.gw.com/cgi-bin/man-cgi?strptime+3+NetBSD-current +*/ +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + //#include + //__RCSID("$NetBSD: strptime.c,v 1.62 2017/08/24 01:01:09 ginsbach Exp $"); + +#include +#include +#include +#include + +static const unsigned char* conv_num(const unsigned char*, int*, unsigned int, unsigned int); +static const unsigned char* find_string(const unsigned char*, int*, const char* const*, const char* const*, int); + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } + +#define TM_YEAR_BASE 1900 + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define S_YEAR (1 << 0) +#define S_MON (1 << 1) +#define S_YDAY (1 << 2) +#define S_MDAY (1 << 3) +#define S_WDAY (1 << 4) +#define S_HOUR (1 << 5) + +#define HAVE_MDAY(s) (s & S_MDAY) +#define HAVE_MON(s) (s & S_MON) +#define HAVE_WDAY(s) (s & S_WDAY) +#define HAVE_YDAY(s) (s & S_YDAY) +#define HAVE_YEAR(s) (s & S_YEAR) +#define HAVE_HOUR(s) (s & S_HOUR) + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define HOURSPERDAY 24 + +#define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" +#define HERE_D_FMT "%y/%m/%d" +#define HERE_T_FMT_AMPM "%I:%M:%S %p" +#define HERE_T_FMT "%H:%M:%S" + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + + /* + ** Since everything in isleap is modulo 400 (or a factor of 400), we know that + ** isleap(y) == isleap(y % 400) + ** and so + ** isleap(a + b) == isleap((a + b) % 400) + ** or + ** isleap(a + b) == isleap(a % 400 + b % 400) + ** This is true even if % means modulo rather than Fortran remainder + ** (which is allowed by C89 but not by C99 or later). + ** We use this to avoid addition overflow problems. + */ + +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) + +#ifdef _MSC_VER +#define tzname _tzname +#define strncasecmp _strnicmp +#endif + +#ifdef TM_ZONE +static char* utc = "UTC"; +#endif +/* RFC-822/RFC-2822 */ +static const char* const nast[] = { + "EST", "CST", "MST", "PST", "\0\0\0" +}; +static const char* const nadt[] = { + "EDT", "CDT", "MDT", "PDT", "\0\0\0" +}; +static const char* weekday_name[] = +{ + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" +}; +static const char* ab_weekday_name[] = +{ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char* month_name[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; +static const char* ab_month_name[] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +static const char* am_pm[] = { "AM", "PM" }; + + +/* + * Table to determine the ordinal date for the start of a month. + * Ref: http://en.wikipedia.org/wiki/ISO_week_date + */ +static const int start_of_month[2][13] = { + /* non-leap year */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* leap year */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* + * Calculate the week day of the first day of a year. Valid for + * the Gregorian calendar, which began Sept 14, 1752 in the UK + * and its colonies. Ref: + * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week + */ + +static int +first_wday_of(int yr) +{ + return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) / 4) + + (isleap(yr) ? 6 : 0) + 1) % 7; +} + +#define delim(p) ((p) == '\0' || isspace((unsigned char)(p))) + +static int +fromzone(const unsigned char** bp, struct tm* tm, int mandatory) +{ + // timezone_t tz; + char buf[512], * p; + const unsigned char* rp; + + for (p = buf, rp = *bp; !delim(*rp) && p < &buf[sizeof(buf) - 1]; rp++) + *p++ = *rp; + *p = '\0'; + + if (mandatory) + *bp = rp; + if (!isalnum((unsigned char)*buf)) + return 0; + // tz = tzalloc(buf); + // if (tz == NULL) + // return 0; + + *bp = rp; + tm->tm_isdst = 0; /* XXX */ +#ifdef TM_GMTOFF + tm->TM_GMTOFF = tzgetgmtoff(tz, tm->tm_isdst); +#endif +#ifdef TM_ZONE + // Can't use tzgetname() here because we are going to free() + tm->TM_ZONE = NULL; /* XXX */ +#endif + // tzfree(tz); + return 1; +} + +char* strptime(const char* buf, const char* fmt, struct tm* tm) +{ + unsigned char c; + const unsigned char* bp, * ep, * zname; + int alt_format, i, split_year = 0, neg = 0, state = 0, + day_offset = -1, week_offset = 0, offs, mandatory; + const char* new_fmt; + + bp = (const unsigned char*)buf; + + while (bp != NULL && (c = *fmt++) != '\0') { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + i = 0; + + /* Eat up white-space. */ + if (isspace(c)) { + while (isspace(*bp)) + bp++; + continue; + } + + if (c != '%') + goto literal; + + + again: switch (c = *fmt++) { + case '%': /* "%%" is converted to "%". */ + literal : + if (c != *bp++) + return NULL; + LEGAL_ALT(0); + continue; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + // new_fmt = _TIME_LOCALE(loc)->d_t_fmt; + new_fmt = HERE_D_T_FMT; + state |= S_WDAY | S_MON | S_MDAY | S_YEAR; + goto recurse; + + case 'F': /* The date as "%Y-%m-%d". */ + new_fmt = "%Y-%m-%d"; + LEGAL_ALT(0); + state |= S_MON | S_MDAY | S_YEAR; + goto recurse; + + case 'R': /* The time as "%H:%M". */ + new_fmt = "%H:%M"; + LEGAL_ALT(0); + goto recurse; + + case 'r': /* The time in 12-hour clock representation. */ + // new_fmt = _TIME_LOCALE(loc)->t_fmt_ampm; + new_fmt = HERE_T_FMT_AMPM; + LEGAL_ALT(0); + goto recurse; + + case 'X': /* The time, using the locale's format. */ + /* fall through */ + + case 'T': /* The time as "%H:%M:%S". */ + new_fmt = HERE_T_FMT; + LEGAL_ALT(0); + + recurse: + bp = (const unsigned char*)strptime((const char*)bp, + new_fmt, tm); + LEGAL_ALT(ALT_E); + continue; + + case 'x': /* The date, using the locale's format. */ + /* fall throug */ + + case 'D': /* The date as "%y/%m/%d". */ + { + new_fmt = HERE_D_FMT; + LEGAL_ALT(0); + state |= S_MON | S_MDAY | S_YEAR; + const int year = split_year ? tm->tm_year : 0; + + bp = (const unsigned char*)strptime((const char*)bp, + new_fmt, tm); + LEGAL_ALT(ALT_E); + tm->tm_year += year; + if (split_year && tm->tm_year % (2000 - TM_YEAR_BASE) <= 68) + tm->tm_year -= 2000 - TM_YEAR_BASE; + split_year = 1; + continue; + } + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + bp = find_string(bp, &tm->tm_wday, weekday_name, ab_weekday_name, 7); + LEGAL_ALT(0); + state |= S_WDAY; + continue; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + bp = find_string(bp, &tm->tm_mon, month_name, ab_month_name, 12); + LEGAL_ALT(0); + state |= S_MON; + continue; + + case 'C': /* The century number. */ + i = 20; + bp = conv_num(bp, &i, 0, 99); + + i = i * 100 - TM_YEAR_BASE; + if (split_year) + i += tm->tm_year % 100; + split_year = 1; + tm->tm_year = i; + LEGAL_ALT(ALT_E); + state |= S_YEAR; + continue; + + case 'd': /* The day of month. */ + case 'e': + bp = conv_num(bp, &tm->tm_mday, 1, 31); + LEGAL_ALT(ALT_O); + state |= S_MDAY; + continue; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + bp = conv_num(bp, &tm->tm_hour, 0, 23); + LEGAL_ALT(ALT_O); + state |= S_HOUR; + continue; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + bp = conv_num(bp, &tm->tm_hour, 1, 12); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + LEGAL_ALT(ALT_O); + state |= S_HOUR; + continue; + + case 'j': /* The day of year. */ + i = 1; + bp = conv_num(bp, &i, 1, 366); + tm->tm_yday = i - 1; + LEGAL_ALT(0); + state |= S_YDAY; + continue; + + case 'M': /* The minute. */ + bp = conv_num(bp, &tm->tm_min, 0, 59); + LEGAL_ALT(ALT_O); + continue; + + case 'm': /* The month. */ + i = 1; + bp = conv_num(bp, &i, 1, 12); + tm->tm_mon = i - 1; + LEGAL_ALT(ALT_O); + state |= S_MON; + continue; + + case 'p': /* The locale's equivalent of AM/PM. */ + bp = find_string(bp, &i, am_pm, NULL, 2); + if (HAVE_HOUR(state) && tm->tm_hour > 11) + return NULL; + tm->tm_hour += i * 12; + LEGAL_ALT(0); + continue; + + case 'S': /* The seconds. */ + bp = conv_num(bp, &tm->tm_sec, 0, 61); + LEGAL_ALT(ALT_O); + continue; + +#ifndef TIME_MAX +#define TIME_MAX INT64_MAX +#endif + case 's': /* seconds since the epoch */ + { + time_t sse = 0; + uint64_t rulim = TIME_MAX; + + if (*bp < '0' || *bp > '9') { + bp = NULL; + continue; + } + + do { + sse *= 10; + sse += *bp++ - '0'; + rulim /= 10; + } while ((sse * 10 <= TIME_MAX) && + rulim && *bp >= '0' && *bp <= '9'); + + if (sse < 0 || (uint64_t)sse > TIME_MAX) { + bp = NULL; + continue; + } +#ifdef _WIN32 + if (localtime_s(tm, &sse) == 0) +#else + if (localtime_r(&sse, tm)) +#endif + state |= S_YDAY | S_WDAY | S_MON | S_MDAY | S_YEAR; + else + bp = NULL; + } + continue; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + /* + * This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so save the + * week for now in case it can be used later. + */ + bp = conv_num(bp, &i, 0, 53); + LEGAL_ALT(ALT_O); + if (c == 'U') + day_offset = TM_SUNDAY; + else + day_offset = TM_MONDAY; + week_offset = i; + continue; + + case 'w': /* The day of week, beginning on sunday. */ + bp = conv_num(bp, &tm->tm_wday, 0, 6); + LEGAL_ALT(ALT_O); + state |= S_WDAY; + continue; + + case 'u': /* The day of week, monday = 1. */ + bp = conv_num(bp, &i, 1, 7); + tm->tm_wday = i % 7; + LEGAL_ALT(ALT_O); + state |= S_WDAY; + continue; + + case 'g': /* The year corresponding to the ISO week + * number but without the century. + */ + bp = conv_num(bp, &i, 0, 99); + continue; + + case 'G': /* The year corresponding to the ISO week + * number with century. + */ + do + bp++; + while (isdigit(*bp)); + continue; + + case 'V': /* The ISO 8601:1988 week number as decimal */ + bp = conv_num(bp, &i, 0, 53); + continue; + + case 'Y': /* The year. */ + i = TM_YEAR_BASE; /* just for data sanity... */ + bp = conv_num(bp, &i, 0, 9999); + tm->tm_year = i - TM_YEAR_BASE; + LEGAL_ALT(ALT_E); + state |= S_YEAR; + continue; + + case 'y': /* The year within 100 years of the epoch. */ + /* LEGAL_ALT(ALT_E | ALT_O); */ + bp = conv_num(bp, &i, 0, 99); + + if (split_year) + /* preserve century */ + i += (tm->tm_year / 100) * 100; + else { + split_year = 1; + if (i <= 68) + i = i + 2000 - TM_YEAR_BASE; + } + tm->tm_year = i; + state |= S_YEAR; + continue; + + case 'Z': // time zone name + case 'z': // +#ifdef _WIN32 + _tzset(); +#else + tzset(); +#endif + mandatory = c == 'z'; + /* + * We recognize all ISO 8601 formats: + * Z = Zulu time/UTC + * [+-]hhmm + * [+-]hh:mm + * [+-]hh + * We recognize all RFC-822/RFC-2822 formats: + * UT|GMT + * North American : UTC offsets + * E[DS]T = Eastern : -4 | -5 + * C[DS]T = Central : -5 | -6 + * M[DS]T = Mountain: -6 | -7 + * P[DS]T = Pacific : -7 | -8 + * Nautical/Military + * [A-IL-M] = -1 ... -9 (J not used) + * [N-Y] = +1 ... +12 + * Note: J maybe used to denote non-nautical + * local time + */ + if (mandatory) + while (isspace(*bp)) + bp++; + + zname = bp; + switch (*bp++) { + case 'G': + if (*bp++ != 'M') + goto namedzone; + /*FALLTHROUGH*/ + case 'U': + if (*bp++ != 'T') + goto namedzone; + else if (!delim(*bp) && *bp++ != 'C') + goto namedzone; + /*FALLTHROUGH*/ + case 'Z': + if (!delim(*bp)) + goto namedzone; + tm->tm_isdst = 0; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = 0; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = utc; +#endif + continue; + case '+': + neg = 0; + break; + case '-': + neg = 1; + break; + default: + namedzone: + bp = zname; + + /* Nautical / Military style */ + if (delim(bp[1]) && + ((*bp >= 'A' && *bp <= 'I') || + (*bp >= 'L' && *bp <= 'Y'))) { +#ifdef TM_GMTOFF + /* Argh! No 'J'! */ + if (*bp >= 'A' && *bp <= 'I') + tm->TM_GMTOFF = + (int)*bp - ('A' - 1); + else if (*bp >= 'L' && *bp <= 'M') + tm->TM_GMTOFF = (int)*bp - 'A'; + else if (*bp >= 'N' && *bp <= 'Y') + tm->TM_GMTOFF = 'M' - (int)*bp; + tm->TM_GMTOFF *= SECSPERHOUR; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + bp++; + continue; + } + /* 'J' is local time */ + if (delim(bp[1]) && *bp == 'J') { +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -timezone; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + bp++; + continue; + } + + /* + * From our 3 letter hard-coded table + * XXX: Can be removed, handled by tzload() + */ + if (delim(bp[0]) || delim(bp[1]) || + delim(bp[2]) || !delim(bp[3])) + goto loadzone; + ep = find_string(bp, &i, nast, NULL, 4); + if (ep != NULL) { +#ifdef TM_GMTOFF + tm->TM_GMTOFF = (-5 - i) * SECSPERHOUR; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = __UNCONST(nast[i]); +#endif + bp = ep; + continue; + } + ep = find_string(bp, &i, nadt, NULL, 4); + if (ep != NULL) { + tm->tm_isdst = 1; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = (-4 - i) * SECSPERHOUR; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = __UNCONST(nadt[i]); +#endif + bp = ep; + continue; + } + /* + * Our current timezone + */ + ep = find_string(bp, &i, + (const char* const*)tzname, + NULL, 2); + if (ep != NULL) { + tm->tm_isdst = i; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -timezone; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = tzname[i]; +#endif + bp = ep; + continue; + } + loadzone: + /* + * The hard way, load the zone! + */ + if (fromzone(&bp, tm, mandatory)) + continue; + goto out; + } + offs = 0; + for (i = 0; i < 4; ) { + if (isdigit(*bp)) { + offs = offs * 10 + (*bp++ - '0'); + i++; + continue; + } + if (i == 2 && *bp == ':') { + bp++; + continue; + } + break; + } + if (isdigit(*bp)) + goto out; + switch (i) { + case 2: + offs *= SECSPERHOUR; + break; + case 4: + i = offs % 100; + offs /= 100; + if (i >= SECSPERMIN) + goto out; + /* Convert minutes into decimal */ + offs = offs * SECSPERHOUR + i * SECSPERMIN; + break; + default: + out: + if (mandatory) + return NULL; + bp = zname; + continue; + } + /* ISO 8601 & RFC 3339 limit to 23:59 max */ + if (offs >= (HOURSPERDAY * SECSPERHOUR)) + goto out; + if (neg) + offs = -offs; + tm->tm_isdst = 0; /* XXX */ +#ifdef TM_GMTOFF + tm->TM_GMTOFF = offs; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + continue; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + while (isspace(*bp)) + bp++; + LEGAL_ALT(0); + continue; + + + default: /* Unknown/unsupported conversion. */ + return NULL; + } + } + + if (!HAVE_YDAY(state) && HAVE_YEAR(state)) { + if (HAVE_MON(state) && HAVE_MDAY(state)) { + /* calculate day of year (ordinal date) */ + tm->tm_yday = start_of_month[isleap_sum(tm->tm_year, + TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); + state |= S_YDAY; + } + else if (day_offset != -1) { + /* + * Set the date to the first Sunday (or Monday) + * of the specified week of the year. + */ + if (!HAVE_WDAY(state)) { + tm->tm_wday = day_offset; + state |= S_WDAY; + } + tm->tm_yday = (7 - + first_wday_of(tm->tm_year + TM_YEAR_BASE) + + day_offset) % 7 + (week_offset - 1) * 7 + + tm->tm_wday - day_offset; + state |= S_YDAY; + } + } + + if (HAVE_YDAY(state) && HAVE_YEAR(state)) { + int isleap; + + if (!HAVE_MON(state)) { + /* calculate month of day of year */ + i = 0; + isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE); + while (tm->tm_yday >= start_of_month[isleap][i]) + i++; + if (i > 12) { + i = 1; + tm->tm_yday -= start_of_month[isleap][12]; + tm->tm_year++; + } + tm->tm_mon = i - 1; + state |= S_MON; + } + + if (!HAVE_MDAY(state)) { + /* calculate day of month */ + isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE); + tm->tm_mday = tm->tm_yday - + start_of_month[isleap][tm->tm_mon] + 1; + state |= S_MDAY; + } + + if (!HAVE_WDAY(state)) { + /* calculate day of week */ + i = 0; + week_offset = first_wday_of(tm->tm_year); + while (i++ <= tm->tm_yday) { + if (week_offset++ >= 6) + week_offset = 0; + } + tm->tm_wday = week_offset; + state |= S_WDAY; + } + } + + return (char*)bp; +} + + +static const unsigned char* +conv_num(const unsigned char* buf, int* dest, unsigned int llim, unsigned int ulim) +{ + unsigned int result = 0; + unsigned char ch; + + /* The limit also determines the number of valid digits. */ + unsigned int rulim = ulim; + + ch = *buf; + if (ch < '0' || ch > '9') + return NULL; + + do { + result *= 10; + result += ch - '0'; + rulim /= 10; + ch = *++buf; + } while ((result <= ulim) && rulim && ch >= '0' && ch <= '9'); + + if (result < llim || result > ulim) + return NULL; + + *dest = result; + return buf; +} + +static const unsigned char* +find_string(const unsigned char* bp, int* tgt, const char* const* n1, + const char* const* n2, int c) +{ + int i; + size_t len; + + /* check full name - then abbreviated ones */ + for (; n1 != NULL; n1 = n2, n2 = NULL) { + for (i = 0; i < c; i++, n1++) { + len = strlen(*n1); + if (strncasecmp(*n1, (const char*)bp, len) == 0) { + *tgt = i; + return bp + len; + } + } + } + + /* Nothing matched */ + return NULL; +} +#pragma endregion + +#endif//WIN32 + + +void win_clear() +{ +#ifdef WIN32 + WSACleanup(); +#endif // WIN32 +} + diff --git a/muduo/base/CrossPlatformAdapterFunction.h b/muduo/base/CrossPlatformAdapterFunction.h new file mode 100644 index 000000000..e226c9ae6 --- /dev/null +++ b/muduo/base/CrossPlatformAdapterFunction.h @@ -0,0 +1,345 @@ +#pragma once + +#include + +#include + + + +#ifdef __linux__ +#include +#include +#define auto_ptr unique_ptr +#endif // __linux__ + + +//////////////////////WIN32///////////////////////////////// +#ifdef WIN32 +#pragma warning(disable:4068) +//head +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//lib +#pragma comment(lib, "Ws2_32.lib") + +#include +#include +#pragma comment(lib, "Dbghelp.lib") + +//function +#define S_ISREG(m) (((m) & 0170000) == (0100000)) +#define S_ISDIR(m) (((m) & 0170000) == (0040000)) + +#define __builtin_expect(EXP, C) (EXP) +#ifndef __attribute__ +#define __attribute__(x) +#endif +#undef min +#undef max +#ifdef ERROR +#undef ERROR +#endif +#define timegm _mkgmtime + +#define eventfd(initval, flags) sockets::createNonblockingOrDie(AF_INET) +#define timerfd_create(clockid, flags) muduo::net::sockets::createNonblockingOrDie(AF_INET) + + +#define SOL_TCP 0 +#define TCP_INFO 0 + +//type +typedef long long ssize_t; +#define pid_t int32_t +#undef off_t +#define off_t uint64_t +#define sa_family_t int32_t +#define rlim_t uint64_t +typedef uint64_t uid_t; +typedef uint32_t in_addr_t; + + + +//keyword +#define __thread __declspec( thread ) +#define O_CLOEXEC 0 +#define RLIMIT_AS 0 +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#undef SSIZE_MAX +#define SSIZE_MAX (LLONG_MAX >> 1) +#define __MAX_ALLOCA_CUTOFF 65536 +#undef POLLRDHUP +#define POLLRDHUP 0 +#define EFD_NONBLOCK FIONBIO +#define EFD_CLOEXEC 0 +#define CLOCK_MONOTONIC 0 +#define TFD_NONBLOCK FIONBIO +#define TFD_CLOEXEC 0 +#undef POLLPRI +#define POLLPRI 0 +#define SOCK_NONBLOCK 0 +#define SOCK_CLOEXEC 0 + + +//struct +struct iovec { + void *iov_base; /* Starting address */ + ssize_t iov_len; /* Number of bytes to transfer */ +}; + +struct rlimit { + rlim_t rlim_cur; /* Soft limit */ + rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */ +}; + +struct itimerspec { + struct timespec it_interval; /* Interval for periodic timer */ + struct timespec it_value; /* Initial expiration */ +}; + + + +//class + + + + +//function +void setbuffer(FILE *stream, char *buf, size_t size); + +char * strerror_r(int errnum, char *buf, size_t buflen); + + + +size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *stream); + +struct tm *gmtime_r(const time_t *timep, struct tm *result); + +int gettimeofday(struct timeval *tv, struct timezone *tz); + +int gethostbyname_r(const char *name, + struct hostent *ret, char *buf, size_t buflen, + struct hostent **result, int *h_errnop); + +char* strptime(const char* s, + const char* f, + struct tm* tm); + +/* Swap bytes in 16 bit value. */ +#define __bswap_constant_16(x) \ + ((unsigned short int)((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))) + +/* Swap bytes in 32 bit value. */ +#define __bswap_constant_32(x) \ + ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ + (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) + +static __inline unsigned int +__bswap_32(unsigned int __bsx) +{ + return __bswap_constant_32(__bsx); +} + +/* Swap bytes in 64 bit value. */ +# define __bswap_constant_64(x) \ + ((((x) & 0xff00000000000000ull) >> 56) \ + | (((x) & 0x00ff000000000000ull) >> 40) \ + | (((x) & 0x0000ff0000000000ull) >> 24) \ + | (((x) & 0x000000ff00000000ull) >> 8) \ + | (((x) & 0x00000000ff000000ull) << 8) \ + | (((x) & 0x0000000000ff0000ull) << 24) \ + | (((x) & 0x000000000000ff00ull) << 40) \ + | (((x) & 0x00000000000000ffull) << 56)) + +static __inline uint64_t +__bswap_64(uint64_t __bsx) +{ + return __bswap_constant_64(__bsx); +} + +static __inline unsigned short int +__bswap_16(unsigned short int __bsx) +{ + return __bswap_constant_16(__bsx); +} + +uint32_t be32toh(uint32_t x); +uint64_t be64toh(uint64_t x); +uint32_t htobe32(uint32_t x); +uint64_t htobe64(uint64_t x); + +# define htobe16(x) __bswap_16 (x) +# define htole16(x) (x) +# define be16toh(x) __bswap_16 (x) +# define le16toh(x) (x) + +# define htole32(x) (x) +# define le32toh(x) (x) +# define htole64(x) (x) +# define le64toh(x) (x) + +ssize_t pread(int fd, void *buf, size_t count, off_t offset); + +ssize_t winreadsock(int fd, void *buf, size_t count); + +ssize_t winwritesock(int fd, const void *buf, size_t count); + +ssize_t winclosesock(int fd); + +ssize_t readv(int fd, const struct iovec *iov, int iovcnt); + +void bzero(void *s, size_t n); + +char *basename(const char* full_path); + +int nanosleep(const struct timespec *req, struct timespec *rem); + +int setrlimit(int resource, const struct rlimit *rlim); + +int timerfd_settime(int fd, int flags, + const struct itimerspec *new_value, + struct itimerspec *old_value); + +unsigned int sleep(unsigned int seconds); + +int poll(struct pollfd *fds, size_t nfds, int timeout); + +uid_t geteuid(); + +int muduo_eventloop_poll_timeout_ms(); +void muduo_ignore_sigpipe(); +void muduo_close_fd_compat(int fd); +bool muduo_eventloop_use_timerqueue_loop(); +ssize_t muduo_eventloop_wakeup_write(int fd); +ssize_t muduo_eventloop_wakeup_read(int fd); +bool muduo_channel_default_log_hup(); +bool muduo_channel_return_after_close(); +int muduo_socket_connect_compat(int sockfd, const struct sockaddr* addr); +ssize_t muduo_socket_read_compat(int sockfd, void* buf, size_t count); +ssize_t muduo_socket_write_compat(int sockfd, const void* buf, size_t count); +int muduo_socket_close_compat(int sockfd); +std::string muduo_make_loop_thread_name(const std::string& base, int index); + +bool muduo_use_epoll_poller(); +int muduo_open_idle_fd(int family); +void muduo_close_idle_fd(int fd); +void muduo_handle_emfile_idle_fd(int listenfd, int* idlefd, int family); + +ssize_t muduo_read_timerfd(int timerfd, uint64_t* howmany); +void muduo_close_timerfd(int timerfd); +FILE* muduo_fopen_append(const char* filename); +ssize_t muduo_read_file_compat(int fd, void* buf, size_t count); +int muduo_create_nonblocking_or_die(int family); +pid_t muduo_gettid(); +void muduo_after_fork_in_child(); +muduo::string muduo_stack_trace(bool demangle); + +struct tcp_info { + typedef uint32_t __u8; + typedef uint32_t __u32; + typedef uint64_t __u64; + __u8 tcpi_state; //tcp state: TCP_SYN_SENT,TCP_SYN_RECV,TCP_FIN_WAIT1,TCP_CLOSE etc + __u8 tcpi_ca_state; //congestion state: + __u8 tcpi_retransmits; //重传数,表示当前待重传的包数,这个值在重传完毕后清零 + __u8 tcpi_probes; ///* 持续定时器或保活定时器发送且未确认的段数*/ + __u8 tcpi_backoff; //用来计算持续定时器的下一个设计值的指数退避算法指数,在传送超时是会递增。 + __u8 tcpi_options; //tcp头部选项是否包含:扩展因子、时间戳、MSS等内容 + __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; //扩展因子数值 + __u8 tcpi_delivery_rate_app_limited : 1; //限速标志 + + __u32 tcpi_rto; //重传超时时间,这个和RTT有关系,RTT越大,rto越大 + __u32 tcpi_ato; //用来延时确认的估值,单位为微秒. + //在收到TCP报文时,会根据本次与上次接收的时间间隔来调整改制,在设置延迟确认定时器也会根据 + //条件修改该值 + __u32 tcpi_snd_mss; // 本端的MSS + __u32 tcpi_rcv_mss; // 对端的MSS + + __u32 tcpi_unacked; //未确认的数据段数 + __u32 tcpi_sacked; //2个含义:server端在listen阶段,可以接收连接的数量;收到的SACK报文数量 + __u32 tcpi_lost; //本端在发送出去被丢失的报文数。重传完成后清零 + __u32 tcpi_retrans; /* 重传且未确认的数据段数 */ + __u32 tcpi_fackets; + + /* Times. */ + __u32 tcpi_last_data_sent; //当前时间-最近一个包的发送时间,单位是毫秒 + __u32 tcpi_last_ack_sent; /* 未使用*/ + __u32 tcpi_last_data_recv; //当前时间-最近接收数据包的时间,单位是毫秒 + __u32 tcpi_last_ack_recv; //当前时间-最近接收ack的时间,单位是毫秒 + + /* Metrics. */ + __u32 tcpi_pmtu; /* 最后一次更新的路径MTU */ + __u32 tcpi_rcv_ssthresh; //当前接收窗口的大小 + __u32 tcpi_rtt; //smoothed round trip time,微妙 + __u32 tcpi_rttvar; //描述RTT的平均偏差,该值越大,说明RTT抖动越大 + __u32 tcpi_snd_ssthresh; //拥塞控制慢开始阈值 + __u32 tcpi_snd_cwnd; //拥塞控制窗口大小 + __u32 tcpi_advmss; //本端的MSS上限 + __u32 tcpi_reordering; /* 没有丢包时,可以重新排序的数据段数 */ + + __u32 tcpi_rcv_rtt; // 作为接收端,测出的RTT值,单位为微秒. 这个值不是对方计算并传送过来的rtt,而是作为接收端,在没发送数据的情况下 + // 通过接收发送端发送的数据的情况计算得到的rtt值。在数据发送方,如果不接受数据,这个值一般情况下为0。 + __u32 tcpi_rcv_space; /* 当前接收缓存的大小 */ + + __u32 tcpi_total_retrans; //统计总重传的包数,持续增长。 + + __u64 tcpi_pacing_rate; //发送速率 + __u64 tcpi_max_pacing_rate; //最大发送速率,默认是unlimited,可以通过SO_MAX_PACING_RATE来设置 + __u64 tcpi_bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked */ + __u64 tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */ + __u32 tcpi_segs_out; /* RFC4898 tcpEStatsPerfSegsOut */ + __u32 tcpi_segs_in; /* RFC4898 tcpEStatsPerfSegsIn */ + + __u32 tcpi_notsent_bytes; + __u32 tcpi_min_rtt; + __u32 tcpi_data_segs_in; /* RFC4898 tcpEStatsDataSegsIn */ + __u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */ + + __u64 tcpi_delivery_rate; + + __u64 tcpi_busy_time; /* Time (usec) busy sending data */ + __u64 tcpi_rwnd_limited; /* Time (usec) limited by receive window */ + __u64 tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */ +}; + +int getsockopt(int sockfd, int level, int optname, + int *optval, socklen_t *optlen); +void setsockopt(int sockfd, int level, int optname, + int *optval, socklen_t optlen); +int getsockopt(int sockfd, int level, int optname, + tcp_info *optval, socklen_t *optlen); +int accept4(int sockfd, struct sockaddr *addr, + socklen_t *addrlen, int flags); + +int32_t win_connect(int sockfd, const struct sockaddr* addr +); + +#endif//WIN32 + +void win_clear(); + +namespace muduo +{ + namespace ProcessInfo + { + std::string localip(); + } + +}//namespace muduo + + + + diff --git a/muduo/base/CurrentThread.cc b/muduo/base/CurrentThread.cc index 15edff0b5..2ded6351f 100644 --- a/muduo/base/CurrentThread.cc +++ b/muduo/base/CurrentThread.cc @@ -21,6 +21,7 @@ static_assert(std::is_same::value, "pid_t should be int"); string stackTrace(bool demangle) { +#ifdef __linux__ string stack; const int max_frames = 200; void* frame[max_frames]; @@ -71,6 +72,9 @@ string stackTrace(bool demangle) free(strings); } return stack; +#else + return muduo_stack_trace(demangle); +#endif } } // namespace CurrentThread diff --git a/muduo/base/FileUtil.cc b/muduo/base/FileUtil.cc index 2f7c24b15..80db12832 100644 --- a/muduo/base/FileUtil.cc +++ b/muduo/base/FileUtil.cc @@ -6,6 +6,9 @@ #include "muduo/base/FileUtil.h" #include "muduo/base/Logging.h" +#ifndef __linux__ +#include +#endif #include #include #include @@ -16,7 +19,11 @@ using namespace muduo; FileUtil::AppendFile::AppendFile(StringArg filename) +#ifdef __linux__ : fp_(::fopen(filename.c_str(), "ae")), // 'e' for O_CLOEXEC +#else + : fp_(muduo_fopen_append(filename.c_str())), +#endif writtenBytes_(0) { assert(fp_); @@ -129,7 +136,11 @@ int FileUtil::ReadSmallFile::readToString(int maxSize, while (content->size() < implicit_cast(maxSize)) { size_t toRead = std::min(implicit_cast(maxSize) - content->size(), sizeof(buf_)); +#ifdef __linux__ ssize_t n = ::read(fd_, buf_, toRead); +#else + ssize_t n = muduo_read_file_compat(fd_, buf_, toRead); +#endif if (n > 0) { content->append(buf_, n); diff --git a/muduo/base/LogFile.cc b/muduo/base/LogFile.cc index 2a12abe40..17690aac9 100644 --- a/muduo/base/LogFile.cc +++ b/muduo/base/LogFile.cc @@ -29,7 +29,9 @@ LogFile::LogFile(const string& basename, lastRoll_(0), lastFlush_(0) { +#ifdef __linux__ assert(basename.find('/') == string::npos); +#endif rollFile(); } diff --git a/muduo/base/Mutex.h b/muduo/base/Mutex.h index 20746bdc6..ef097c66f 100644 --- a/muduo/base/Mutex.h +++ b/muduo/base/Mutex.h @@ -10,6 +10,7 @@ #include "muduo/base/noncopyable.h" #include #include +#include // Thread safety annotations { // https://clang.llvm.org/docs/ThreadSafetyAnalysis.html @@ -118,6 +119,7 @@ namespace muduo // mutable MutexLock mutex_; // std::vector data_ GUARDED_BY(mutex_); // }; +#ifdef __muduo_asynchronization__ class CAPABILITY("mutex") MutexLock : noncopyable { public: @@ -222,6 +224,10 @@ class SCOPED_CAPABILITY MutexLockGuard : noncopyable MutexLock& mutex_; }; +#else +typedef std::mutex MutexLock; +typedef std::unique_lock MutexLockGuard; +#endif//__muduo_asynchronization__ } // namespace muduo diff --git a/muduo/base/ProcessInfo.cc b/muduo/base/ProcessInfo.cc index 6ab5ad184..0fbf71ad6 100644 --- a/muduo/base/ProcessInfo.cc +++ b/muduo/base/ProcessInfo.cc @@ -7,6 +7,7 @@ #include "muduo/base/CurrentThread.h" #include "muduo/base/FileUtil.h" +#ifdef __linux__ #include #include @@ -56,9 +57,12 @@ int g_clockTicks = static_cast(::sysconf(_SC_CLK_TCK)); int g_pageSize = static_cast(::sysconf(_SC_PAGE_SIZE)); } // namespace detail } // namespace muduo +#endif//__linux__ using namespace muduo; +#ifdef __linux__ using namespace muduo::detail; +#endif//__linux__ pid_t ProcessInfo::pid() { @@ -72,6 +76,7 @@ string ProcessInfo::pidString() return buf; } +#ifdef __linux__ uid_t ProcessInfo::uid() { return ::getuid(); @@ -120,6 +125,7 @@ bool ProcessInfo::isDebugBuild() return true; #endif } +#endif // __linux__ string ProcessInfo::hostname() { @@ -137,6 +143,7 @@ string ProcessInfo::hostname() } } +#ifdef __linux__ string ProcessInfo::procname() { return procname(procStat()).as_string(); @@ -244,3 +251,4 @@ std::vector ProcessInfo::threads() return result; } +#endif // __linux__ diff --git a/muduo/base/Thread.cc b/muduo/base/Thread.cc index 630a0c9ef..405ea0ad4 100644 --- a/muduo/base/Thread.cc +++ b/muduo/base/Thread.cc @@ -25,7 +25,11 @@ namespace detail pid_t gettid() { +#ifdef __linux__ return static_cast(::syscall(SYS_gettid)); +#else + return muduo_gettid(); +#endif } void afterFork() @@ -43,12 +47,17 @@ class ThreadNameInitializer { muduo::CurrentThread::t_threadName = "main"; CurrentThread::tid(); +#ifdef __linux__ pthread_atfork(NULL, NULL, &afterFork); +#else + muduo_after_fork_in_child(); +#endif } }; ThreadNameInitializer init; +#ifdef __muduo_asynchronization__ struct ThreadData { typedef muduo::Thread::ThreadFunc ThreadFunc; @@ -112,7 +121,7 @@ void* startThread(void* obj) delete data; return NULL; } - +#endif //__muduo_asynchronization__ } // namespace detail void CurrentThread::cacheTid() @@ -138,7 +147,7 @@ void CurrentThread::sleepUsec(int64_t usec) } AtomicInt32 Thread::numCreated_; - +#ifdef __muduo_asynchronization__ Thread::Thread(ThreadFunc func, const string& n) : started_(false), joined_(false), @@ -196,5 +205,5 @@ int Thread::join() joined_ = true; return pthread_join(pthreadId_, NULL); } - +#endif//__muduo_asynchronization__ } // namespace muduo diff --git a/muduo/base/Thread.h b/muduo/base/Thread.h index d2305a2c9..2d33425f1 100644 --- a/muduo/base/Thread.h +++ b/muduo/base/Thread.h @@ -14,9 +14,10 @@ #include #include +#include namespace muduo { - +#ifdef __muduo_asynchronization__ class Thread : noncopyable { public: @@ -49,6 +50,35 @@ class Thread : noncopyable static AtomicInt32 numCreated_; }; +#else +class Thread : noncopyable +{ +public: + typedef std::function ThreadFunc; + + explicit Thread(ThreadFunc func, const string& name = string()) + : func_(func), + name_(name) + { + numCreated_.incrementAndGet(); + } + // FIXME: make it movable in C++11 + ~Thread() {if (started())thread_.join();} + void start() { std::thread t(func_); thread_.swap(t); } + int join() { thread_.join(); return 1; } + + bool started() const { return thread_.joinable(); } + const string& name() const { return name_; } + + static int numCreated() { return numCreated_.get(); } + +private: + ThreadFunc func_; + string name_; + static AtomicInt32 numCreated_; + std::thread thread_; +}; +#endif//__muduo_asynchronization__ } // namespace muduo #endif // MUDUO_BASE_THREAD_H diff --git a/muduo/base/ThreadLocal.h b/muduo/base/ThreadLocal.h index b8283bc3d..2c8355452 100644 --- a/muduo/base/ThreadLocal.h +++ b/muduo/base/ThreadLocal.h @@ -9,6 +9,7 @@ #include "muduo/base/Mutex.h" #include "muduo/base/noncopyable.h" +#ifdef __linux__ #include namespace muduo @@ -56,4 +57,5 @@ class ThreadLocal : noncopyable } // namespace muduo +#endif//__linux__ #endif // MUDUO_BASE_THREADLOCAL_H diff --git a/muduo/base/ThreadPool.cc b/muduo/base/ThreadPool.cc index 6905dd7d5..6fc235f54 100644 --- a/muduo/base/ThreadPool.cc +++ b/muduo/base/ThreadPool.cc @@ -14,8 +14,13 @@ using namespace muduo; ThreadPool::ThreadPool(const string& nameArg) : mutex_(), +#ifdef __muduo_asynchronization__ notEmpty_(mutex_), notFull_(mutex_), +#else + notEmpty_(), + notFull_(), +#endif name_(nameArg), maxQueueSize_(0), running_(false) @@ -80,7 +85,11 @@ void ThreadPool::run(Task task) MutexLockGuard lock(mutex_); while (isFull() && running_) { +#ifdef __muduo_asynchronization__ notFull_.wait(); +#else + notFull_.wait(lock); +#endif } if (!running_) return; assert(!isFull()); @@ -96,7 +105,11 @@ ThreadPool::Task ThreadPool::take() // always use a while-loop, due to spurious wakeup while (queue_.empty() && running_) { +#ifdef __muduo_asynchronization__ notEmpty_.wait(); +#else + notEmpty_.wait(lock); +#endif } Task task; if (!queue_.empty()) @@ -113,7 +126,9 @@ ThreadPool::Task ThreadPool::take() bool ThreadPool::isFull() const { +#ifdef __muduo_asynchronization__ mutex_.assertLocked(); +#endif return maxQueueSize_ > 0 && queue_.size() >= maxQueueSize_; } diff --git a/muduo/base/TimeZone.cc b/muduo/base/TimeZone.cc index ca9d2ced4..d12a36d05 100644 --- a/muduo/base/TimeZone.cc +++ b/muduo/base/TimeZone.cc @@ -110,11 +110,19 @@ class File : noncopyable string readBytes(int n) { +#ifdef __linux__ char buf[n]; ssize_t nr = ::fread(buf, 1, n, fp_); if (nr != n) throw std::logic_error("no enough data"); return string(buf, n); +#else + std::vector buf(n); + ssize_t nr = ::fread(buf.data(), 1, n, fp_); + if (nr != n) + throw std::logic_error("no enough data"); + return string(buf.data(), n); +#endif } string readToEnd() diff --git a/muduo/base/Timestamp.h b/muduo/base/Timestamp.h index ccb522d3c..0f90fe7bd 100644 --- a/muduo/base/Timestamp.h +++ b/muduo/base/Timestamp.h @@ -20,9 +20,13 @@ namespace muduo /// This class is immutable. /// It's recommended to pass it by value, since it's passed in register on x64. /// +#ifdef __linux__ class Timestamp : public muduo::copyable, public boost::equality_comparable, public boost::less_than_comparable +#else +class Timestamp : public muduo::copyable +#endif { public: /// diff --git a/muduo/base/process_info.cpp b/muduo/base/process_info.cpp new file mode 100644 index 000000000..5a46c2910 --- /dev/null +++ b/muduo/base/process_info.cpp @@ -0,0 +1,50 @@ +#include "process_info.h" +//////////////////////WIN32///////////////////////////////// +#ifdef __linux__ +#include +#include +#include +#include + +#include "muduo/base/ProcessInfo.h" +#endif // __linux__ + +#ifdef WIN32 + +#include +#include + +namespace muduo +{ + namespace ProcessInfo + { + + std::string hostname() + { + // HOST_NAME_MAX 64 + // _POSIX_HOST_NAME_MAX 255 + char buf[256]; + if (::gethostname(buf, sizeof buf) == 0) + { + buf[sizeof(buf) - 1] = '\0'; + return buf; + } + else + { + return "unknownhost"; + } + } + + } + +}//namespace muduo + + +#endif//WIN32 + +std::string localip() +{ + auto host = muduo::ProcessInfo::hostname(); + struct hostent* host_entry = gethostbyname(host.c_str()); + return inet_ntoa(*((struct in_addr*)host_entry->h_addr)); +} \ No newline at end of file diff --git a/muduo/base/process_info.h b/muduo/base/process_info.h new file mode 100644 index 000000000..18a63002e --- /dev/null +++ b/muduo/base/process_info.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string localip(); \ No newline at end of file diff --git a/muduo/base/tests/CMakeLists.txt b/muduo/base/tests/CMakeLists.txt index 73388b43b..20121788f 100644 --- a/muduo/base/tests/CMakeLists.txt +++ b/muduo/base/tests/CMakeLists.txt @@ -1,36 +1,24 @@ -add_executable(asynclogging_test AsyncLogging_test.cc) -target_link_libraries(asynclogging_test muduo_base) +add_executable(asyncloggingtest asyncloggingtest/AsyncLoggingTest.cc) +target_link_libraries(asyncloggingtest muduo_base) -add_executable(atomic_unittest Atomic_unittest.cc) -add_test(NAME atomic_unittest COMMAND atomic_unittest) +add_executable(blockingqueuetest blockingqueuetest/BlockingQueueTest.cc) +target_link_libraries(blockingqueuetest muduo_base) -add_executable(blockingqueue_test BlockingQueue_test.cc) -target_link_libraries(blockingqueue_test muduo_base) +add_executable(boundedblockingqueuetest boundedblockingqueuetest/BoundedBlockingQueueTest.cc) +target_link_libraries(boundedblockingqueuetest muduo_base) -add_executable(blockingqueue_bench BlockingQueue_bench.cc) -target_link_libraries(blockingqueue_bench muduo_base) +add_executable(datetest datetest/DateTest.cc) +target_link_libraries(datetest muduo_base) +add_test(NAME datetest COMMAND datetest) -add_executable(blockingqueue_bench2 BlockingQueue_bench2.cc) -target_link_libraries(blockingqueue_bench2 muduo_base) -# set_target_properties(blockingqueue_bench2 PROPERTIES COMPILE_FLAGS "-std=c++17") +add_executable(exceptiontest exceptiontest/ExceptionTest.cc) +target_link_libraries(exceptiontest muduo_base) +add_test(NAME exceptiontest COMMAND exceptiontest) -add_executable(boundedblockingqueue_test BoundedBlockingQueue_test.cc) -target_link_libraries(boundedblockingqueue_test muduo_base) +add_executable(fileutiltest fileutiltest/FileUtilTest.cc) +target_link_libraries(fileutiltest muduo_base) +add_test(NAME fileutiltest COMMAND fileutiltest) -add_executable(date_unittest Date_unittest.cc) -target_link_libraries(date_unittest muduo_base) -add_test(NAME date_unittest COMMAND date_unittest) - -add_executable(exception_test Exception_test.cc) -target_link_libraries(exception_test muduo_base) -add_test(NAME exception_test COMMAND exception_test) - -add_executable(fileutil_test FileUtil_test.cc) -target_link_libraries(fileutil_test muduo_base) -add_test(NAME fileutil_test COMMAND fileutil_test) - -add_executable(fork_test Fork_test.cc) -target_link_libraries(fork_test muduo_base) if(ZLIB_FOUND) add_executable(gzipfile_test GzipFile_test.cc) @@ -38,14 +26,11 @@ if(ZLIB_FOUND) add_test(NAME gzipfile_test COMMAND gzipfile_test) endif() -add_executable(logfile_test LogFile_test.cc) -target_link_libraries(logfile_test muduo_base) +add_executable(logfiletest logfiletest/LogFileTest.cc) +target_link_libraries(logfiletest muduo_base) -add_executable(logging_test Logging_test.cc) -target_link_libraries(logging_test muduo_base) - -add_executable(logstream_bench LogStream_bench.cc) -target_link_libraries(logstream_bench muduo_base) +add_executable(logstreambench logstreambench/LogStreamBench.cc) +target_link_libraries(logstreambench muduo_base) if(BOOSTTEST_LIBRARY) add_executable(logstream_test LogStream_test.cc) @@ -53,41 +38,20 @@ target_link_libraries(logstream_test muduo_base boost_unit_test_framework) add_test(NAME logstream_test COMMAND logstream_test) endif() -add_executable(mutex_test Mutex_test.cc) -target_link_libraries(mutex_test muduo_base) - -add_executable(processinfo_test ProcessInfo_test.cc) -target_link_libraries(processinfo_test muduo_base) - -add_executable(singleton_test Singleton_test.cc) -target_link_libraries(singleton_test muduo_base) - -add_executable(singleton_threadlocal_test SingletonThreadLocal_test.cc) -target_link_libraries(singleton_threadlocal_test muduo_base) - -add_executable(thread_bench Thread_bench.cc) -target_link_libraries(thread_bench muduo_base) - -add_executable(thread_test Thread_test.cc) -target_link_libraries(thread_test muduo_base) - -add_executable(threadlocal_test ThreadLocal_test.cc) -target_link_libraries(threadlocal_test muduo_base) - -add_executable(threadlocalsingleton_test ThreadLocalSingleton_test.cc) -target_link_libraries(threadlocalsingleton_test muduo_base) +add_executable(threadbench threadbench/ThreadBench.cc) +target_link_libraries(threadbench muduo_base) -add_executable(threadpool_test ThreadPool_test.cc) -target_link_libraries(threadpool_test muduo_base) +add_executable(threadtest threadtest/ThreadTest.cc) +target_link_libraries(threadtest muduo_base) -add_executable(timestamp_unittest Timestamp_unittest.cc) -target_link_libraries(timestamp_unittest muduo_base) -add_test(NAME timestamp_unittest COMMAND timestamp_unittest) +add_executable(threadpooltest threadpooltest/ThreadPoolTest.cc) +target_link_libraries(threadpooltest muduo_base) -add_executable(timezone_unittest TimeZone_unittest.cc) -target_link_libraries(timezone_unittest muduo_base) -add_test(NAME timezone_unittest COMMAND timezone_unittest) +add_executable(timestamptest timestamptest/TimestampTest.cc) +target_link_libraries(timestamptest muduo_base) +add_test(NAME timestamptest COMMAND timestamptest) -add_executable(timezone_util TimeZone_util.cc) -target_link_libraries(timezone_util muduo_base) +add_executable(timezonetest timezonetest/TimeZoneTest.cc) +target_link_libraries(timezonetest muduo_base) +add_test(NAME timezonetest COMMAND timezonetest) diff --git a/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.cc b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.cc new file mode 100644 index 000000000..6267fd381 --- /dev/null +++ b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.cc @@ -0,0 +1,71 @@ +#include + +#include +#include +#include + +#include +#include + +off_t kRollSize = 500 * 1000 * 1000; + +muduo::AsyncLogging* g_asyncLog = NULL; + +void asyncOutput(const char* msg, int len) +{ + g_asyncLog->append(msg, len); +} + +void bench(bool longLog) +{ + muduo::Logger::setOutput(asyncOutput); + + int cnt = 0; + const int kBatch = 1000; + muduo::string empty = " "; + muduo::string longStr(3000, 'X'); + longStr += " "; + + for (int t = 0; t < 30; ++t) + { + muduo::Timestamp start = muduo::Timestamp::now(); + for (int i = 0; i < kBatch; ++i) + { + LOG_INFO << "Hello 0123456789" << " abcdefghijklmnopqrstuvwxyz " + << (longLog ? longStr : empty) + << cnt; + ++cnt; + } + muduo::Timestamp end = muduo::Timestamp::now(); + printf("%f\n", timeDifference(end, start) * 1000000 / kBatch); + struct timespec ts = { 0, 500 * 1000 * 1000 }; + nanosleep(&ts, NULL); + } +} + +TEST(AsyncLogging, Test) +{ + { + // set max virtual memory to 2GB. + size_t kOneGB = 1000 * 1024 * 1024; + rlimit rl = { 2 * kOneGB, 2 * kOneGB }; + setrlimit(RLIMIT_AS, &rl); + } + + printf("pid = %d\n", getpid()); + + + muduo::AsyncLogging log("test", kRollSize); + log.start(); + g_asyncLog = &log; + + + bench(true); + bench(false); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.sln b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.sln new file mode 100644 index 000000000..c7302dbde --- /dev/null +++ b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncLoggingTest", "AsyncLoggingTest.vcxproj", "{587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Debug|x64.ActiveCfg = Debug|x64 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Debug|x64.Build.0 = Debug|x64 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Debug|x86.ActiveCfg = Debug|Win32 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Debug|x86.Build.0 = Debug|Win32 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Release|x64.ActiveCfg = Release|x64 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Release|x64.Build.0 = Release|x64 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Release|x86.ActiveCfg = Release|Win32 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.vcxproj.filters b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.vcxproj.filters new file mode 100644 index 000000000..67c522b6e --- /dev/null +++ b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.vcxproj.filters @@ -0,0 +1,64 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.vcxproj.user b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.vcxproj.user new file mode 100644 index 000000000..88a550947 --- /dev/null +++ b/muduo/base/tests/asyncloggingtest/AsyncLoggingTest.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/muduo/base/tests/asyncloggingtest/asyncloggingtest.vcxproj b/muduo/base/tests/asyncloggingtest/asyncloggingtest.vcxproj new file mode 100644 index 000000000..a6d985ec1 --- /dev/null +++ b/muduo/base/tests/asyncloggingtest/asyncloggingtest.vcxproj @@ -0,0 +1,174 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {587B56C6-DFBC-4CE0-BFE8-E51E71B9F3B0} + Win32Proj + AsyncLoggingTest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;WIN32;%(PreprocessorDefinitions) + ../../../../;../../../../contrib/windows/include/;../../../;F:\work\turn-based-game\third_party\boost/;%(AdditionalIncludeDirectories) + MultiThreadedDebug + stdcpp20 + stdc17 + + + Console + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib/gtest.lib;%(AdditionalDependencies) + ../../../../lib;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/blockingqueuetest/BlockingQueueTest.cc b/muduo/base/tests/blockingqueuetest/BlockingQueueTest.cc new file mode 100644 index 000000000..947a72091 --- /dev/null +++ b/muduo/base/tests/blockingqueuetest/BlockingQueueTest.cc @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace std; + +class BLQTest : public testing::Test { +protected: + + BLQTest() + : m_vQueue(), + m_vThreads() + { + } + + virtual void SetUp() + { + for (size_t i = 0; i < 5; ++i) + { + m_vThreads.push_back(std::move(thread(bind(&BLQTest::Produce, this)))); + } + + for (size_t i = 0; i < 5; ++i) + { + m_vThreads.push_back(std::move(thread(bind(&BLQTest::Consume, this)))); + } + } + + void JoinAll() + { + for_each(m_vThreads.begin(), m_vThreads.end(), bind(&thread::join, placeholders::_1)); + } + + virtual void TearDown() + { + m_vThreads.clear(); + } + + virtual ~BLQTest() { } + + + void Produce() + { + + + for (size_t i = 0; i < 100; i++) + { + char buf[32]; + snprintf(buf, sizeof buf, "hello %zd", i); + m_vQueue.put(buf); + //printf("tid=%d, put data = %s, size = %zd\n", std::this_thread::get_id(), buf, m_vQueue.size()); + } + + + + } + + void Consume() + { + for (size_t i = 0; i < 100; i++) + { + std::string d(m_vQueue.take()); + //printf("tid=%d, get data = %s, size = %zd\n", std::this_thread::get_id(), d.c_str(), m_vQueue.size()); + } + } + + + muduo::BlockingQueue m_vQueue; + vector m_vThreads; +}; + +TEST_F(BLQTest, TestConsumeNumEqProduceNum) { + JoinAll(); + EXPECT_EQ(0, (int)m_vQueue.size()); +} + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + + diff --git a/muduo/base/tests/blockingqueuetest/BlockingQueueTest.sln b/muduo/base/tests/blockingqueuetest/BlockingQueueTest.sln new file mode 100644 index 000000000..670dea36c --- /dev/null +++ b/muduo/base/tests/blockingqueuetest/BlockingQueueTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockingQueueTest", "BlockingQueueTest.vcxproj", "{1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Debug|x64.ActiveCfg = Debug|x64 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Debug|x64.Build.0 = Debug|x64 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Debug|x86.ActiveCfg = Debug|Win32 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Debug|x86.Build.0 = Debug|Win32 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Release|x64.ActiveCfg = Release|x64 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Release|x64.Build.0 = Release|x64 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Release|x86.ActiveCfg = Release|Win32 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/blockingqueuetest/BlockingQueueTest.vcxproj.filters b/muduo/base/tests/blockingqueuetest/BlockingQueueTest.vcxproj.filters new file mode 100644 index 000000000..80fdd9c0b --- /dev/null +++ b/muduo/base/tests/blockingqueuetest/BlockingQueueTest.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/blockingqueuetest/blockingqueuetest.vcxproj b/muduo/base/tests/blockingqueuetest/blockingqueuetest.vcxproj new file mode 100644 index 000000000..7c960db58 --- /dev/null +++ b/muduo/base/tests/blockingqueuetest/blockingqueuetest.vcxproj @@ -0,0 +1,166 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {1AE7B548-9CA5-4B22-AFD3-F80E95D07FCE} + Win32Proj + BlockingQueueTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + ../../../../lib;%(AdditionalLibraryDirectories) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + Level4 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreaded + + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.cc b/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.cc new file mode 100644 index 000000000..4a2f0ac68 --- /dev/null +++ b/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.cc @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace std; + +class BBQTest : public testing::Test { +protected: + + BBQTest() + : m_vQueue(20), + m_vThreads() + { + } + + virtual void SetUp() + { + for (size_t i = 0; i < 5; ++i) + { + m_vThreads.push_back(std::move(thread(bind(&BBQTest::Produce, this)))); + } + + for (size_t i = 0; i < 5; ++i) + { + m_vThreads.push_back(std::move(thread(bind(&BBQTest::Consume, this)))); + } + } + + void JoinAll() + { + for_each(m_vThreads.begin(), m_vThreads.end(), bind(&thread::join, placeholders::_1)); + } + + virtual void TearDown() + { + m_vThreads.clear(); + } + + virtual ~BBQTest() { } + + + void Produce() + { + + + for (size_t i = 0; i < 1000; i++) + { + char buf[32]; + snprintf(buf, sizeof buf, "hello %zd", i); + m_vQueue.put(buf); + //printf("tid=%d, put data = %s, size = %zd\n", std::this_thread::get_id(), buf, m_vQueue.size()); + } + + + + } + + void Consume() + { + for (size_t i = 0; i < 1000; i++) + { + std::string d(m_vQueue.take()); + //printf("tid=%d, get data = %s, size = %zd\n", std::this_thread::get_id(), d.c_str(), m_vQueue.size()); + } + } + + + muduo::BoundedBlockingQueue m_vQueue; + vector m_vThreads; +}; + +TEST_F(BBQTest, TestConsumeNumEqProduceNum) { + JoinAll(); + EXPECT_EQ(0, (int)m_vQueue.size()); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + + diff --git a/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.sln b/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.sln new file mode 100644 index 000000000..08e27520e --- /dev/null +++ b/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BoundedBlockingQueueTest", "BoundedBlockingQueueTest.vcxproj", "{190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Debug|x64.ActiveCfg = Debug|x64 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Debug|x64.Build.0 = Debug|x64 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Debug|x86.ActiveCfg = Debug|Win32 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Debug|x86.Build.0 = Debug|Win32 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Release|x64.ActiveCfg = Release|x64 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Release|x64.Build.0 = Release|x64 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Release|x86.ActiveCfg = Release|Win32 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.vcxproj.filters b/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.vcxproj.filters new file mode 100644 index 000000000..3e54e2671 --- /dev/null +++ b/muduo/base/tests/boundedblockingqueuetest/BoundedBlockingQueueTest.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/boundedblockingqueuetest/boundedblockingqueuetest.vcxproj b/muduo/base/tests/boundedblockingqueuetest/boundedblockingqueuetest.vcxproj new file mode 100644 index 000000000..da72b1468 --- /dev/null +++ b/muduo/base/tests/boundedblockingqueuetest/boundedblockingqueuetest.vcxproj @@ -0,0 +1,166 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {190E5920-AEE1-4BE0-A7B7-6E39EA962C9B} + Win32Proj + BoundedBlockingQueueTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/countdownlatchtest/CountDownLatchTest.cc b/muduo/base/tests/countdownlatchtest/CountDownLatchTest.cc new file mode 100644 index 000000000..e01d1e867 --- /dev/null +++ b/muduo/base/tests/countdownlatchtest/CountDownLatchTest.cc @@ -0,0 +1,72 @@ +#include +#include + +#include +#include + +using namespace std; + +class CountDownLatchTest : public testing::Test { +protected: + + CountDownLatchTest() + : latch_(5), + count_(5) + { + } + + virtual void SetUp() + { + for (int i = 0; i < count_; ++i) + { + m_vThreads.push_back(std::move(thread(bind(&CountDownLatchTest::ThreadFunc, this)))); + } + + } + + + + virtual void TearDown() + { + m_vThreads.clear(); + } + + virtual ~CountDownLatchTest() { } + + + void Run() + { + latch_.wait(); + + } + + void JoinAll() + { + for_each(m_vThreads.begin(), m_vThreads.end(), bind(&thread::join, placeholders::_1)); + } + + void ThreadFunc() + { + latch_.countDown(); + } + + + muduo::CountDownLatch latch_; + vector m_vThreads; + int count_; + +}; + +TEST_F(CountDownLatchTest, TestAfterRunCountEqZero) { + Run(); + JoinAll(); + EXPECT_EQ(0, latch_.getCount()); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + + diff --git a/muduo/base/tests/countdownlatchtest/CountDownLatchTest.sln b/muduo/base/tests/countdownlatchtest/CountDownLatchTest.sln new file mode 100644 index 000000000..f10e40925 --- /dev/null +++ b/muduo/base/tests/countdownlatchtest/CountDownLatchTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CountDownLatchTest", "CountDownLatchTest.vcxproj", "{37134601-A822-4D53-9A43-80D0E65ABEC6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Debug|x64.ActiveCfg = Debug|x64 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Debug|x64.Build.0 = Debug|x64 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Debug|x86.ActiveCfg = Debug|Win32 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Debug|x86.Build.0 = Debug|Win32 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Release|x64.ActiveCfg = Release|x64 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Release|x64.Build.0 = Release|x64 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Release|x86.ActiveCfg = Release|Win32 + {37134601-A822-4D53-9A43-80D0E65ABEC6}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/countdownlatchtest/CountDownLatchTest.vcxproj.filters b/muduo/base/tests/countdownlatchtest/CountDownLatchTest.vcxproj.filters new file mode 100644 index 000000000..ceab4cd6f --- /dev/null +++ b/muduo/base/tests/countdownlatchtest/CountDownLatchTest.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + + + 头文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/countdownlatchtest/countdownlatchtest.vcxproj b/muduo/base/tests/countdownlatchtest/countdownlatchtest.vcxproj new file mode 100644 index 000000000..762f85b6f --- /dev/null +++ b/muduo/base/tests/countdownlatchtest/countdownlatchtest.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {37134601-A822-4D53-9A43-80D0E65ABEC6} + Win32Proj + CountDownLatchTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/datetest/DateTest.cc b/muduo/base/tests/datetest/DateTest.cc new file mode 100644 index 000000000..655b913de --- /dev/null +++ b/muduo/base/tests/datetest/DateTest.cc @@ -0,0 +1,97 @@ + +#include +#include +#include + +#include +#include + +using muduo::Date; + +const int kMonthsOfYear = 12; + +int isLeapYear(int year) +{ + if (year % 400 == 0) + return 1; + else if (year % 100 == 0) + return 0; + else if (year % 4 == 0) + return 1; + else + return 0; +} + +int daysOfMonth(int year, int month) +{ + static int days[2][kMonthsOfYear + 1] = + { + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + }; + return days[isLeapYear(year)][month]; +} + +void passByConstReference(const Date& x) +{ + printf("%s\n", x.toIsoString().c_str()); +} + +void passByValue(Date x) +{ + printf("%s\n", x.toIsoString().c_str()); +} + +TEST(DateTest, YearMonthDay) { + + time_t now = time(NULL); + struct tm t1 = *gmtime(&now); + struct tm t2 = *localtime(&now); + Date someDay(2008, 9, 10); + printf("%s\n", someDay.toIsoString().c_str()); + passByValue(someDay); + passByConstReference(someDay); + Date todayUtc(t1); + printf("%s\n", todayUtc.toIsoString().c_str()); + Date todayLocal(t2); + printf("%s\n", todayLocal.toIsoString().c_str()); + + int julianDayNumber = 2415021; + int weekDay = 1; // Monday + + for (int year = 1900; year < 2500; ++year) + { + EXPECT_EQ(isLeapYear(year), Date(year, 3, 1).julianDayNumber() - Date(year, 2, 29).julianDayNumber()); + for (int month = 1; month <= kMonthsOfYear; ++month) + { + for (int day = 1; day <= daysOfMonth(year, month); ++day) + { + Date d(year, month, day); + // printf("%s %d\n", d.toString().c_str(), d.weekDay()); + EXPECT_EQ(year, d.year()); + EXPECT_EQ(month, d.month()); + EXPECT_EQ(day, d.day()); + EXPECT_EQ(weekDay, d.weekDay()); + EXPECT_EQ(julianDayNumber, d.julianDayNumber()); + + Date d2(julianDayNumber); + EXPECT_EQ(year, d2.year()); + EXPECT_EQ(month, d2.month()); + EXPECT_EQ(day, d2.day()); + EXPECT_EQ(weekDay, d2.weekDay()); + EXPECT_EQ(julianDayNumber, d2.julianDayNumber()); + + ++julianDayNumber; + weekDay = (weekDay + 1) % 7; + } + } + } +} + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/muduo/base/tests/datetest/DateTest.sln b/muduo/base/tests/datetest/DateTest.sln new file mode 100644 index 000000000..747b477e8 --- /dev/null +++ b/muduo/base/tests/datetest/DateTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DateTest", "DateTest.vcxproj", "{49D7BE11-50BF-4ED9-BED7-245B0AFEA195}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Debug|x64.ActiveCfg = Debug|x64 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Debug|x64.Build.0 = Debug|x64 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Debug|x86.ActiveCfg = Debug|Win32 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Debug|x86.Build.0 = Debug|Win32 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Release|x64.ActiveCfg = Release|x64 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Release|x64.Build.0 = Release|x64 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Release|x86.ActiveCfg = Release|Win32 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/datetest/DateTest.vcxproj.filters b/muduo/base/tests/datetest/DateTest.vcxproj.filters new file mode 100644 index 000000000..860d94d91 --- /dev/null +++ b/muduo/base/tests/datetest/DateTest.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + + + 头文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/datetest/datetest.vcxproj b/muduo/base/tests/datetest/datetest.vcxproj new file mode 100644 index 000000000..70c8188cb --- /dev/null +++ b/muduo/base/tests/datetest/datetest.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {49D7BE11-50BF-4ED9-BED7-245B0AFEA195} + Win32Proj + DateTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/exceptiontest/ExceptionTest.cc b/muduo/base/tests/exceptiontest/ExceptionTest.cc new file mode 100644 index 000000000..ebfdd536a --- /dev/null +++ b/muduo/base/tests/exceptiontest/ExceptionTest.cc @@ -0,0 +1,37 @@ +#include +#include + +class Bar +{ +public: + void test() + { + throw muduo::Exception("oops"); + } +}; + +void foo() +{ + Bar b; + b.test(); +} + +TEST(ExceptionTest, PrintStack) { + try + { + foo(); + } + catch (const muduo::Exception& ex) + { + printf("reason: %s\n", ex.what()); + printf("stack trace: %s\n", ex.stackTrace()); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + + diff --git a/muduo/base/tests/exceptiontest/ExceptionTest.sln b/muduo/base/tests/exceptiontest/ExceptionTest.sln new file mode 100644 index 000000000..ac96175f9 --- /dev/null +++ b/muduo/base/tests/exceptiontest/ExceptionTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExceptionTest", "ExceptionTest.vcxproj", "{932AE905-D0E4-463F-BC41-23C7A89CD0EE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Debug|x64.ActiveCfg = Debug|x64 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Debug|x64.Build.0 = Debug|x64 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Debug|x86.ActiveCfg = Debug|Win32 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Debug|x86.Build.0 = Debug|Win32 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Release|x64.ActiveCfg = Release|x64 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Release|x64.Build.0 = Release|x64 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Release|x86.ActiveCfg = Release|Win32 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/exceptiontest/ExceptionTest.vcxproj.filters b/muduo/base/tests/exceptiontest/ExceptionTest.vcxproj.filters new file mode 100644 index 000000000..eaed42df5 --- /dev/null +++ b/muduo/base/tests/exceptiontest/ExceptionTest.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + + + 头文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/exceptiontest/exceptiontest.vcxproj b/muduo/base/tests/exceptiontest/exceptiontest.vcxproj new file mode 100644 index 000000000..668da3440 --- /dev/null +++ b/muduo/base/tests/exceptiontest/exceptiontest.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {932AE905-D0E4-463F-BC41-23C7A89CD0EE} + Win32Proj + ExceptionTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + DebugFull + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/fileutiltest/FileUtilTest.cc b/muduo/base/tests/fileutiltest/FileUtilTest.cc new file mode 100644 index 000000000..2786e0005 --- /dev/null +++ b/muduo/base/tests/fileutiltest/FileUtilTest.cc @@ -0,0 +1,46 @@ +#include +#include + +#include +#include + +using namespace muduo; + +TEST(FileUtilTest, ReadFile) +{ + std::string result; + int64_t size = 0; +#ifdef __linux__ + int err = FileUtil::readFile("/proc/self", 1024, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/proc/self", 1024, &result, NULL); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/proc/self/cmdline", 1024, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/dev/null", 1024, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/dev/zero", 1024, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/notexist", 1024, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/dev/zero", 102400, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/dev/zero", 102400, &result, NULL); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); +#endif // __linux__ + +#ifdef WIN32 + int err = FileUtil::readFile("ReadMe.txt", 1024, &result, &size); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); + err = FileUtil::readFile("/dev/zero", 102400, &result, NULL); + printf("%d %zd %" PRIu64 "\n", err, result.size(), size); +#endif // WIN32 + +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/muduo/base/tests/fileutiltest/FileUtilTest.sln b/muduo/base/tests/fileutiltest/FileUtilTest.sln new file mode 100644 index 000000000..18f0ee8b6 --- /dev/null +++ b/muduo/base/tests/fileutiltest/FileUtilTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileUtilTest", "FileUtilTest.vcxproj", "{94A588FF-E1CE-4B66-91AB-82E6929CED5E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Debug|x64.ActiveCfg = Debug|x64 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Debug|x64.Build.0 = Debug|x64 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Debug|x86.ActiveCfg = Debug|Win32 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Debug|x86.Build.0 = Debug|Win32 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Release|x64.ActiveCfg = Release|x64 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Release|x64.Build.0 = Release|x64 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Release|x86.ActiveCfg = Release|Win32 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/fileutiltest/FileUtilTest.vcxproj.filters b/muduo/base/tests/fileutiltest/FileUtilTest.vcxproj.filters new file mode 100644 index 000000000..8bbc97624 --- /dev/null +++ b/muduo/base/tests/fileutiltest/FileUtilTest.vcxproj.filters @@ -0,0 +1,60 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 头文件 + + + 头文件 + + + 头文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/fileutiltest/ReadMe.txt b/muduo/base/tests/fileutiltest/ReadMe.txt new file mode 100644 index 000000000..db276a672 --- /dev/null +++ b/muduo/base/tests/fileutiltest/ReadMe.txt @@ -0,0 +1,30 @@ +======================================================================== + 控制台应用程序:FileUtilTest 项目概述 +======================================================================== + +应用程序向导已为您创建了此 FileUtilTest 应用程序。 + +本文件概要介绍组成 FileUtilTest 应用程序的每个文件的内容。 + + +FileUtilTest.vcxproj + 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。 + +FileUtilTest.vcxproj.filters + 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cc”文件与“源文件”筛选器关联。 + +FileUtilTest.cc + 这是主应用程序源文件。 + +///////////////////////////////////////////////////////////////////////////// +其他标准文件: + +StdAfx.h, StdAfx.cc + 这些文件用于生成名为 FileUtilTest.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。 + +///////////////////////////////////////////////////////////////////////////// +其他注释: + +应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。 + +///////////////////////////////////////////////////////////////////////////// diff --git a/muduo/base/tests/fileutiltest/fileutiltest.vcxproj b/muduo/base/tests/fileutiltest/fileutiltest.vcxproj new file mode 100644 index 000000000..0b15bae68 --- /dev/null +++ b/muduo/base/tests/fileutiltest/fileutiltest.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {94A588FF-E1CE-4B66-91AB-82E6929CED5E} + Win32Proj + FileUtilTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/logfiletest/LogFileTest.cc b/muduo/base/tests/logfiletest/LogFileTest.cc new file mode 100644 index 000000000..4f05f8b68 --- /dev/null +++ b/muduo/base/tests/logfiletest/LogFileTest.cc @@ -0,0 +1,46 @@ + +#include + +#include +#include +#include + +using namespace muduo; + +std::unique_ptr g_logFile; + + +void outputFunc(const char* msg, int len) +{ + g_logFile->append(msg, len); +} + +void flushFunc() +{ + g_logFile->flush(); +} + + +TEST(LogFileTest, MultipleThread) +{ + g_logFile.reset(new muduo::LogFile("test", 200 * 1000)); + muduo::Logger::setOutput(outputFunc); + muduo::Logger::setFlush(flushFunc); + + std::string line = "1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ "; + + for (int i = 0; i < 10000; ++i) + { + LOG_INFO << line << i; + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/muduo/base/tests/logfiletest/LogFileTest.sln b/muduo/base/tests/logfiletest/LogFileTest.sln new file mode 100644 index 000000000..a2cbaecad --- /dev/null +++ b/muduo/base/tests/logfiletest/LogFileTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogFileTest", "LogFileTest.vcxproj", "{27156BC0-93BE-4D78-924B-8D45CAF4332F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Debug|x64.ActiveCfg = Debug|x64 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Debug|x64.Build.0 = Debug|x64 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Debug|x86.ActiveCfg = Debug|Win32 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Debug|x86.Build.0 = Debug|Win32 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Release|x64.ActiveCfg = Release|x64 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Release|x64.Build.0 = Release|x64 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Release|x86.ActiveCfg = Release|Win32 + {27156BC0-93BE-4D78-924B-8D45CAF4332F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/logfiletest/LogFileTest.vcxproj.filters b/muduo/base/tests/logfiletest/LogFileTest.vcxproj.filters new file mode 100644 index 000000000..9a9e69866 --- /dev/null +++ b/muduo/base/tests/logfiletest/LogFileTest.vcxproj.filters @@ -0,0 +1,58 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/logfiletest/LogFileTest.vcxproj.user b/muduo/base/tests/logfiletest/LogFileTest.vcxproj.user new file mode 100644 index 000000000..88a550947 --- /dev/null +++ b/muduo/base/tests/logfiletest/LogFileTest.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/muduo/base/tests/logfiletest/logfiletest.vcxproj b/muduo/base/tests/logfiletest/logfiletest.vcxproj new file mode 100644 index 000000000..8449d1563 --- /dev/null +++ b/muduo/base/tests/logfiletest/logfiletest.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {27156BC0-93BE-4D78-924B-8D45CAF4332F} + Win32Proj + LogFileTest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;WIN32;%(PreprocessorDefinitions) + ../../../../;F:\work\muduo-cross-platform\contrib\windows\include;../../../;F:\work\turn-based-game\third_party\boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/logstreambench/LogStreamBench.cc b/muduo/base/tests/logstreambench/LogStreamBench.cc new file mode 100644 index 000000000..63c9265c9 --- /dev/null +++ b/muduo/base/tests/logstreambench/LogStreamBench.cc @@ -0,0 +1,96 @@ + + + +#include +#include +#define __STDC_FORMAT_MACROS +#include + + +#include + + +#include +#include + +using namespace muduo; + +const size_t N = 1000000; + +#pragma GCC diagnostic ignored "-Wold-style-cast" + +template +void benchPrintf(const char* fmt) +{ + char buf[32]; + Timestamp start(Timestamp::now()); + for (size_t i = 0; i < N; ++i) + snprintf(buf, sizeof buf, fmt, (T)(i)); + Timestamp end(Timestamp::now()); + + printf("benchPrintf %f\n", timeDifference(end, start)); +} + +template +void benchStringStream() +{ + Timestamp start(Timestamp::now()); + std::ostringstream os; + + for (size_t i = 0; i < N; ++i) + { + os << (T)(i); + os.seekp(0, std::ios_base::beg); + } + Timestamp end(Timestamp::now()); + + printf("benchStringStream %f\n", timeDifference(end, start)); +} + +template +void benchLogStream() +{ + Timestamp start(Timestamp::now()); + LogStream os; + for (size_t i = 0; i < N; ++i) + { + os << (T)(i); + os.resetBuffer(); + } + Timestamp end(Timestamp::now()); + + printf("benchLogStream %f\n", timeDifference(end, start)); +} + +TEST(LogStreamBench, Bench) +{ + benchPrintf("%d"); + + puts("int"); + benchPrintf("%d"); + benchStringStream(); + benchLogStream(); + + puts("double"); + benchPrintf("%.12g"); + benchStringStream(); + benchLogStream(); + + puts("int64_t"); + benchPrintf("%" PRId64); + benchStringStream(); + benchLogStream(); + + puts("void*"); + benchPrintf("%p"); + benchStringStream(); + benchLogStream(); + +} + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/muduo/base/tests/logstreambench/LogStreamBench.sln b/muduo/base/tests/logstreambench/LogStreamBench.sln new file mode 100644 index 000000000..eaa9dd1ee --- /dev/null +++ b/muduo/base/tests/logstreambench/LogStreamBench.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogStreamBench", "LogStreamBench.vcxproj", "{57E12867-A26E-4C1B-9014-CA178138030A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {57E12867-A26E-4C1B-9014-CA178138030A}.Debug|x64.ActiveCfg = Debug|x64 + {57E12867-A26E-4C1B-9014-CA178138030A}.Debug|x64.Build.0 = Debug|x64 + {57E12867-A26E-4C1B-9014-CA178138030A}.Debug|x86.ActiveCfg = Debug|Win32 + {57E12867-A26E-4C1B-9014-CA178138030A}.Debug|x86.Build.0 = Debug|Win32 + {57E12867-A26E-4C1B-9014-CA178138030A}.Release|x64.ActiveCfg = Release|x64 + {57E12867-A26E-4C1B-9014-CA178138030A}.Release|x64.Build.0 = Release|x64 + {57E12867-A26E-4C1B-9014-CA178138030A}.Release|x86.ActiveCfg = Release|Win32 + {57E12867-A26E-4C1B-9014-CA178138030A}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/logstreambench/LogStreamBench.vcxproj.filters b/muduo/base/tests/logstreambench/LogStreamBench.vcxproj.filters new file mode 100644 index 000000000..a186a030f --- /dev/null +++ b/muduo/base/tests/logstreambench/LogStreamBench.vcxproj.filters @@ -0,0 +1,34 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/logstreambench/logstreambench.vcxproj b/muduo/base/tests/logstreambench/logstreambench.vcxproj new file mode 100644 index 000000000..d54d23c1d --- /dev/null +++ b/muduo/base/tests/logstreambench/logstreambench.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {57E12867-A26E-4C1B-9014-CA178138030A} + Win32Proj + LogStreamBench + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/logstreamtest/LogStreamTest.cc b/muduo/base/tests/logstreamtest/LogStreamTest.cc new file mode 100644 index 000000000..97bbd03fd --- /dev/null +++ b/muduo/base/tests/logstreamtest/LogStreamTest.cc @@ -0,0 +1,241 @@ +#include + +#include +#include + +//#define BOOST_TEST_MODULE LogStreamTest +#define BOOST_TEST_MAIN +//#define BOOST_TEST_DYN_LINK +#include + +using std::string; + +TEST(LogStreamTest, testLogStreamBooleans) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + EXPECT_EQ(buf.toString(), string("")); + os << true; + EXPECT_EQ(buf.toString(), string("1")); + os << '\n'; + EXPECT_EQ(buf.toString(), string("1\n")); + os << false; + EXPECT_EQ(buf.toString(), string("1\n0")); +} + +TEST(LogStreamTest, testLogStreamIntegers) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + EXPECT_EQ(buf.toString(), string("")); + os << 1; + EXPECT_EQ(buf.toString(), string("1")); + os << 0; + EXPECT_EQ(buf.toString(), string("10")); + os << -1; + EXPECT_EQ(buf.toString(), string("10-1")); + os.resetBuffer(); + + os << 0 << " " << 123 << 'x' << 0x64; + EXPECT_EQ(buf.toString(), string("0 123x100")); +} + +TEST(LogStreamTest, testLogStreamIntegerLimits) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + os << -2147483647; + EXPECT_EQ(buf.toString(), string("-2147483647")); + os << static_cast(-2147483647 - 1); + EXPECT_EQ(buf.toString(), string("-2147483647-2147483648")); + os << ' '; + os << 2147483647; + EXPECT_EQ(buf.toString(), string("-2147483647-2147483648 2147483647")); + os.resetBuffer(); + + os << std::numeric_limits::min(); + EXPECT_EQ(buf.toString(), string("-32768")); + os.resetBuffer(); + + os << std::numeric_limits::max(); + EXPECT_EQ(buf.toString(), string("32767")); + os.resetBuffer(); + + os << std::numeric_limits::min(); + EXPECT_EQ(buf.toString(), string("0")); + os.resetBuffer(); + + os << std::numeric_limits::max(); + EXPECT_EQ(buf.toString(), string("65535")); + os.resetBuffer(); + + os << std::numeric_limits::min(); + EXPECT_EQ(buf.toString(), string("-2147483648")); + os.resetBuffer(); + + os << std::numeric_limits::max(); + EXPECT_EQ(buf.toString(), string("2147483647")); + os.resetBuffer(); + + os << std::numeric_limits::min(); + EXPECT_EQ(buf.toString(), string("0")); + os.resetBuffer(); + + os << std::numeric_limits::max(); + EXPECT_EQ(buf.toString(), string("4294967295")); + os.resetBuffer(); + + os << std::numeric_limits::min(); + EXPECT_EQ(buf.toString(), string("-9223372036854775808")); + os.resetBuffer(); + + os << std::numeric_limits::max(); + EXPECT_EQ(buf.toString(), string("9223372036854775807")); + os.resetBuffer(); + + os << std::numeric_limits::min(); + EXPECT_EQ(buf.toString(), string("0")); + os.resetBuffer(); + + os << std::numeric_limits::max(); + EXPECT_EQ(buf.toString(), string("18446744073709551615")); + os.resetBuffer(); + + int16_t a = 0; + int32_t b = 0; + int64_t c = 0; + os << a; + os << b; + os << c; + EXPECT_EQ(buf.toString(), string("000")); +} + +TEST(LogStreamTest, testLogStreamFloats) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + + os << 0.0; + EXPECT_EQ(buf.toString(), string("0")); + os.resetBuffer(); + + os << 1.0; + EXPECT_EQ(buf.toString(), string("1")); + os.resetBuffer(); + + os << 0.1; + EXPECT_EQ(buf.toString(), string("0.1")); + os.resetBuffer(); + + os << 0.05; + EXPECT_EQ(buf.toString(), string("0.05")); + os.resetBuffer(); + + os << 0.15; + EXPECT_EQ(buf.toString(), string("0.15")); + os.resetBuffer(); + + double a = 0.1; + os << a; + EXPECT_EQ(buf.toString(), string("0.1")); + os.resetBuffer(); + + double b = 0.05; + os << b; + EXPECT_EQ(buf.toString(), string("0.05")); + os.resetBuffer(); + + double c = 0.15; + os << c; + EXPECT_EQ(buf.toString(), string("0.15")); + os.resetBuffer(); + + os << a + b; + EXPECT_EQ(buf.toString(), string("0.15")); + os.resetBuffer(); + + EXPECT_TRUE(a + b != c); + + os << 1.23456789; + EXPECT_EQ(buf.toString(), string("1.23456789")); + os.resetBuffer(); + + os << 1.234567; + EXPECT_EQ(buf.toString(), string("1.234567")); + os.resetBuffer(); + + os << -123.456; + EXPECT_EQ(buf.toString(), string("-123.456")); + os.resetBuffer(); +} + +TEST(LogStreamTest, testLogStreamVoid) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + + os << static_cast(0); + EXPECT_EQ(buf.toString(), string("0x0")); + os.resetBuffer(); + + os << reinterpret_cast(8888); + EXPECT_EQ(buf.toString(), string("0x22B8")); + os.resetBuffer(); +} + +TEST(LogStreamTest, testLogStreamStrings) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + + os << "Hello "; + EXPECT_EQ(buf.toString(), string("Hello ")); + + string chenshuo = "Shuo Chen"; + os << chenshuo; + EXPECT_EQ(buf.toString(), string("Hello Shuo Chen")); +} + +TEST(LogStreamTest, testLogStreamFmts) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + + os << muduo::Fmt("%4d", 1); + EXPECT_EQ(buf.toString(), string(" 1")); + os.resetBuffer(); + + os << muduo::Fmt("%4.2f", 1.2); + EXPECT_EQ(buf.toString(), string("1.20")); + os.resetBuffer(); + + os << muduo::Fmt("%4.2f", 1.2) << muduo::Fmt("%4d", 43); + EXPECT_EQ(buf.toString(), string("1.20 43")); + os.resetBuffer(); +} + +TEST(LogStreamTest, testLogStreamLong) +{ + muduo::LogStream os; + const muduo::LogStream::Buffer& buf = os.buffer(); + for (int i = 0; i < 399; ++i) + { + os << "123456789 "; + EXPECT_EQ(buf.length(), 10 * (i + 1)); + EXPECT_EQ(buf.avail(), 4000 - 10 * (i + 1)); + } + + os << "abcdefghi "; + EXPECT_EQ(buf.length(), 3990); + EXPECT_EQ(buf.avail(), 10); + + os << "abcdefghi"; + EXPECT_EQ(buf.length(), 3999); + EXPECT_EQ(buf.avail(), 1); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/base/tests/logstreamtest/LogStreamTest.sln b/muduo/base/tests/logstreamtest/LogStreamTest.sln new file mode 100644 index 000000000..bf98dd2a1 --- /dev/null +++ b/muduo/base/tests/logstreamtest/LogStreamTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogStreamTest", "LogStreamTest.vcxproj", "{5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Debug|x64.ActiveCfg = Debug|x64 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Debug|x64.Build.0 = Debug|x64 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Debug|x86.ActiveCfg = Debug|Win32 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Debug|x86.Build.0 = Debug|Win32 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Release|x64.ActiveCfg = Release|x64 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Release|x64.Build.0 = Release|x64 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Release|x86.ActiveCfg = Release|Win32 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/logstreamtest/LogStreamTest.vcxproj.filters b/muduo/base/tests/logstreamtest/LogStreamTest.vcxproj.filters new file mode 100644 index 000000000..003c8e443 --- /dev/null +++ b/muduo/base/tests/logstreamtest/LogStreamTest.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/logstreamtest/logstreamtest.vcxproj b/muduo/base/tests/logstreamtest/logstreamtest.vcxproj new file mode 100644 index 000000000..a738c3ff2 --- /dev/null +++ b/muduo/base/tests/logstreamtest/logstreamtest.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {5B7C09BD-00A0-4C5C-9E31-D7A34BA5D87E} + Win32Proj + LogStreamTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + MultiThreadedDebug + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + MultiThreaded + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/threadbench/ThreadBench.cc b/muduo/base/tests/threadbench/ThreadBench.cc new file mode 100644 index 000000000..0ad6f4269 --- /dev/null +++ b/muduo/base/tests/threadbench/ThreadBench.cc @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + + +#include +#include + +std::mutex g_mutex; +std::map g_delays; +std::atomic numCreated; + +void threadFunc() +{ + //printf("tid=%d\n", muduo::CurrentThread::tid()); + ++numCreated; +} + +void threadFunc2(muduo::Timestamp start) +{ + muduo::Timestamp now(muduo::Timestamp::now()); + int delay = static_cast(timeDifference(now, start) * 1000000); + std::unique_lock lock(g_mutex); + ++g_delays[delay]; +} + + + +TEST(TestThreadBench, TestThreadBench) +{ + printf("tid=%d\n", muduo::CurrentThread::tid()); + muduo::Timestamp start(muduo::Timestamp::now()); + + int kThreads = 100 * 1000; + for (int i = 0; i < kThreads; ++i) + { + std::thread t1(threadFunc); + t1.join(); + } + + double timeUsed = timeDifference(muduo::Timestamp::now(), start); + printf("thread creation time %f us\n", timeUsed * 1000000 / kThreads); + printf("number of created threads %d\n", numCreated.load(std::memory_order_relaxed)); + + for (int i = 0; i < kThreads; ++i) + { + muduo::Timestamp now(muduo::Timestamp::now()); + std::thread t2(std::bind(threadFunc2, now)); + + t2.join(); + } + { + std::unique_lock lock(g_mutex); + for (std::map::iterator it = g_delays.begin(); + it != g_delays.end(); ++it) + { + printf("delay = %d, count = %d\n", + it->first, it->second); + } + } + + +} + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + + diff --git a/muduo/base/tests/threadbench/ThreadBench.sln b/muduo/base/tests/threadbench/ThreadBench.sln new file mode 100644 index 000000000..132244793 --- /dev/null +++ b/muduo/base/tests/threadbench/ThreadBench.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThreadBench", "ThreadBench.vcxproj", "{2B766537-6FB7-4D97-BDE5-D67B3D3828D9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Debug|x64.ActiveCfg = Debug|x64 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Debug|x64.Build.0 = Debug|x64 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Debug|x86.ActiveCfg = Debug|Win32 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Debug|x86.Build.0 = Debug|Win32 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Release|x64.ActiveCfg = Release|x64 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Release|x64.Build.0 = Release|x64 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Release|x86.ActiveCfg = Release|Win32 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/threadbench/ThreadBench.vcxproj.filters b/muduo/base/tests/threadbench/ThreadBench.vcxproj.filters new file mode 100644 index 000000000..0d3edf1ec --- /dev/null +++ b/muduo/base/tests/threadbench/ThreadBench.vcxproj.filters @@ -0,0 +1,31 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/threadbench/threadbench.vcxproj b/muduo/base/tests/threadbench/threadbench.vcxproj new file mode 100644 index 000000000..366c72e64 --- /dev/null +++ b/muduo/base/tests/threadbench/threadbench.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {2B766537-6FB7-4D97-BDE5-D67B3D3828D9} + Win32Proj + ThreadBench + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/threadpooltest/ThreadPoolTest.cc b/muduo/base/tests/threadpooltest/ThreadPoolTest.cc new file mode 100644 index 000000000..1f36b4e41 --- /dev/null +++ b/muduo/base/tests/threadpooltest/ThreadPoolTest.cc @@ -0,0 +1,63 @@ + + +#include // std::bind +#include + +#include + +#include +#include +#include +#include + +void print() +{ + printf("tid=%d\n", muduo::CurrentThread::tid()); +} + +void printString(const std::string& str) +{ + LOG_INFO << str; + std::this_thread::sleep_for(std::chrono::microseconds(100 * 1000)); + +} + +void test(int maxSize) +{ + LOG_WARN << "Test ThreadPool with max queue size = " << maxSize; + muduo::ThreadPool pool("MainThreadPool"); + pool.setMaxQueueSize(maxSize); + pool.start(5); + + LOG_WARN << "Adding"; + pool.run(print); + pool.run(print); + for (int i = 0; i < 100; ++i) + { + char buf[32]; + snprintf(buf, sizeof buf, "task %d", i); + pool.run(std::bind(printString, std::string(buf))); + } + LOG_WARN << "Done"; + + muduo::CountDownLatch latch(1); + pool.run(std::bind(&muduo::CountDownLatch::countDown, &latch)); + latch.wait(); + pool.stop(); +} + +TEST(ThreadTestPool, Test) +{ + test(0); + test(1); + test(5); + test(10); + test(50); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/muduo/base/tests/threadpooltest/ThreadPoolTest.sln b/muduo/base/tests/threadpooltest/ThreadPoolTest.sln new file mode 100644 index 000000000..09c41c16e --- /dev/null +++ b/muduo/base/tests/threadpooltest/ThreadPoolTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThreadPoolTest", "ThreadPoolTest.vcxproj", "{B78FDD24-D819-4B47-BB0C-8D779A06BD1B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Debug|x64.ActiveCfg = Debug|x64 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Debug|x64.Build.0 = Debug|x64 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Debug|x86.ActiveCfg = Debug|Win32 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Debug|x86.Build.0 = Debug|Win32 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Release|x64.ActiveCfg = Release|x64 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Release|x64.Build.0 = Release|x64 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Release|x86.ActiveCfg = Release|Win32 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/threadpooltest/ThreadPoolTest.vcxproj.filters b/muduo/base/tests/threadpooltest/ThreadPoolTest.vcxproj.filters new file mode 100644 index 000000000..7046e7b58 --- /dev/null +++ b/muduo/base/tests/threadpooltest/ThreadPoolTest.vcxproj.filters @@ -0,0 +1,61 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/threadpooltest/threadpooltest.vcxproj b/muduo/base/tests/threadpooltest/threadpooltest.vcxproj new file mode 100644 index 000000000..fb55803ba --- /dev/null +++ b/muduo/base/tests/threadpooltest/threadpooltest.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {B78FDD24-D819-4B47-BB0C-8D779A06BD1B} + Win32Proj + ThreadPoolTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/threadtest/ThreadTest.cc b/muduo/base/tests/threadtest/ThreadTest.cc new file mode 100644 index 000000000..3ad3ca008 --- /dev/null +++ b/muduo/base/tests/threadtest/ThreadTest.cc @@ -0,0 +1,101 @@ +#include +#include // std::cout, std::endl +#include // std::this_thread::sleep_for +#include // std::chrono::seconds + +#include + +#include + +std::atomic numCreated; + +void mysleep(int seconds) +{ + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +void threadFunc() +{ + printf("tid=%d\n", muduo::CurrentThread::tid()); + ++numCreated; +} + +void threadFunc2(int x) +{ + printf("tid=%d, x=%d\n", muduo::CurrentThread::tid(), x); + ++numCreated; +} + +void threadFunc3() +{ + printf("tid=%d\n", muduo::CurrentThread::tid()); + ++numCreated; + mysleep(1); +} + +class Foo +{ +public: + explicit Foo(double x) + : x_(x) + { + } + + void memberFunc() + { + printf("tid=%d, Foo::x_=%f\n", muduo::CurrentThread::tid(), x_); + } + + void memberFunc2(const std::string& text) + { + printf("tid=%d, Foo::x_=%f, text=%s\n", muduo::CurrentThread::tid(), x_, text.c_str()); + } + +private: + double x_; +}; + +TEST(ThreadTest, FooTest) +{ + printf("tid=%d\n", muduo::CurrentThread::tid()); + + std::thread t1(threadFunc); + t1.join(); + + std::thread t2(std::bind(threadFunc2, 42), + "thread for free function with argument"); + t2.join(); + + Foo foo(87.53); + std::thread t3(std::bind(&Foo::memberFunc, &foo), + "thread for member function without argument"); + + t3.join(); + + std::thread t4(std::bind(&Foo::memberFunc2, std::ref(foo), std::string("Shuo Chen"))); + t4.join(); + + { + std::thread t5(threadFunc3); + // t5 may destruct eariler than thread creation. + t5.join(); + } + mysleep(2); + { + std::thread t6(threadFunc3); + mysleep(2); + t6.join(); + // t6 destruct later than thread creation. + } + std::this_thread::sleep_for(std::chrono::seconds(2)); + printf("number of created threads %d\n", numCreated.load(std::memory_order_relaxed)); +} + + + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/base/tests/threadtest/ThreadTest.sln b/muduo/base/tests/threadtest/ThreadTest.sln new file mode 100644 index 000000000..b9ff0276b --- /dev/null +++ b/muduo/base/tests/threadtest/ThreadTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThreadTest", "ThreadTest.vcxproj", "{83558E19-5989-4180-95CB-7ED932BE9E74}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {83558E19-5989-4180-95CB-7ED932BE9E74}.Debug|x64.ActiveCfg = Debug|x64 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Debug|x64.Build.0 = Debug|x64 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Debug|x86.ActiveCfg = Debug|Win32 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Debug|x86.Build.0 = Debug|Win32 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Release|x64.ActiveCfg = Release|x64 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Release|x64.Build.0 = Release|x64 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Release|x86.ActiveCfg = Release|Win32 + {83558E19-5989-4180-95CB-7ED932BE9E74}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/threadtest/ThreadTest.vcxproj.filters b/muduo/base/tests/threadtest/ThreadTest.vcxproj.filters new file mode 100644 index 000000000..1873dea03 --- /dev/null +++ b/muduo/base/tests/threadtest/ThreadTest.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/threadtest/threadtest.vcxproj b/muduo/base/tests/threadtest/threadtest.vcxproj new file mode 100644 index 000000000..63768b317 --- /dev/null +++ b/muduo/base/tests/threadtest/threadtest.vcxproj @@ -0,0 +1,156 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {83558E19-5989-4180-95CB-7ED932BE9E74} + Win32Proj + ThreadTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/timestamptest/TimestampTest.cc b/muduo/base/tests/timestamptest/TimestampTest.cc new file mode 100644 index 000000000..e533c244f --- /dev/null +++ b/muduo/base/tests/timestamptest/TimestampTest.cc @@ -0,0 +1,77 @@ + +#include +#include + + +#include + +#include + +using muduo::Timestamp; + +void passByConstReference(const Timestamp& x) +{ + printf("%s\n", x.toString().c_str()); +} + +void passByValue(Timestamp x) +{ + printf("%s\n", x.toString().c_str()); +} + +void benchmark() +{ + const int kNumber = 1000 * 1000; + + std::vector stamps; + stamps.reserve(kNumber); + for (int i = 0; i < kNumber; ++i) + { + stamps.push_back(Timestamp::now()); + } + printf("%s\n", stamps.front().toString().c_str()); + printf("%s\n", stamps.back().toString().c_str()); + printf("%f\n", timeDifference(stamps.back(), stamps.front())); + + int increments[100] = { 0 }; + int64_t start = stamps.front().microSecondsSinceEpoch(); + for (int i = 1; i < kNumber; ++i) + { + int64_t next = stamps[i].microSecondsSinceEpoch(); + int64_t inc = next - start; + start = next; + if (inc < 0) + { + printf("reverse!\n"); + } + else if (inc < 100) + { + ++increments[inc]; + } + else + { + printf("big gap %d\n", static_cast(inc)); + } + } + + for (int i = 0; i < 100; ++i) + { + printf("%2d: %d\n", i, increments[i]); + } +} + +TEST(TestTimeStamp, Test) +{ + Timestamp now(Timestamp::now()); + printf("%s\n", now.toString().c_str()); + passByValue(now); + passByConstReference(now); + benchmark(); +} + + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/base/tests/timestamptest/TimestampTest.filters b/muduo/base/tests/timestamptest/TimestampTest.filters new file mode 100644 index 000000000..d8e53f344 --- /dev/null +++ b/muduo/base/tests/timestamptest/TimestampTest.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/timestamptest/TimestampTest.sln b/muduo/base/tests/timestamptest/TimestampTest.sln new file mode 100644 index 000000000..2b27e9d1a --- /dev/null +++ b/muduo/base/tests/timestamptest/TimestampTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TimestampTest", "TimestampTest.vcxproj", "{A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Debug|x64.ActiveCfg = Debug|x64 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Debug|x64.Build.0 = Debug|x64 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Debug|x86.ActiveCfg = Debug|Win32 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Debug|x86.Build.0 = Debug|Win32 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Release|x64.ActiveCfg = Release|x64 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Release|x64.Build.0 = Release|x64 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Release|x86.ActiveCfg = Release|Win32 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/timestamptest/timestamptest.vcxproj b/muduo/base/tests/timestamptest/timestamptest.vcxproj new file mode 100644 index 000000000..6661e879c --- /dev/null +++ b/muduo/base/tests/timestamptest/timestamptest.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {A8715DD5-35BF-4E17-9C7A-ADDC5E12E999} + Win32Proj + TimestampTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/base/tests/timezonetest/New_York b/muduo/base/tests/timezonetest/New_York new file mode 100644 index 000000000..2b6c2eea1 Binary files /dev/null and b/muduo/base/tests/timezonetest/New_York differ diff --git a/muduo/base/tests/timezonetest/TimeZoneTest.cc b/muduo/base/tests/timezonetest/TimeZoneTest.cc new file mode 100644 index 000000000..2e0c5591f --- /dev/null +++ b/muduo/base/tests/timezonetest/TimeZoneTest.cc @@ -0,0 +1,437 @@ +#include "muduo/base/TimeZone.h" +#include "muduo/base/Types.h" + +#include +#include +#include + +using muduo::DateTime; +using muduo::TimeZone; + +struct tm getTm(int year, int month, int day, + int hour, int minute, int seconds) +{ + struct tm gmt; + muduo::memZero(&gmt, sizeof gmt); + gmt.tm_year = year - 1900; + gmt.tm_mon = month - 1; + gmt.tm_mday = day; + gmt.tm_hour = hour; + gmt.tm_min = minute; + gmt.tm_sec = seconds; + return gmt; +} + +struct tm getTm(const char* str) +{ + struct tm gmt; + muduo::memZero(&gmt, sizeof gmt); + strptime(str, "%F %T", &gmt); + return gmt; +} + +time_t getGmt(int year, int month, int day, + int hour, int minute, int seconds) +{ + struct tm gmt = getTm(year, month, day, hour, minute, seconds); + return timegm(&gmt); +} + +time_t getGmt(const char* str) +{ + struct tm gmt = getTm(str); + return timegm(&gmt); +} + +struct TestCase +{ + const char* gmt; + const char* local; + bool postTransition; +}; + +int failure = 0; + +void test(const TimeZone& tz, TestCase tc) +{ + const time_t gmt = getGmt(tc.gmt); + + { + int utcOffset = 0; + std::string local = tz.toLocalTime(gmt, &utcOffset).toIsoString(); + char buf[64]; + snprintf(buf, sizeof buf, " %+03d%02d", utcOffset / 3600, utcOffset % 3600 / 60); + local += buf; + + if (local != tc.local) + { + printf("WRONG: "); + printf("'%s' -> '%s' got '%s'\n", tc.gmt, tc.local, local.c_str()); + failure++; + } + else + { + printf("'%s' -> '%s'\n", tc.gmt, local.c_str()); + } + } + + { + struct tm local = getTm(tc.local); + DateTime localtime(local.tm_year+1900, local.tm_mon+1, local.tm_mday, + local.tm_hour, local.tm_min, local.tm_sec); + const int64_t result = tz.fromLocalTime(localtime, tc.postTransition); + if (result != gmt) + { + failure++; + printf("WRONG fromLocalTime: input %s expect %s got %s\n", + tc.local, tc.gmt, tz.toUtcTime(result).toIsoString().c_str()); + } + } +} + +void testNewYork() +{ + TimeZone tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/America/New_York"); + TestCase cases[] = + { + + // Unix Epoch is 1969-12-31 local time. + { "1970-01-01 00:00:00", "1969-12-31 19:00:00 -0500", false }, + + { "2006-03-07 00:00:00", "2006-03-06 19:00:00 -0500", false }, + { "2006-04-02 06:59:59", "2006-04-02 01:59:59 -0500", false }, + { "2006-04-02 07:00:00", "2006-04-02 03:00:00 -0400", false }, + { "2006-05-01 00:00:00", "2006-04-30 20:00:00 -0400", false }, + { "2006-05-02 01:00:00", "2006-05-01 21:00:00 -0400", false }, + { "2006-10-21 05:00:00", "2006-10-21 01:00:00 -0400", false }, + { "2006-10-29 05:59:59", "2006-10-29 01:59:59 -0400", false }, + { "2006-10-29 06:00:00", "2006-10-29 01:00:00 -0500", true }, + { "2006-10-29 06:30:00", "2006-10-29 01:30:00 -0500", true }, + { "2006-12-31 06:00:00", "2006-12-31 01:00:00 -0500", false }, + { "2007-01-01 00:00:00", "2006-12-31 19:00:00 -0500", false }, + + { "2007-03-07 00:00:00", "2007-03-06 19:00:00 -0500", false }, + { "2007-03-11 06:59:59", "2007-03-11 01:59:59 -0500", false }, + { "2007-03-11 07:00:00", "2007-03-11 03:00:00 -0400", false }, + { "2007-05-01 00:00:00", "2007-04-30 20:00:00 -0400", false }, + { "2007-05-02 01:00:00", "2007-05-01 21:00:00 -0400", false }, + { "2007-10-31 05:00:00", "2007-10-31 01:00:00 -0400", false }, + { "2007-11-04 05:59:59", "2007-11-04 01:59:59 -0400", false }, + { "2007-11-04 06:00:00", "2007-11-04 01:00:00 -0500", true }, + { "2007-11-04 06:59:59", "2007-11-04 01:59:59 -0500", true }, + { "2007-12-31 06:00:00", "2007-12-31 01:00:00 -0500", false }, + { "2008-01-01 00:00:00", "2007-12-31 19:00:00 -0500", false }, + + { "2009-03-07 00:00:00", "2009-03-06 19:00:00 -0500", false }, + { "2009-03-08 06:59:59", "2009-03-08 01:59:59 -0500", false }, + { "2009-03-08 07:00:00", "2009-03-08 03:00:00 -0400", false }, + { "2009-05-01 00:00:00", "2009-04-30 20:00:00 -0400", false }, + { "2009-05-02 01:00:00", "2009-05-01 21:00:00 -0400", false }, + { "2009-10-31 05:00:00", "2009-10-31 01:00:00 -0400", false }, + { "2009-11-01 05:59:59", "2009-11-01 01:59:59 -0400", false }, + { "2009-11-01 06:00:00", "2009-11-01 01:00:00 -0500", true }, + { "2009-11-01 06:59:59", "2009-11-01 01:59:59 -0500", true }, + { "2009-12-31 06:00:00", "2009-12-31 01:00:00 -0500", false }, + { "2010-01-01 00:00:00", "2009-12-31 19:00:00 -0500", false }, + + { "2010-03-13 00:00:00", "2010-03-12 19:00:00 -0500", false }, + { "2010-03-14 06:59:59", "2010-03-14 01:59:59 -0500", false }, + { "2010-03-14 07:00:00", "2010-03-14 03:00:00 -0400", false }, + { "2010-05-01 00:00:00", "2010-04-30 20:00:00 -0400", false }, + { "2010-05-02 01:00:00", "2010-05-01 21:00:00 -0400", false }, + { "2010-11-06 05:00:00", "2010-11-06 01:00:00 -0400", false }, + { "2010-11-07 05:59:59", "2010-11-07 01:59:59 -0400", false }, + { "2010-11-07 06:00:00", "2010-11-07 01:00:00 -0500", true }, + { "2010-11-07 06:59:59", "2010-11-07 01:59:59 -0500", true }, + { "2010-12-31 06:00:00", "2010-12-31 01:00:00 -0500", false }, + { "2011-01-01 00:00:00", "2010-12-31 19:00:00 -0500", false }, + + { "2011-03-01 00:00:00", "2011-02-28 19:00:00 -0500", false }, + { "2011-03-13 06:59:59", "2011-03-13 01:59:59 -0500", false }, + { "2011-03-13 07:00:00", "2011-03-13 03:00:00 -0400", false }, + { "2011-05-01 00:00:00", "2011-04-30 20:00:00 -0400", false }, + { "2011-05-02 01:00:00", "2011-05-01 21:00:00 -0400", false }, + { "2011-11-06 05:59:59", "2011-11-06 01:59:59 -0400", false }, + { "2011-11-06 06:00:00", "2011-11-06 01:00:00 -0500", true }, + { "2011-11-06 06:59:59", "2011-11-06 01:59:59 -0500", true }, + { "2011-12-31 06:00:00", "2011-12-31 01:00:00 -0500", false }, + { "2012-01-01 00:00:00", "2011-12-31 19:00:00 -0500", false }, + + }; + + for (const auto& c : cases) + { + test(tz, c); + } +} + +void testLondon() +{ + // UTC time isdst offset Local time (London) + // 2010-03-28 01:00:00Z 1 1.0 2010-03-28 02:00:00 + // 2010-10-31 01:00:00Z 0 0.0 2010-10-31 01:00:00 + // 2011-03-27 01:00:00Z 1 1.0 2011-03-27 02:00:00 + // 2011-10-30 01:00:00Z 0 0.0 2011-10-30 01:00:00 + // 2012-03-25 01:00:00Z 1 1.0 2012-03-25 02:00:00 + // 2012-10-28 01:00:00Z 0 0.0 2012-10-28 01:00:00 + // 2013-03-31 01:00:00Z 1 1.0 2013-03-31 02:00:00 + // 2013-10-27 01:00:00Z 0 0.0 2013-10-27 01:00:00 + // 2014-03-30 01:00:00Z 1 1.0 2014-03-30 02:00:00 + // 2014-10-26 01:00:00Z 0 0.0 2014-10-26 01:00:00 + + TimeZone tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/Europe/London"); + TestCase cases[] = + { + + { "2011-03-26 00:00:00", "2011-03-26 00:00:00 +0000", false }, + { "2011-03-27 00:59:59", "2011-03-27 00:59:59 +0000", false }, + { "2011-03-27 01:00:00", "2011-03-27 02:00:00 +0100", false }, + { "2011-10-30 00:59:59", "2011-10-30 01:59:59 +0100", false }, + { "2011-10-30 01:00:00", "2011-10-30 01:00:00 +0000", true }, + { "2011-10-30 01:59:59", "2011-10-30 01:59:59 +0000", true }, + { "2011-12-31 22:00:00", "2011-12-31 22:00:00 +0000", false }, + { "2012-01-01 00:00:00", "2012-01-01 00:00:00 +0000", false }, + + { "2012-03-24 00:00:00", "2012-03-24 00:00:00 +0000", false }, + { "2012-03-25 00:59:59", "2012-03-25 00:59:59 +0000", false }, + { "2012-03-25 01:00:00", "2012-03-25 02:00:00 +0100", false }, + { "2012-10-28 00:59:59", "2012-10-28 01:59:59 +0100", false }, + { "2012-10-28 01:00:00", "2012-10-28 01:00:00 +0000", true }, + { "2012-10-28 01:59:59", "2012-10-28 01:59:59 +0000", true }, + { "2012-12-31 22:00:00", "2012-12-31 22:00:00 +0000", false }, + { "2013-01-01 00:00:00", "2013-01-01 00:00:00 +0000", false }, + + }; + + for (const auto& c : cases) + { + test(tz, c); + } +} + +void testHongKong() +{ + TimeZone tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/Asia/Hong_Kong"); + TestCase cases[] = + { + + { "2011-04-03 00:00:00", "2011-04-03 08:00:00 +0800", false}, + + }; + + for (const auto& c : cases) + { + test(tz, c); + } + + tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/PRC"); + for (const auto& c : cases) + { + test(tz, c); + } + +} + +void testSydney() +{ + // DST starts in winter + // UTC time isdst offset Local time (London) + // 2010-04-03 16:00:00Z isdst 0 offset 10.0 2010-04-04 02:00:00 + // 2010-10-02 16:00:00Z isdst 1 offset 11.0 2010-10-03 03:00:00 + // 2011-04-02 16:00:00Z isdst 0 offset 10.0 2011-04-03 02:00:00 + // 2011-10-01 16:00:00Z isdst 1 offset 11.0 2011-10-02 03:00:00 + // 2012-03-31 16:00:00Z isdst 0 offset 10.0 2012-04-01 02:00:00 + // 2012-10-06 16:00:00Z isdst 1 offset 11.0 2012-10-07 03:00:00 + + TimeZone tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/Australia/Sydney"); + TestCase cases[] = + { + + { "2011-01-01 00:00:00", "2011-01-01 11:00:00 +1100", false }, + { "2011-04-02 15:59:59", "2011-04-03 02:59:59 +1100", false }, + { "2011-04-02 16:00:00", "2011-04-03 02:00:00 +1000", true }, + { "2011-04-02 16:59:59", "2011-04-03 02:59:59 +1000", true }, + { "2011-05-02 01:00:00", "2011-05-02 11:00:00 +1000", false }, + { "2011-10-01 15:59:59", "2011-10-02 01:59:59 +1000", false }, + { "2011-10-01 16:00:00", "2011-10-02 03:00:00 +1100", false }, + { "2011-12-31 22:00:00", "2012-01-01 09:00:00 +1100", false }, + + }; + + for (const auto& c : cases) + { + test(tz, c); + } +} + +void testUtc() +{ + TimeZone utc = TimeZone::loadZoneFile("/usr/share/zoneinfo/UTC"); + const int kRange = 100*1000*1000; + for (time_t t = -kRange; t <= kRange; t += 11) + { + struct tm* t1 = gmtime(&t); + char buf[80]; + strftime(buf, sizeof buf, "%F %T", t1); + + struct DateTime t2 = TimeZone::toUtcTime(t); + std::string t2str = t2.toIsoString(); + if (t2str != buf) + { + printf("'%s' != '%s'\n", buf, t2str.c_str()); + failure++; + assert(0); + } + + struct DateTime t3 = utc.toLocalTime(t); + std::string t3str = t3.toIsoString(); + if (t3str != buf) + { + printf("'%s' != '%s'\n", buf, t3str.c_str()); + failure++; + assert(0); + } + + + int64_t u1 = TimeZone::fromUtcTime(t2); + if (t != u1) + { + printf("%lld != %lld\n", static_cast(t), static_cast(u1)); + failure++; + assert(0); + } + } +} + +void testFixedTimezone() +{ + TimeZone tz(8*3600, "CST"); + TestCase cases[] = + { + { "2014-04-03 00:00:00", "2014-04-03 08:00:00 +0800", false}, + }; + + for (const auto& c : cases) + { + test(tz, c); + } + + tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/Etc/GMT-8"); + for (const auto& c : cases) + { + test(tz, c); + } + +} + +struct LocalToUtcTestCase +{ + const char* gmt; + const char* local; + bool postTransition; +}; + +void testLosAngeles() +{ + // UTC time isdst offset Local time Prior second local time + // 2021-03-14 10:00:00Z 1 -7.0 2021-03-14 03:00:00 + // 2021-11-07 09:00:00Z 0 -8.0 2021-11-07 01:00:00 + // 2022-03-13 10:00:00Z 1 -7.0 2022-03-13 03:00:00 2022-03-13 01:59:59 + // 2022-11-06 09:00:00Z 0 -8.0 2022-11-06 01:00:00 2022-11-06 01:59:59 + // 2023-03-12 10:00:00Z 1 -7.0 2023-03-12 03:00:00 + // 2023-11-05 09:00:00Z 0 -8.0 2023-11-05 01:00:00 + + TimeZone tz = TimeZone::loadZoneFile("/usr/share/zoneinfo/America/Los_Angeles"); + int utcOffset = 0; + printf("1234567890 in Los Angeles: %s", tz.toLocalTime(1234567890, &utcOffset).toIsoString().c_str()); + printf(" %+03d%02d\n", utcOffset / 3600, utcOffset % 3600 / 60); + printf("1666666666 in Los Angeles: %s\n", tz.toLocalTime(1666666666).toIsoString().c_str()); + printf("Now in Los Angeles: %s\n", tz.toLocalTime(time(nullptr)).toIsoString().c_str()); + + LocalToUtcTestCase cases[] = + { + // Unix Epoch is 1969-12-31 local time. + {"1970-01-01 00:00:00", "1969-12-31 16:00:00 -0800", false }, + {"1970-01-01 00:00:00", "1969-12-31 16:00:00 -0800", true }, + + {"2022-01-01 18:00:00", "2022-01-01 10:00:00", false}, + {"2022-01-01 18:00:00", "2022-01-01 10:00:00", true }, + // Before DST + {"2022-03-13 09:00:00", "2022-03-13 01:00:00", false }, + {"2022-03-13 09:00:00", "2022-03-13 01:00:00", true }, + {"2022-03-13 09:59:59", "2022-03-13 01:59:59", false }, + {"2022-03-13 09:59:59", "2022-03-13 01:59:59", true }, + // local time doesn't exist, skipped + {"2022-03-13 10:00:00", "2022-03-13 02:00:00", false }, + {"2022-03-13 09:00:00", "2022-03-13 02:00:00", true }, + {"2022-03-13 10:59:59", "2022-03-13 02:59:59", false }, + {"2022-03-13 09:59:59", "2022-03-13 02:59:59", true }, + // in DST + {"2022-03-13 10:00:00", "2022-03-13 03:00:00", false }, + {"2022-03-13 10:00:00", "2022-03-13 03:00:00", true }, + // Before back to winter time + {"2022-11-06 07:59:59", "2022-11-06 00:59:59", false }, + {"2022-11-06 07:59:59", "2022-11-06 00:59:59", true }, + // Time repeats + {"2022-11-06 08:00:00", "2022-11-06 01:00:00", false }, + {"2022-11-06 09:00:00", "2022-11-06 01:00:00", true }, + {"2022-11-06 08:59:59", "2022-11-06 01:59:59", false }, + {"2022-11-06 09:59:59", "2022-11-06 01:59:59", true }, + // After DST + {"2022-11-06 10:00:00", "2022-11-06 02:00:00", false }, + {"2022-11-06 10:00:00", "2022-11-06 02:00:00", true }, + {"2023-01-01 06:00:00", "2022-12-31 22:00:00", false }, + {"2023-01-01 06:00:00", "2022-12-31 22:00:00", true }, + }; + + for (const auto& tc : cases) + { + int64_t gmt = getGmt(tc.gmt); + struct tm local = getTm(tc.local); + DateTime localtime(local.tm_year+1900, local.tm_mon+1, local.tm_mday, + local.tm_hour, local.tm_min, local.tm_sec); + int64_t actual = tz.fromLocalTime(localtime, tc.postTransition); + printf("Local %s -> %sZ\n", tc.local, tc.gmt); + if (gmt != actual) + { + failure++; + printf("WRONG: input %s post %d expect %s got %s\n", + tc.local, tc.postTransition, tc.gmt, + tz.toUtcTime(actual).toIsoString().c_str()); + } + } + + int64_t start = getGmt("1950-01-01 00:00:00"); + int64_t end = getGmt("2037-01-01 00:00:00"); + for (int64_t utc = start; utc <= end; utc += 30) + { + DateTime local = tz.toLocalTime(utc); + int64_t gmt = tz.fromLocalTime(local); + if (utc != gmt) + { + // try post transistion + int64_t post = tz.fromLocalTime(local, true); + if (post != utc) + { + failure++; + printf("WRONG: input %s local %s got %s\n", + tz.toUtcTime(utc).toIsoString().c_str(), + local.toIsoString().c_str(), + tz.toUtcTime(gmt).toIsoString().c_str()); + } + } + } +} + +int main() +{ + testLosAngeles(); + testNewYork(); + testLondon(); + testSydney(); + testHongKong(); + testFixedTimezone(); + testUtc(); + + return failure; +} diff --git a/muduo/base/tests/timezonetest/TimeZoneTest.sln b/muduo/base/tests/timezonetest/TimeZoneTest.sln new file mode 100644 index 000000000..9284276d3 --- /dev/null +++ b/muduo/base/tests/timezonetest/TimeZoneTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TimeZoneTest", "TimeZoneTest.vcxproj", "{ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Debug|x64.ActiveCfg = Debug|x64 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Debug|x64.Build.0 = Debug|x64 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Debug|x86.ActiveCfg = Debug|Win32 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Debug|x86.Build.0 = Debug|Win32 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Release|x64.ActiveCfg = Release|x64 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Release|x64.Build.0 = Release|x64 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Release|x86.ActiveCfg = Release|Win32 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/base/tests/timezonetest/TimeZoneTest.vcxproj.filters b/muduo/base/tests/timezonetest/TimeZoneTest.vcxproj.filters new file mode 100644 index 000000000..ad126ce72 --- /dev/null +++ b/muduo/base/tests/timezonetest/TimeZoneTest.vcxproj.filters @@ -0,0 +1,31 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/base/tests/timezonetest/TimeZoneTest.vcxproj.user b/muduo/base/tests/timezonetest/TimeZoneTest.vcxproj.user new file mode 100644 index 000000000..88a550947 --- /dev/null +++ b/muduo/base/tests/timezonetest/TimeZoneTest.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/muduo/base/tests/timezonetest/timezonetest.vcxproj b/muduo/base/tests/timezonetest/timezonetest.vcxproj new file mode 100644 index 000000000..3f2004357 --- /dev/null +++ b/muduo/base/tests/timezonetest/timezonetest.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {ECF951A4-6A86-4A54-8AC3-0ED3AAE46F43} + Win32Proj + TimeZoneTest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;WIN32;%(PreprocessorDefinitions) + ../../../../;../../../../contrib\windows\include;../../../;F:\work\turn-based-game\third_party\boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + MultiThreaded + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/gtest/gtest-assertion-result.h b/muduo/gtest/gtest-assertion-result.h new file mode 100644 index 000000000..56fe128f2 --- /dev/null +++ b/muduo/gtest/gtest-assertion-result.h @@ -0,0 +1,237 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements the AssertionResult type. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ + +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-port.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +// C4800 is a level 3 warning in Visual Studio 2015 and earlier. +// This warning is not emitted in Visual Studio 2017. +// This warning is off by default starting in Visual Studio 2019 but can be +// enabled with command-line options. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename std::enable_if< + !std::is_convertible::value>::type* + /*enabler*/ + = nullptr) + : success_(success) {} + +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true if and only if the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_ != nullptr ? message_->c_str() : ""; + } + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template + AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_ == nullptr) message_ = ::std::make_unique<::std::string>(); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + std::unique_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ diff --git a/muduo/gtest/gtest-death-test.h b/muduo/gtest/gtest-death-test.h new file mode 100644 index 000000000..08fef8c7d --- /dev/null +++ b/muduo/gtest/gtest-death-test.h @@ -0,0 +1,345 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include "gtest/internal/gtest-death-test-internal.h" + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +namespace testing { + +#ifdef GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// The final parameter to each of these macros is a matcher applied to any data +// the sub-process wrote to stderr. For compatibility with existing tests, a +// bare string is interpreted as a regular expression matcher. +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// + +// Asserts that a given `statement` causes the program to exit, with an +// integer exit status that satisfies `predicate`, and emitting error output +// that matches `matcher`. +#define ASSERT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_) + +// Like `ASSERT_EXIT`, but continues on to successive tests in the +// test suite, if any: +#define EXPECT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given `statement` causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches `matcher`. +#define ASSERT_DEATH(statement, matcher) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Like `ASSERT_DEATH`, but continues on to successive tests in the +// test suite, if any: +#define EXPECT_DEATH(statement, matcher) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + ExitedWithCode(const ExitedWithCode&) = default; + void operator=(const ExitedWithCode& other) = delete; + bool operator()(int exit_status) const; + + private: + const int exit_code_; +}; + +#if !defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_FUCHSIA) +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + + private: + const int signum_; +}; +#endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +#ifdef NDEBUG + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +#else + +#define EXPECT_DEBUG_DEATH(statement, regex) EXPECT_DEATH(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) ASSERT_DEATH(statement, regex) + +#endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters +// on systems that support death tests. This allows one to write such a macro on +// a system that does not support death tests and be sure that it will compile +// on a death-test supporting system. It is exposed publicly so that systems +// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST +// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and +// ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter if and only if EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#ifdef GTEST_HAS_DEATH_TEST +#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/muduo/gtest/gtest-matchers.h b/muduo/gtest/gtest-matchers.h new file mode 100644 index 000000000..eae210e99 --- /dev/null +++ b/muduo/gtest/gtest-matchers.h @@ -0,0 +1,923 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GTEST_MAYBE_5046_ 5046 +#else +#define GTEST_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherMatcher that implements the matcher interface: +// using is_gtest_matcher = void; +// bool MatchAndExplain(const T&, std::ostream*); +// (MatchResultListener* can also be used instead of std::ostream*) +// void DescribeTo(std::ostream*); +// void DescribeNegationTo(std::ostream*); +// +// 2. a factory function that creates a Matcher object from a +// FooMatcherMatcher. + +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != nullptr) *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true if and only if the listener is interested in an explanation + // of the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != nullptr; } + + private: + ::std::ostream* const stream_; + + MatchResultListener(const MatchResultListener&) = delete; + MatchResultListener& operator=(const MatchResultListener&) = delete; +}; + +inline MatchResultListener::~MatchResultListener() = default; + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class GTEST_API_ MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() = default; + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Test) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +namespace internal { + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(nullptr) {} + + private: + DummyMatchResultListener(const DummyMatchResultListener&) = delete; + DummyMatchResultListener& operator=(const DummyMatchResultListener&) = delete; +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + StreamMatchResultListener(const StreamMatchResultListener&) = delete; + StreamMatchResultListener& operator=(const StreamMatchResultListener&) = + delete; +}; + +struct SharedPayloadBase { + std::atomic ref{1}; + void Ref() { ref.fetch_add(1, std::memory_order_relaxed); } + bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; } +}; + +template +struct SharedPayload : SharedPayloadBase { + explicit SharedPayload(const T& v) : value(v) {} + explicit SharedPayload(T&& v) : value(std::move(v)) {} + + static void Destroy(SharedPayloadBase* shared) { + delete static_cast(shared); + } + + T value; +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase : private MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener'. + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + GTEST_CHECK_(vtable_ != nullptr); + return vtable_->match_and_explain(*this, x, listener); + } + + // Returns true if and only if this matcher matches x. + bool Matches(const T& x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, false); + } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, true); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(const T& x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + if (vtable_ == nullptr) return nullptr; + return vtable_->get_describer(*this); + } + + protected: + MatcherBase() : vtable_(nullptr), buffer_() {} + + // Constructs a matcher from its implementation. + template + explicit MatcherBase(const MatcherInterface* impl) + : vtable_(nullptr), buffer_() { + Init(impl); + } + + template ::type::is_gtest_matcher> + MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT + Init(std::forward(m)); + } + + MatcherBase(const MatcherBase& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + if (IsShared()) buffer_.shared->Ref(); + } + + MatcherBase& operator=(const MatcherBase& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + if (IsShared()) buffer_.shared->Ref(); + return *this; + } + + MatcherBase(MatcherBase&& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + other.vtable_ = nullptr; + } + + MatcherBase& operator=(MatcherBase&& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + other.vtable_ = nullptr; + return *this; + } + + ~MatcherBase() override { Destroy(); } + + private: + struct VTable { + bool (*match_and_explain)(const MatcherBase&, const T&, + MatchResultListener*); + void (*describe)(const MatcherBase&, std::ostream*, bool negation); + // Returns the captured object if it implements the interface, otherwise + // returns the MatcherBase itself. + const MatcherDescriberInterface* (*get_describer)(const MatcherBase&); + // Called on shared instances when the reference count reaches 0. + void (*shared_destroy)(SharedPayloadBase*); + }; + + bool IsShared() const { + return vtable_ != nullptr && vtable_->shared_destroy != nullptr; + } + + // If the implementation uses a listener, call that. + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) { + return P::Get(m).MatchAndExplain(value, listener->stream()); + } + + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener)) { + return P::Get(m).MatchAndExplain(value, listener); + } + + template + static void DescribeImpl(const MatcherBase& m, std::ostream* os, + bool negation) { + if (negation) { + P::Get(m).DescribeNegationTo(os); + } else { + P::Get(m).DescribeTo(os); + } + } + + template + static const MatcherDescriberInterface* GetDescriberImpl( + const MatcherBase& m) { + // If the impl is a MatcherDescriberInterface, then return it. + // Otherwise use MatcherBase itself. + // This allows us to implement the GetDescriber() function without support + // from the impl, but some users really want to get their impl back when + // they call GetDescriber(). + // We use std::get on a tuple as a workaround of not having `if constexpr`. + return std::get<( + std::is_convertible::value + ? 1 + : 0)>(std::make_tuple(&m, &P::Get(m))); + } + + template + const VTable* GetVTable() { + static constexpr VTable kVTable = {&MatchAndExplainImpl

, + &DescribeImpl

, &GetDescriberImpl

, + P::shared_destroy}; + return &kVTable; + } + + union Buffer { + // Add some types to give Buffer some common alignment/size use cases. + void* ptr; + double d; + int64_t i; + // And add one for the out-of-line cases. + SharedPayloadBase* shared; + }; + + void Destroy() { + if (IsShared() && buffer_.shared->Unref()) { + vtable_->shared_destroy(buffer_.shared); + } + } + + template + static constexpr bool IsInlined() { + return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) && + std::is_trivially_copy_constructible::value && + std::is_trivially_destructible::value; + } + + template ()> + struct ValuePolicy { + static const M& Get(const MatcherBase& m) { + // When inlined along with Init, need to be explicit to avoid violating + // strict aliasing rules. + const M* ptr = + static_cast(static_cast(&m.buffer_)); + return *ptr; + } + static void Init(MatcherBase& m, M impl) { + ::new (static_cast(&m.buffer_)) M(impl); + } + static constexpr auto shared_destroy = nullptr; + }; + + template + struct ValuePolicy { + using Shared = SharedPayload; + static const M& Get(const MatcherBase& m) { + return static_cast(m.buffer_.shared)->value; + } + template + static void Init(MatcherBase& m, Arg&& arg) { + m.buffer_.shared = new Shared(std::forward(arg)); + } + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + struct ValuePolicy*, B> { + using M = const MatcherInterface; + using Shared = SharedPayload>; + static const M& Get(const MatcherBase& m) { + return *static_cast(m.buffer_.shared)->value; + } + static void Init(MatcherBase& m, M* impl) { + m.buffer_.shared = new Shared(std::unique_ptr(impl)); + } + + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + void Init(M&& m) { + using MM = typename std::decay::type; + using Policy = ValuePolicy; + vtable_ = GetVTable(); + Policy::Init(*this, std::forward(m)); + } + + const VTable* vtable_; + Buffer buffer_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a std::shared_ptr to const +// MatcherInterface. Don't inherit from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + explicit Matcher() {} // NOLINT + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template + explicit Matcher( + const MatcherInterface* impl, + typename std::enable_if::value>::type* = + nullptr) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) : internal::MatcherBase(std::forward(m)) {} // NOLINT + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) { + } + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +// Prints a matcher in a human-readable format. +template +std::ostream& operator<<(std::ostream& os, const Matcher& matcher) { + matcher.DescribeTo(&os); + return os; +} + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); } + + void DescribeNegationTo(::std::ostream* os) const override { + impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + }; + + Impl impl_; +}; + +// Creates a matcher from its implementation. +// DEPRECATED: Especially in the generic code, prefer: +// Matcher(new MyMatcherImpl(...)); +// +// MakeMatcher may create a Matcher that accepts its argument by value, which +// leads to unnecessary copies & lack of support for non-copyable types. +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +namespace internal { +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +template +class ComparisonBase { + public: + explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} + + using is_gtest_matcher = void; + + template + bool MatchAndExplain(const Lhs& lhs, std::ostream*) const { + return Op()(lhs, Unwrap(rhs_)); + } + void DescribeTo(std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + void DescribeNegationTo(std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + + private: + template + static const T& Unwrap(const T& v) { + return v; + } + template + static const T& Unwrap(std::reference_wrapper v) { + return v; + } + + Rhs rhs_; +}; + +template +class EqMatcher : public ComparisonBase, Rhs, std::equal_to<>> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::equal_to<>>(rhs) {} + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template +class NeMatcher + : public ComparisonBase, Rhs, std::not_equal_to<>> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::not_equal_to<>>(rhs) {} + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template +class LtMatcher : public ComparisonBase, Rhs, std::less<>> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::less<>>(rhs) {} + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template +class GtMatcher : public ComparisonBase, Rhs, std::greater<>> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::greater<>>(rhs) {} + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template +class LeMatcher + : public ComparisonBase, Rhs, std::less_equal<>> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::less_equal<>>(rhs) {} + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template +class GeMatcher + : public ComparisonBase, Rhs, std::greater_equal<>> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::greater_equal<>>(rhs) {} + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +template ::value>::type> +using StringLike = T; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + return MatchAndExplain(std::string(s), listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(std::string(s), listener); + } + + // Matches anything that can convert to std::string. + // + // This is a template, not just a plain function with const std::string&, + // because absl::string_view has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const std::string s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) + : RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const std::shared_ptr regex_; + const bool full_match_; +}; +} // namespace internal + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +template +PolymorphicMatcher MatchesRegex( + const internal::StringLike& regex) { + return MatchesRegex(new internal::RE(std::string(regex))); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +template +PolymorphicMatcher ContainsRegex( + const internal::StringLike& regex) { + return ContainsRegex(new internal::RE(std::string(regex))); +} + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template +inline internal::EqMatcher Eq(T x) { + return internal::EqMatcher(x); +} + +// Constructs a Matcher from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template +Matcher::Matcher(T value) { + *this = Eq(value); +} + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq(x) is just a convenient short-hand for Matcher(Eq(x)) +// or Matcher(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher(Lt(5)) to be explicit about the type, +// for example. +template +inline Matcher TypedEq(const Rhs& rhs) { + return Eq(rhs); +} + +// Creates a polymorphic matcher that matches anything >= x. +template +inline internal::GeMatcher Ge(Rhs x) { + return internal::GeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template +inline internal::GtMatcher Gt(Rhs x) { + return internal::GtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template +inline internal::LeMatcher Le(Rhs x) { + return internal::LeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template +inline internal::LtMatcher Lt(Rhs x) { + return internal::LtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template +inline internal::NeMatcher Ne(Rhs x) { + return internal::NeMatcher(x); +} +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ diff --git a/muduo/gtest/gtest-message.h b/muduo/gtest/gtest-message.h new file mode 100644 index 000000000..59b805e4e --- /dev/null +++ b/muduo/gtest/gtest-message.h @@ -0,0 +1,252 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include +#include +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +#ifdef GTEST_HAS_ABSL +#include + +#include "absl/strings/internal/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + + // Streams a non-pointer value to this object. If building a version of + // GoogleTest with ABSL, this overload is only enabled if the value does not + // have an AbslStringify definition. + template ::value, // NOLINT + int>::type = 0 +#endif // GTEST_HAS_ABSL + > + inline Message& operator<<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator<<; + *ss_ << val; + return *this; + } + +#ifdef GTEST_HAS_ABSL + // Streams a non-pointer value with an AbslStringify definition to this + // object. + template ::value, // NOLINT + int>::type = 0> + inline Message& operator<<(const T& val) { + // ::operator<< is needed here for a similar reason as with the non-Abseil + // version above + using ::operator<<; + *ss_ << absl::StrCat(val); + return *this; + } +#endif // GTEST_HAS_ABSL + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator<<(T* const& pointer) { // NOLINT + if (pointer == nullptr) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator<<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator<<(bool b) { return *this << (b ? "true" : "false"); } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator<<(const wchar_t* wide_c_str); + Message& operator<<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator<<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + // We'll hold the text streamed to this object here. + const std::unique_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator<<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/muduo/gtest/gtest-param-test.h b/muduo/gtest/gtest-param-test.h new file mode 100644 index 000000000..49a47ead0 --- /dev/null +++ b/muduo/gtest/gtest-param-test.h @@ -0,0 +1,546 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test suite +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more than once) the first argument to the +// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the +// actual test suite name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests +// in the given test suite, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_SUITE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test suite is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test suite FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test suite StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test suite StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_SUITE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_SUITE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename std::iterator_traits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename std::iterator_traits::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test suite BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_SUITE_P(NumSequence, +// BarTest, +// Values("one", "two", "three")); +// +// This instantiates tests from test suite BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// +template +internal::ValueArray Values(T... v) { + return internal::ValueArray(std::move(v)...); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test suite FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { return Values(false, true); } + +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// std::tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// std::tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder Combine(const Generator&... g) { + return internal::CartesianProductHolder(g...); +} + +// ConvertGenerator() wraps a parameter generator in order to cast each produced +// value through a known type before supplying it to the test suite +// +// Synopsis: +// ConvertGenerator(gen) +// - returns a generator producing the same elements as generated by gen, but +// each element is static_cast to type T before being returned +// +// It is useful when using the Combine() function to get the generated +// parameters in a custom type instead of std::tuple +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// struct ParamType { +// using TupleT = std::tuple; +// std::string animal; +// Color color; +// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {} +// }; +// class AnimalTest +// : public testing::TestWithParam {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// ConvertGenerator( +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE)))); +// +template +internal::ParamConverterGenerator ConvertGenerator( + internal::ParamGenerator gen) { + return internal::ParamConverterGenerator(gen); +} + +#define TEST_P(test_suite_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name, \ + private ::testing::internal::GTestNonCopyable { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + void TestBody() override; \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify +// generator and an optional function or functor that generates custom test name +// suffixes based on the test parameters. Such a function or functor should +// accept one argument of type testing::TestParamInfo, and +// return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define GTEST_EXPAND_(arg) arg +#define GTEST_GET_FIRST_(first, ...) first +#define GTEST_GET_SECOND_(first, second, ...) second + +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))))(info); \ + } \ + static int gtest_##prefix##test_suite_name##_dummy_ \ + GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + +// Allow Marking a Parameterized test class as not needing to be instantiated. +#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \ + namespace gtest_do_not_use_outside_namespace_scope {} \ + static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \ + GTEST_STRINGIFY_(T)) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TEST_CASE_P \ + static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \ + ""); \ + INSTANTIATE_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/muduo/gtest/gtest-printers.h b/muduo/gtest/gtest-printers.h new file mode 100644 index 000000000..d1766e640 --- /dev/null +++ b/muduo/gtest/gtest-printers.h @@ -0,0 +1,1161 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// * Prefer AbslStringify(..) to operator<<(..), per https://abseil.io/tips/215. +// * Define foo::PrintTo(..) if the type already has AbslStringify(..), but an +// alternative presentation in test results is of interest. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTEST_HAS_ABSL +#include "absl/strings/internal/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Definitions in the internal* namespaces are subject to change without notice. +// DO NOT USE THEM IN USER CODE! +namespace internal { + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +struct ContainerPrinter { + template (0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value>::type> + static void PrintValue(const T& container, std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (auto&& elem : container) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(elem, os) here as PrintTo() doesn't + // handle `elem` being a native array. + internal::UniversalPrint(elem, os); + ++count; + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; + } +}; + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +struct FunctionPointerPrinter { + template ::value>::type> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); + } + } +}; + +struct PointerPrinter { + template + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } + } +}; + +namespace internal_stream_operator_without_lexical_name_lookup { + +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); + +struct StreamPrinter { + template ::value>::type> + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + // (Use SFINAE via return type, because it seems GCC < 12 doesn't handle name + // lookup properly when we do it in the template parameter list.) + static auto PrintValue(const T& value, ::std::ostream* os) + -> decltype((void)(*os << value)) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. + *os << value; + } +}; + +} // namespace internal_stream_operator_without_lexical_name_lookup + +struct ProtobufPrinter { + // We print a protobuf using its ShortDebugString() when the string + // doesn't exceed this many characters; otherwise we print it using + // DebugString() for better readability. + static const size_t kProtobufOneLinerMaxLength = 50; + + template ::value>::type> + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +struct ConvertibleToIntegerPrinter { + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(internal::BiggestInt value, ::std::ostream* os) { + *os << value; + } +}; + +struct ConvertibleToStringViewPrinter { +#if GTEST_INTERNAL_HAS_STRING_VIEW + static void PrintValue(internal::StringView value, ::std::ostream* os) { + internal::UniversalPrint(value, os); + } +#endif +}; + +#ifdef GTEST_HAS_ABSL +struct ConvertibleToAbslStringifyPrinter { + template < + typename T, + typename = typename std::enable_if< + absl::strings_internal::HasAbslStringify::value>::type> // NOLINT + static void PrintValue(const T& value, ::std::ostream* os) { + *os << absl::StrCat(value); + } +}; +#endif // GTEST_HAS_ABSL + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, ::std::ostream* os); +struct RawBytesPrinter { + // SFINAE on `sizeof` to make sure we have a complete type. + template + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo( + static_cast( + // Load bearing cast to void* to support iOS + reinterpret_cast(std::addressof(value))), + sizeof(value), os); + } +}; + +struct FallbackPrinter { + template + static void PrintValue(const T&, ::std::ostream* os) { + *os << "(incomplete type)"; + } +}; + +// Try every printer in order and return the first one that works. +template +struct FindFirstPrinter : FindFirstPrinter {}; + +template +struct FindFirstPrinter< + T, decltype(Printer::PrintValue(std::declval(), nullptr)), + Printer, Printers...> { + using type = Printer; +}; + +// Select the best printer in the following order: +// - Print containers (they have begin/end/etc). +// - Print function pointers. +// - Print object pointers. +// - Print protocol buffers. +// - Use the stream operator, if available. +// - Print types convertible to BiggestInt. +// - Print types convertible to StringView, if available. +// - Fallback to printing the raw bytes of the object. +template +void PrintWithFallback(const T& value, ::std::ostream* os) { + using Printer = typename FindFirstPrinter< + T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + ProtobufPrinter, +#ifdef GTEST_HAS_ABSL + ConvertibleToAbslStringifyPrinter, +#endif // GTEST_HAS_ABSL + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ConvertibleToIntegerPrinter, ConvertibleToStringViewPrinter, + RawBytesPrinter, FallbackPrinter>::type; + Printer::PrintValue(value, os); +} + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); +#ifdef __cpp_lib_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); +#ifdef __cpp_lib_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string); + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage(const T1& value, + const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + internal::PrintWithFallback(value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); +inline void PrintTo(char16_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#ifdef __cpp_lib_char8_t +inline void PrintTo(char8_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#endif + +// gcc/clang __{u,}int128_t +#if defined(__SIZEOF_INT128__) +GTEST_API_ void PrintTo(__uint128_t v, ::std::ostream* os); +GTEST_API_ void PrintTo(__int128_t v, ::std::ostream* os); +#endif // __SIZEOF_INT128__ + +// The default resolution used to print floating-point values uses only +// 6 digits, which can be confusing if a test compares two values whose +// difference lies in the 7th digit. So we'd like to print out numbers +// in full precision. +// However if the value is something simple like 1.1, full will print a +// long string like 1.100000001 due to floating-point numbers not using +// a base of 10. This routiune returns an appropriate resolution for a +// given floating-point number, that is, 6 if it will be accurate, or a +// max_digits10 value (full precision) if it won't, for values between +// 0.0001 and one million. +// It does this by computing what those digits would be (by multiplying +// by an appropriate power of 10), then dividing by that power again to +// see if gets the original value back. +// A similar algorithm applies for values larger than one million; note +// that for those values, we must divide to get a six-digit number, and +// then multiply to possibly get the original value again. +template +int AppropriateResolution(FloatType val) { + int full = std::numeric_limits::max_digits10; + if (val < 0) val = -val; + + if (val < 1000000) { + FloatType mulfor6 = 1e10; + if (val >= 100000.0) { // 100,000 to 999,999 + mulfor6 = 1.0; + } else if (val >= 10000.0) { + mulfor6 = 1e1; + } else if (val >= 1000.0) { + mulfor6 = 1e2; + } else if (val >= 100.0) { + mulfor6 = 1e3; + } else if (val >= 10.0) { + mulfor6 = 1e4; + } else if (val >= 1.0) { + mulfor6 = 1e5; + } else if (val >= 0.1) { + mulfor6 = 1e6; + } else if (val >= 0.01) { + mulfor6 = 1e7; + } else if (val >= 0.001) { + mulfor6 = 1e8; + } else if (val >= 0.0001) { + mulfor6 = 1e9; + } + if (static_cast(static_cast(val * mulfor6 + 0.5)) / + mulfor6 == + val) + return 6; + } else if (val < 1e10) { + FloatType divfor6 = 1.0; + if (val >= 1e9) { // 1,000,000,000 to 9,999,999,999 + divfor6 = 10000; + } else if (val >= 1e8) { // 100,000,000 to 999,999,999 + divfor6 = 1000; + } else if (val >= 1e7) { // 10,000,000 to 99,999,999 + divfor6 = 100; + } else if (val >= 1e6) { // 1,000,000 to 9,999,999 + divfor6 = 10; + } + if (static_cast(static_cast(val / divfor6 + 0.5)) * + divfor6 == + val) + return 6; + } + return full; +} + +inline void PrintTo(float f, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(f)); + *os << f; + os->precision(old_precision); +} + +inline void PrintTo(double d, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(d)); + *os << d; + os->precision(old_precision); +} + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#ifdef __cpp_lib_char8_t +// Overloads for u8 strings. +GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); +inline void PrintTo(char8_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif +// Overloads for u16 strings. +GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os); +inline void PrintTo(char16_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +// Overloads for u32 strings. +GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os); +inline void PrintTo(char32_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::std::string. +GTEST_API_ void PrintStringTo(const ::std::string& s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::std::u8string +#ifdef __cpp_lib_char8_t +GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { + PrintU8StringTo(s, os); +} +#endif + +// Overloads for ::std::u16string +GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) { + PrintU16StringTo(s, os); +} + +// Overloads for ::std::u32string +GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) { + PrintU32StringTo(s, os); +} + +// Overloads for ::std::wstring. +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring& s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Overload for internal::StringView. +inline void PrintTo(internal::StringView sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } + +#if GTEST_HAS_RTTI +inline void PrintTo(const std::type_info& info, std::ostream* os) { + *os << internal::GetTypeName(info); +} +#endif // GTEST_HAS_RTTI + +template +void PrintTo(std::reference_wrapper ref, ::std::ostream* os) { + UniversalPrinter::Print(ref.get(), os); +} + +inline const void* VoidifyPointer(const void* p) { return p; } +inline const void* VoidifyPointer(volatile const void* p) { + return const_cast(p); +} + +template +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + // We can't print the value. Just print the pointer.. + *os << "(" << (VoidifyPointer)(ptr.get()) << ")"; + } +} +template ::value && + !std::is_array::value>::type> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = "; + UniversalPrinter::Print(*ptr, os); + *os << ")"; + } +} + +template +void PrintTo(const std::unique_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +template +void PrintTo(const std::shared_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T&, std::integral_constant, + ::std::ostream*) {} + +template +void PrintTupleTo(const T& t, std::integral_constant, + ::std::ostream* os) { + PrintTupleTo(t, std::integral_constant(), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (I > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter::type>::Print( + std::get(t), os); +} + +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + *os << "("; + PrintTupleTo(t, std::integral_constant(), os); + *os << ")"; +} + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Remove any const-qualifiers before passing a type to UniversalPrinter. +template +class UniversalPrinter : public UniversalPrinter {}; + +#if GTEST_INTERNAL_HAS_ANY + +// Printer for std::any / absl::any + +template <> +class UniversalPrinter { + public: + static void Print(const Any& value, ::std::ostream* os) { + if (value.has_value()) { + *os << "value of type " << GetTypeName(value); + } else { + *os << "no value"; + } + } + + private: + static std::string GetTypeName(const Any& value) { +#if GTEST_HAS_RTTI + return internal::GetTypeName(value.type()); +#else + static_cast(value); // possibly unused + return ""; +#endif // GTEST_HAS_RTTI + } +}; + +#endif // GTEST_INTERNAL_HAS_ANY + +#if GTEST_INTERNAL_HAS_OPTIONAL + +// Printer for std::optional / absl::optional + +template +class UniversalPrinter> { + public: + static void Print(const Optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +template <> +class UniversalPrinter { + public: + static void Print(decltype(Nullopt()), ::std::ostream* os) { + *os << "(nullopt)"; + } +}; + +#endif // GTEST_INTERNAL_HAS_OPTIONAL + +#if GTEST_INTERNAL_HAS_VARIANT + +// Printer for std::variant / absl::variant + +template +class UniversalPrinter> { + public: + static void Print(const Variant& value, ::std::ostream* os) { + *os << '('; +#ifdef GTEST_HAS_ABSL + absl::visit(Visitor{os, value.index()}, value); +#else + std::visit(Visitor{os, value.index()}, value); +#endif // GTEST_HAS_ABSL + *os << ')'; + } + + private: + struct Visitor { + template + void operator()(const U& u) const { + *os << "'" << GetTypeName() << "(index = " << index + << ")' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + std::size_t index; + }; +}; + +#endif // GTEST_INTERNAL_HAS_VARIANT + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray(const char* begin, size_t len, + ::std::ostream* os); + +#ifdef __cpp_lib_char8_t +// This overload prints a (const) char8_t array compactly. +GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, + ::std::ostream* os); +#endif + +// This overload prints a (const) char16_t array compactly. +GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) char32_t array compactly. +GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray(const wchar_t* begin, size_t len, + ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter> { + public: + static void Print(std::reference_wrapper value, ::std::ostream* os) { + UniversalTersePrinter::Print(value.get(), os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter : public UniversalTersePrinter { +}; + +#ifdef __cpp_lib_char8_t +template <> +class UniversalTersePrinter { + public: + static void Print(const char8_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u8string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(const char16_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u16string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +template <> +class UniversalTersePrinter { + public: + static void Print(const char32_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u32string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +typedef ::std::vector<::std::string> Strings; + +// Tersely prints the first N fields of a tuple to a string vector, +// one element for each field. +template +void TersePrintPrefixToStrings(const Tuple&, std::integral_constant, + Strings*) {} +template +void TersePrintPrefixToStrings(const Tuple& t, + std::integral_constant, + Strings* strings) { + TersePrintPrefixToStrings(t, std::integral_constant(), + strings); + ::std::stringstream ss; + UniversalTersePrint(std::get(t), &ss); + strings->push_back(ss.str()); +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TersePrintPrefixToStrings( + value, std::integral_constant::value>(), + &result); + return result; +} + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gtest/internal/custom/gtest-printers.h" + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/muduo/gtest/gtest-spi.h b/muduo/gtest/gtest-spi.h new file mode 100644 index 000000000..c0613b695 --- /dev/null +++ b/muduo/gtest/gtest-spi.h @@ -0,0 +1,250 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include + +#include "gtest/gtest.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + ScopedFakeTestPartResultReporter(const ScopedFakeTestPartResultReporter&) = + delete; + ScopedFakeTestPartResultReporter& operator=( + const ScopedFakeTestPartResultReporter&) = delete; +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + SingleFailureChecker(const SingleFailureChecker&) = delete; + SingleFailureChecker& operator=(const SingleFailureChecker&) = delete; +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures (e.g. a failure from an ASSERT_EQ, but +// not a non-fatal failure, as from EXPECT_EQ). It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper { \ + public: \ + static void Execute() { statement; } \ + }; \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, \ + >est_failures); \ + GTestExpectFatalFailureHelper::Execute(); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper { \ + public: \ + static void Execute() { statement; } \ + }; \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures); \ + GTestExpectFatalFailureHelper::Execute(); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures (e.g. a failure from an EXPECT_EQ, +// but not from an ASSERT_EQ). It asserts that the given statement will cause +// exactly one non-fatal Google Test failure with 'substr' being part of the +// failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do { \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, \ + >est_failures); \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures); \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/muduo/gtest/gtest-test-part.h b/muduo/gtest/gtest-test-part.h new file mode 100644 index 000000000..41c8a9a0d --- /dev/null +++ b/muduo/gtest/gtest-test-part.h @@ -0,0 +1,192 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure, // Failed and the test should be terminated. + kSkip // Skipped. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, const char* a_file_name, int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == nullptr ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) {} + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? nullptr : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true if and only if the test part was skipped. + bool skipped() const { return type_ == kSkip; } + + // Returns true if and only if the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true if and only if the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true if and only if the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + // Returns true if and only if the test part failed. + bool failed() const { return fatally_failed() || nonfatally_failed(); } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() = default; + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + TestPartResultArray(const TestPartResultArray&) = delete; + TestPartResultArray& operator=(const TestPartResultArray&) = delete; +}; + +// This interface knows how to report a test part result. +class GTEST_API_ TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() = default; + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + ~HasNewFatalFailureHelper() override; + void ReportTestPartResult(const TestPartResult& result) override; + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + HasNewFatalFailureHelper(const HasNewFatalFailureHelper&) = delete; + HasNewFatalFailureHelper& operator=(const HasNewFatalFailureHelper&) = delete; +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ diff --git a/muduo/gtest/gtest-typed-test.h b/muduo/gtest/gtest-typed-test.h new file mode 100644 index 000000000..72de536bc --- /dev/null +++ b/muduo/gtest/gtest-typed-test.h @@ -0,0 +1,331 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test suite, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_SUITE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_SUITE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test suite as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +// TYPED_TEST_SUITE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template +// static std::string GetName(int) { +// if (std::is_same()) return "char"; +// if (std::is_same()) return "int"; +// if (std::is_same()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test suite +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_SUITE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test suite as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test suite name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test suite name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_SUITE above, +// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" + +// Implements typed tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test suite. +#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_ + +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestSuiteName) \ + gtest_type_params_##TestSuiteName##_NameGenerator + +#define TYPED_TEST_SUITE(CaseName, Types, ...) \ + typedef ::testing::internal::GenerateTypeList::type \ + GTEST_TYPE_PARAMS_(CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +#define TYPED_TEST(CaseName, TestName) \ + static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \ + "test-name must not be empty"); \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##CaseName##_##TestName##_registered_ \ + GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + GTEST_STRINGIFY_(CaseName), \ + GTEST_STRINGIFY_(TestName), 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)::TestBody() + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE \ + static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \ + TYPED_TEST_SUITE +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Implements type-parameterized tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test suite are defined in. The exact +// name of the namespace is subject to change without notice. +#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test suite. +#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \ + gtest_typed_test_suite_p_state_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test suite. +#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \ + gtest_registered_test_names_##TestSuiteName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +#define TYPED_TEST_SUITE_P(SuiteName) \ + static ::testing::internal::TypedTestSuitePState \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE_P \ + static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \ + TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template \ + class TestName : public SuiteName { \ + private: \ + typedef SuiteName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template \ + void GTEST_SUITE_NAMESPACE_( \ + SuiteName)::TestName::TestBody() + +// Note: this won't work correctly if the trailing arguments are macros. +#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_( \ + SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ + GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define REGISTER_TYPED_TEST_CASE_P \ + static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \ + ""); \ + REGISTER_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ + static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ + "test-suit-prefix must not be empty"); \ + static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestSuite< \ + SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ + ::testing::internal::GenerateTypeList::type>:: \ + Register(GTEST_STRINGIFY_(Prefix), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \ + GTEST_STRINGIFY_(SuiteName), \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::GenerateTypeList::type>()) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TYPED_TEST_CASE_P \ + static_assert( \ + ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \ + INSTANTIATE_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ diff --git a/muduo/gtest/gtest.h b/muduo/gtest/gtest.h new file mode 100644 index 000000000..de7d528fc --- /dev/null +++ b/muduo/gtest/gtest.h @@ -0,0 +1,2321 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-assertion-result.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest-matchers.h" +#include "gtest/gtest-message.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" +#include "gtest/gtest-typed-test.h" +#include "gtest/gtest_pred_impl.h" +#include "gtest/gtest_prod.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag controls whether the test runner should continue execution past +// first failure. +GTEST_DECLARE_bool_(fail_fast); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints only test failures. +GTEST_DECLARE_bool_(brief); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test Environments are recreated for each +// repeat of the tests. The default value is true. If set to false the global +// test Environment objects are only set up once, for the first iteration, and +// only torn down once, for the last. +GTEST_DECLARE_bool_(recreate_environments_when_repeating); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4805 4100) + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); +std::set* GetIgnoredParameterizedTestSuites(); + +// A base class that prevents subclasses from being copyable. +// We do this instead of using '= delete' so as to avoid triggering warnings +// inside user code regarding any of our declarations. +class GTestNonCopyable { + public: + GTestNonCopyable() = default; + GTestNonCopyable(const GTestNonCopyable&) = delete; + GTestNonCopyable& operator=(const GTestNonCopyable&) = delete; + ~GTestNonCopyable() = default; +}; + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestSuite; + +// Old API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TestCase = TestSuite; +#endif +class TestInfo; +class UnitTest; + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestSuites, and +// each TestSuite contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::SetUpTestSuite() before running the first + // test in test suite Foo. Hence a sub-class can define its own + // SetUpTestSuite() method to shadow the one defined in the super + // class. + static void SetUpTestSuite() {} + + // Tears down the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::TearDownTestSuite() after running the last + // test in test suite Foo. Hence a sub-class can define its own + // TearDownTestSuite() method to shadow the one defined in the super + // class. + static void TearDownTestSuite() {} + + // Legacy API is deprecated but still available. Use SetUpTestSuite and + // TearDownTestSuite instead. +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + static void TearDownTestCase() {} + static void SetUpTestCase() {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns true if and only if the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true if and only if the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true if and only if the current test was skipped. + static bool IsSkipped(); + + // Returns true if and only if the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test suite, or for the entire + // invocation of the test program when used outside of the context of a + // test suite. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestSuite or TearDownTestSuite are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + // We do not define a custom serialization except for values that can be + // converted to int64_t, but other values could be logged in this way. + template ::value, + bool> = true> + static void RecordProperty(const std::string& key, const T& value) { + RecordProperty(key, (Message() << value).GetString()); + } + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true if and only if the current test has the same fixture class + // as the first test in the current test suite. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const std::unique_ptr gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } + + // We disallow copying Tests. + Test(const Test&) = delete; + Test& operator=(const Test&) = delete; +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) + : key_(a_key), value_(a_value) {} + + // Gets the user supplied key. + const char* key() const { return key_.c_str(); } + + // Gets the user supplied value. + const char* value() const { return value_.c_str(); } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { value_ = new_value; } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true if and only if the test passed (i.e. no test part failed). + bool Passed() const { return !Skipped() && !Failed(); } + + // Returns true if and only if the test was skipped. + bool Skipped() const; + + // Returns true if and only if the test failed. + bool Failed() const; + + // Returns true if and only if the test fatally failed. + bool HasFatalFailure() const; + + // Returns true if and only if the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test case start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestSuite; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the start time. + void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testsuite tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properties_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + TestResult(const TestResult&) = delete; + TestResult& operator=(const TestResult&) = delete; +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test suite name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test suite name. + const char* test_suite_name() const { return test_suite_name_.c_str(); } + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const char* test_case_name() const { return test_suite_name(); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_ != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_ != nullptr) return value_param_->c_str(); + return nullptr; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test suite Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true if and only if this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#ifdef GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestSuite; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, internal::CodeLocation code_location, + internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_suite_name, const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Skip and records the test result for this object. + void Skip(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_suite_name_; // test suite name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const std::unique_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True if and only if this test should run + bool is_disabled_; // True if and only if this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + TestInfo(const TestInfo&) = delete; + TestInfo& operator=(const TestInfo&) = delete; +}; + +// A test suite, which consists of a vector of TestInfos. +// +// TestSuite is not copyable. +class GTEST_API_ TestSuite { + public: + // Creates a TestSuite with the given name. + // + // TestSuite does NOT have a default constructor. Always use this + // constructor to create a TestSuite object. + // + // Arguments: + // + // name: name of the test suite + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite(const char* name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + + // Destructor of TestSuite. + virtual ~TestSuite(); + + // Gets the name of the TestSuite. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test suite. + const char* type_param() const { + if (type_param_ != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns true if any test in this test suite should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test suite. + int successful_test_count() const; + + // Gets the number of skipped tests in this test suite. + int skipped_test_count() const; + + // Gets the number of failed tests in this test suite. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test suite. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test suite that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test suite. + int total_test_count() const; + + // Returns true if and only if the test suite passed. + bool Passed() const { return !Failed(); } + + // Returns true if and only if the test suite failed. + bool Failed() const { + return failed_test_count() > 0 || ad_hoc_test_result().Failed(); + } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test suite start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestSuite and TearDownTestSuite. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestSuite. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestSuite. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test suite. Will delete the TestInfo upon + // destruction of the TestSuite object. + void AddTestInfo(TestInfo* test_info); + + // Clears the results of all tests in this test suite. + void ClearResult(); + + // Clears the results of all tests in the given test suite. + static void ClearTestSuiteResult(TestSuite* test_suite) { + test_suite->ClearResult(); + } + + // Runs every test in this TestSuite. + void Run(); + + // Skips the execution of tests under this TestSuite + void Skip(); + + // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed + // for catching exceptions thrown from SetUpTestSuite(). + void RunSetUpTestSuite() { + if (set_up_tc_ != nullptr) { + (*set_up_tc_)(); + } + } + + // Runs TearDownTestSuite() for this TestSuite. This wrapper is + // needed for catching exceptions thrown from TearDownTestSuite(). + void RunTearDownTestSuite() { + if (tear_down_tc_ != nullptr) { + (*tear_down_tc_)(); + } + } + + // Returns true if and only if test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true if and only if test skipped. + static bool TestSkipped(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Skipped(); + } + + // Returns true if and only if test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true if and only if the test is disabled and will be reported in + // the XML report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true if and only if test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if and only if this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test suite. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test suite. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test suite. + internal::SetUpTestSuiteFunc set_up_tc_; + // Pointer to the function that tears down the test suite. + internal::TearDownTestSuiteFunc tear_down_tc_; + // True if and only if any test in this test suite should run. + bool should_run_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestSuite and + // TearDownTestSuite. + TestResult ad_hoc_test_result_; + + // We disallow copying TestSuites. + TestSuite(const TestSuite&) = delete; + TestSuite& operator=(const TestSuite&) = delete; +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() = default; + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() = default; + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test suite starts. + virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {} + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired when a test is disabled + virtual void OnTestDisabled(const TestInfo& /*test_info*/) {} + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test suite ends. + virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestDisabled(const TestInfo& /*test_info*/) override {} + void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} + void OnTestEnd(const TestInfo& /*test_info*/) override {} + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + void SuppressEventForwarding(bool); + + private: + friend class TestSuite; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + TestEventListeners(const TestEventListeners&) = delete; + TestEventListeners& operator=(const TestEventListeners&) = delete; +}; + +// A UnitTest consists of a vector of TestSuites. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestSuite object for the test that's currently running, + // or NULL if no test is running. + const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_); + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_); +#endif + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + int successful_test_case_count() const; + int failed_test_case_count() const; + int total_test_case_count() const; + int test_case_to_run_count() const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const; + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test suites. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked + // from SetUpTestSuite or TearDownTestSuite, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableTestSuite(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend std::set* internal::GetIgnoredParameterizedTestSuites(); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + UnitTest(const UnitTest&) = delete; + UnitTest& operator=(const UnitTest&) = delete; +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleTest(); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return EqFailure(lhs_expression, rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), false); +} + +// This block of code defines operator==/!= +// to block lexical scope lookup. +// It prevents using invalid operator==/!= defined at namespace scope. +struct faketype {}; +inline bool operator==(faketype, faketype) { return true; } +inline bool operator!=(faketype, faketype) { return false; } + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +class EqHelper { + public: + // This templatized version is for the general case. + template < + typename T1, typename T2, + // Disable this overload for cases where one argument is a pointer + // and the other is the null pointer constant. + typename std::enable_if::value || + !std::is_pointer::value>::type* = nullptr> + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + template + static AssertionResult Compare( + const char* lhs_expression, const char* rhs_expression, + // Handle cases where '0' is used as a null pointer literal. + std::nullptr_t /* lhs */, T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, static_cast(nullptr), + rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op) \ + template \ + AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) { \ + if (val1 op val2) { \ + return AssertionSuccess(); \ + } else { \ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op); \ + } \ + } + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const char* needle, + const char* haystack); +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const wchar_t* needle, + const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const char* needle, + const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const wchar_t* needle, + const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::string& needle, + const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::string& needle, + const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::wstring& needle, + const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::wstring& needle, + const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, RawType rhs_value) { + const FloatingPoint lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << rhs_value; + + return EqFailure(lhs_expression, rhs_expression, + StringStreamToString(&lhs_ss), StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, const char* file, int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, const char* srcfile, int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) {} + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + AssertHelperData(const AssertHelperData&) = delete; + AssertHelperData& operator=(const AssertHelperData&) = delete; + }; + + AssertHelperData* const data_; + + AssertHelper(const AssertHelper&) = delete; + AssertHelper& operator=(const AssertHelper&) = delete; +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), Combine(), and ConvertGenerator(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// ~FooTest() override { +// // Can use GetParam() here. +// } +// void SetUp() override { +// // Can use GetParam() here. +// } +// void TearDown override { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() = default; + + // The current parameter value. Is also available in the test fixture's + // constructor. + static const ParamType& GetParam() { + GTEST_CHECK_(parameter_ != nullptr) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { parameter_ = parameter; } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template + friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = nullptr; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface {}; + +// Macros for indicating success/failure in test code. + +// Skips test in runtime. +// Skipping test aborts current function. +// Skipped tests are neither successful nor failed. +#define GTEST_SKIP() GTEST_SKIP_("") + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Like GTEST_FAIL(), but at the given source file location. +#define GTEST_FAIL_AT(file, line) \ + return GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !(defined(GTEST_DONT_DEFINE_FAIL) && GTEST_DONT_DEFINE_FAIL) +#define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !(defined(GTEST_DONT_DEFINE_SUCCEED) && GTEST_DONT_DEFINE_SUCCEED) +#define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define GTEST_EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_FATAL_FAILURE_) +#define GTEST_ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Define these macros to 1 to omit the definition of the corresponding +// EXPECT or ASSERT, which clashes with some users' own code. + +#if !(defined(GTEST_DONT_DEFINE_EXPECT_TRUE) && GTEST_DONT_DEFINE_EXPECT_TRUE) +#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) +#endif + +#if !(defined(GTEST_DONT_DEFINE_EXPECT_FALSE) && GTEST_DONT_DEFINE_EXPECT_FALSE) +#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_TRUE) && GTEST_DONT_DEFINE_ASSERT_TRUE) +#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_FALSE) && GTEST_DONT_DEFINE_ASSERT_FALSE) +#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) +#endif + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_EQ) && GTEST_DONT_DEFINE_ASSERT_EQ) +#define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_NE) && GTEST_DONT_DEFINE_ASSERT_NE) +#define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_LE) && GTEST_DONT_DEFINE_ASSERT_LE) +#define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_LT) && GTEST_DONT_DEFINE_ASSERT_LT) +#define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_GE) && GTEST_DONT_DEFINE_ASSERT_GE) +#define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_GT) && GTEST_DONT_DEFINE_ASSERT_GT) +#define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error) \ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, val1, val2, \ + abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error) \ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, val1, val2, \ + abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + +#ifdef GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +#define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + ScopedTrace(const ScopedTrace&) = delete; + ScopedTrace& operator=(const ScopedTrace&) = delete; +}; + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + const ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)( \ + __FILE__, __LINE__, (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles if and only if type1 and type2 +// are the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +constexpr bool StaticAssertTypeEq() noexcept { + static_assert(std::is_same::value, "T1 and T2 are not the same type"); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test suite, and the second +// parameter is the name of the test within the test suite. +// +// The convention is to end the test suite name with "Test". For +// example, a test suite for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ + ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !(defined(GTEST_DONT_DEFINE_TEST) && GTEST_DONT_DEFINE_TEST) +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test suite name. The second parameter is the +// name of the test within the test suite. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } +#define GTEST_TEST_F(test_fixture, test_name) \ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) +#if !(defined(GTEST_DONT_DEFINE_TEST_F) && GTEST_DONT_DEFINE_TEST_F) +#define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name) +#endif + +// Returns a path to a temporary directory, which should be writable. It is +// implementation-dependent whether or not the path is terminated by the +// directory-separator character. +GTEST_API_ std::string TempDir(); + +// Returns a path to a directory that contains ancillary data files that might +// be used by tests. It is implementation dependent whether or not the path is +// terminated by the directory-separator character. The directory and the files +// in it should be considered read-only. +GTEST_API_ std::string SrcDir(); + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4805 4100 + +// Dynamically registers a test with the framework. +// +// This is an advanced API only to be used when the `TEST` macros are +// insufficient. The macros should be preferred when possible, as they avoid +// most of the complexity of calling this function. +// +// The `factory` argument is a factory callable (move-constructible) object or +// function pointer that creates a new instance of the Test object. It +// handles ownership to the caller. The signature of the callable is +// `Fixture*()`, where `Fixture` is the test fixture class for the test. All +// tests registered with the same `test_suite_name` must return the same +// fixture type. This is checked at runtime. +// +// The framework will infer the fixture class from the factory and will call +// the `SetUpTestSuite` and `TearDownTestSuite` for it. +// +// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is +// undefined. +// +// Use case example: +// +// class MyFixture : public ::testing::Test { +// public: +// // All of these optional, just like in regular macro usage. +// static void SetUpTestSuite() { ... } +// static void TearDownTestSuite() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } +// }; +// +// class MyTest : public MyFixture { +// public: +// explicit MyTest(int data) : data_(data) {} +// void TestBody() override { ... } +// +// private: +// int data_; +// }; +// +// void RegisterMyTests(const std::vector& values) { +// for (int v : values) { +// ::testing::RegisterTest( +// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, +// std::to_string(v).c_str(), +// __FILE__, __LINE__, +// // Important to use the fixture type as the return type here. +// [=]() -> MyFixture* { return new MyTest(v); }); +// } +// } +// ... +// int main(int argc, char** argv) { +// ::testing::InitGoogleTest(&argc, argv); +// std::vector values_to_test = LoadValuesFromConfig(); +// RegisterMyTests(values_to_test); +// ... +// return RUN_ALL_TESTS(); +// } +// +template +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory) { + using TestT = typename std::remove_pointer::type; + + class FactoryImpl : public internal::TestFactoryBase { + public: + explicit FactoryImpl(Factory f) : factory_(std::move(f)) {} + Test* CreateTest() override { return factory_(); } + + private: + Factory factory_; + }; + + return internal::MakeAndRegisterTestInfo( + test_suite_name, test_name, type_param, value_param, + internal::CodeLocation(file, line), internal::GetTypeId(), + internal::SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + internal::SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + new FactoryImpl{std::move(factory)}); +} + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); } + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/muduo/gtest/gtest_pred_impl.h b/muduo/gtest/gtest_pred_impl.h new file mode 100644 index 000000000..47a24aa68 --- /dev/null +++ b/muduo/gtest/gtest_pred_impl.h @@ -0,0 +1,279 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Implements a family of generic predicate assertion macros. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +#include "gtest/gtest-assertion-result.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, const char* e1, + Pred pred, const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, v1), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, #v1, pred, v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, const char* e1, + const char* e2, Pred pred, const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, #v1, #v2, pred, v1, v2), \ + on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, Pred pred, + const T1& v1, const T2& v2, const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure) \ + GTEST_ASSERT_( \ + ::testing::AssertPred3Helper(#pred, #v1, #v2, #v3, pred, v1, v2, v3), \ + on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, + const char* e4, Pred pred, const T1& v1, + const T2& v2, const T3& v3, const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, #v1, #v2, #v3, #v4, pred, \ + v1, v2, v3, v4), \ + on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, + const char* e4, const char* e5, Pred pred, + const T1& v1, const T2& v2, const T3& v3, + const T4& v4, const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ", " << e5 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n" + << e5 << " evaluates to " << ::testing::PrintToString(v5); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, #v1, #v2, #v3, #v4, #v5, \ + pred, v1, v2, v3, v4, v5), \ + on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/muduo/gtest/gtest_prod.h b/muduo/gtest/gtest_prod.h new file mode 100644 index 000000000..1f37dc31c --- /dev/null +++ b/muduo/gtest/gtest_prod.h @@ -0,0 +1,60 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google C++ Testing and Mocking Framework definitions useful in production +// code. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/muduo/gtest/internal/custom/README.md b/muduo/gtest/internal/custom/README.md new file mode 100644 index 000000000..cb49e2c75 --- /dev/null +++ b/muduo/gtest/internal/custom/README.md @@ -0,0 +1,44 @@ +# Customization Points + +The custom directory is an injection point for custom user configurations. + +## Header `gtest.h` + +### The following macros can be defined: + +* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of + `OsStackTraceGetterInterface`. +* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See + `testing::TempDir` for semantics and signature. + +## Header `gtest-port.h` + +The following macros can be defined: + +### Logging: + +* `GTEST_LOG_(severity)` +* `GTEST_CHECK_(condition)` +* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too. + +### Threading: + +* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided. +* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal` + are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)` + and `GTEST_DEFINE_STATIC_MUTEX_(mutex)` +* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)` +* `GTEST_LOCK_EXCLUDED_(locks)` + +### Underlying library support features + +* `GTEST_HAS_CXXABI_H_` + +### Exporting API symbols: + +* `GTEST_API_` - Specifier for exported symbols. + +## Header `gtest-printers.h` + +* See documentation at `gtest/gtest-printers.h` for details on how to define a + custom printer. diff --git a/muduo/gtest/internal/custom/gtest-port.h b/muduo/gtest/internal/custom/gtest-port.h new file mode 100644 index 000000000..db02881c0 --- /dev/null +++ b/muduo/gtest/internal/custom/gtest-port.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ diff --git a/muduo/gtest/internal/custom/gtest-printers.h b/muduo/gtest/internal/custom/gtest-printers.h new file mode 100644 index 000000000..b9495d837 --- /dev/null +++ b/muduo/gtest/internal/custom/gtest-printers.h @@ -0,0 +1,42 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ diff --git a/muduo/gtest/internal/custom/gtest.h b/muduo/gtest/internal/custom/gtest.h new file mode 100644 index 000000000..afaaf17ba --- /dev/null +++ b/muduo/gtest/internal/custom/gtest.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ diff --git a/muduo/gtest/internal/gtest-death-test-internal.h b/muduo/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 000000000..8e9c988b9 --- /dev/null +++ b/muduo/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,307 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include + +#include +#include + +#include "gtest/gtest-matchers.h" +#include "gtest/internal/gtest-internal.h" + +GTEST_DECLARE_string_(internal_run_death_test); + +namespace testing { +namespace internal { + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#ifdef GTEST_HAS_DEATH_TEST + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() = default; + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) {} + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + + private: + DeathTest* const test_; + ReturnSentinel(const ReturnSentinel&) = delete; + ReturnSentinel& operator=(const ReturnSentinel&) = delete; + }; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + DeathTest(const DeathTest&) = delete; + DeathTest& operator=(const DeathTest&) = delete; +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() = default; + virtual bool Create(const char* statement, + Matcher matcher, const char* file, + int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test) override; +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher MakeDeathTestMatcher( + Matcher matcher) { + return matcher; +} + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +#if GTEST_HAS_EXCEPTIONS +#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf( \ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +#else +#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +#endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create( \ + #statement, \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != nullptr) { \ + std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + const ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \ + : fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + InternalRunDeathTestFlag(const InternalRunDeathTestFlag&) = delete; + InternalRunDeathTestFlag& operator=(const InternalRunDeathTestFlag&) = delete; +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/muduo/gtest/internal/gtest-filepath.h b/muduo/gtest/internal/gtest-filepath.h new file mode 100644 index 000000000..5189c81da --- /dev/null +++ b/muduo/gtest/internal/gtest-filepath.h @@ -0,0 +1,227 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#include + +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +#if GTEST_HAS_FILE_SYSTEM + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") {} + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {} + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true if and only if the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurrence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + // Returns the length of the path root, including the directory separator at + // the end of the prefix. Returns zero by definition if the path is relative. + // Examples: + // - [Windows] "..\Sibling" => 0 + // - [Windows] "\Windows" => 1 + // - [Windows] "C:/Windows\Notepad.exe" => 3 + // - [Windows] "\\Host\Share\C$/Windows" => 13 + // - [UNIX] "/bin" => 1 + size_t CalculateRootLength() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GTEST_HAS_FILE_SYSTEM + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/muduo/gtest/internal/gtest-internal.h b/muduo/gtest/internal/gtest-internal.h new file mode 100644 index 000000000..a04a9201c --- /dev/null +++ b/muduo/gtest/internal/gtest-internal.h @@ -0,0 +1,1560 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#include "gtest/internal/gtest-port.h" + +#ifdef GTEST_OS_LINUX +#include +#include +#include +#include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-type-util.h" + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo##bar + +// Stringifies its argument. +// Work around a bug in visual studio which doesn't accept code like this: +// +// #define GTEST_STRINGIFY_(name) #name +// #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ... +// MACRO(, x, y) +// +// Complaining about the argument to GTEST_STRINGIFY_ being empty. +// This is allowed by the spec. +#define GTEST_STRINGIFY_HELPER_(name, ...) #name +#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, ) + +namespace proto2 { +class MessageLite; +} + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test suites. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// An IgnoredValue object can be implicitly constructed from ANY value. +class IgnoredValue { + struct Sink {}; + + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + // Disable the conversion if T already has a magical conversion operator. + // Otherwise we get ambiguity. + template ::value, + int>::type = 0> + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4275 /* an exported class was derived from a class that was not exported */) + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); + +} // namespace edit_distance + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, const char* expression_text, + const char* actual_predicate_value, const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8 * sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = ~static_cast(0) >> + (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const uint32_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { return ReinterpretBits(kExponentBitMask); } + + // Non-static methods + + // Returns the bits that represents this number. + const Bits& bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true if and only if this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true if and only if this number is at most kMaxUlps ULP's away + // from rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) <= + kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits& sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits& sam1, + const Bits& sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test suite, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() = default; + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + TestFactoryBase(const TestFactoryBase&) = delete; + TestFactoryBase& operator=(const TestFactoryBase&) = delete; +}; + +// This class provides implementation of TestFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + Test* CreateTest() override { return new TestClass; } +}; + +#ifdef GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestSuite() and TearDownTestSuite() functions. +using SetUpTestSuiteFunc = void (*)(); +using TearDownTestSuiteFunc = void (*)(); + +struct CodeLocation { + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} + + std::string file; + int line; +}; + +// Helper to identify which setup function for TestCase / TestSuite to call. +// Only one function is allowed, either TestCase or TestSute but not both. + +// Utility functions to help SuiteApiResolver +using SetUpTearDownSuiteFuncType = void (*)(); + +inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull( + SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) { + return a == def ? nullptr : a; +} + +template +// Note that SuiteApiResolver inherits from T because +// SetUpTestSuite()/TearDownTestSuite() could be protected. This way +// SuiteApiResolver can access them. +struct SuiteApiResolver : T { + // testing::Test is only forward declared at this point. So we make it a + // dependent class for the compiler to be OK with it. + using Test = + typename std::conditional::type; + + static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both SetUpTestSuite and SetUpTestCase, please " + "make sure there is only one present at " + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::SetUpTestSuite; +#endif + } + + static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both TearDownTestSuite and TearDownTestCase," + " please make sure there is only one present at" + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::TearDownTestSuite; +#endif + } +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// State of the definition of a type-parameterized test suite. +class GTEST_API_ TypedTestSuitePState { + public: + TypedTestSuitePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test suite hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, + "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.insert( + ::std::make_pair(test_name, CodeLocation(file, line))); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames(const char* test_suite_name, + const char* file, int line, + const char* registered_tests); + + private: + typedef ::std::map> RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TypedTestCasePState = TypedTestSuitePState; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == nullptr) { + return nullptr; + } + while (IsSpace(*(++comma))) { + } + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == nullptr ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector<::std::string>* dest); + +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template +struct NameGeneratorSelector { + typedef Provided type; +}; + +template +void GenerateNamesRecursively(internal::None, std::vector*, int) {} + +template +void GenerateNamesRecursively(Types, std::vector* result, int i) { + result->push_back(NameGenerator::template GetName(i)); + GenerateNamesRecursively(typename Types::Tail(), result, + i + 1); +} + +template +std::vector GenerateNames() { + std::vector result; + GenerateNamesRecursively(Types(), &result, 0); + return result; +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const CodeLocation& code_location, + const char* case_name, const char* test_names, int index, + const std::vector& type_names = + GenerateNames()) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[static_cast(index)]) + .c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName().c_str(), + nullptr, // No value parameter. + code_location, GetTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite( + code_location.file.c_str(), code_location.line), + SuiteApiResolver::GetTearDownCaseOrSuite( + code_location.file.c_str(), code_location.line), + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest::Register(prefix, + code_location, + case_name, + test_names, + index + 1, + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location); +GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation( + const char* case_name); + +// TypeParameterizedTestSuite::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestSuitePState* state, const char* case_name, + const char* test_names, + const std::vector& type_names = + GenerateNames()) { + RegisterTypeParameterizedTestSuiteInstantiation(case_name); + std::string test_name = + StripTrailingSpaces(GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), code_location.line) + .c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, test_location, case_name, test_names, 0, type_names); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestSuite::Register(prefix, code_location, + state, case_name, + SkipComma(test_names), + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestSuitePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// Helper for declaring std::string within 'if' statement +// in pre C++17 build environment. +struct TrueWithString { + TrueWithString() = default; + explicit TrueWithString(const char* str) : value(str) {} + explicit TrueWithString(const std::string& str) : value(str) {} + explicit operator bool() const { return true; } + std::string value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const uint32_t kMaxRange = 1u << 31; + + explicit Random(uint32_t seed) : state_(seed) {} + + void Reseed(uint32_t seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + uint32_t Generate(uint32_t range); + + private: + uint32_t state_; + Random(const Random&) = delete; + Random& operator=(const Random&) = delete; +}; + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + typename std::remove_const::type>::type + +// HasDebugStringAndShortDebugString::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. +template +class HasDebugStringAndShortDebugString { + private: + template + static auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().DebugString())>::type; + template + static std::false_type CheckDebugString(...); + + template + static auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().ShortDebugString())>::type; + template + static std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +#ifdef GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +template +constexpr bool HasDebugStringAndShortDebugString::value; +#endif + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { + return '\0'; +} + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(nullptr, nullptr)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template (0)) == sizeof(IsContainer)> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public std::false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl { + using value_type = decltype(*std::declval()); + using type = + std::is_same::type>::type, + C>; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { + return lhs == rhs; +} + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T (&lhs)[N], const U (&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { + *to = from; +} + +// This overload is used when k >= 1. +template +inline void CopyArray(const T (&from)[N], U (*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && ArrayEq(begin(), size(), rhs.begin()); + } + + private: + static_assert(!std::is_const::value, "Type must not be const"); + static_assert(!std::is_reference::value, + "Type must not be a reference"); + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); +}; + +// Backport of std::index_sequence. +template +struct IndexSequence { + using type = IndexSequence; +}; + +// Double the IndexSequence, and one if plus_one is true. +template +struct DoubleSequence; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; + +// Backport of std::make_index_sequence. +// It uses O(ln(N)) instantiation depth. +template +struct MakeIndexSequenceImpl + : DoubleSequence::type, + N / 2>::type {}; + +template <> +struct MakeIndexSequenceImpl<0> : IndexSequence<> {}; + +template +using MakeIndexSequence = typename MakeIndexSequenceImpl::type; + +template +using IndexSequenceFor = typename MakeIndexSequence::type; + +template +struct Ignore { + Ignore(...); // NOLINT +}; + +template +struct ElemFromListImpl; +template +struct ElemFromListImpl> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; + +template +struct ElemFromList { + using type = + decltype(ElemFromListImpl::type>::Apply( + static_cast(nullptr)...)); +}; + +struct FlatTupleConstructTag {}; + +template +class FlatTuple; + +template +struct FlatTupleElemBase; + +template +struct FlatTupleElemBase, I> { + using value_type = typename ElemFromList::type; + FlatTupleElemBase() = default; + template + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward(t)) {} + value_type value; +}; + +template +struct FlatTupleBase; + +template +struct FlatTupleBase, IndexSequence> + : FlatTupleElemBase, Idx>... { + using Indices = IndexSequence; + FlatTupleBase() = default; + template + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase, Idx>(FlatTupleConstructTag{}, + std::forward(args))... {} + + template + const typename ElemFromList::type& Get() const { + return FlatTupleElemBase, I>::value; + } + + template + typename ElemFromList::type& Get() { + return FlatTupleElemBase, I>::value; + } + + template + auto Apply(F&& f) -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } + + template + auto Apply(F&& f) const -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } +}; + +// Analog to std::tuple but with different tradeoffs. +// This class minimizes the template instantiation depth, thus allowing more +// elements than std::tuple would. std::tuple has been seen to require an +// instantiation depth of more than 10x the number of elements in some +// implementations. +// FlatTuple and ElemFromList are not recursive and have a fixed depth +// regardless of T... +// MakeIndexSequence, on the other hand, it is recursive but with an +// instantiation depth of O(ln(N)). +template +class FlatTuple + : private FlatTupleBase, + typename MakeIndexSequence::type> { + using Indices = typename FlatTupleBase< + FlatTuple, typename MakeIndexSequence::type>::Indices; + + public: + FlatTuple() = default; + template + explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args) + : FlatTuple::FlatTupleBase(tag, std::forward(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; +}; + +// Utility functions to be called with static_assert to induce deprecation +// warnings. +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TEST_SUITE_P") +constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE_P is deprecated, please use " + "TYPED_TEST_SUITE_P") +constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE is deprecated, please use " + "TYPED_TEST_SUITE") +constexpr bool TypedTestCaseIsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " + "REGISTER_TYPED_TEST_SUITE_P") +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TYPED_TEST_SUITE_P") +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + +} // namespace internal +} // namespace testing + +namespace std { +// Some standard library implementations use `struct tuple_size` and some use +// `class tuple_size`. Clang warns about the mismatch. +// https://reviews.llvm.org/D55466 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +struct tuple_size> + : std::integral_constant {}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace std + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) = \ + ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +#define GTEST_SKIP_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) + +// Suppress MSVC warning 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +// NOTE: The "else" is important to keep this expansion to prevent a top-level +// "else" from attaching to our "if". +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } else /* NOLINT */ \ + static_assert(true, "") // User must have a semicolon after expansion. + +#if GTEST_HAS_EXCEPTIONS + +namespace testing { +namespace internal { + +class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } +}; + +} // namespace internal +} // namespace testing + +#if GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e)) + +#else // GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) \ + std::string { "an std::exception-derived error" } + +#endif // GTEST_HAS_RTTI + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (typename std::conditional< \ + std::is_same::type>::type, \ + std::exception>::value, \ + const ::testing::internal::NeverThrown&, const std::exception&>::type \ + e) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#if GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (std::exception const& e) { \ + gtest_msg.value = "it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (...) { \ + gtest_msg.value = "it throws."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__) \ + : fail(("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: " + \ + gtest_msg.value) \ + .c_str()) + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__) \ + : fail("Expected: " #statement \ + " throws an exception.\n" \ + " Actual: it doesn't.") + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// representation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage( \ + gtest_ar_, text, #actual, #expected) \ + .c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::HasNewFatalFailureHelper \ + gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else /* NOLINT */ \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__) \ + : fail("Expected: " #statement \ + " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &) = delete; /* NOLINT */ \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &&) noexcept = delete; /* NOLINT */ \ + \ + private: \ + void TestBody() override; \ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/muduo/gtest/internal/gtest-param-util.h b/muduo/gtest/internal/gtest-param-util.h new file mode 100644 index 000000000..6a81c37fa --- /dev/null +++ b/muduo/gtest/internal/gtest-param-util.h @@ -0,0 +1,1026 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type and function utilities for implementing parameterized tests. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) + : param(a_param), index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template + std::string operator()(const TestParamInfo& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// Utility Functions + +// Outputs a message explaining invalid registration of different +// fixture class for the same test suite. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location); + +template +class ParamGeneratorInterface; +template +class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() = default; + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + std::unique_ptr> impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() = default; + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + std::shared_ptr> impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), + end_(end), + step_(step), + end_index_(CalculateEndIndex(begin, end, step)) {} + ~RangeGenerator() override = default; + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, begin_, 0, step_); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + ~Iterator() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + value_ = static_cast(value_ + step_); + index_++; + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const T* Current() const override { return &value_; } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), + value_(other.value_), + index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast(i + step)) end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + ~ValuesInIteratorRangeGenerator() override = default; + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, container_.begin()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + ~Iterator() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++iterator_; + value_.reset(); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + const T* Current() const override { + if (value_.get() == nullptr) value_.reset(new T(*iterator_)); + return value_.get(); + } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of std::unique_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable std::unique_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template +std::string DefaultParamName(const TestParamInfo& info) { + Message name_stream; + name_stream << info.index; + return name_stream.GetString(); +} + +template +void TestNotEmpty() { + static_assert(sizeof(T) == 0, "Empty arguments are not allowed."); +} +template +void TestNotEmpty(const T&) {} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) + : parameter_(parameter) {} + Test* CreateTest() override { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + ParameterizedTestFactory(const ParameterizedTestFactory&) = delete; + ParameterizedTestFactory& operator=(const ParameterizedTestFactory&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() = default; + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestSuiteInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + using ParamType = typename TestSuite::ParamType; + + TestMetaFactory() = default; + + TestFactoryBase* CreateTestFactory(ParamType parameter) override { + return new ParameterizedTestFactory(parameter); + } + + private: + TestMetaFactory(const TestMetaFactory&) = delete; + TestMetaFactory& operator=(const TestMetaFactory&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfoBase is a generic interface +// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds +// a collection of pointers to the ParameterizedTestSuiteInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestSuiteInfoBase { + public: + virtual ~ParameterizedTestSuiteInfoBase() = default; + + // Base part of test suite name for display purposes. + virtual const std::string& GetTestSuiteName() const = 0; + // Test suite id to verify identity. + virtual TypeId GetTestSuiteTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test suite right before running them in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestSuiteInfoBase() {} + + private: + ParameterizedTestSuiteInfoBase(const ParameterizedTestSuiteInfoBase&) = + delete; + ParameterizedTestSuiteInfoBase& operator=( + const ParameterizedTestSuiteInfoBase&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Report a the name of a test_suit as safe to ignore +// as the side effect of construction of this type. +struct GTEST_API_ MarkAsIgnored { + explicit MarkAsIgnored(const char* test_suite); +}; + +GTEST_API_ void InsertSyntheticTestCase(const std::string& name, + CodeLocation location, bool has_test_p); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test suite and generators +// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that +// test suite. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestSuiteInstantiation(). + using ParamType = typename TestSuite::ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + using ParamNameGeneratorFunc = std::string(const TestParamInfo&); + + explicit ParameterizedTestSuiteInfo(const char* name, + CodeLocation code_location) + : test_suite_name_(name), code_location_(code_location) {} + + // Test suite base name for display purposes. + const std::string& GetTestSuiteName() const override { + return test_suite_name_; + } + // Test suite id to verify identity. + TypeId GetTestSuiteTypeId() const override { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_suite_name is the base name of the test suite (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test suite base name and DoBar is test base name. + void AddTestPattern(const char* test_suite_name, const char* test_base_name, + TestMetaFactoryBase* meta_factory, + CodeLocation code_location) { + tests_.push_back(std::shared_ptr(new TestInfo( + test_suite_name, test_base_name, meta_factory, code_location))); + } + // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestSuiteInstantiation(const std::string& instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.push_back( + InstantiationInfo(instantiation_name, func, name_func, file, line)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test suite + // right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more than once. + void RegisterTests() override { + bool generated_instantiations = false; + + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + std::shared_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); + gen_it != instantiations_.end(); ++gen_it) { + const std::string& instantiation_name = gen_it->name; + ParamGenerator generator((*gen_it->generator)()); + ParamNameGeneratorFunc* name_func = gen_it->name_func; + const char* file = gen_it->file; + int line = gen_it->line; + + std::string test_suite_name; + if (!instantiation_name.empty()) + test_suite_name = instantiation_name + "/"; + test_suite_name += test_info->test_suite_base_name; + + size_t i = 0; + std::set test_param_names; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + generated_instantiations = true; + + Message test_name_stream; + + std::string param_name = + name_func(TestParamInfo(*param_it, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid, in " << file << " line " << line << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name << "', in " + << file << " line " << line << std::endl; + + test_param_names.insert(param_name); + + if (!test_info->test_base_name.empty()) { + test_name_stream << test_info->test_base_name << "/"; + } + test_name_stream << param_name; + MakeAndRegisterTestInfo( + test_suite_name.c_str(), test_name_stream.GetString().c_str(), + nullptr, // No type parameter. + PrintToString(*param_it).c_str(), test_info->code_location, + GetTestSuiteTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_, + !tests_.empty()); + } + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory, + CodeLocation a_code_location) + : test_suite_base_name(a_test_suite_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory), + code_location(a_code_location) {} + + const std::string test_suite_base_name; + const std::string test_base_name; + const std::unique_ptr> test_meta_factory; + const CodeLocation code_location; + }; + using TestInfoContainer = ::std::vector>; + // Records data received from INSTANTIATE_TEST_SUITE_P macros: + // + struct InstantiationInfo { + InstantiationInfo(const std::string& name_in, + GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, const char* file_in, + int line_in) + : name(name_in), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!IsAlNum(name[index]) && name[index] != '_') return false; + } + + return true; + } + + const std::string test_suite_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + ParameterizedTestSuiteInfo(const ParameterizedTestSuiteInfo&) = delete; + ParameterizedTestSuiteInfo& operator=(const ParameterizedTestSuiteInfo&) = + delete; +}; // class ParameterizedTestSuiteInfo + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +template +using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteRegistry contains a map of +// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P +// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding +// ParameterizedTestSuiteInfo descriptors. +class ParameterizedTestSuiteRegistry { + public: + ParameterizedTestSuiteRegistry() = default; + ~ParameterizedTestSuiteRegistry() { + for (auto& test_suite_info : test_suite_infos_) { + delete test_suite_info; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test suite. + template + ParameterizedTestSuiteInfo* GetTestSuitePatternHolder( + const char* test_suite_name, CodeLocation code_location) { + ParameterizedTestSuiteInfo* typed_test_info = nullptr; + for (auto& test_suite_info : test_suite_infos_) { + if (test_suite_info->GetTestSuiteName() == test_suite_name) { + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name, code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestSuiteInfo>(test_suite_info); + } + break; + } + } + if (typed_test_info == nullptr) { + typed_test_info = new ParameterizedTestSuiteInfo( + test_suite_name, code_location); + test_suite_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (auto& test_suite_info : test_suite_infos_) { + test_suite_info->RegisterTests(); + } + } +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder(test_case_name, code_location); + } + +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + private: + using TestSuiteInfoContainer = ::std::vector; + + TestSuiteInfoContainer test_suite_infos_; + + ParameterizedTestSuiteRegistry(const ParameterizedTestSuiteRegistry&) = + delete; + ParameterizedTestSuiteRegistry& operator=( + const ParameterizedTestSuiteRegistry&) = delete; +}; + +// Keep track of what type-parameterized test suite are defined and +// where as well as which are intatiated. This allows susequently +// identifying suits that are defined but never used. +class TypeParameterizedTestSuiteRegistry { + public: + // Add a suite definition + void RegisterTestSuite(const char* test_suite_name, + CodeLocation code_location); + + // Add an instantiation of a suit. + void RegisterInstantiation(const char* test_suite_name); + + // For each suit repored as defined but not reported as instantiation, + // emit a test that reports that fact (configurably, as an error). + void CheckForInstantiations(); + + private: + struct TypeParameterizedTestSuiteInfo { + explicit TypeParameterizedTestSuiteInfo(CodeLocation c) + : code_location(c), instantiated(false) {} + + CodeLocation code_location; + bool instantiated; + }; + + std::map suites_; +}; + +} // namespace internal + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { +// Used in the Values() function to provide polymorphic capabilities. + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4100) + +template +class ValueArray { + public: + explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {} + + template + operator ParamGenerator() const { // NOLINT + return ValuesIn(MakeVector(MakeIndexSequence())); + } + + private: + template + std::vector MakeVector(IndexSequence) const { + return std::vector{static_cast(v_.template Get())...}; + } + + FlatTuple v_; +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4100 + +template +class CartesianProductGenerator + : public ParamGeneratorInterface<::std::tuple> { + public: + typedef ::std::tuple ParamType; + + CartesianProductGenerator(const std::tuple...>& g) + : generators_(g) {} + ~CartesianProductGenerator() override = default; + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generators_, false); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generators_, true); + } + + private: + template + class IteratorImpl; + template + class IteratorImpl> + : public ParamIteratorInterface { + public: + IteratorImpl(const ParamGeneratorInterface* base, + const std::tuple...>& generators, + bool is_end) + : base_(base), + begin_(std::get(generators).begin()...), + end_(std::get(generators).end()...), + current_(is_end ? end_ : begin_) { + ComputeCurrentValue(); + } + ~IteratorImpl() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + void Advance() override { + assert(!AtEnd()); + // Advance the last iterator. + ++std::get(current_); + // if that reaches end, propagate that up. + AdvanceIfEnd(); + ComputeCurrentValue(); + } + ParamIteratorInterface* Clone() const override { + return new IteratorImpl(*this); + } + + const ParamType* Current() const override { return current_value_.get(); } + + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const IteratorImpl* typed_other = + CheckedDowncastToActualType(&other); + + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + if (AtEnd() && typed_other->AtEnd()) return true; + + bool same = true; + bool dummy[] = { + (same = same && std::get(current_) == + std::get(typed_other->current_))...}; + (void)dummy; + return same; + } + + private: + template + void AdvanceIfEnd() { + if (std::get(current_) != std::get(end_)) return; + + bool last = ThisI == 0; + if (last) { + // We are done. Nothing else to propagate. + return; + } + + constexpr size_t NextI = ThisI - (ThisI != 0); + std::get(current_) = std::get(begin_); + ++std::get(current_); + AdvanceIfEnd(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = std::make_shared(*std::get(current_)...); + } + bool AtEnd() const { + bool at_end = false; + bool dummy[] = { + (at_end = at_end || std::get(current_) == std::get(end_))...}; + (void)dummy; + return at_end; + } + + const ParamGeneratorInterface* const base_; + std::tuple::iterator...> begin_; + std::tuple::iterator...> end_; + std::tuple::iterator...> current_; + std::shared_ptr current_value_; + }; + + using Iterator = IteratorImpl::type>; + + std::tuple...> generators_; +}; + +template +class CartesianProductHolder { + public: + CartesianProductHolder(const Gen&... g) : generators_(g...) {} + template + operator ParamGenerator<::std::tuple>() const { + return ParamGenerator<::std::tuple>( + new CartesianProductGenerator(generators_)); + } + + private: + std::tuple generators_; +}; + +template +class ParamGeneratorConverter : public ParamGeneratorInterface { + public: + ParamGeneratorConverter(ParamGenerator gen) // NOLINT + : generator_(std::move(gen)) {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generator_.begin(), generator_.end()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generator_.end(), generator_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, ParamIterator it, + ParamIterator end) + : base_(base), it_(it), end_(end) { + if (it_ != end_) value_ = std::make_shared(static_cast(*it_)); + } + ~Iterator() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++it_; + if (it_ != end_) value_ = std::make_shared(static_cast(*it_)); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const To* Current() const override { return value_.get(); } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const ParamIterator other_it = + CheckedDowncastToActualType(&other)->it_; + return it_ == other_it; + } + + private: + Iterator(const Iterator& other) = default; + + const ParamGeneratorInterface* const base_; + ParamIterator it_; + ParamIterator end_; + std::shared_ptr value_; + }; // class ParamGeneratorConverter::Iterator + + ParamGenerator generator_; +}; // class ParamGeneratorConverter + +template +class ParamConverterGenerator { + public: + ParamConverterGenerator(ParamGenerator g) // NOLINT + : generator_(std::move(g)) {} + + template + operator ParamGenerator() const { // NOLINT + return ParamGenerator(new ParamGeneratorConverter(generator_)); + } + + private: + ParamGenerator generator_; +}; + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ diff --git a/muduo/gtest/internal/gtest-port-arch.h b/muduo/gtest/internal/gtest-port-arch.h new file mode 100644 index 000000000..3162f2b1c --- /dev/null +++ b/muduo/gtest/internal/gtest-port-arch.h @@ -0,0 +1,122 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +#define GTEST_OS_CYGWIN 1 +#elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +#define GTEST_OS_WINDOWS_MINGW 1 +#define GTEST_OS_WINDOWS 1 +#elif defined _WIN32 +#define GTEST_OS_WINDOWS 1 +#ifdef _WIN32_WCE +#define GTEST_OS_WINDOWS_MOBILE 1 +#elif defined(WINAPI_FAMILY) +#include +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define GTEST_OS_WINDOWS_DESKTOP 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +#define GTEST_OS_WINDOWS_PHONE 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define GTEST_OS_WINDOWS_RT 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +#define GTEST_OS_WINDOWS_PHONE 1 +#define GTEST_OS_WINDOWS_TV_TITLE 1 +#else +// WINAPI_FAMILY defined but no known partition matched. +// Default to desktop. +#define GTEST_OS_WINDOWS_DESKTOP 1 +#endif +#else +#define GTEST_OS_WINDOWS_DESKTOP 1 +#endif // _WIN32_WCE +#elif defined __OS2__ +#define GTEST_OS_OS2 1 +#elif defined __APPLE__ +#define GTEST_OS_MAC 1 +#include +#if TARGET_OS_IPHONE +#define GTEST_OS_IOS 1 +#endif +#elif defined __DragonFly__ +#define GTEST_OS_DRAGONFLY 1 +#elif defined __FreeBSD__ +#define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +#define GTEST_OS_FUCHSIA 1 +#elif defined(__GNU__) +#define GTEST_OS_GNU_HURD 1 +#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__) +#define GTEST_OS_GNU_KFREEBSD 1 +#elif defined __linux__ +#define GTEST_OS_LINUX 1 +#if defined __ANDROID__ +#define GTEST_OS_LINUX_ANDROID 1 +#endif +#elif defined __MVS__ +#define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +#define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +#define GTEST_OS_AIX 1 +#elif defined(__hpux) +#define GTEST_OS_HPUX 1 +#elif defined __native_client__ +#define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +#define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +#define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +#define GTEST_OS_QNX 1 +#elif defined(__HAIKU__) +#define GTEST_OS_HAIKU 1 +#elif defined ESP8266 +#define GTEST_OS_ESP8266 1 +#elif defined ESP32 +#define GTEST_OS_ESP32 1 +#elif defined(__XTENSA__) +#define GTEST_OS_XTENSA 1 +#elif defined(__hexagon__) +#define GTEST_OS_QURT 1 +#elif defined(CPU_QN9090) || defined(CPU_QN9090HN) +#define GTEST_OS_NXP_QN9090 1 +#elif defined(NRF52) +#define GTEST_OS_NRF52 1 +#endif // __CYGWIN__ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ diff --git a/muduo/gtest/internal/gtest-port.h b/muduo/gtest/internal/gtest-port.h new file mode 100644 index 000000000..b887e24ed --- /dev/null +++ b/muduo/gtest/internal/gtest-port.h @@ -0,0 +1,2481 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_FILE_SYSTEM - Define it to 1/0 to indicate whether or not a +// file system is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_DRAGONFLY - DragonFlyBSD +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_GNU_HURD - GNU/Hurd +// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD +// GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_OS2 - OS/2 +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #ifdef +// which controls that code. For example: +// +// #ifdef GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GTEST_USES_RE2 - the RE2 regular expression library is used +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. +// GTEST_HAS_ABSL - Google Test is compiled with Abseil. + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter or +// UniversalPrinter specializations. +// Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter +// or +// UniversalPrinter +// specializations. Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher or +// Matcher +// specializations. Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter or +// UniversalPrinter +// specializations. Always defined to 0 or 1. +// GTEST_USE_OWN_FLAGFILE_FLAG_ - Always defined to 0 or 1. +// GTEST_HAS_CXXABI_H_ - Always defined to 0 or 1. +// GTEST_CAN_STREAM_RESULTS_ - Always defined to 0 or 1. +// GTEST_HAS_ALT_PATH_SEP_ - Always defined to 0 or 1. +// GTEST_WIDE_STRING_USES_UTF16_ - Always defined to 0 or 1. +// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Always defined to 0 or 1. +// GTEST_HAS_NOTIFICATION_- Always defined to 0 or 1. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Regular expressions: +// RE - a simple regular expression class using +// 1) the RE2 syntax on all platforms when built with RE2 +// and Abseil as dependencies +// 2) the POSIX Extended Regular Expression syntax on +// UNIX-like platforms, +// 3) A reduced regular exception syntax on other platforms, +// including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// TimeInMillis - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an int32_t environment variable. +// StringFromGTestEnv() - parses a string environment variable. +// +// Deprecation warnings: +// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as +// deprecated; calling a marked function +// should generate a compiler warning + +// The definition of GTEST_INTERNAL_CPLUSPLUS_LANG comes first because it can +// potentially be used as an #include guard. +#if defined(_MSVC_LANG) +#define GTEST_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define GTEST_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + +#if !defined(GTEST_INTERNAL_CPLUSPLUS_LANG) || \ + GTEST_INTERNAL_CPLUSPLUS_LANG < 201402L +#error C++ versions less than C++14 are not supported. +#endif + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include + +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include +#include +#include +#include +#include +#include +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include +#include +#include + +#ifndef _WIN32_WCE +#include +#include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +#include +#include +#endif + +#include "gtest/internal/custom/gtest-port.h" +#include "gtest/internal/gtest-port-arch.h" + +#ifndef GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +#define GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ 0 +#endif + +#ifndef GTEST_HAS_NOTIFICATION_ +#define GTEST_HAS_NOTIFICATION_ 0 +#endif + +#ifdef GTEST_HAS_ABSL +#include "absl/flags/declare.h" +#include "absl/flags/flag.h" +#include "absl/flags/reflection.h" +#endif + +#if !defined(GTEST_DEV_EMAIL_) +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +#define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +#define GTEST_GCC_VER_ \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if defined(_MSC_VER) +#define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) __pragma(warning(disable : warnings)) +#define GTEST_DISABLE_MSC_WARNINGS_POP_() __pragma(warning(pop)) +#else +// Not all compilers are MSVC +#define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +#define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +#define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() _Pragma("clang diagnostic pop") +#else +#define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#ifdef GTEST_OS_WINDOWS +#ifndef GTEST_OS_WINDOWS_MOBILE +#include +#include +#endif +// In order to avoid having to include , use forward declaration +#if defined(GTEST_OS_WINDOWS_MINGW) && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#elif defined(GTEST_OS_XTENSA) +#include +// Xtensa toolchains define strcasecmp in the string.h header instead of +// strings.h. string.h is already included. +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +#include +#include +#endif // GTEST_OS_WINDOWS + +#ifdef GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +#include // NOLINT +#endif + +// Defines this to true if and only if Google Test can use POSIX regular +// expressions. +#ifndef GTEST_HAS_POSIX_RE +#ifdef GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +#define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +#else +#if !(defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT)) +#define GTEST_HAS_POSIX_RE 1 +#else +#define GTEST_HAS_POSIX_RE 0 +#endif +#endif // GTEST_OS_LINUX_ANDROID +#endif + +// Select the regular expression implementation. +#ifdef GTEST_HAS_ABSL +// When using Abseil, RE2 is required. +#include "absl/strings/string_view.h" +#include "re2/re2.h" +#define GTEST_USES_RE2 1 +#elif GTEST_HAS_POSIX_RE +#include // NOLINT +#define GTEST_USES_POSIX_RE 1 +#else +// Use our own simple regex implementation. +#define GTEST_USES_SIMPLE_RE 1 +#endif + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +#if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +#ifndef _HAS_EXCEPTIONS +#define _HAS_EXCEPTIONS 1 +#endif // _HAS_EXCEPTIONS +#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +#elif defined(__clang__) +// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang +// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files, +// there can be cleanups for ObjC exceptions which also need cleanups, even if +// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which +// checks for C++ exceptions starting at clang r206352, but which checked for +// cleanups prior to that. To reliably check for C++ exception availability with +// clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +#if defined(__EXCEPTIONS) && __EXCEPTIONS && __has_feature(cxx_exceptions) +#define GTEST_HAS_EXCEPTIONS 1 +#else +#define GTEST_HAS_EXCEPTIONS 0 +#endif +#elif defined(__GNUC__) && defined(__EXCEPTIONS) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__IBMCPP__) && defined(__EXCEPTIONS) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +#define GTEST_HAS_EXCEPTIONS 1 +#else +// For other compilers, we assume exceptions are disabled to be +// conservative. +#define GTEST_HAS_EXCEPTIONS 0 +#endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +#if (!(defined(GTEST_OS_LINUX_ANDROID) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_HAIKU) || \ + defined(GTEST_OS_ESP32) || defined(GTEST_OS_ESP8266) || \ + defined(GTEST_OS_XTENSA) || defined(GTEST_OS_QURT) || \ + defined(GTEST_OS_NXP_QN9090) || defined(GTEST_OS_NRF52))) +#define GTEST_HAS_STD_WSTRING 1 +#else +#define GTEST_HAS_STD_WSTRING 0 +#endif +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_FILE_SYSTEM +// Most platforms support a file system. +#define GTEST_HAS_FILE_SYSTEM 1 +#endif // GTEST_HAS_FILE_SYSTEM + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +#ifdef _MSC_VER + +#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled. +#define GTEST_HAS_RTTI 1 +#else +#define GTEST_HAS_RTTI 0 +#endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is +// enabled. +#elif defined(__GNUC__) + +#ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +#if defined(GTEST_OS_LINUX_ANDROID) && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +#define GTEST_HAS_RTTI 0 +#else +#define GTEST_HAS_RTTI 1 +#endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +#else +#define GTEST_HAS_RTTI 0 +#endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +#elif defined(__clang__) + +#define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +#elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +#ifdef __RTTI_ALL__ +#define GTEST_HAS_RTTI 1 +#else +#define GTEST_HAS_RTTI 0 +#endif + +#else + +// For all other compilers, we assume RTTI is enabled. +#define GTEST_HAS_RTTI 1 + +#endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +#include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_MAC) || \ + defined(GTEST_OS_HPUX) || defined(GTEST_OS_QNX) || \ + defined(GTEST_OS_FREEBSD) || defined(GTEST_OS_NACL) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_OPENBSD) || defined(GTEST_OS_HAIKU) || \ + defined(GTEST_OS_GNU_HURD)) +#define GTEST_HAS_PTHREAD 1 +#else +#define GTEST_HAS_PTHREAD 0 +#endif +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +#include // NOLINT + +// For timespec and nanosleep, used below. +#include // NOLINT +#endif + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +#if defined(GTEST_OS_LINUX) && !defined(__ia64__) +#if defined(GTEST_OS_LINUX_ANDROID) +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +#if defined(__LP64__) || (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +#define GTEST_HAS_CLONE 1 +#else +#define GTEST_HAS_CLONE 0 +#endif +#else +#define GTEST_HAS_CLONE 1 +#endif +#else +#define GTEST_HAS_CLONE 0 +#endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile / embedded ones. Also, if the port doesn't have +// a file system, stream redirection is not supported. +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_ESP8266) || \ + defined(GTEST_OS_XTENSA) || defined(GTEST_OS_QURT) || \ + !GTEST_HAS_FILE_SYSTEM +#define GTEST_HAS_STREAM_REDIRECTION 0 +#else +#define GTEST_HAS_STREAM_REDIRECTION 1 +#endif // !GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// pops up a dialog window that cannot be suppressed programmatically. +#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_SOLARIS) || \ + (defined(GTEST_OS_MAC) && !defined(GTEST_OS_IOS)) || \ + (defined(GTEST_OS_WINDOWS_DESKTOP) && _MSC_VER) || \ + defined(GTEST_OS_WINDOWS_MINGW) || defined(GTEST_OS_AIX) || \ + defined(GTEST_OS_HPUX) || defined(GTEST_OS_OPENBSD) || \ + defined(GTEST_OS_QNX) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_HAIKU) || defined(GTEST_OS_GNU_HURD)) +// Death tests require a file system to work properly. +#if GTEST_HAS_FILE_SYSTEM +#define GTEST_HAS_DEATH_TEST 1 +#endif // GTEST_HAS_FILE_SYSTEM +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +#define GTEST_HAS_TYPED_TEST 1 +#define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_AIX) || defined(GTEST_OS_OS2) +#define GTEST_WIDE_STRING_USES_UTF16_ 1 +#else +#define GTEST_WIDE_STRING_USES_UTF16_ 0 +#endif + +// Determines whether test results can be streamed to a socket. +#if defined(GTEST_OS_LINUX) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_OPENBSD) || \ + defined(GTEST_OS_GNU_HURD) || defined(GTEST_OS_MAC) +#define GTEST_CAN_STREAM_RESULTS_ 1 +#else +#define GTEST_CAN_STREAM_RESULTS_ 0 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + switch (0) \ + case 0: \ + default: // NOLINT +#endif + +// GTEST_HAVE_ATTRIBUTE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. +// +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define GTEST_HAVE_ATTRIBUTE_(x) __has_attribute(x) +#else +#define GTEST_HAVE_ATTRIBUTE_(x) 0 +#endif + +// GTEST_HAVE_FEATURE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_feature`. +#ifdef __has_feature +#define GTEST_HAVE_FEATURE_(x) __has_feature(x) +#else +#define GTEST_HAVE_FEATURE_(x) 0 +#endif + +// Use this annotation after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +// Example: +// +// GTEST_ATTRIBUTE_UNUSED_ int foo = bar(); +#if GTEST_HAVE_ATTRIBUTE_(unused) +#define GTEST_ATTRIBUTE_UNUSED_ __attribute__((unused)) +#else +#define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// Use this annotation before a function that takes a printf format string. +#if GTEST_HAVE_ATTRIBUTE_(format) && defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, first_to_check))) +#elif GTEST_HAVE_ATTRIBUTE_(format) +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((format(printf, string_index, first_to_check))) +#else +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if GTEST_HAVE_ATTRIBUTE_(warn_unused_result) +#define GTEST_MUST_USE_RESULT_ __attribute__((warn_unused_result)) +#else +#define GTEST_MUST_USE_RESULT_ +#endif + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +#define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +#define GTEST_INTENTIONAL_CONST_COND_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +#define GTEST_HAS_SEH 1 +#else +// Assume no SEH. +#define GTEST_HAS_SEH 0 +#endif + +#endif // GTEST_HAS_SEH + +#ifndef GTEST_IS_THREADSAFE + +#if (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT)) || \ + GTEST_HAS_PTHREAD) +#define GTEST_IS_THREADSAFE 1 +#endif + +#endif // GTEST_IS_THREADSAFE + +#ifdef GTEST_IS_THREADSAFE +// Some platforms don't support including these threading related headers. +#include // NOLINT +#include // NOLINT +#endif // GTEST_IS_THREADSAFE + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +#if GTEST_LINKED_AS_SHARED_LIBRARY +#define GTEST_API_ __declspec(dllimport) +#elif GTEST_CREATE_SHARED_LIBRARY +#define GTEST_API_ __declspec(dllexport) +#endif +#elif GTEST_HAVE_ATTRIBUTE_(visibility) +#define GTEST_API_ __attribute__((visibility("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +#define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +#define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#if GTEST_HAVE_ATTRIBUTE_(noinline) +// Ask the compiler to never inline a given function. +#define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +#define GTEST_NO_INLINE_ +#endif + +#if GTEST_HAVE_ATTRIBUTE_(disable_tail_calls) +// Ask the compiler not to perform tail call optimization inside +// the marked function. +#define GTEST_NO_TAIL_CALL_ __attribute__((disable_tail_calls)) +#elif defined(__GNUC__) && !defined(__NVCOMPILER) +#define GTEST_NO_TAIL_CALL_ \ + __attribute__((optimize("no-optimize-sibling-calls"))) +#else +#define GTEST_NO_TAIL_CALL_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +#if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +#define GTEST_HAS_CXXABI_H_ 1 +#else +#define GTEST_HAS_CXXABI_H_ 0 +#endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_memory) +#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ __attribute__((no_sanitize_memory)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif + +// A function level attribute to disable AddressSanitizer instrumentation. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_address) +#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif + +// A function level attribute to disable HWAddressSanitizer instrumentation. +#if GTEST_HAVE_FEATURE_(hwaddress_sanitizer) && \ + GTEST_HAVE_ATTRIBUTE_(no_sanitize) +#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ + __attribute__((no_sanitize("hwaddress"))) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +#endif + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_thread) +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ __attribute((no_sanitize_thread)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif + +namespace testing { + +class Message; + +// Legacy imports for backwards compatibility. +// New code should use std:: names directly. +using std::get; +using std::make_tuple; +using std::tuple; +using std::tuple_element; +using std::tuple_size; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// accessible constructors on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret { + Secret(const Secret&) = delete; +}; + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines RE. + +#ifdef GTEST_USES_RE2 + +// This is almost `using RE = ::RE2`, except it is copy-constructible, and it +// needs to disambiguate the `std::string`, `absl::string_view`, and `const +// char*` constructors. +class GTEST_API_ RE { + public: + RE(absl::string_view regex) : regex_(regex) {} // NOLINT + RE(const char* regex) : RE(absl::string_view(regex)) {} // NOLINT + RE(const std::string& regex) : RE(absl::string_view(regex)) {} // NOLINT + RE(const RE& other) : RE(other.pattern()) {} + + const std::string& pattern() const { return regex_.pattern(); } + + static bool FullMatch(absl::string_view str, const RE& re) { + return RE2::FullMatch(str, re.regex_); + } + static bool PartialMatch(absl::string_view str, const RE& re) { + return RE2::PartialMatch(str, re.regex_); + } + + private: + RE2 regex_; +}; + +#elif defined(GTEST_USES_POSIX_RE) || defined(GTEST_USES_SIMPLE_RE) +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_.c_str(); } + + // FullMatch(str, re) returns true if and only if regular expression re + // matches the entire str. + // PartialMatch(str, re) returns true if and only if regular expression re + // matches a substring of str (including str itself). + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + std::string pattern_; + bool is_valid_; + +#ifdef GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + std::string full_pattern_; // For FullMatch(); + +#endif +}; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // ::testing::internal::RE implementation + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { GTEST_INFO, GTEST_WARNING, GTEST_ERROR, GTEST_FATAL }; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTestLog(const GTestLog&) = delete; + GTestLog& operator=(const GTestLog&) = delete; +}; + +#if !defined(GTEST_LOG_) + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__) \ + .GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(nullptr); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsis: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " << gtest_error + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { + typedef const T& type; +}; +template +struct ConstRef { + typedef T& type; +}; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertible to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { + return x; +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { + static_assert(std::is_base_of::value, + "target type not derived from source type"); +#if GTEST_HAS_RTTI + GTEST_CHECK_(base == nullptr || dynamic_cast(base) != nullptr); +#endif + return static_cast(base); +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector GetArgvs(); + +#ifdef GTEST_HAS_DEATH_TEST + +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#ifdef GTEST_IS_THREADSAFE + +#ifdef GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true if and only if the handle is a valid handle object that can be + // closed. + bool IsCloseable() const; + + Handle handle_; + + AutoHandle(const AutoHandle&) = delete; + AutoHandle& operator=(const AutoHandle&) = delete; +}; +#endif + +#if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +#else +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +// TODO(b/203539622): Replace unconditionally with absl::Notification. +class GTEST_API_ Notification { + public: + Notification() : notified_(false) {} + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + std::lock_guard lock(mu_); + notified_ = true; + cv_.notify_all(); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + std::unique_lock lock(mu_); + cv_.wait(lock, [this]() { return notified_; }); + } + + private: + std::mutex mu_; + std::condition_variable cv_; + bool notified_; +}; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +#if GTEST_HAS_PTHREAD && !defined(GTEST_OS_WINDOWS_MINGW) + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() = default; + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return nullptr; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() override { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr)); + finished_ = true; + } + } + + void Run() override { + if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true if and only if we know that the thread function has + // finished. + pthread_t thread_; // The native thread object. + + ThreadWithParam(const ThreadWithParam&) = delete; + ThreadWithParam& operator=(const ThreadWithParam&) = delete; +}; +#endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +#elif defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTestMutexLock(const GTestMutexLock&) = delete; + GTestMutexLock& operator=(const GTestMutexLock&) = delete; +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + ThreadLocalBase(const ThreadLocalBase&) = delete; + ThreadLocalBase& operator=(const ThreadLocalBase&) = delete; +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable* runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) {} + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) : func_(func), param_(param) {} + virtual ~RunnableImpl() {} + virtual void Run() { func_(param_); } + + private: + UserThreadFunc* const func_; + const T param_; + + RunnableImpl(const RunnableImpl&) = delete; + RunnableImpl& operator=(const RunnableImpl&) = delete; + }; + + ThreadWithParam(const ThreadWithParam&) = delete; + ThreadWithParam& operator=(const ThreadWithParam&) = delete; +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() override { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + ValueHolder(const ValueHolder&) = delete; + ValueHolder& operator=(const ValueHolder&) = delete; + }; + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this)) + ->pointer(); + } + + ThreadLocalValueHolderBase* NewValueForCurrentThread() const override { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + ValueHolderFactory(const ValueHolderFactory&) = delete; + ValueHolderFactory& operator=(const ValueHolderFactory&) = delete; + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + DefaultValueHolderFactory(const DefaultValueHolderFactory&) = delete; + DefaultValueHolderFactory& operator=(const DefaultValueHolderFactory&) = + delete; + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + InstanceValueHolderFactory(const InstanceValueHolderFactory&) = delete; + InstanceValueHolderFactory& operator=(const InstanceValueHolderFactory&) = + delete; + }; + + std::unique_ptr default_factory_; + + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; +}; + +#elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + has_owner_ = false; + } + ~Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); } + + private: + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTestMutexLock(const GTestMutexLock&) = delete; + GTestMutexLock& operator=(const GTestMutexLock&) = delete; +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class GTEST_API_ ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() = default; +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + ValueHolder(const ValueHolder&) = delete; + ValueHolder& operator=(const ValueHolder&) = delete; + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != nullptr) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() = default; + virtual ~ValueHolderFactory() = default; + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + ValueHolderFactory(const ValueHolderFactory&) = delete; + ValueHolderFactory& operator=(const ValueHolderFactory&) = delete; + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() = default; + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + DefaultValueHolderFactory(const DefaultValueHolderFactory&) = delete; + DefaultValueHolderFactory& operator=(const DefaultValueHolderFactory&) = + delete; + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + InstanceValueHolderFactory(const InstanceValueHolderFactory&) = delete; + InstanceValueHolderFactory& operator=(const InstanceValueHolderFactory&) = + delete; + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + std::unique_ptr default_factory_; + + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; +}; + +#endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +#ifdef GTEST_OS_WINDOWS +#define GTEST_PATH_SEP_ "\\" +#define GTEST_HAS_ALT_PATH_SEP_ 1 +#else +#define GTEST_PATH_SEP_ "/" +#define GTEST_HAS_ALT_PATH_SEP_ 0 +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +#ifdef __cpp_lib_char8_t +inline bool IsXDigit(char8_t ch) { + return isxdigit(static_cast(ch)) != 0; +} +#endif +inline bool IsXDigit(char16_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(char32_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// File system porting. +#if GTEST_HAS_FILE_SYSTEM +#ifdef GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +#ifdef GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +#else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; } +#endif // GTEST_OS_WINDOWS_MOBILE + +#elif defined(GTEST_OS_ESP8266) +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +#ifdef GTEST_OS_QURT +// QuRT doesn't support any directory functions, including rmdir +inline int RmDir(const char*) { return 0; } +#else +inline int RmDir(const char* dir) { return rmdir(dir); } +#endif +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS +#endif // GTEST_HAS_FILE_SYSTEM + +// Other functions with a different name on Windows. + +#ifdef GTEST_OS_WINDOWS + +#ifdef __BORLANDC__ +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +#else // !__BORLANDC__ +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_ZOS) || \ + defined(GTEST_OS_IOS) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(ESP_PLATFORM) +inline int DoIsATTY(int /* fd */) { return 0; } +#else +inline int DoIsATTY(int fd) { return _isatty(fd); } +#endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +#endif // __BORLANDC__ + +#else + +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} + +#endif // GTEST_OS_WINDOWS + +inline int IsATTY(int fd) { + // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout + // to a file on Linux), which is unexpected, so save the previous value, and + // restore it after the call. + int savedErrno = errno; + int isAttyValue = DoIsATTY(fd); + errno = savedErrno; + + return isAttyValue; +} + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. +#if GTEST_HAS_FILE_SYSTEM +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_ESP8266) && \ + !defined(GTEST_OS_XTENSA) && !defined(GTEST_OS_QURT) +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW) + struct wchar_codecvt : public std::codecvt {}; + std::wstring_convert converter; + std::wstring wide_path = converter.from_bytes(path); + std::wstring wide_mode = converter.from_bytes(mode); + return _wfopen(wide_path.c_str(), wide_mode.c_str()); +#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + return fopen(path, mode); +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +} +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) +inline FILE* FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +inline int FClose(FILE* fp) { return fclose(fp); } +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +#endif // GTEST_HAS_FILE_SYSTEM + +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT + +inline const char* GetEnv(const char* name) { +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) + // We are on an embedded platform, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. + return nullptr; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != nullptr && env[0] != '\0') ? env : nullptr; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +#ifdef GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +[[noreturn]] void Abort(); +#else +[[noreturn]] inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if defined(_MSC_VER) && !defined(GTEST_OS_WINDOWS_MOBILE) +// MSVC 2005 and above support variadic macros. +#define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s +#define GTEST_SNPRINTF_ _snprintf +#else +#define GTEST_SNPRINTF_ snprintf +#endif + +// The biggest signed integer type the compiler supports. +// +// long long is guaranteed to be at least 64-bits in C++11. +using BiggestInt = long long; // NOLINT + +// The maximum number a BiggestInt can represent. +constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits::max)(); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + using UInt = void; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + using Int = std::int32_t; + using UInt = std::uint32_t; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + using Int = std::int64_t; + using UInt = std::uint64_t; +}; + +// Integer types of known sizes. +using TimeInMillis = int64_t; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +#define GTEST_FLAG_NAME_(name) gtest_##name +#define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +// Pick a command line flags implementation. +#ifdef GTEST_HAS_ABSL + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + ABSL_FLAG(bool, GTEST_FLAG_NAME_(name), default_val, doc) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + ABSL_FLAG(int32_t, GTEST_FLAG_NAME_(name), default_val, doc) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + ABSL_FLAG(std::string, GTEST_FLAG_NAME_(name), default_val, doc) + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) \ + ABSL_DECLARE_FLAG(bool, GTEST_FLAG_NAME_(name)) +#define GTEST_DECLARE_int32_(name) \ + ABSL_DECLARE_FLAG(int32_t, GTEST_FLAG_NAME_(name)) +#define GTEST_DECLARE_string_(name) \ + ABSL_DECLARE_FLAG(std::string, GTEST_FLAG_NAME_(name)) + +#define GTEST_FLAG_SAVER_ ::absl::FlagSaver + +#define GTEST_FLAG_GET(name) ::absl::GetFlag(GTEST_FLAG(name)) +#define GTEST_FLAG_SET(name, value) \ + (void)(::absl::SetFlag(>EST_FLAG(name), value)) +#define GTEST_USE_OWN_FLAGFILE_FLAG_ 0 + +#else // GTEST_HAS_ABSL + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DEFINE_string_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) \ + namespace testing { \ + GTEST_API_ extern bool GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DECLARE_int32_(name) \ + namespace testing { \ + GTEST_API_ extern std::int32_t GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DECLARE_string_(name) \ + namespace testing { \ + GTEST_API_ extern ::std::string GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") + +#define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +#define GTEST_FLAG_GET(name) ::testing::GTEST_FLAG(name) +#define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value) +#define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 + +#endif // GTEST_HAS_ABSL + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +GTEST_API_ bool ParseInt32(const Message& src_text, const char* str, + int32_t* value); + +// Parses a bool/int32_t/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#if !defined(GTEST_INTERNAL_DEPRECATED) + +// Internal Macro to mark an API deprecated, for googletest usage only +// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or +// GTEST_INTERNAL_DEPRECATED(message) myFunction(); Every usage of +// a deprecated entity will trigger a warning when compiled with +// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). +// For msvc /W3 option will need to be used +// Note that for 'other' compilers this macro evaluates to nothing to prevent +// compilations errors. +#if defined(_MSC_VER) +#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__GNUC__) +#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define GTEST_INTERNAL_DEPRECATED(message) +#endif + +#endif // !defined(GTEST_INTERNAL_DEPRECATED) + +#ifdef GTEST_HAS_ABSL +// Always use absl::any for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_ANY 1 +#include "absl/types/any.h" +namespace testing { +namespace internal { +using Any = ::absl::any; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ + (!defined(_MSC_VER) || GTEST_HAS_RTTI) +// Otherwise for C++17 and higher use std::any for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_ANY 1 +#include +namespace testing { +namespace internal { +using Any = ::std::any; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::any is not +// supported. +#endif // __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_ANY +#define GTEST_INTERNAL_HAS_ANY 0 +#endif + +#ifdef GTEST_HAS_ABSL +// Always use absl::optional for UniversalPrinter<> specializations if +// googletest is built with absl support. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include "absl/types/optional.h" +namespace testing { +namespace internal { +template +using Optional = ::absl::optional; +inline ::absl::nullopt_t Nullopt() { return ::absl::nullopt; } +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L +// Otherwise for C++17 and higher use std::optional for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include +namespace testing { +namespace internal { +template +using Optional = ::std::optional; +inline ::std::nullopt_t Nullopt() { return ::std::nullopt; } +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::optional is not +// supported. +#endif // __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_OPTIONAL +#define GTEST_INTERNAL_HAS_OPTIONAL 0 +#endif + +#ifdef GTEST_HAS_ABSL +// Always use absl::string_view for Matcher<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include "absl/strings/string_view.h" +namespace testing { +namespace internal { +using StringView = ::absl::string_view; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L +// Otherwise for C++17 and higher use std::string_view for Matcher<> +// specializations. +#define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include +namespace testing { +namespace internal { +using StringView = ::std::string_view; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::string_view is not +// supported. +#endif // __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= + // 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_STRING_VIEW +#define GTEST_INTERNAL_HAS_STRING_VIEW 0 +#endif + +#ifdef GTEST_HAS_ABSL +// Always use absl::variant for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include "absl/types/variant.h" +namespace testing { +namespace internal { +template +using Variant = ::absl::variant; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L +// Otherwise for C++17 and higher use std::variant for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include +namespace testing { +namespace internal { +template +using Variant = ::std::variant; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::variant is not supported. +#endif // __has_include() && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_VARIANT +#define GTEST_INTERNAL_HAS_VARIANT 0 +#endif + +#if defined(GTEST_INTERNAL_CPLUSPLUS_LANG) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L +#define GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 +#endif + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/muduo/gtest/internal/gtest-string.h b/muduo/gtest/internal/gtest-string.h new file mode 100644 index 000000000..7c05b5833 --- /dev/null +++ b/muduo/gtest/internal/gtest-string.h @@ -0,0 +1,178 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by gtest-internal.h. +// It should not be #included by other files. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +#include +#endif + +#include + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#ifdef GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true if and only if they have the same + // content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true if and only if they have the + // same content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true if and only if the given string ends with the given suffix, + // ignoring case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive(const std::string& str, + const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value to given width with leading zeros. + static std::string FormatIntWidthN(int value, int width); + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats an int value as "%X". + static std::string FormatHexUInt32(uint32_t value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/muduo/gtest/internal/gtest-type-util.h b/muduo/gtest/internal/gtest-type-util.h new file mode 100644 index 000000000..f94cf614a --- /dev/null +++ b/muduo/gtest/internal/gtest-type-util.h @@ -0,0 +1,220 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type utilities needed for implementing typed and type-parameterized +// tests. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +#if GTEST_HAS_CXXABI_H_ +#include +#elif defined(__HP_aCC) +#include +#endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + + // Strip redundant spaces in typename to match MSVC + // For example, std::pair -> std::pair + static const char to_search[] = ", "; + static const char replace_str[] = ","; + size_t pos = 0; + while (true) { + // Get the next occurrence from the current position + pos = s.find(to_search, pos); + if (pos == std::string::npos) { + break; + } + // Replace this occurrence of substring + s.replace(pos, strlen(to_search), replace_str); + pos += strlen(replace_str); + } + return s; +} + +#if GTEST_HAS_RTTI +// GetTypeName(const std::type_info&) returns a human-readable name of type T. +inline std::string GetTypeName(const std::type_info& type) { + const char* const name = type.name(); +#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +#if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +#endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +#elif defined(_MSC_VER) + // Strip struct and class due to differences between + // MSVC and other compilers. std::pair is printed as + // "struct std::pair" when using MSVC vs "std::pair" with + // other compilers. + std::string s = name; + // Only strip the leading "struct " and "class ", so uses rfind == 0 to + // ensure that + if (s.rfind("struct ", 0) == 0) { + s = s.substr(strlen("struct ")); + } else if (s.rfind("class ", 0) == 0) { + s = s.substr(strlen("class ")); + } + return s; +#else + return name; +#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC +} +#endif // GTEST_HAS_RTTI + +// GetTypeName() returns a human-readable name of type T if and only if +// RTTI is enabled, otherwise it returns a dummy type name. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +#if GTEST_HAS_RTTI + return GetTypeName(typeid(T)); +#else + return ""; +#endif // GTEST_HAS_RTTI +} + +// A unique type indicating an empty node +struct None {}; + +#define GTEST_TEMPLATE_ \ + template \ + class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +#define GTEST_BIND_(TmplSel, T) TmplSel::template Bind::type + +template +struct Templates { + using Head = TemplateSel; + using Tail = Templates; +}; + +template +struct Templates { + using Head = TemplateSel; + using Tail = None; +}; + +// Tuple-like type lists +template +struct Types { + using Head = Head_; + using Tail = Types; +}; + +template +struct Types { + using Head = Head_; + using Tail = None; +}; + +// Helper metafunctions to tell apart a single type from types +// generated by ::testing::Types +template +struct ProxyTypeList { + using type = Types; +}; + +template +struct is_proxy_type_list : std::false_type {}; + +template +struct is_proxy_type_list> : std::true_type {}; + +// Generator which conditionally creates type lists. +// It recognizes if a requested type list should be created +// and prevents creating a new type list nested within another one. +template +struct GenerateTypeList { + private: + using proxy = typename std::conditional::value, T, + ProxyTypeList>::type; + + public: + using type = typename proxy::type; +}; + +} // namespace internal + +template +using Types = internal::ProxyTypeList; + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/muduo/net/Acceptor.cc b/muduo/net/Acceptor.cc index 39d0cb6be..6b1a6d21e 100644 --- a/muduo/net/Acceptor.cc +++ b/muduo/net/Acceptor.cc @@ -27,7 +27,11 @@ Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reusepor acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())), acceptChannel_(loop, acceptSocket_.fd()), listening_(false), +#ifdef __linux__ idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) +#else + idleFd_(muduo_open_idle_fd(listenAddr.family())) +#endif { assert(idleFd_ >= 0); acceptSocket_.setReuseAddr(true); @@ -41,7 +45,11 @@ Acceptor::~Acceptor() { acceptChannel_.disableAll(); acceptChannel_.remove(); +#ifdef __linux__ ::close(idleFd_); +#else + muduo_close_idle_fd(idleFd_); +#endif } void Acceptor::listen() @@ -79,10 +87,14 @@ void Acceptor::handleRead() // By Marc Lehmann, author of libev. if (errno == EMFILE) { +#ifdef __linux__ ::close(idleFd_); idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL); ::close(idleFd_); idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); +#else + muduo_handle_emfile_idle_fd(acceptSocket_.fd(), &idleFd_, AF_INET); +#endif } } } diff --git a/muduo/net/CMakeLists.txt b/muduo/net/CMakeLists.txt index 7510d8760..8a79c1f16 100644 --- a/muduo/net/CMakeLists.txt +++ b/muduo/net/CMakeLists.txt @@ -1,8 +1,8 @@ include(CheckFunctionExists) -check_function_exists(accept4 HAVE_ACCEPT4) -if(NOT HAVE_ACCEPT4) - set_source_files_properties(SocketsOps.cc PROPERTIES COMPILE_FLAGS "-DNO_ACCEPT4") +check_function_exists(acppept4 HAVE_AcppEPT4) +if(NOT HAVE_AcppEPT4) + set_source_files_properties(SocketsOps.cc PROPERTIES COMPILE_FLAGS "-DNO_AcppEPT4") endif() set(net_SRCS @@ -26,16 +26,14 @@ set(net_SRCS Timer.cc TimerQueue.cc ) - +include_directories(${PROJECT_SOURCE_DIR}/contrib/windows/include) add_library(muduo_net ${net_SRCS}) +if (WIN32) + target_compile_options(muduo_net PUBLIC /FI"muduo/base/CrossPlatformAdapterFunction.h") +endif () target_link_libraries(muduo_net muduo_base) -#add_library(muduo_net_cpp11 ${net_SRCS}) -#target_link_libraries(muduo_net_cpp11 muduo_base_cpp11) -#set_target_properties(muduo_net_cpp11 PROPERTIES COMPILE_FLAGS "-std=c++0x") - install(TARGETS muduo_net DESTINATION lib) -#install(TARGETS muduo_net_cpp11 DESTINATION lib) set(HEADERS Buffer.h @@ -53,13 +51,14 @@ set(HEADERS ) install(FILES ${HEADERS} DESTINATION include/muduo/net) -add_subdirectory(http) -add_subdirectory(inspect) - -if(MUDUO_BUILD_EXAMPLES) +if(NOT CMAKE_BUILD_NO_EXAMPLES) add_subdirectory(tests) endif() +if (MSVC) + add_subdirectory(protobuf) + add_subdirectory(protorpc) +else(MSVC) if(PROTOBUF_FOUND) add_subdirectory(protobuf) add_subdirectory(protorpc) @@ -67,3 +66,4 @@ else() add_subdirectory(protobuf EXCLUDE_FROM_ALL) add_subdirectory(protorpc EXCLUDE_FROM_ALL) endif() +endif() \ No newline at end of file diff --git a/muduo/net/Callbacks.h b/muduo/net/Callbacks.h index 443ed791e..7a2f01030 100644 --- a/muduo/net/Callbacks.h +++ b/muduo/net/Callbacks.h @@ -15,6 +15,9 @@ #include #include +#ifndef __linux__ +#include +#endif namespace muduo { diff --git a/muduo/net/Channel.cc b/muduo/net/Channel.cc index 1e9a40ae7..224b6eebe 100644 --- a/muduo/net/Channel.cc +++ b/muduo/net/Channel.cc @@ -27,7 +27,11 @@ Channel::Channel(EventLoop* loop, int fd__) events_(0), revents_(0), index_(-1), +#ifdef __linux__ logHup_(true), +#else + logHup_(muduo_channel_default_log_hup()), +#endif tied_(false), eventHandling_(false), addedToLoop_(false) @@ -91,6 +95,13 @@ void Channel::handleEventWithGuard(Timestamp receiveTime) LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP"; } if (closeCallback_) closeCallback_(); +#ifndef __linux__ + if (muduo_channel_return_after_close()) + { + eventHandling_ = false; + return; + } +#endif } if (revents_ & POLLNVAL) diff --git a/muduo/net/EventLoop.cc b/muduo/net/EventLoop.cc index b3feebe85..bc09a5ef4 100644 --- a/muduo/net/EventLoop.cc +++ b/muduo/net/EventLoop.cc @@ -28,7 +28,11 @@ namespace { __thread EventLoop* t_loopInThisThread = 0; +#ifdef __linux__ const int kPollTimeMs = 10000; +#else +const int kPollTimeMs = muduo_eventloop_poll_timeout_ms(); +#endif int createEventfd() { @@ -47,7 +51,11 @@ class IgnoreSigPipe public: IgnoreSigPipe() { +#ifdef __linux__ ::signal(SIGPIPE, SIG_IGN); +#else + muduo_ignore_sigpipe(); +#endif // LOG_TRACE << "Ignore SIGPIPE"; } }; @@ -96,7 +104,11 @@ EventLoop::~EventLoop() << " destructs in thread " << CurrentThread::tid(); wakeupChannel_->disableAll(); wakeupChannel_->remove(); +#ifdef __linux__ ::close(wakeupFd_); +#else + muduo_close_fd_compat(wakeupFd_); +#endif t_loopInThisThread = NULL; } @@ -124,6 +136,12 @@ void EventLoop::loop() currentActiveChannel_ = channel; currentActiveChannel_->handleEvent(pollReturnTime_); } +#ifndef __linux__ + if (muduo_eventloop_use_timerqueue_loop()) + { + timerQueue_->loop(); + } +#endif currentActiveChannel_ = NULL; eventHandling_ = false; doPendingFunctors(); @@ -233,9 +251,14 @@ void EventLoop::abortNotInLoopThread() void EventLoop::wakeup() { +#ifdef __linux__ uint64_t one = 1; ssize_t n = sockets::write(wakeupFd_, &one, sizeof one); if (n != sizeof one) +#else + ssize_t n = muduo_eventloop_wakeup_write(wakeupFd_); + if (n != static_cast(sizeof(uint64_t))) +#endif { LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8"; } @@ -243,9 +266,14 @@ void EventLoop::wakeup() void EventLoop::handleRead() { +#ifdef __linux__ uint64_t one = 1; ssize_t n = sockets::read(wakeupFd_, &one, sizeof one); if (n != sizeof one) +#else + ssize_t n = muduo_eventloop_wakeup_read(wakeupFd_); + if (n != static_cast(sizeof(uint64_t))) +#endif { LOG_ERROR << "EventLoop::handleRead() reads " << n << " bytes instead of 8"; } diff --git a/muduo/net/EventLoopThread.cc b/muduo/net/EventLoopThread.cc index c1b6fa56d..7031163da 100644 --- a/muduo/net/EventLoopThread.cc +++ b/muduo/net/EventLoopThread.cc @@ -19,7 +19,11 @@ EventLoopThread::EventLoopThread(const ThreadInitCallback& cb, exiting_(false), thread_(std::bind(&EventLoopThread::threadFunc, this), name), mutex_(), +#ifdef __muduo_asynchronization__ cond_(mutex_), +#else + cond_(), +#endif callback_(cb) { } @@ -46,7 +50,11 @@ EventLoop* EventLoopThread::startLoop() MutexLockGuard lock(mutex_); while (loop_ == NULL) { +#ifdef __muduo_asynchronization__ cond_.wait(); +#else + cond_.wait(lock); +#endif } loop = loop_; } diff --git a/muduo/net/EventLoopThreadPool.cc b/muduo/net/EventLoopThreadPool.cc index 1ee594954..61e31a68e 100644 --- a/muduo/net/EventLoopThreadPool.cc +++ b/muduo/net/EventLoopThreadPool.cc @@ -39,8 +39,12 @@ void EventLoopThreadPool::start(const ThreadInitCallback& cb) for (int i = 0; i < numThreads_; ++i) { +#ifdef __linux__ char buf[name_.size() + 32]; snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i); +#else + std::string buf = muduo_make_loop_thread_name(name_, i); +#endif EventLoopThread* t = new EventLoopThread(cb, buf); threads_.push_back(std::unique_ptr(t)); loops_.push_back(t->startLoop()); diff --git a/muduo/net/SocketsOps.cc b/muduo/net/SocketsOps.cc index 465c507a6..5eb440acc 100644 --- a/muduo/net/SocketsOps.cc +++ b/muduo/net/SocketsOps.cc @@ -76,6 +76,7 @@ const struct sockaddr_in6* sockets::sockaddr_in6_cast(const struct sockaddr* add int sockets::createNonblockingOrDie(sa_family_t family) { +#ifdef __linux__ #if VALGRIND int sockfd = ::socket(family, SOCK_STREAM, IPPROTO_TCP); if (sockfd < 0) @@ -90,6 +91,13 @@ int sockets::createNonblockingOrDie(sa_family_t family) { LOG_SYSFATAL << "sockets::createNonblockingOrDie"; } +#endif +#else + int sockfd = muduo_create_nonblocking_or_die(family); + if (sockfd < 0) + { + LOG_SYSFATAL << "sockets::createNonblockingOrDie"; + } #endif return sockfd; } @@ -158,12 +166,20 @@ int sockets::accept(int sockfd, struct sockaddr_in6* addr) int sockets::connect(int sockfd, const struct sockaddr* addr) { +#ifdef __linux__ return ::connect(sockfd, addr, static_cast(sizeof(struct sockaddr_in6))); +#else + return muduo_socket_connect_compat(sockfd, addr); +#endif } ssize_t sockets::read(int sockfd, void *buf, size_t count) { +#ifdef __linux__ return ::read(sockfd, buf, count); +#else + return muduo_socket_read_compat(sockfd, buf, count); +#endif } ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt) @@ -173,12 +189,20 @@ ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt) ssize_t sockets::write(int sockfd, const void *buf, size_t count) { +#ifdef __linux__ return ::write(sockfd, buf, count); +#else + return muduo_socket_write_compat(sockfd, buf, count); +#endif } void sockets::close(int sockfd) { +#ifdef __linux__ if (::close(sockfd) < 0) +#else + if (muduo_socket_close_compat(sockfd) < 0) +#endif { LOG_SYSERR << "sockets::close"; } diff --git a/muduo/net/TcpClient.cc b/muduo/net/TcpClient.cc index 1e668b3c2..857278b51 100644 --- a/muduo/net/TcpClient.cc +++ b/muduo/net/TcpClient.cc @@ -78,7 +78,11 @@ TcpClient::~TcpClient() bool unique = false; { MutexLockGuard lock(mutex_); +#ifdef __linux__ unique = connection_.unique(); +#else + unique = connection_.use_count() == 1; +#endif conn = connection_; } if (conn) diff --git a/muduo/net/TimerId.h b/muduo/net/TimerId.h index fcc52646e..585edd5ae 100644 --- a/muduo/net/TimerId.h +++ b/muduo/net/TimerId.h @@ -42,6 +42,8 @@ class TimerId : public muduo::copyable friend class TimerQueue; + const Timer* GetTimer() { return timer_; } + private: Timer* timer_; int64_t sequence_; diff --git a/muduo/net/TimerQueue.cc b/muduo/net/TimerQueue.cc index 89119aeb0..b8f45e65e 100644 --- a/muduo/net/TimerQueue.cc +++ b/muduo/net/TimerQueue.cc @@ -16,6 +16,7 @@ #include "muduo/net/EventLoop.h" #include "muduo/net/Timer.h" #include "muduo/net/TimerId.h" +#include "muduo/net/SocketsOps.h" #include #include @@ -57,12 +58,24 @@ struct timespec howMuchTimeFromNow(Timestamp when) void readTimerfd(int timerfd, Timestamp now) { uint64_t howmany; +#ifdef __linux__ ssize_t n = ::read(timerfd, &howmany, sizeof howmany); LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString(); if (n != sizeof howmany) { LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8"; } +#else + ssize_t n = muduo_read_timerfd(timerfd, &howmany); + if (n > 0) + { + LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString(); + if (n != sizeof howmany) + { + LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8"; + } + } +#endif } void resetTimerfd(int timerfd, Timestamp expiration) @@ -105,7 +118,11 @@ TimerQueue::~TimerQueue() { timerfdChannel_.disableAll(); timerfdChannel_.remove(); +#ifdef __linux__ ::close(timerfd_); +#else + muduo_close_timerfd(timerfd_); +#endif // do not remove channel, since we're in EventLoop::dtor(); for (const Entry& timer : timers_) { @@ -160,6 +177,10 @@ void TimerQueue::cancelInLoop(TimerId timerId) assert(timers_.size() == activeTimers_.size()); } +void TimerQueue::loop() +{ + handleRead(); +} void TimerQueue::handleRead() { loop_->assertInLoopThread(); @@ -173,6 +194,10 @@ void TimerQueue::handleRead() // safe to callback outside critical section for (const Entry& it : expired) { + if (cancelingTimers_.contains(ActiveTimer{ it.second, it.second->sequence() })) + { + continue; + } it.second->run(); } callingExpiredTimers_ = false; diff --git a/muduo/net/TimerQueue.h b/muduo/net/TimerQueue.h index 85da6b71a..4aa8309ec 100644 --- a/muduo/net/TimerQueue.h +++ b/muduo/net/TimerQueue.h @@ -49,6 +49,7 @@ class TimerQueue : noncopyable void cancel(TimerId timerId); + void loop(); private: // FIXME: use unique_ptr instead of raw pointers. diff --git a/muduo/net/poller/DefaultPoller.cc b/muduo/net/poller/DefaultPoller.cc index 5ea0d03a9..113ce6a9b 100644 --- a/muduo/net/poller/DefaultPoller.cc +++ b/muduo/net/poller/DefaultPoller.cc @@ -8,7 +8,9 @@ #include "muduo/net/Poller.h" #include "muduo/net/poller/PollPoller.h" +#ifdef __linux__ #include "muduo/net/poller/EPollPoller.h" +#endif #include @@ -16,6 +18,7 @@ using namespace muduo::net; Poller* Poller::newDefaultPoller(EventLoop* loop) { +#ifdef __linux__ if (::getenv("MUDUO_USE_POLL")) { return new PollPoller(loop); @@ -24,4 +27,7 @@ Poller* Poller::newDefaultPoller(EventLoop* loop) { return new EPollPoller(loop); } +#else + return new PollPoller(loop); +#endif } diff --git a/muduo/net/poller/EPollPoller.cc b/muduo/net/poller/EPollPoller.cc index b2f913a4c..c38b6e8b0 100644 --- a/muduo/net/poller/EPollPoller.cc +++ b/muduo/net/poller/EPollPoller.cc @@ -6,6 +6,8 @@ // Author: Shuo Chen (chenshuo at chenshuo dot com) +#ifdef __linux__ + #include "muduo/net/poller/EPollPoller.h" #include "muduo/base/Logging.h" @@ -206,3 +208,4 @@ const char* EPollPoller::operationToString(int op) return "Unknown Operation"; } } +#endif // __linux__ diff --git a/muduo/net/poller/PollPoller.cc b/muduo/net/poller/PollPoller.cc index 793307997..59f234590 100644 --- a/muduo/net/poller/PollPoller.cc +++ b/muduo/net/poller/PollPoller.cc @@ -12,6 +12,9 @@ #include "muduo/base/Types.h" #include "muduo/net/Channel.h" +#ifndef __linux__ +#include +#endif #include #include #include diff --git a/muduo/net/poller/WindowsPollPoller.cc b/muduo/net/poller/WindowsPollPoller.cc new file mode 100644 index 000000000..042aa4a46 --- /dev/null +++ b/muduo/net/poller/WindowsPollPoller.cc @@ -0,0 +1,142 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) + +#include "muduo/net/poller/PollPoller.h" + +#include "muduo/base/Logging.h" +#include "muduo/base/Types.h" +#include "muduo/net/Channel.h" + +#include +#include +#include +#include + +using namespace muduo; +using namespace muduo::net; + +PollPoller::PollPoller(EventLoop* loop) + : Poller(loop) +{ +} + +PollPoller::~PollPoller() = default; + +Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels) +{ + // XXX pollfds_ shouldn't change + int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs); + int savedErrno = errno; + Timestamp now(Timestamp::now()); + if (numEvents > 0) + { + LOG_TRACE << numEvents << " events happened"; + fillActiveChannels(numEvents, activeChannels); + } + else if (numEvents == 0) + { + LOG_TRACE << " nothing happened"; + } + else + { + if (savedErrno != EINTR) + { + errno = savedErrno; + LOG_SYSERR << "PollPoller::poll()"; + } + } + return now; +} + +void PollPoller::fillActiveChannels(int numEvents, + ChannelList* activeChannels) const +{ + for (PollFdList::const_iterator pfd = pollfds_.begin(); + pfd != pollfds_.end() && numEvents > 0; ++pfd) + { + if (pfd->revents > 0) + { + --numEvents; + ChannelMap::const_iterator ch = channels_.find(pfd->fd); + assert(ch != channels_.end()); + Channel* channel = ch->second; + assert(channel->fd() == pfd->fd); + channel->set_revents(pfd->revents); + // pfd->revents = 0; + activeChannels->push_back(channel); + } + } +} + +void PollPoller::updateChannel(Channel* channel) +{ + Poller::assertInLoopThread(); + LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events(); + if (channel->index() < 0) + { + // a new one, add to pollfds_ + assert(channels_.find(channel->fd()) == channels_.end()); + struct pollfd pfd; + pfd.fd = channel->fd(); + pfd.events = static_cast(channel->events()); + pfd.revents = 0; + pollfds_.push_back(pfd); + int idx = static_cast(pollfds_.size())-1; + channel->set_index(idx); + channels_[pfd.fd] = channel; + } + else + { + // update existing one + assert(channels_.find(channel->fd()) != channels_.end()); + assert(channels_[channel->fd()] == channel); + int idx = channel->index(); + assert(0 <= idx && idx < static_cast(pollfds_.size())); + struct pollfd& pfd = pollfds_[idx]; + assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1); + pfd.fd = channel->fd(); + pfd.events = static_cast(channel->events()); + pfd.revents = 0; + if (channel->isNoneEvent()) + { + // ignore this pollfd + pfd.fd = -channel->fd()-1; + } + } +} + +void PollPoller::removeChannel(Channel* channel) +{ + Poller::assertInLoopThread(); + LOG_TRACE << "fd = " << channel->fd(); + assert(channels_.find(channel->fd()) != channels_.end()); + assert(channels_[channel->fd()] == channel); + assert(channel->isNoneEvent()); + int idx = channel->index(); + assert(0 <= idx && idx < static_cast(pollfds_.size())); + const struct pollfd& pfd = pollfds_[idx]; (void)pfd; + assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events()); + size_t n = channels_.erase(channel->fd()); + assert(n == 1); (void)n; + if (implicit_cast(idx) == pollfds_.size()-1) + { + pollfds_.pop_back(); + } + else + { + int channelAtEnd = pollfds_.back().fd; + iter_swap(pollfds_.begin()+idx, pollfds_.end()-1); + if (channelAtEnd < 0) + { + channelAtEnd = -channelAtEnd-1; + } + channels_[channelAtEnd]->set_index(idx); + pollfds_.pop_back(); + } +} + diff --git a/muduo/net/premake4.lua b/muduo/net/premake4.lua new file mode 100644 index 000000000..2a2c75ccb --- /dev/null +++ b/muduo/net/premake4.lua @@ -0,0 +1,45 @@ +project "net" + kind "StaticLib" + language "C++" + links('base') + targetdir(libdir) + targetname('muduo_net') + includedirs('../..') + headersdir('muduo/net') + headers { + 'Buffer.h', + 'Callbacks.h', + 'Channel.h', + 'Endian.h', + 'EventLoop.h', + 'EventLoopThread.h', + 'EventLoopThreadPool.h', + 'InetAddress.h', + 'TcpClient.h', + 'TcpConnection.h', + 'TcpServer.h', + 'TimerId.h', + } + + files { + 'Acceptor.cc', + 'Buffer.cc', + 'Channel.cc', + 'Connector.cc', + 'EventLoop.cc', + 'EventLoopThread.cc', + 'EventLoopThreadPool.cc', + 'InetAddress.cc', + 'Poller.cc', + 'poller/DefaultPoller.cc', + 'poller/EPollPoller.cc', + 'poller/PollPoller.cc', + 'Socket.cc', + 'SocketsOps.cc', + 'TcpClient.cc', + 'TcpConnection.cc', + 'TcpServer.cc', + 'Timer.cc', + 'TimerQueue.cc', + } + diff --git a/muduo/net/protobuf/CMakeLists.txt b/muduo/net/protobuf/CMakeLists.txt index a7e49d9f4..fe55e199f 100644 --- a/muduo/net/protobuf/CMakeLists.txt +++ b/muduo/net/protobuf/CMakeLists.txt @@ -1,14 +1,37 @@ + +if(MSVC) +LINK_DIRECTORIES("${protobuf_BINARY_DIR}/Debug") +LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/Debug") +endif (MSVC) + add_library(muduo_protobuf_codec ProtobufCodecLite.cc) +if (NOT MSVC) set_target_properties(muduo_protobuf_codec PROPERTIES COMPILE_FLAGS "-Wno-error=shadow") -target_link_libraries(muduo_protobuf_codec muduo_net protobuf z) +endif () +if (TARGET protobuf::libprotobuf) + set(MUDUO_PROTOBUF_LIB protobuf::libprotobuf) +elseif(TARGET libprotobuf) + set(MUDUO_PROTOBUF_LIB libprotobuf) +elseif(TARGET protobuf) + set(MUDUO_PROTOBUF_LIB protobuf) +else() + message(FATAL_ERROR "No protobuf library target found") +endif() + +if (TARGET zlibstatic) + set(MUDUO_ZLIB_LIB zlibstatic) +elseif(TARGET zlib) + set(MUDUO_ZLIB_LIB zlib) +elseif(TARGET ZLIB::ZLIB) + set(MUDUO_ZLIB_LIB ZLIB::ZLIB) +else() + message(FATAL_ERROR "No zlib library target found") +endif() -#add_library(muduo_protobuf_codec_cpp11 ProtobufCodecLite.cc) -#set_target_properties(muduo_protobuf_codec_cpp11 PROPERTIES COMPILE_FLAGS "-std=c++0x -Wno-error=shadow") -#target_link_libraries(muduo_protobuf_codec_cpp11 muduo_net_cpp11 protobuf z) +target_link_libraries(muduo_protobuf_codec muduo_net ${MUDUO_PROTOBUF_LIB} ${MUDUO_ZLIB_LIB}) install(TARGETS muduo_protobuf_codec DESTINATION lib) -#install(TARGETS muduo_protobuf_codec_cpp11 DESTINATION lib) file(GLOB HEADERS "*.h") install(FILES ${HEADERS} DESTINATION include/muduo/net/protobuf) diff --git a/muduo/net/protobuf/ProtobufCodecLite.cc b/muduo/net/protobuf/ProtobufCodecLite.cc index 9d406ee20..9af058535 100644 --- a/muduo/net/protobuf/ProtobufCodecLite.cc +++ b/muduo/net/protobuf/ProtobufCodecLite.cc @@ -178,7 +178,8 @@ void ProtobufCodecLite::defaultErrorCallback(const TcpConnectionPtr& conn, Timestamp, ErrorCode errorCode) { - LOG_ERROR << "ProtobufCodecLite::defaultErrorCallback - " << errorCodeToString(errorCode); + LOG_ERROR << "ProtobufCodecLite::defaultErrorCallback - " << errorCodeToString(errorCode) + << " - " << (conn ? conn->peerAddress().toIpPort() : "no connection"); if (conn && conn->connected()) { conn->shutdown(); diff --git a/muduo/net/protorpc/CMakeLists.txt b/muduo/net/protorpc/CMakeLists.txt index 1e5f1536f..92dbe249b 100644 --- a/muduo/net/protorpc/CMakeLists.txt +++ b/muduo/net/protorpc/CMakeLists.txt @@ -1,34 +1,65 @@ +if(MSVC) +LINK_DIRECTORIES("${protobuf_BINARY_DIR}/Debug") +LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/Debug") +endif (MSVC) + add_custom_command(OUTPUT rpc.pb.cc rpc.pb.h COMMAND protoc ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/rpc.proto -I${CMAKE_CURRENT_SOURCE_DIR} DEPENDS rpc.proto VERBATIM ) +if (NOT MSVC) set_source_files_properties(rpc.pb.cc PROPERTIES COMPILE_FLAGS "-Wno-conversion") +endif() include_directories(${PROJECT_BINARY_DIR}) add_library(muduo_protorpc_wire rpc.pb.cc RpcCodec.cc) +if (NOT MSVC) set_target_properties(muduo_protorpc_wire PROPERTIES COMPILE_FLAGS "-Wno-error=shadow") +endif() -#add_library(muduo_protorpc_wire_cpp11 rpc.pb.cc RpcCodec.cc) -#set_target_properties(muduo_protorpc_wire_cpp11 PROPERTIES COMPILE_FLAGS "-std=c++0x -Wno-error=shadow") -if(MUDUO_BUILD_EXAMPLES) +if(NOT CMAKE_BUILD_NO_EXAMPLES) add_executable(protobuf_rpc_wire_test RpcCodec_test.cc) target_link_libraries(protobuf_rpc_wire_test muduo_protorpc_wire muduo_protobuf_codec) +if (NOT MSVC) set_target_properties(protobuf_rpc_wire_test PROPERTIES COMPILE_FLAGS "-Wno-error=shadow") +endif () endif() add_library(muduo_protorpc RpcChannel.cc RpcServer.cc) +if (TARGET protobuf::libprotobuf) + set(MUDUO_PROTOBUF_LIB protobuf::libprotobuf) +elseif(TARGET libprotobuf) + set(MUDUO_PROTOBUF_LIB libprotobuf) +elseif(TARGET protobuf) + set(MUDUO_PROTOBUF_LIB protobuf) +else() + message(FATAL_ERROR "No protobuf library target found") +endif() + +if (TARGET zlibstatic) + set(MUDUO_ZLIB_LIB zlibstatic) +elseif(TARGET zlib) + set(MUDUO_ZLIB_LIB zlib) +elseif(TARGET ZLIB::ZLIB) + set(MUDUO_ZLIB_LIB ZLIB::ZLIB) +else() + message(FATAL_ERROR "No zlib library target found") +endif() + +target_link_libraries(muduo_protorpc muduo_protorpc_wire muduo_protobuf_codec muduo_net ${MUDUO_PROTOBUF_LIB} ${MUDUO_ZLIB_LIB}) +if (NOT MSVC) set_target_properties(muduo_protorpc PROPERTIES COMPILE_FLAGS "-Wno-error=shadow") -target_link_libraries(muduo_protorpc muduo_protorpc_wire muduo_protobuf_codec muduo_net protobuf z) +endif() + if(TCMALLOC_LIBRARY) target_link_libraries(muduo_protorpc tcmalloc_and_profiler) endif() install(TARGETS muduo_protorpc_wire muduo_protorpc DESTINATION lib) -#install(TARGETS muduo_protorpc_wire_cpp11 DESTINATION lib) set(HEADERS RpcCodec.h diff --git a/muduo/net/protorpc/RpcChannel.cc b/muduo/net/protorpc/RpcChannel.cc index d18e081fc..295795cc6 100644 --- a/muduo/net/protorpc/RpcChannel.cc +++ b/muduo/net/protorpc/RpcChannel.cc @@ -86,7 +86,9 @@ void RpcChannel::onRpcMessage(const TcpConnectionPtr& conn, if (message.type() == RESPONSE) { int64_t id = message.id(); +#ifdef __linux__ assert(message.has_response() || message.has_error()); +#endif OutstandingCall out = { NULL, NULL }; @@ -103,7 +105,11 @@ void RpcChannel::onRpcMessage(const TcpConnectionPtr& conn, if (out.response) { std::unique_ptr d(out.response); +#ifdef __linux__ if (message.has_response()) +#else + if (!message.response().empty()) +#endif { out.response->ParseFromString(message.response()); } @@ -137,7 +143,11 @@ void RpcChannel::onRpcMessage(const TcpConnectionPtr& conn, int64_t id = message.id(); service->CallMethod(method, NULL, get_pointer(request), response, NewCallback(this, &RpcChannel::doneCallback, response, id)); +#ifdef __linux__ error = NO_ERROR; +#else + error = RPC_NO_ERROR; +#endif } else { @@ -167,7 +177,11 @@ void RpcChannel::onRpcMessage(const TcpConnectionPtr& conn, codec_.send(conn_, response); } } +#ifdef __linux__ else if (message.type() == ERROR) +#else + else if (message.type() == RPC_ERROR) +#endif { } } diff --git a/muduo/net/protorpc/autogen.bat b/muduo/net/protorpc/autogen.bat new file mode 100644 index 000000000..61ea35bf9 --- /dev/null +++ b/muduo/net/protorpc/autogen.bat @@ -0,0 +1,3 @@ +for /f "delims=\" %%a in ('dir /b /a-d /o-d "*.proto"') do ( + protoc -I=./ --cpp_out=. %%a) +pause \ No newline at end of file diff --git a/muduo/net/protorpc/rpc.proto b/muduo/net/protorpc/rpc.proto index 941e235aa..87575abf0 100644 --- a/muduo/net/protorpc/rpc.proto +++ b/muduo/net/protorpc/rpc.proto @@ -1,3 +1,4 @@ +syntax = "proto3"; package muduo.net; // option go_package = "muduorpc"; option java_package = "com.chenshuo.muduo.protorpc"; @@ -5,14 +6,14 @@ option java_outer_classname = "RpcProto"; enum MessageType { - REQUEST = 1; - RESPONSE = 2; - ERROR = 3; // not used + REQUEST = 0; + RESPONSE = 1; + RPC_ERROR = 2; // not used } enum ErrorCode { - NO_ERROR = 0; + RPC_NO_ERROR = 0; WRONG_PROTO = 1; NO_SERVICE = 2; NO_METHOD = 3; @@ -23,14 +24,14 @@ enum ErrorCode message RpcMessage { - required MessageType type = 1; - required fixed64 id = 2; + MessageType type = 1; + fixed64 id = 2; - optional string service = 3; - optional string method = 4; - optional bytes request = 5; + string service = 3; + string method = 4; + bytes request = 5; - optional bytes response = 6; + bytes response = 6; - optional ErrorCode error = 7; + ErrorCode error = 7; } diff --git a/muduo/net/protorpc/rpcservice.proto b/muduo/net/protorpc/rpcservice.proto index f97323486..e96ef9151 100644 --- a/muduo/net/protorpc/rpcservice.proto +++ b/muduo/net/protorpc/rpcservice.proto @@ -1,3 +1,4 @@ +syntax = "proto3"; package muduo.net; option cc_generic_services = true; @@ -18,21 +19,21 @@ message ListRpcRequest message ListRpcResponse { - required ErrorCode error = 1; + ErrorCode error = 1; repeated string service_name = 2; repeated string method_name = 3; } message GetServiceRequest { - required string service_name = 1; + string service_name = 1; } message GetServiceResponse { - required ErrorCode error = 1; - repeated string proto_file = 2; - repeated string proto_file_name = 3; + ErrorCode error = 1; + string proto_file = 2; + string proto_file_name = 3; } // the meta service diff --git a/muduo/net/tests/CMakeLists.txt b/muduo/net/tests/CMakeLists.txt index a7aa3a0c9..bcc2ce53e 100644 --- a/muduo/net/tests/CMakeLists.txt +++ b/muduo/net/tests/CMakeLists.txt @@ -1,20 +1,20 @@ -add_executable(channel_test Channel_test.cc) -target_link_libraries(channel_test muduo_net) +add_executable(channeltest channeltest/ChannelTest.cc) +target_link_libraries(channeltest muduo_net) -add_executable(echoserver_unittest EchoServer_unittest.cc) -target_link_libraries(echoserver_unittest muduo_net) +add_executable(echoserverunittest echoserverunittest/EchoServerUnitTest.cc) +target_link_libraries(echoserverunittest muduo_net) -add_executable(echoclient_unittest EchoClient_unittest.cc) -target_link_libraries(echoclient_unittest muduo_net) +add_executable(echoclientunittest echoclientunittest/EchoClientUnitTest.cc) +target_link_libraries(echoclientunittest muduo_net) -add_executable(eventloop_unittest EventLoop_unittest.cc) -target_link_libraries(eventloop_unittest muduo_net) +add_executable(eventloopunittest eventloopunittest/EventLoopUnitTest.cc) +target_link_libraries(eventloopunittest muduo_net) -add_executable(eventloopthread_unittest EventLoopThread_unittest.cc) -target_link_libraries(eventloopthread_unittest muduo_net) +add_executable(eventloopthreadunittest eventloopthreadunittest/EventLoopThreadUnitTest.cc) +target_link_libraries(eventloopthreadunittest muduo_net) -add_executable(eventloopthreadpool_unittest EventLoopThreadPool_unittest.cc) -target_link_libraries(eventloopthreadpool_unittest muduo_net) +add_executable(eventloopthreadpoolunittest eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.cc) +target_link_libraries(eventloopthreadpoolunittest muduo_net) if(BOOSTTEST_LIBRARY) add_executable(buffer_unittest Buffer_unittest.cc) @@ -28,21 +28,20 @@ add_test(NAME inetaddress_unittest COMMAND inetaddress_unittest) if(ZLIB_FOUND) add_executable(zlibstream_unittest ZlibStream_unittest.cc) target_link_libraries(zlibstream_unittest muduo_net boost_unit_test_framework z) - # set_target_properties(zlibstream_unittest PROPERTIES COMPILE_FLAGS "-std=c++0x") endif() endif() -add_executable(tcpclient_reg1 TcpClient_reg1.cc) -target_link_libraries(tcpclient_reg1 muduo_net) +add_executable(tcpclientreg1 tcpclientreg1/TcpClientReg1.cc) +target_link_libraries(tcpclientreg1 muduo_net) -add_executable(tcpclient_reg2 TcpClient_reg2.cc) -target_link_libraries(tcpclient_reg2 muduo_net) +add_executable(tcpclientreg2 tcpclientreg2/TcpClientReg2.cc) +target_link_libraries(tcpclientreg2 muduo_net) -add_executable(tcpclient_reg3 TcpClient_reg3.cc) -target_link_libraries(tcpclient_reg3 muduo_net) +add_executable(tcpclientreg3 tcpclientreg3/TcpClientReg3.cc) +target_link_libraries(tcpclientreg3 muduo_net) -add_executable(timerqueue_unittest TimerQueue_unittest.cc) -target_link_libraries(timerqueue_unittest muduo_net) -add_test(NAME timerqueue_unittest COMMAND timerqueue_unittest) +add_executable(timerqueueunittest timerqueueunittest/TimerQueueUnitTest.cc) +target_link_libraries(timerqueueunittest muduo_net) +add_test(NAME timerqueueunittest COMMAND timerqueueunittest) diff --git a/muduo/net/tests/bufferunittest/BufferUnittest.cc b/muduo/net/tests/bufferunittest/BufferUnittest.cc new file mode 100644 index 000000000..13d30ab43 --- /dev/null +++ b/muduo/net/tests/bufferunittest/BufferUnittest.cc @@ -0,0 +1,175 @@ +#include + + +#include + +using std::string; +using muduo::net::Buffer; + +TEST(BufferUnittest,testBufferAppendRetrieve) +{ + Buffer buf; + EXPECT_EQ(buf.readableBytes(), 0); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); + + const string str(200, 'x'); + buf.append(str); + EXPECT_EQ(buf.readableBytes(), str.size()); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - str.size()); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); + + const string str2 = buf.retrieveAsString(50); + EXPECT_EQ(str2.size(), 50); + EXPECT_EQ(buf.readableBytes(), str.size() - str2.size()); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - str.size()); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend + str2.size()); + EXPECT_EQ(str2, string(50, 'x')); + + buf.append(str); + EXPECT_EQ(buf.readableBytes(), 2 * str.size() - str2.size()); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 2 * str.size()); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend + str2.size()); + + const string str3 = buf.retrieveAllAsString(); + EXPECT_EQ(str3.size(), 350); + EXPECT_EQ(buf.readableBytes(), 0); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); + EXPECT_EQ(str3, string(350, 'x')); +} + +TEST(BufferUnittest, testBufferGrow) +{ + Buffer buf; + buf.append(string(400, 'y')); + EXPECT_EQ(buf.readableBytes(), 400); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 400); + + buf.retrieve(50); + EXPECT_EQ(buf.readableBytes(), 350); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 400); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend + 50); + + buf.append(string(1000, 'z')); + EXPECT_EQ(buf.readableBytes(), 1350); + EXPECT_EQ(buf.writableBytes(), 0); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend + 50); // FIXME + + buf.retrieveAll(); + EXPECT_EQ(buf.readableBytes(), 0); + EXPECT_EQ(buf.writableBytes(), 1400); // FIXME + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); +} + +TEST(BufferUnittest, testBufferInsideGrow) +{ + Buffer buf; + buf.append(string(800, 'y')); + EXPECT_EQ(buf.readableBytes(), 800); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 800); + + buf.retrieve(500); + EXPECT_EQ(buf.readableBytes(), 300); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 800); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend + 500); + + buf.append(string(300, 'z')); + EXPECT_EQ(buf.readableBytes(), 600); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 600); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); +} + +TEST(BufferUnittest, testBufferShrink) +{ + Buffer buf; + buf.append(string(2000, 'y')); + EXPECT_EQ(buf.readableBytes(), 2000); + EXPECT_EQ(buf.writableBytes(), 0); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); + + buf.retrieve(1500); + EXPECT_EQ(buf.readableBytes(), 500); + EXPECT_EQ(buf.writableBytes(), 0); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend + 1500); + + buf.shrink(0); + EXPECT_EQ(buf.readableBytes(), 500); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 500); + EXPECT_EQ(buf.retrieveAllAsString(), string(500, 'y')); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); +} + +TEST(BufferUnittest, testBufferPrepend) +{ + Buffer buf; + buf.append(string(200, 'y')); + EXPECT_EQ(buf.readableBytes(), 200); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 200); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend); + + int x = 0; + buf.prepend(&x, sizeof x); + EXPECT_EQ(buf.readableBytes(), 204); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize - 200); + EXPECT_EQ(buf.prependableBytes(), Buffer::kCheapPrepend - 4); +} + +TEST(BufferUnittest, testBufferReadInt) +{ + Buffer buf; + buf.append("HTTP"); + + EXPECT_EQ(buf.readableBytes(), 4); + EXPECT_EQ(buf.peekInt8(), 'H'); + int top16 = buf.peekInt16(); + EXPECT_EQ(top16, 'H' * 256 + 'T'); + EXPECT_EQ(buf.peekInt32(), top16 * 65536 + 'T' * 256 + 'P'); + + EXPECT_EQ(buf.readInt8(), 'H'); + EXPECT_EQ(buf.readInt16(), 'T' * 256 + 'T'); + EXPECT_EQ(buf.readInt8(), 'P'); + EXPECT_EQ(buf.readableBytes(), 0); + EXPECT_EQ(buf.writableBytes(), Buffer::kInitialSize); + + buf.appendInt8(-1); + buf.appendInt16(-2); + buf.appendInt32(-3); + EXPECT_EQ(buf.readableBytes(), 7); + EXPECT_EQ(buf.readInt8(), -1); + EXPECT_EQ(buf.readInt16(), -2); + EXPECT_EQ(buf.readInt32(), -3); +} + +TEST(BufferUnittest, testBufferFindEOL) +{ + Buffer buf; + buf.append(string(100000, 'x')); + const char* null = NULL; + EXPECT_EQ(buf.findEOL(), null); + EXPECT_EQ(buf.findEOL(buf.peek() + 90000), null); +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +void output(Buffer&& buf, const void* inner) +{ + Buffer newbuf(std::move(buf)); + // printf("New Buffer at %p, inner %p\n", &newbuf, newbuf.peek()); + EXPECT_EQ(inner, newbuf.peek()); +} + +// NOTE: This test fails in g++ 4.4, passes in g++ 4.6. +TEST(Buffer, testMove) +{ + Buffer buf; + buf.append("muduo", 5); + const void* inner = buf.peek(); + // printf("Buffer at %p, inner %p\n", &buf, inner); + output(std::move(buf), inner); +} +#endif +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/net/tests/bufferunittest/BufferUnittest.sln b/muduo/net/tests/bufferunittest/BufferUnittest.sln new file mode 100644 index 000000000..71e8ccade --- /dev/null +++ b/muduo/net/tests/bufferunittest/BufferUnittest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BufferUnittest", "BufferUnittest.vcxproj", "{99E298B0-12E1-4225-B4AD-8140C15A1F43}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Debug|x64.ActiveCfg = Debug|x64 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Debug|x64.Build.0 = Debug|x64 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Debug|x86.ActiveCfg = Debug|Win32 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Debug|x86.Build.0 = Debug|Win32 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Release|x64.ActiveCfg = Release|x64 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Release|x64.Build.0 = Release|x64 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Release|x86.ActiveCfg = Release|Win32 + {99E298B0-12E1-4225-B4AD-8140C15A1F43}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/bufferunittest/BufferUnittest.vcxproj.filters b/muduo/net/tests/bufferunittest/BufferUnittest.vcxproj.filters new file mode 100644 index 000000000..eeabdf02b --- /dev/null +++ b/muduo/net/tests/bufferunittest/BufferUnittest.vcxproj.filters @@ -0,0 +1,85 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/bufferunittest/bufferunittest.vcxproj b/muduo/net/tests/bufferunittest/bufferunittest.vcxproj new file mode 100644 index 000000000..2fb0b9c13 --- /dev/null +++ b/muduo/net/tests/bufferunittest/bufferunittest.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {99E298B0-12E1-4225-B4AD-8140C15A1F43} + Win32Proj + BufferUnittest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + ../../../../lib;%(AdditionalLibraryDirectories) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib/gtest.lib;%(AdditionalDependencies) + ../../../../lib;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/channeltest/ChannelTest.cc b/muduo/net/tests/channeltest/ChannelTest.cc new file mode 100644 index 000000000..4b608d3e5 --- /dev/null +++ b/muduo/net/tests/channeltest/ChannelTest.cc @@ -0,0 +1,124 @@ +#include + +#include + +#include + +#include + + +#include +#include +#include + + +using namespace muduo; +using namespace muduo::net; + +void print(const char* msg) +{ + static std::map lasts; + Timestamp& last = lasts[msg]; + Timestamp now = Timestamp::now(); + printf("%s tid %d %s delay %f\n", now.toString().c_str(), CurrentThread::tid(), + msg, timeDifference(now, last)); + last = now; +} + +namespace muduo +{ + namespace net + { + namespace detail + { + int createTimerfd(); + void readTimerfd(int timerfd, Timestamp now); + } + } +} + +// Use relative time, immunized to wall clock changes. +class PeriodicTimer +{ +public: + PeriodicTimer(EventLoop* loop, double interval, const TimerCallback& cb) + : loop_(loop), + timerfd_(muduo::net::detail::createTimerfd()), + timerfdChannel_(loop, timerfd_), + interval_(interval), + cb_(cb) + { + timerfdChannel_.setReadCallback( + std::bind(&PeriodicTimer::handleRead, this)); + timerfdChannel_.enableReading(); + } + + void start() + { + struct itimerspec spec; + bzero(&spec, sizeof spec); + spec.it_interval = toTimeSpec(interval_); + spec.it_value = spec.it_interval; + + int ret = ::timerfd_settime(timerfd_, 0 /* relative timer */, &spec, NULL); + if (ret) + { + LOG_SYSERR << "timerfd_settime()"; + } + + } + + ~PeriodicTimer() + { + timerfdChannel_.disableAll(); + timerfdChannel_.remove(); + #ifdef __linux__ + ::close(timerfd_); + #endif // __linux__ + #ifdef WIN32 + ::winclosesock(timerfd_); + #endif // WIN32 + } + +private: + void handleRead() + { + loop_->assertInLoopThread(); + muduo::net::detail::readTimerfd(timerfd_, Timestamp::now()); + if (cb_) + cb_(); + } + + static struct timespec toTimeSpec(double seconds) + { + struct timespec ts; + bzero(&ts, sizeof ts); + const int64_t kNanoSecondsPerSecond = 1000000000; + const int kMinInterval = 100000; + int64_t nanoseconds = static_cast(seconds * kNanoSecondsPerSecond); + if (nanoseconds < kMinInterval) + nanoseconds = kMinInterval; + ts.tv_sec = static_cast(nanoseconds / kNanoSecondsPerSecond); + ts.tv_nsec = static_cast(nanoseconds % kNanoSecondsPerSecond); + return ts; + } + + EventLoop* loop_; + const int timerfd_; + Channel timerfdChannel_; + const double interval_; // in seconds + TimerCallback cb_; +}; + +int main(int argc, char* argv[]) +{ + LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid() + << " Try adjusting the wall clock, see what happens."; + EventLoop loop; + PeriodicTimer timer(&loop, 1, std::bind(print, "PeriodicTimer")); + timer.start(); + loop.runEvery(1, std::bind(print, "EventLoop::runEvery")); + loop.loop(); +} + + diff --git a/muduo/net/tests/channeltest/ChannelTest.sln b/muduo/net/tests/channeltest/ChannelTest.sln new file mode 100644 index 000000000..7d88bea2d --- /dev/null +++ b/muduo/net/tests/channeltest/ChannelTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChannelTest", "ChannelTest.vcxproj", "{A8647223-22F1-4878-B4A2-ECBB218DB407}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Debug|x64.ActiveCfg = Debug|x64 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Debug|x64.Build.0 = Debug|x64 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Debug|x86.ActiveCfg = Debug|Win32 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Debug|x86.Build.0 = Debug|Win32 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Release|x64.ActiveCfg = Release|x64 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Release|x64.Build.0 = Release|x64 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Release|x86.ActiveCfg = Release|Win32 + {A8647223-22F1-4878-B4A2-ECBB218DB407}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/channeltest/ChannelTest.vcxproj.filters b/muduo/net/tests/channeltest/ChannelTest.vcxproj.filters new file mode 100644 index 000000000..92e2fc2b2 --- /dev/null +++ b/muduo/net/tests/channeltest/ChannelTest.vcxproj.filters @@ -0,0 +1,82 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/channeltest/channeltest.vcxproj b/muduo/net/tests/channeltest/channeltest.vcxproj new file mode 100644 index 000000000..2118f9d87 --- /dev/null +++ b/muduo/net/tests/channeltest/channeltest.vcxproj @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {A8647223-22F1-4878-B4A2-ECBB218DB407} + Win32Proj + ChannelTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + + Console + + + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/echoclientunittest/EchoClientUnitTest.cc b/muduo/net/tests/echoclientunittest/EchoClientUnitTest.cc new file mode 100644 index 000000000..44d973ac8 --- /dev/null +++ b/muduo/net/tests/echoclientunittest/EchoClientUnitTest.cc @@ -0,0 +1,114 @@ +#include "muduo/net/TcpClient.h" + +#include "muduo/base/Logging.h" +#include "muduo/base/Thread.h" +#include "muduo/net/EventLoop.h" +#include "muduo/net/InetAddress.h" + +#include + +#include +#include + +using namespace muduo; +using namespace muduo::net; + +int numThreads = 0; +class EchoClient; +std::vector> clients; +int current = 0; + +class EchoClient : noncopyable +{ + public: + EchoClient(EventLoop* loop, const InetAddress& listenAddr, const string& id) + : loop_(loop), + client_(loop, listenAddr, "EchoClient"+id) + { + client_.setConnectionCallback( + std::bind(&EchoClient::onConnection, this, _1)); + client_.setMessageCallback( + std::bind(&EchoClient::onMessage, this, _1, _2, _3)); + //client_.enableRetry(); + } + + void connect() + { + client_.connect(); + } + // void stop(); + + private: + void onConnection(const TcpConnectionPtr& conn) + { + LOG_TRACE << conn->localAddress().toIpPort() << " -> " + << conn->peerAddress().toIpPort() << " is " + << (conn->connected() ? "UP" : "DOWN"); + + if (conn->connected()) + { + ++current; + if (implicit_cast(current) < clients.size()) + { + clients[current]->connect(); + } + LOG_INFO << "*** connected " << current; + } + conn->send("world\n"); + } + + void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) + { + string msg(buf->retrieveAllAsString()); + LOG_TRACE << conn->name() << " recv " << msg.size() << " bytes at " << time.toString(); + if (msg == "quit\n") + { + conn->send("bye\n"); + conn->shutdown(); + } + else if (msg == "shutdown\n") + { + loop_->quit(); + } + else + { + conn->send(msg); + } + } + + EventLoop* loop_; + TcpClient client_; +}; + +int main(int argc, char* argv[]) +{ + LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); + if (argc > 1) + { + EventLoop loop; + bool ipv6 = argc > 3; + InetAddress serverAddr(argv[1], 2000, ipv6); + + int n = 1; + if (argc > 2) + { + n = atoi(argv[2]); + } + + clients.reserve(n); + for (int i = 0; i < n; ++i) + { + char buf[32]; + snprintf(buf, sizeof buf, "%d", i+1); + clients.emplace_back(new EchoClient(&loop, serverAddr, buf)); + } + + clients[current]->connect(); + loop.loop(); + } + else + { + printf("Usage: %s host_ip [current#]\n", argv[0]); + } +} + diff --git a/muduo/net/tests/echoclientunittest/EchoClientUnitTest.sln b/muduo/net/tests/echoclientunittest/EchoClientUnitTest.sln new file mode 100644 index 000000000..6eb2d4350 --- /dev/null +++ b/muduo/net/tests/echoclientunittest/EchoClientUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EchoClientUnitTest", "EchoClientUnitTest.vcxproj", "{B4109AF3-FD46-430D-ABBD-F8814F26EBDE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Debug|x64.ActiveCfg = Debug|x64 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Debug|x64.Build.0 = Debug|x64 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Debug|x86.ActiveCfg = Debug|Win32 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Debug|x86.Build.0 = Debug|Win32 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Release|x64.ActiveCfg = Release|x64 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Release|x64.Build.0 = Release|x64 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Release|x86.ActiveCfg = Release|Win32 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/echoclientunittest/EchoClientUnitTest.vcxproj.filters b/muduo/net/tests/echoclientunittest/EchoClientUnitTest.vcxproj.filters new file mode 100644 index 000000000..3ed1bf9fc --- /dev/null +++ b/muduo/net/tests/echoclientunittest/EchoClientUnitTest.vcxproj.filters @@ -0,0 +1,100 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/echoclientunittest/echoclientunittest.vcxproj b/muduo/net/tests/echoclientunittest/echoclientunittest.vcxproj new file mode 100644 index 000000000..c3ddc4150 --- /dev/null +++ b/muduo/net/tests/echoclientunittest/echoclientunittest.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {B4109AF3-FD46-430D-ABBD-F8814F26EBDE} + Win32Proj + EchoClientUnitTest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/echoserverunittest/EchoServerUnitTest.cc b/muduo/net/tests/echoserverunittest/EchoServerUnitTest.cc new file mode 100644 index 000000000..08756688b --- /dev/null +++ b/muduo/net/tests/echoserverunittest/EchoServerUnitTest.cc @@ -0,0 +1,86 @@ +#include "muduo/net/TcpServer.h" + +#include "muduo/base/Logging.h" +#include "muduo/base/Thread.h" +#include "muduo/net/EventLoop.h" +#include "muduo/net/InetAddress.h" + +#include + +#include +#include + +using namespace muduo; +using namespace muduo::net; + +int numThreads = 0; + +class EchoServer +{ + public: + EchoServer(EventLoop* loop, const InetAddress& listenAddr) + : loop_(loop), + server_(loop, listenAddr, "EchoServer") + { + server_.setConnectionCallback( + std::bind(&EchoServer::onConnection, this, _1)); + server_.setMessageCallback( + std::bind(&EchoServer::onMessage, this, _1, _2, _3)); + server_.setThreadNum(numThreads); + } + + void start() + { + server_.start(); + } + // void stop(); + + private: + void onConnection(const TcpConnectionPtr& conn) + { + LOG_TRACE << conn->peerAddress().toIpPort() << " -> " + << conn->localAddress().toIpPort() << " is " + << (conn->connected() ? "UP" : "DOWN"); + LOG_INFO << conn->getTcpInfoString(); + + conn->send("hello\n"); + } + + void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) + { + string msg(buf->retrieveAllAsString()); + LOG_TRACE << conn->name() << " recv " << msg.size() << " bytes at " << time.toString(); + if (msg == "exit\n") + { + conn->send("bye\n"); + conn->shutdown(); + } + if (msg == "quit\n") + { + loop_->quit(); + } + conn->send(msg); + } + + EventLoop* loop_; + TcpServer server_; +}; + +int main(int argc, char* argv[]) +{ + LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); + LOG_INFO << "sizeof TcpConnection = " << sizeof(TcpConnection); + if (argc > 1) + { + numThreads = atoi(argv[1]); + } + bool ipv6 = argc > 2; + EventLoop loop; + InetAddress listenAddr(2000, false, ipv6); + EchoServer server(&loop, listenAddr); + + server.start(); + + loop.loop(); +} + diff --git a/muduo/net/tests/echoserverunittest/EchoServerUnitTest.sln b/muduo/net/tests/echoserverunittest/EchoServerUnitTest.sln new file mode 100644 index 000000000..6af4786c1 --- /dev/null +++ b/muduo/net/tests/echoserverunittest/EchoServerUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EchoServerUnitTest", "EchoServerUnitTest.vcxproj", "{975E412C-F2B8-4C24-824D-26A227A570C4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {975E412C-F2B8-4C24-824D-26A227A570C4}.Debug|x64.ActiveCfg = Debug|x64 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Debug|x64.Build.0 = Debug|x64 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Debug|x86.ActiveCfg = Debug|Win32 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Debug|x86.Build.0 = Debug|Win32 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Release|x64.ActiveCfg = Release|x64 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Release|x64.Build.0 = Release|x64 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Release|x86.ActiveCfg = Release|Win32 + {975E412C-F2B8-4C24-824D-26A227A570C4}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/echoserverunittest/EchoServerUnitTest.vcxproj.filters b/muduo/net/tests/echoserverunittest/EchoServerUnitTest.vcxproj.filters new file mode 100644 index 000000000..40bbb383d --- /dev/null +++ b/muduo/net/tests/echoserverunittest/EchoServerUnitTest.vcxproj.filters @@ -0,0 +1,106 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/echoserverunittest/echoserverunittest.vcxproj b/muduo/net/tests/echoserverunittest/echoserverunittest.vcxproj new file mode 100644 index 000000000..1cba92c37 --- /dev/null +++ b/muduo/net/tests/echoserverunittest/echoserverunittest.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {975E412C-F2B8-4C24-824D-26A227A570C4} + Win32Proj + EchoServerUnitTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + + Console + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.cc b/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.cc new file mode 100644 index 000000000..53b0e7fdb --- /dev/null +++ b/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.cc @@ -0,0 +1,83 @@ + + + +#include + +#include + +#include + +#include +#include + + + +using namespace muduo; +using namespace muduo::net; + +void print(EventLoop* p = NULL) +{ + printf("main(): pid = %d, tid = %d, loop = %p\n", + getpid(), CurrentThread::tid(), p); +} + +void init(EventLoop* p) +{ + printf("init(): pid = %d, tid = %d, loop = %p\n", + getpid(), CurrentThread::tid(), p); +} + +TEST(EventLoopThreadPoolTest, ThreadPoolTest) +{ + print(); + + EventLoop loop; + loop.runAfter(11, boost::bind(&EventLoop::quit, &loop)); + + { + printf("Single thread %p:\n", &loop); + EventLoopThreadPool model(&loop, "single"); + model.setThreadNum(0); + model.start(init); + EXPECT_TRUE(model.getNextLoop() == &loop); + EXPECT_TRUE(model.getNextLoop() == &loop); + EXPECT_TRUE(model.getNextLoop() == &loop); + } + + { + printf("Another thread:\n"); + EventLoopThreadPool model(&loop, "another"); + model.setThreadNum(1); + model.start(init); + EventLoop* nextLoop = model.getNextLoop(); + nextLoop->runAfter(2, boost::bind(print, nextLoop)); + EXPECT_TRUE(nextLoop != &loop); + EXPECT_TRUE(nextLoop == model.getNextLoop()); + EXPECT_TRUE(nextLoop == model.getNextLoop()); + ::sleep(3); + } + + { + printf("Three threads:\n"); + EventLoopThreadPool model(&loop, "three"); + model.setThreadNum(3); + model.start(init); + EventLoop* nextLoop = model.getNextLoop(); + nextLoop->runInLoop(boost::bind(print, nextLoop)); + EXPECT_TRUE(nextLoop != &loop); + EXPECT_TRUE(nextLoop != model.getNextLoop()); + EXPECT_TRUE(nextLoop != model.getNextLoop()); + EXPECT_TRUE(nextLoop == model.getNextLoop()); + } + + loop.loop(); +} + + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.sln b/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.sln new file mode 100644 index 000000000..2a257c08b --- /dev/null +++ b/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EventLoopThreadPoolUnitTest", "EventLoopThreadPoolUnitTest.vcxproj", "{D4514E7B-1E4B-491A-B961-2AF41658C9AA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Debug|x64.ActiveCfg = Debug|x64 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Debug|x64.Build.0 = Debug|x64 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Debug|x86.ActiveCfg = Debug|Win32 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Debug|x86.Build.0 = Debug|Win32 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Release|x64.ActiveCfg = Release|x64 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Release|x64.Build.0 = Release|x64 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Release|x86.ActiveCfg = Release|Win32 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.vcxproj.filters b/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.vcxproj.filters new file mode 100644 index 000000000..51c11e9c0 --- /dev/null +++ b/muduo/net/tests/eventloopthreadpoolunittest/EventLoopThreadPoolUnitTest.vcxproj.filters @@ -0,0 +1,97 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/eventloopthreadpoolunittest/eventloopthreadpoolunittest.vcxproj b/muduo/net/tests/eventloopthreadpoolunittest/eventloopthreadpoolunittest.vcxproj new file mode 100644 index 000000000..6c69baf0e --- /dev/null +++ b/muduo/net/tests/eventloopthreadpoolunittest/eventloopthreadpoolunittest.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {D4514E7B-1E4B-491A-B961-2AF41658C9AA} + Win32Proj + EventLoopThreadPoolUnitTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + ../../../../lib;%(AdditionalLibraryDirectories) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.cc b/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.cc new file mode 100644 index 000000000..d67c12852 --- /dev/null +++ b/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.cc @@ -0,0 +1,48 @@ +#include "muduo/net/EventLoopThread.h" +#include "muduo/net/EventLoop.h" +#include "muduo/base/Thread.h" +#include "muduo/base/CountDownLatch.h" + +#include +#include + +using namespace muduo; +using namespace muduo::net; + +void print(EventLoop* p = NULL) +{ + printf("print: pid = %d, tid = %d, loop = %p\n", + getpid(), CurrentThread::tid(), p); +} + +void quit(EventLoop* p) +{ + print(p); + p->quit(); +} + +int main() +{ + print(); + + { + EventLoopThread thr1; // never start + } + + { + // dtor calls quit() + EventLoopThread thr2; + EventLoop* loop = thr2.startLoop(); + loop->runInLoop(std::bind(print, loop)); + CurrentThread::sleepUsec(500 * 1000); + } + + { + // quit() before dtor + EventLoopThread thr3; + EventLoop* loop = thr3.startLoop(); + loop->runInLoop(std::bind(quit, loop)); + CurrentThread::sleepUsec(500 * 1000); + } +} + diff --git a/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.sln b/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.sln new file mode 100644 index 000000000..8fb6e1289 --- /dev/null +++ b/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EventLoopThreadUnitTest", "EventLoopThreadUnitTest.vcxproj", "{16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Debug|x64.ActiveCfg = Debug|x64 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Debug|x64.Build.0 = Debug|x64 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Debug|x86.ActiveCfg = Debug|Win32 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Debug|x86.Build.0 = Debug|Win32 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Release|x64.ActiveCfg = Release|x64 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Release|x64.Build.0 = Release|x64 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Release|x86.ActiveCfg = Release|Win32 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.vcxproj.filters b/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.vcxproj.filters new file mode 100644 index 000000000..a9441362d --- /dev/null +++ b/muduo/net/tests/eventloopthreadunittest/EventLoopThreadUnitTest.vcxproj.filters @@ -0,0 +1,94 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/eventloopthreadunittest/eventloopthreadunittest.vcxproj b/muduo/net/tests/eventloopthreadunittest/eventloopthreadunittest.vcxproj new file mode 100644 index 000000000..dcb30be8e --- /dev/null +++ b/muduo/net/tests/eventloopthreadunittest/eventloopthreadunittest.vcxproj @@ -0,0 +1,177 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {16F9BBF2-6B9B-4D46-974E-DC89F9DAD84C} + Win32Proj + EventLoopThreadUnitTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + + Console + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/eventloopunittest/EventLoopUnitTest.cc b/muduo/net/tests/eventloopunittest/EventLoopUnitTest.cc new file mode 100644 index 000000000..6cb6d7d9f --- /dev/null +++ b/muduo/net/tests/eventloopunittest/EventLoopUnitTest.cc @@ -0,0 +1,42 @@ +#include "muduo/net/EventLoop.h" +#include "muduo/base/Thread.h" + +#include +#include +#include + +using namespace muduo; +using namespace muduo::net; + +EventLoop* g_loop; + +void callback() +{ + printf("callback(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); + EventLoop anotherLoop; +} + +void threadFunc() +{ + printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); + + assert(EventLoop::getEventLoopOfCurrentThread() == NULL); + EventLoop loop; + assert(EventLoop::getEventLoopOfCurrentThread() == &loop); + loop.runAfter(1.0, callback); + loop.loop(); +} + +int main() +{ + printf("main(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); + + assert(EventLoop::getEventLoopOfCurrentThread() == NULL); + EventLoop loop; + assert(EventLoop::getEventLoopOfCurrentThread() == &loop); + + Thread thread(threadFunc); + thread.start(); + + loop.loop(); +} diff --git a/muduo/net/tests/eventloopunittest/EventLoopUnitTest.sln b/muduo/net/tests/eventloopunittest/EventLoopUnitTest.sln new file mode 100644 index 000000000..decab1199 --- /dev/null +++ b/muduo/net/tests/eventloopunittest/EventLoopUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EventLoopUnitTest", "EventLoopUnitTest.vcxproj", "{06414945-F9AA-4606-82C7-0EF46440CAFA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Debug|x64.ActiveCfg = Debug|x64 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Debug|x64.Build.0 = Debug|x64 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Debug|x86.ActiveCfg = Debug|Win32 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Debug|x86.Build.0 = Debug|Win32 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Release|x64.ActiveCfg = Release|x64 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Release|x64.Build.0 = Release|x64 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Release|x86.ActiveCfg = Release|Win32 + {06414945-F9AA-4606-82C7-0EF46440CAFA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/eventloopunittest/EventLoopUnitTest.vcxproj.filters b/muduo/net/tests/eventloopunittest/EventLoopUnitTest.vcxproj.filters new file mode 100644 index 000000000..5df075679 --- /dev/null +++ b/muduo/net/tests/eventloopunittest/EventLoopUnitTest.vcxproj.filters @@ -0,0 +1,82 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/eventloopunittest/eventloopunittest.vcxproj b/muduo/net/tests/eventloopunittest/eventloopunittest.vcxproj new file mode 100644 index 000000000..6b9ce2832 --- /dev/null +++ b/muduo/net/tests/eventloopunittest/eventloopunittest.vcxproj @@ -0,0 +1,177 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {06414945-F9AA-4606-82C7-0EF46440CAFA} + Win32Proj + EventLoopUnitTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.cc b/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.cc new file mode 100644 index 000000000..a09254afa --- /dev/null +++ b/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.cc @@ -0,0 +1,56 @@ +#include + +#include + +#include + +//#define BOOST_TEST_MODULE InetAddressTest + + + +using muduo::string; +using muduo::net::InetAddress; + +TEST(InetAddressUnitTest, testInetAddress) +{ + InetAddress addr0(1234); + EXPECT_EQ(addr0.toIp(), string("0.0.0.0")); + EXPECT_EQ(addr0.toIpPort(), string("0.0.0.0:1234")); + EXPECT_EQ(addr0.toPort(), 1234); + + InetAddress addr1(4321, true); + EXPECT_EQ(addr1.toIp(), string("127.0.0.1")); + EXPECT_EQ(addr1.toIpPort(), string("127.0.0.1:4321")); + EXPECT_EQ(addr1.toPort(), 4321); + + InetAddress addr2("1.2.3.4", 8888); + EXPECT_EQ(addr2.toIp(), string("1.2.3.4")); + EXPECT_EQ(addr2.toIpPort(), string("1.2.3.4:8888")); + EXPECT_EQ(addr2.toPort(), 8888); + + InetAddress addr3("255.254.253.252", 65535); + EXPECT_EQ(addr3.toIp(), string("255.254.253.252")); + EXPECT_EQ(addr3.toIpPort(), string("255.254.253.252:65535")); + EXPECT_EQ(addr3.toPort(), 65535); +} + +TEST(InetAddressUnitTest, testInetAddressResolve) +{ + InetAddress addr(80); + if (InetAddress::resolve("google.com", &addr)) + { + LOG_INFO << "google.com resolved to " << addr.toIpPort(); + } + else + { + LOG_ERROR << "Unable to resolve google.com"; + } +} + + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.sln b/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.sln new file mode 100644 index 000000000..46b0a0936 --- /dev/null +++ b/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InetAddressUnitTest", "InetAddressUnitTest.vcxproj", "{D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Debug|x64.ActiveCfg = Debug|x64 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Debug|x64.Build.0 = Debug|x64 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Debug|x86.ActiveCfg = Debug|Win32 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Debug|x86.Build.0 = Debug|Win32 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Release|x64.ActiveCfg = Release|x64 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Release|x64.Build.0 = Release|x64 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Release|x86.ActiveCfg = Release|Win32 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.vcxproj.filters b/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.vcxproj.filters new file mode 100644 index 000000000..3c080d722 --- /dev/null +++ b/muduo/net/tests/inetaddressunittest/InetAddressUnitTest.vcxproj.filters @@ -0,0 +1,88 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/inetaddressunittest/inetaddressunittest.vcxproj b/muduo/net/tests/inetaddressunittest/inetaddressunittest.vcxproj new file mode 100644 index 000000000..454dacfab --- /dev/null +++ b/muduo/net/tests/inetaddressunittest/inetaddressunittest.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {D98C05C5-2D17-4409-BFA6-6B0AF6B56F10} + Win32Proj + InetAddressUnitTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/tcpclientreg1/TcpClientReg1.cc b/muduo/net/tests/tcpclientreg1/TcpClientReg1.cc new file mode 100644 index 000000000..8e6ec7cec --- /dev/null +++ b/muduo/net/tests/tcpclientreg1/TcpClientReg1.cc @@ -0,0 +1,39 @@ +// TcpClient::stop() called in the same iteration of IO event + +#include + +#include +#include +#include + + +using namespace muduo; +using namespace muduo::net; + +TcpClient* g_client; + +void timeout() +{ + LOG_INFO << "timeout"; + g_client->stop(); +} + +TEST(Main, tcpClientReg1) +{ + EventLoop loop; + InetAddress serverAddr("127.0.0.1", 2); // no such server + TcpClient client(&loop, serverAddr, "TcpClient"); + g_client = &client; + loop.runAfter(0.0, timeout); + loop.runAfter(1.0, std::bind(&EventLoop::quit, &loop)); + client.connect(); + CurrentThread::sleepUsec(100 * 1000); + loop.loop(); +} + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/net/tests/tcpclientreg1/TcpClientReg1.sln b/muduo/net/tests/tcpclientreg1/TcpClientReg1.sln new file mode 100644 index 000000000..ef6427ce4 --- /dev/null +++ b/muduo/net/tests/tcpclientreg1/TcpClientReg1.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TcpClientReg1", "TcpClientReg1.vcxproj", "{F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Debug|x64.ActiveCfg = Debug|x64 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Debug|x64.Build.0 = Debug|x64 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Debug|x86.ActiveCfg = Debug|Win32 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Debug|x86.Build.0 = Debug|Win32 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Release|x64.ActiveCfg = Release|x64 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Release|x64.Build.0 = Release|x64 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Release|x86.ActiveCfg = Release|Win32 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/tcpclientreg1/TcpClientReg1.vcxproj.filters b/muduo/net/tests/tcpclientreg1/TcpClientReg1.vcxproj.filters new file mode 100644 index 000000000..c53e00e2f --- /dev/null +++ b/muduo/net/tests/tcpclientreg1/TcpClientReg1.vcxproj.filters @@ -0,0 +1,106 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/tcpclientreg1/tcpclientreg1.vcxproj b/muduo/net/tests/tcpclientreg1/tcpclientreg1.vcxproj new file mode 100644 index 000000000..62ce81381 --- /dev/null +++ b/muduo/net/tests/tcpclientreg1/tcpclientreg1.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {F4DEB0E1-CA5E-4BD5-A2AC-71B6BE818F6B} + Win32Proj + TcpClientReg1 + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/tcpclientreg2/TcpClientReg2.cc b/muduo/net/tests/tcpclientreg2/TcpClientReg2.cc new file mode 100644 index 000000000..425cd18fd --- /dev/null +++ b/muduo/net/tests/tcpclientreg2/TcpClientReg2.cc @@ -0,0 +1,42 @@ +// TcpClient destructs when TcpConnection is connected but unique. + + +#include + +#include +#include +#include + + +using namespace muduo; +using namespace muduo::net; + +void threadFunc(EventLoop* loop) +{ + InetAddress serverAddr("127.0.0.1", 1234); // should succeed + TcpClient client(loop, serverAddr, "TcpClient"); + client.connect(); + + CurrentThread::sleepUsec(1000 * 1000); + // client destructs when connected. +} + +TEST(main, tcpClientReg2) +{ + Logger::setLogLevel(Logger::DEBUG); + + EventLoop loop; + loop.runAfter(3.0, std::bind(&EventLoop::quit, &loop)); + std::thread thr(std::bind(threadFunc, &loop)); + + loop.loop(); + thr.join(); +} + + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/net/tests/tcpclientreg2/TcpClientReg2.sln b/muduo/net/tests/tcpclientreg2/TcpClientReg2.sln new file mode 100644 index 000000000..3deaf4d65 --- /dev/null +++ b/muduo/net/tests/tcpclientreg2/TcpClientReg2.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TcpClientReg2", "TcpClientReg2.vcxproj", "{AE71F524-4577-4ECA-95BE-FB7C77E0FD67}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Debug|x64.ActiveCfg = Debug|x64 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Debug|x64.Build.0 = Debug|x64 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Debug|x86.ActiveCfg = Debug|Win32 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Debug|x86.Build.0 = Debug|Win32 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Release|x64.ActiveCfg = Release|x64 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Release|x64.Build.0 = Release|x64 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Release|x86.ActiveCfg = Release|Win32 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/tcpclientreg2/TcpClientReg2.vcxproj.filters b/muduo/net/tests/tcpclientreg2/TcpClientReg2.vcxproj.filters new file mode 100644 index 000000000..4bb737203 --- /dev/null +++ b/muduo/net/tests/tcpclientreg2/TcpClientReg2.vcxproj.filters @@ -0,0 +1,106 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/tcpclientreg2/tcpclientreg2.vcxproj b/muduo/net/tests/tcpclientreg2/tcpclientreg2.vcxproj new file mode 100644 index 000000000..2bc0cae88 --- /dev/null +++ b/muduo/net/tests/tcpclientreg2/tcpclientreg2.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {AE71F524-4577-4ECA-95BE-FB7C77E0FD67} + Win32Proj + TcpClientReg2 + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/tcpclientreg3/TcpClientReg3.cc b/muduo/net/tests/tcpclientreg3/TcpClientReg3.cc new file mode 100644 index 000000000..d70d3f5ba --- /dev/null +++ b/muduo/net/tests/tcpclientreg3/TcpClientReg3.cc @@ -0,0 +1,37 @@ +// TcpClient destructs in a different thread. + +#include + +#include +#include +#include +#include + + +#include + +using namespace muduo; +using namespace muduo::net; + +TEST( Main, tcpClientReg3) +{ + Logger::setLogLevel(Logger::DEBUG); + + EventLoopThread loopThread; + { + InetAddress serverAddr("127.0.0.1", 1234); // should succeed + TcpClient client(loopThread.startLoop(), serverAddr, "TcpClient"); + client.connect(); + CurrentThread::sleepUsec(500 * 1000); // wait for connect + client.disconnect(); + } + + CurrentThread::sleepUsec(1000 * 1000); +} + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/muduo/net/tests/tcpclientreg3/TcpClientReg3.sln b/muduo/net/tests/tcpclientreg3/TcpClientReg3.sln new file mode 100644 index 000000000..cbe27f745 --- /dev/null +++ b/muduo/net/tests/tcpclientreg3/TcpClientReg3.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TcpClientReg3", "TcpClientReg3.vcxproj", "{5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Debug|x64.ActiveCfg = Debug|x64 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Debug|x64.Build.0 = Debug|x64 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Debug|x86.ActiveCfg = Debug|Win32 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Debug|x86.Build.0 = Debug|Win32 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Release|x64.ActiveCfg = Release|x64 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Release|x64.Build.0 = Release|x64 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Release|x86.ActiveCfg = Release|Win32 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/tcpclientreg3/TcpClientReg3.vcxproj.filters b/muduo/net/tests/tcpclientreg3/TcpClientReg3.vcxproj.filters new file mode 100644 index 000000000..36ebfe6cd --- /dev/null +++ b/muduo/net/tests/tcpclientreg3/TcpClientReg3.vcxproj.filters @@ -0,0 +1,109 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/tcpclientreg3/tcpclientreg3.vcxproj b/muduo/net/tests/tcpclientreg3/tcpclientreg3.vcxproj new file mode 100644 index 000000000..708229eaa --- /dev/null +++ b/muduo/net/tests/tcpclientreg3/tcpclientreg3.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {5D1F002C-42DF-4B2C-BD6B-68E68BD0C6A8} + Win32Proj + TcpClientReg3 + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.cc b/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.cc new file mode 100644 index 000000000..bcfb903e6 --- /dev/null +++ b/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.cc @@ -0,0 +1,77 @@ + +#include + +#include +#include + + +#include +#include + +using namespace muduo; +using namespace muduo::net; + +int cnt = 0; +EventLoop* g_loop; + +void printTid() +{ + printf("pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); + printf("now %s\n", Timestamp::now().toString().c_str()); +} + +void print(const char* msg) +{ + printf("msg %s %s\n", Timestamp::now().toString().c_str(), msg); + if (++cnt == 20) + { + g_loop->quit(); + } +} + +void cancel(TimerId timer) +{ + g_loop->cancel(timer); + printf("cancelled at %s\n", Timestamp::now().toString().c_str()); +} + +TEST(main, TimerQueueUnitTest) +{ + printTid(); + sleep(1); + { + EventLoop loop; + g_loop = &loop; + + print("main"); + loop.runAfter(1, std::bind(print, "once1")); + loop.runAfter(1.5, std::bind(print, "once1.5")); + loop.runAfter(2.5, std::bind(print, "once2.5")); + loop.runAfter(3.5, std::bind(print, "once3.5")); + TimerId t45 = loop.runAfter(4.5, std::bind(print, "once4.5")); + loop.runAfter(4.2, std::bind(cancel, t45)); + loop.runAfter(4.8, std::bind(cancel, t45)); + loop.runEvery(2, std::bind(print, "every2")); + TimerId t3 = loop.runEvery(3, std::bind(print, "every3")); + loop.runAfter(9.001, std::bind(cancel, t3)); + + loop.loop(); + print("main loop exits"); + } + sleep(1); + { + EventLoopThread loopThread; + EventLoop* loop = loopThread.startLoop(); + loop->runAfter(2, printTid); + sleep(3); + print("thread loop exits"); + } +} + +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.sln b/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.sln new file mode 100644 index 000000000..6505a87d6 --- /dev/null +++ b/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TimerQueueUnitTest", "TimerQueueUnitTest.vcxproj", "{49932EBB-A781-40C7-B7AE-EF5101E6C002}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Debug|x64.ActiveCfg = Debug|x64 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Debug|x64.Build.0 = Debug|x64 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Debug|x86.ActiveCfg = Debug|Win32 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Debug|x86.Build.0 = Debug|Win32 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Release|x64.ActiveCfg = Release|x64 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Release|x64.Build.0 = Release|x64 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Release|x86.ActiveCfg = Release|Win32 + {49932EBB-A781-40C7-B7AE-EF5101E6C002}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.vcxproj.filters b/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.vcxproj.filters new file mode 100644 index 000000000..4bad79aee --- /dev/null +++ b/muduo/net/tests/timerqueueunittest/TimerQueueUnitTest.vcxproj.filters @@ -0,0 +1,88 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/timerqueueunittest/timerqueueunittest.vcxproj b/muduo/net/tests/timerqueueunittest/timerqueueunittest.vcxproj new file mode 100644 index 000000000..6809d84f3 --- /dev/null +++ b/muduo/net/tests/timerqueueunittest/timerqueueunittest.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {49932EBB-A781-40C7-B7AE-EF5101E6C002} + Win32Proj + TimerQueueUnitTest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;WIN32;%(PreprocessorDefinitions) + ../../../../;../../../../contrib/boost/;../../../../contrib/windows/include/;../../../../contrib/googletest/googletest/include/;%(AdditionalIncludeDirectories) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.cc b/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.cc new file mode 100644 index 000000000..19bf1e20b --- /dev/null +++ b/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.cc @@ -0,0 +1,95 @@ +#include +#include + +#include + + +#include + +TEST(ZlibStreamUnitTest, testZlibOutputStream) +{ + muduo::net::Buffer output; + { + muduo::net::ZlibOutputStream stream(&output); + EXPECT_EQ(output.readableBytes(), (size_t)0); + } + EXPECT_EQ(output.readableBytes(), (size_t)8); +} + +TEST(ZlibStreamUnitTest, testZlibOutputStream1) +{ + muduo::net::Buffer output; + muduo::net::ZlibOutputStream stream(&output); + EXPECT_EQ(stream.zlibErrorCode(), Z_OK); + stream.finish(); + EXPECT_EQ(stream.zlibErrorCode(), Z_STREAM_END); +} + +TEST(ZlibStreamUnitTest, testZlibOutputStream2) +{ + muduo::net::Buffer output; + muduo::net::ZlibOutputStream stream(&output); + EXPECT_EQ(stream.zlibErrorCode(), Z_OK); + EXPECT_TRUE(stream.write("01234567890123456789012345678901234567890123456789")); + stream.finish(); + // printf("%zd\n", output.readableBytes()); + EXPECT_EQ(stream.zlibErrorCode(), Z_STREAM_END); +} + +TEST(ZlibStreamUnitTest, testZlibOutputStream3) +{ + muduo::net::Buffer output; + muduo::net::ZlibOutputStream stream(&output); + EXPECT_EQ(stream.zlibErrorCode(), Z_OK); + for (int i = 0; i < 1024 * 1024; ++i) + { + EXPECT_TRUE(stream.write("01234567890123456789012345678901234567890123456789")); + } + stream.finish(); + // printf("total %zd\n", output.readableBytes()); + EXPECT_EQ(stream.zlibErrorCode(), Z_STREAM_END); +} + +TEST(ZlibStreamUnitTest, testZlibOutputStream4) +{ + muduo::net::Buffer output; + muduo::net::ZlibOutputStream stream(&output); + EXPECT_EQ(stream.zlibErrorCode(), Z_OK); + muduo::string input; + for (int i = 0; i < 32768; ++i) + { + input += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-"[rand() % 64]; + } + + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(stream.write(input)); + } + stream.finish(); + // printf("total %zd\n", output.readableBytes()); + EXPECT_EQ(stream.zlibErrorCode(), Z_STREAM_END); +} + +TEST(ZlibStreamUnitTest, testZlibOutputStream5) +{ + muduo::net::Buffer output; + muduo::net::ZlibOutputStream stream(&output); + EXPECT_EQ(stream.zlibErrorCode(), Z_OK); + muduo::string input(1024 * 1024, '_'); + for (int i = 0; i < 64; ++i) + { + EXPECT_TRUE(stream.write(input)); + } + printf("bufsiz %d\n", stream.internalOutputBufferSize()); + LOG_INFO << "total_in " << stream.inputBytes(); + LOG_INFO << "total_out " << stream.outputBytes(); + stream.finish(); + printf("total %zd\n", output.readableBytes()); + EXPECT_EQ(stream.zlibErrorCode(), Z_STREAM_END); +} +int main(int argc, char **argv) +{ + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.sln b/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.sln new file mode 100644 index 000000000..c8aed466d --- /dev/null +++ b/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZlibStreamUnitTest", "ZlibStreamUnitTest.vcxproj", "{E12144A9-1052-4FFC-8851-9F6C86B28F8C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Debug|x64.ActiveCfg = Debug|x64 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Debug|x64.Build.0 = Debug|x64 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Debug|x86.ActiveCfg = Debug|Win32 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Debug|x86.Build.0 = Debug|Win32 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Release|x64.ActiveCfg = Release|x64 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Release|x64.Build.0 = Release|x64 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Release|x86.ActiveCfg = Release|Win32 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.vcxproj.filters b/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.vcxproj.filters new file mode 100644 index 000000000..cde442329 --- /dev/null +++ b/muduo/net/tests/zlibstreamunittest/ZlibStreamUnitTest.vcxproj.filters @@ -0,0 +1,61 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/muduo/net/tests/zlibstreamunittest/zlibstreamunittest.vcxproj b/muduo/net/tests/zlibstreamunittest/zlibstreamunittest.vcxproj new file mode 100644 index 000000000..0f041d9ec --- /dev/null +++ b/muduo/net/tests/zlibstreamunittest/zlibstreamunittest.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {E12144A9-1052-4FFC-8851-9F6C86B28F8C} + Win32Proj + ZlibStreamUnitTest + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;../../../../external/zlib;%(AdditionalIncludeDirectories) + + MultiThreadedDebug + + + Console + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtestd.lib;../../../../lib/zlibd.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ../../../../;../../../../external/googletest/googletest/include;../../../../external/boost;../../../../external/zlib;%(AdditionalIncludeDirectories) + + MultiThreaded + + + Console + true + true + ../../../../lib;%(AdditionalLibraryDirectories) + ../../../../lib/gtest.lib;../../../../lib/zlib.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/runtest.sh b/runtest.sh new file mode 100644 index 000000000..123e48c98 --- /dev/null +++ b/runtest.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +#./autogen.sh + +root_dir=$(pwd) + +cd build +for f in *; + do { ./$f; }; + done + +cd $root_dir diff --git a/vcxproj2mk.sh b/vcxproj2mk.sh new file mode 100644 index 000000000..252148647 --- /dev/null +++ b/vcxproj2mk.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +#http://unix.stackexchange.com/questions/45781/shell-script-fails-syntax-error-unexpected + +vs_file=$1 +build_model=$2 +build_dir=$3 +build_alias=$4 +lib_dir= + + +function ToGenOutPutLibDir() +{ + lib_dir=$(sed -n -e '//,//p' $vs_file | awk -F "<" '{print $2}' | awk -F ">" '{print $2}') +} + +if egrep -q "OutDir" $vs_file; then + ToGenOutPutLibDir WIN32 + if [ -z $lib_dir ]; then + ToGenOutPutLibDir x64 + fi + if [ $lib_dir ]; then + build_alias=$lib_dir"/"$(basename $build_alias); + fi +fi + +echo 'SOURCES = \' +egrep "ClCompile Include=" $vs_file | awk -F"\"" '{print $2}' | sed 's/\\/\//g' | sed 's/^\.\// \.\.\//g' > tmp +cat tmp | sed 's/$/\ \\/g' +rm -rf tmp + +echo + + +#include file , preprocessor ToBackSlash +function ToGen() +{ + sed -n -e '//,/<''$2''>/p' $1 | + egrep --max-count=1 $2| + awk -F"%" '{print $1}' | + awk -F">" '{print $2}' | + sed 's/\\/\//g' | + sed 's/^\.\// \.\.\//g' | + sed 's/.$//' | + sed 's/;/ \\\n/g' | + sed 's/^/ '$3' /g' | + sed 's/WIN32/__linux__/g' | + sed 's/__WINDOWS__/__linux__/g' + echo +} + +#include file , preprocessor ToBackSlash +function ToGenSo() +{ + sed -n -e '//,/<''$2''>/p' $1 | + egrep --max-count=1 $2| + awk -F">" '{print $2}' | + awk -F"<" '{print $1}' | + awk -F"%" '{print $1}' | + sed 's/;$//g' | + sed 's/\\/\//g' | + sed 's/;/ \n/g' | + sed -r 's/.*\///g'| + sed 's/.lib//g'| + sed 's/^/'$3'/g' | + sed 's/$/\\/g' + echo +} + +function ToGenLibDir() +{ + sed -n -e '//,/<''$2''>/p' $1 | + egrep --max-count=1 $2| + awk -F">" '{print $2}' | + awk -F"<" '{print $1}' | + sed 's/.$//' | + sed 's/;/ \\\n/g' | + sed 's/\\/\//g' | + sed 's/^\.\// \.\.\//g' | + sed 's/^/'$3'/g' + echo +} + + +echo 'INCLUDES = \' +ToGen $vs_file AdditionalIncludeDirectories "-I" $build_model +echo 'DEFINEDS = \' +ToGen $vs_file PreprocessorDefinitions "-D" $build_model + + +echo 'DEPEND_LIBS = \' + +if egrep -q "StaticLibrary" $vs_file; then + ToGen $vs_file AdditionalDependencies "" $build_model + echo + echo lib=$build_alias".lib"; + echo .DEFAULT_GOAL := $build_alias".lib"; + bin_type="lib" +elif egrep -q "DynamicLibrary" $vs_file; then + ToGenSo $vs_file AdditionalDependencies "-l" $build_model + echo 'DEPEND_LIBS_DIR = \' + ToGenLibDir $vs_file AdditionalLibraryDirectories "-L" $build_model + echo + echo shared=$build_alias".so"; + echo .DEFAULT_GOAL := $build_alias".so"; + bin_type="shared" +elif egrep -q "Application" $vs_file; then + ToGen $vs_file AdditionalDependencies "" $build_model + echo + echo exe=$build_alias; + echo .DEFAULT_GOAL := $build_alias; + bin_type="exe" +fi + +if [ $build_model = "Debug" ]; then + echo 'CXXFLAGS += -g -pg -D NDEBUG' + echo +fi + +echo BUILD_DIR := $build_dir +