diff --git a/.gitignore b/.gitignore index 44bcb5db..12e1af62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,5 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# Biicode directory -bii -bin - umltest.inner.sh umltest.status +/build +/cmake +/.idea diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e769b2b7..fe851e2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,37 @@ language: cpp sudo: required +dist: trusty compiler: - gcc +- clang +os: +- linux +- osx +matrix: + allow_failures: + - os: osx + - compiler: clang +addons: + apt: + packages: + - libcrypto++-dev + - libfuse-dev install: -- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/update_gcc_version.sh - && chmod +x update_gcc_version.sh - && ./update_gcc_version.sh 4.8 - && rm update_gcc_version.sh -- sudo apt-get install libfuse-dev -# This is needed for packaging 7z distribution packages -- sudo apt-get install software-properties-common && sudo add-apt-repository ppa:george-edison55/precise-backports -y && sudo apt-get update -- sudo apt-get install cmake cmake-data rpm -# CryFS needs cmake >= 3.3, install it. +# Install boost +- wget -O boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.56.0/boost_1_56_0.tar.bz2/download +- tar -xf boost.tar.bz2 +- cd boost_1_56_0 +# TODO We should use clang as toolchain for building boost when clang is used for building our code +- ./bootstrap.sh --with-libraries=filesystem,thread,chrono +- sudo ./b2 -d0 install +- cd .. +- sudo rm -rf boost.tar.bz2 boost_1_56_0 +# Install run_with_fuse.sh +- mkdir cmake +- cd cmake +- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/run_with_fuse.sh +- chmod +x run_with_fuse.sh +# Install cmake >= 3.3 - wget --no-check-certificate https://cmake.org/files/v3.3/cmake-3.3.2-Linux-x86_64.tar.gz && tar -xf cmake-3.3.2-Linux-x86_64.tar.gz && sudo cp -R cmake-3.3.2-Linux-x86_64/* /usr @@ -19,35 +39,14 @@ install: - cmake --version # Use /dev/urandom when /dev/random is accessed, because travis doesn't have enough entropy - sudo cp -a /dev/urandom /dev/random -before_script: -- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/setup_biicode_project.sh - && chmod +x setup_biicode_project.sh - && ./setup_biicode_project.sh - && rm setup_biicode_project.sh script: -#The configure line is needed as a workaround for the following link, otherwise we wouldn't need "bii configure" at all because "bii build" calls it: http://forum.biicode.com/t/error-could-not-find-the-following-static-boost-libraries-boost-thread/374 -- bii cpp:configure || bii cpp:configure -# Build cryfs executable -- bii cpp:build -- -j2 -# Build and run test cases -- bii cpp:build --target messmer_cryfs_test_main -- -j2 -- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/run_with_fuse.sh - && chmod +x run_with_fuse.sh - && ./run_with_fuse.sh "./bin/messmer_cryfs_test_main" - && rm run_with_fuse.sh -# Make distribution packages -- bii clean -- bii cpp:configure -D CMAKE_BUILD_TYPE=Release -- bii build -- -j2 -- cd bii/build/messmer_cryfs && make package && cd ../../.. -after_success: -- bii user ${BII_USERNAME} -p ${BII_PASSWORD} -- bii publish -#deploy: -# provider: biicode -# user: ${BII_USERNAME} -# password: -# secure: ${BII_PASSWORD} -# on: -# branch: develop - +- cmake .. +- make -j2 +- make package -j2 +- ./test/cpp-utils/cpp-utils-test +- ./run_with_fuse.sh ./test/fspp/fspp-test +- ./test/parallelaccessstore/parallelaccessstore-test +- ./test/blockstore/blockstore-test +- ./test/blobstore/blobstore-test +after_script: +- rm run_with_fuse.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index c66790b2..86348775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,26 +1,15 @@ # Earlier cmake versions generate .deb packages for which the package manager says they're bad quality # and asks the user whether they really want to install it. Cmake 3.3 fixes this. -CMAKE_MINIMUM_REQUIRED(VERSION 3.3) +cmake_minimum_required(VERSION 3.3) -INCLUDE(messmer/cmake/tools) -INCLUDE(messmer/gitversion/cmake) +include(utils.cmake) -SETUP_GOOGLETEST() +require_gcc_version(4.8) -# Actually create targets: EXEcutables and libraries. -ADD_BII_TARGETS() - -ACTIVATE_CPP14() - -ADD_BOOST(program_options chrono) - -ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) - -GIT_VERSION_INIT() - -ENABLE_STYLE_WARNINGS() - -SET_TARGET_PROPERTIES(${BII_src_main_TARGET} PROPERTIES OUTPUT_NAME cryfs) +add_subdirectory(vendor) +add_subdirectory(gitversion) +add_subdirectory(src) +add_subdirectory(test) # Fix debfiles permissions. Unfortunately, git doesn't store file permissions. # When installing the .deb package and these files have the wrong permissions, the package manager complains. @@ -64,88 +53,3 @@ SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debfiles/pos INCLUDE(CPack) -# You can safely delete lines from here... - -############################################################################### -# REFERENCE # -############################################################################### -# -# This CMakeLists.txt file helps defining your block building and compiling -# To learn more about the CMake use with biicode, visit http://docs.biicode.com/c++.html -# -# ---------------------------------------------------- -# NEW FEATURE! Include cmake files from remote blocks: -# ----------------------------------------------------- -# Now you can handle cmake dependencies alike you do with c/c++: -# -# INCLUDE(user/block/myrecipe) # include myrecipe.cmake from remote user/block -# -# > EXAMPLE: Include our recipes and activate C++11 in your block (http://www.biicode.com/biicode/cmake) -# -# INCLUDE(biicode/cmake/tools) # Include tools.cmake file from "cmake" block from the "biicode" user -# ACTIVATE_CPP11(INTERFACE ${BII_BLOCK_TARGET}) -# -# Remember to run "bii find" to download out cmake tools file -# -# --------------------- -# INIT_BIICODE_BLOCK() -# --------------------- -# This function creates several helper variables as ${BII_BLOCK_NAME} and ${BII_BLOCK_USER} -# Also it loads variables from the cmake/bii_user_block_vars.cmake -# ${BII_LIB_SRC} File list to create the library -# ${BII_LIB_TYPE} Empty (default, STATIC most casess) STATIC or SHARED -# ${BII_LIB_DEPS} Dependencies to other libraries (user2_block2, user3_blockX) -# ${BII_LIB_SYSTEM_HEADERS} System linking requirements as windows.h, pthread.h, etc -# -# You can use or modify them here, for example, to add or remove files from targets based on OS -# Or use typical cmake configurations done BEFORE defining targets. Examples: -# ADD_DEFINITIONS(-DFOO) -# FIND_PACKAGE(OpenGL QUIET) -# You can add INCLUDE_DIRECTORIES here too -# -# --------------------- -# ADD_BIICODE_TARGETS() -# --------------------- -# -# This function creates the following variables: -# ${BII_BLOCK_TARGET} Interface (no files) target for convenient configuration of all -# targets in this block, as the rest of targets always depend on it -# has name in the form "user_block_interface" -# ${BII_LIB_TARGET} Target library name, usually in the form "user_block". May not exist -# if BII_LIB_SRC is empty -# ${BII_BLOCK_TARGETS} List of all targets defined in this block -# ${BII_BLOCK_EXES} List of executables targets defined in this block -# ${BII_exe_name_TARGET}: Executable target (e.g. ${BII_main_TARGET}. You can also use -# directly the name of the executable target (e.g. user_block_main) -# -# > EXAMPLE: Add include directories to all targets of this block -# -# TARGET_INCLUDE_DIRECTORIES(${BII_BLOCK_TARGET} INTERFACE myincludedir) -# -# You can add private include directories to the Lib (if existing) -# -# > EXAMPLE: Link with pthread: -# -# TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE pthread) -# or link against library: -# TARGET_LINK_LIBRARIES(${BII_LIB_TARGET} PUBLIC pthread) -# or directly use the library target name: -# TARGET_LINK_LIBRARIES(user_block PUBLIC pthread) -# -# NOTE: This can be also done adding pthread to ${BII_LIB_DEPS} -# BEFORE calling ADD_BIICODE_TARGETS() -# -# > EXAMPLE: how to activate C++11 -# -# IF(APPLE) -# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11 -stdlib=libc++") -# ELSEIF (WIN32 OR UNIX) -# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11") -# ENDIF(APPLE) -# -# > EXAMPLE: Set properties to target -# -# SET_TARGET_PROPERTIES(${BII_BLOCK_TARGET} PROPERTIES COMPILE_DEFINITIONS "IOV_MAX=255") -# - - diff --git a/CMakeLists.txt~ b/CMakeLists.txt~ new file mode 100644 index 00000000..3a0c8a4a --- /dev/null +++ b/CMakeLists.txt~ @@ -0,0 +1,165 @@ +<<<<<<< HEAD +# Earlier cmake versions generate .deb packages for which the package manager says they're bad quality +# and asks the user whether they really want to install it. Cmake 3.3 fixes this. +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) + +INCLUDE(messmer/cmake/tools) +INCLUDE(messmer/gitversion/cmake) + +SETUP_GOOGLETEST() + +# Actually create targets: EXEcutables and libraries. +ADD_BII_TARGETS() + +ACTIVATE_CPP14() + +ADD_BOOST(program_options chrono) + +ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) + +GIT_VERSION_INIT() + +ENABLE_STYLE_WARNINGS() + +SET_TARGET_PROPERTIES(${BII_src_main_TARGET} PROPERTIES OUTPUT_NAME cryfs) + +# Fix debfiles permissions. Unfortunately, git doesn't store file permissions. +# When installing the .deb package and these files have the wrong permissions, the package manager complains. +EXECUTE_PROCESS(COMMAND /bin/bash -c "chmod 0755 ${CMAKE_CURRENT_SOURCE_DIR}/debfiles/*") + +INSTALL(TARGETS ${BII_src_main_TARGET} + DESTINATION bin + CONFIGURATIONS Release) + +SET(CPACK_GENERATOR TGZ DEB RPM) +SET(CPACK_PACKAGE_NAME "cryfs") +SET(CPACK_PACKAGE_VERSION "${GITVERSION_VERSION_STRING}") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Encrypt your files and store them in the cloud.") +SET(CPACK_PACKAGE_DESCRIPTION "CryFS encrypts your files, so you can safely store them anywhere. It works well together with cloud services like Dropbox, iCloud, OneDrive and others.") +SET(CPACK_PACKAGE_CONTACT "Sebastian Messmer ") +SET(CPACK_PACKAGE_VENDOR "Sebastian Messmer") +SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +SET(CPACK_PACKAGE_INSTALL_DIRECTORY "CMake ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}") +IF(WIN32 AND NOT UNIX) + # There is a bug in NSI that does not handle full unix paths properly. Make + # sure there is at least one set of four (4) backlasshes. + #SET(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") + #SET(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\cryfs.exe") + #SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} CryFS") + #SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.cryfs.org") + #SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.cryfs.org") + #SET(CPACK_NSIS_CONTACT "messmer@cryfs.org") + #SET(CPACK_NSIS_MODIFY_PATH ON) +ELSE(WIN32 AND NOT UNIX) + SET(CPACK_STRIP_FILES "bin/cryfs") + SET(CPACK_SOURCE_STRIP_FILES "") +ENDIF(WIN32 AND NOT UNIX) +SET(CPACK_PACKAGE_EXECUTABLES "cryfs" "CryFS") +SET(CPACK_DEBIAN_PACKAGE_SECTION "utils") +SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +SET(CPACK_DEBIAN_PACKAGE_RECOMMENDS "fuse") + +SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.cryfs.org") +SET(CPACK_RPM_PACKAGE_LICENSE "LGPLv3") +SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postrm") + +INCLUDE(CPack) + +# You can safely delete lines from here... + +############################################################################### +# REFERENCE # +############################################################################### +# +# This CMakeLists.txt file helps defining your block building and compiling +# To learn more about the CMake use with biicode, visit http://docs.biicode.com/c++.html +# +# ---------------------------------------------------- +# NEW FEATURE! Include cmake files from remote blocks: +# ----------------------------------------------------- +# Now you can handle cmake dependencies alike you do with c/c++: +# +# INCLUDE(user/block/myrecipe) # include myrecipe.cmake from remote user/block +# +# > EXAMPLE: Include our recipes and activate C++11 in your block (http://www.biicode.com/biicode/cmake) +# +# INCLUDE(biicode/cmake/tools) # Include tools.cmake file from "cmake" block from the "biicode" user +# ACTIVATE_CPP11(INTERFACE ${BII_BLOCK_TARGET}) +# +# Remember to run "bii find" to download out cmake tools file +# +# --------------------- +# INIT_BIICODE_BLOCK() +# --------------------- +# This function creates several helper variables as ${BII_BLOCK_NAME} and ${BII_BLOCK_USER} +# Also it loads variables from the cmake/bii_user_block_vars.cmake +# ${BII_LIB_SRC} File list to create the library +# ${BII_LIB_TYPE} Empty (default, STATIC most casess) STATIC or SHARED +# ${BII_LIB_DEPS} Dependencies to other libraries (user2_block2, user3_blockX) +# ${BII_LIB_SYSTEM_HEADERS} System linking requirements as windows.h, pthread.h, etc +# +# You can use or modify them here, for example, to add or remove files from targets based on OS +# Or use typical cmake configurations done BEFORE defining targets. Examples: +# ADD_DEFINITIONS(-DFOO) +# FIND_PACKAGE(OpenGL QUIET) +# You can add INCLUDE_DIRECTORIES here too +# +# --------------------- +# ADD_BIICODE_TARGETS() +# --------------------- +# +# This function creates the following variables: +# ${BII_BLOCK_TARGET} Interface (no files) target for convenient configuration of all +# targets in this block, as the rest of targets always depend on it +# has name in the form "user_block_interface" +# ${BII_LIB_TARGET} Target library name, usually in the form "user_block". May not exist +# if BII_LIB_SRC is empty +# ${BII_BLOCK_TARGETS} List of all targets defined in this block +# ${BII_BLOCK_EXES} List of executables targets defined in this block +# ${BII_exe_name_TARGET}: Executable target (e.g. ${BII_main_TARGET}. You can also use +# directly the name of the executable target (e.g. user_block_main) +# +# > EXAMPLE: Add include directories to all targets of this block +# +# TARGET_INCLUDE_DIRECTORIES(${BII_BLOCK_TARGET} INTERFACE myincludedir) +# +# You can add private include directories to the Lib (if existing) +# +# > EXAMPLE: Link with pthread: +# +# TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE pthread) +# or link against library: +# TARGET_LINK_LIBRARIES(${BII_LIB_TARGET} PUBLIC pthread) +# or directly use the library target name: +# TARGET_LINK_LIBRARIES(user_block PUBLIC pthread) +# +# NOTE: This can be also done adding pthread to ${BII_LIB_DEPS} +# BEFORE calling ADD_BIICODE_TARGETS() +# +# > EXAMPLE: how to activate C++11 +# +# IF(APPLE) +# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11 -stdlib=libc++") +# ELSEIF (WIN32 OR UNIX) +# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11") +# ENDIF(APPLE) +# +# > EXAMPLE: Set properties to target +# +# SET_TARGET_PROPERTIES(${BII_BLOCK_TARGET} PROPERTIES COMPILE_DEFINITIONS "IOV_MAX=255") +# + + +======= +# Earlier cmake versions generate .deb packages for which the package manager says they're bad quality +# and asks the user whether they really want to install it. Cmake 3.3 fixes this. +cmake_minimum_required(VERSION 3.3) + +include(utils.cmake) + +require_gcc_version(4.8) + +add_subdirectory(vendor) +add_subdirectory(src) +add_subdirectory(test) +>>>>>>> cpu/cmake diff --git a/README.md b/README.md index d8430e93..d192164e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Requirements - GCC version >= 4.8 or Clang (TODO which minimal version?) - CMake version >= 3.3 - libcurl4 (including development headers) - - Boost libraries filesystem, system, chrono, thread in version >= 1.56 + - Boost libraries filesystem, system, chrono, program_options, thread in version >= 1.56 - Crypto++ >= 5.6.3 (TODO Lower minimal version possible?) - libFUSE >= 2.8.6 (including development headers) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..5b5b648f --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_subdirectory(cpp-utils) +add_subdirectory(fspp) +add_subdirectory(blockstore) +add_subdirectory(blobstore) +add_subdirectory(cryfs) \ No newline at end of file diff --git a/src/blobstore/CMakeLists.txt b/src/blobstore/CMakeLists.txt new file mode 100644 index 00000000..141e33be --- /dev/null +++ b/src/blobstore/CMakeLists.txt @@ -0,0 +1,29 @@ +project (blobstore) + +set(SOURCES + implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.cpp + implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.cpp + implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.cpp + implementations/onblocks/utils/Math.cpp + implementations/onblocks/BlobStoreOnBlocks.cpp + implementations/onblocks/datanodestore/DataNode.cpp + implementations/onblocks/datanodestore/DataLeafNode.cpp + implementations/onblocks/datanodestore/DataInnerNode.cpp + implementations/onblocks/datanodestore/DataNodeStore.cpp + implementations/onblocks/datatreestore/impl/algorithms.cpp + implementations/onblocks/datatreestore/DataTree.cpp + implementations/onblocks/datatreestore/DataTreeStore.cpp + implementations/onblocks/BlobOnBlocks.cpp +) + +add_library(${PROJECT_NAME} STATIC ${SOURCES}) + +# This is needed by boost thread +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries(${PROJECT_NAME} PRIVATE rt) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") +target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils blockstore) + +target_add_boost(${PROJECT_NAME} filesystem system thread) +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp b/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp new file mode 100644 index 00000000..174183ca --- /dev/null +++ b/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp @@ -0,0 +1,109 @@ +#include "parallelaccessdatatreestore/DataTreeRef.h" +#include "BlobOnBlocks.h" + +#include "datanodestore/DataLeafNode.h" +#include "utils/Math.h" +#include +#include + +using std::function; +using cpputils::unique_ref; +using cpputils::Data; +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datanodestore::DataNodeLayout; +using blockstore::Key; + +namespace blobstore { +namespace onblocks { + +using parallelaccessdatatreestore::DataTreeRef; + +BlobOnBlocks::BlobOnBlocks(unique_ref datatree) +: _datatree(std::move(datatree)), _sizeCache(boost::none) { +} + +BlobOnBlocks::~BlobOnBlocks() { +} + +uint64_t BlobOnBlocks::size() const { + if (_sizeCache == boost::none) { + _sizeCache = _datatree->numStoredBytes(); + } + return *_sizeCache; +} + +void BlobOnBlocks::resize(uint64_t numBytes) { + _datatree->resizeNumBytes(numBytes); + _sizeCache = numBytes; +} + +void BlobOnBlocks::traverseLeaves(uint64_t beginByte, uint64_t sizeBytes, function func) const { + uint64_t endByte = beginByte + sizeBytes; + uint32_t firstLeaf = beginByte / _datatree->maxBytesPerLeaf(); + uint32_t endLeaf = utils::ceilDivision(endByte, _datatree->maxBytesPerLeaf()); + bool writingOutside = size() < endByte; // TODO Calling size() is slow because it has to traverse the tree + _datatree->traverseLeaves(firstLeaf, endLeaf, [&func, beginByte, endByte, endLeaf, writingOutside](DataLeafNode *leaf, uint32_t leafIndex) { + uint64_t indexOfFirstLeafByte = leafIndex * leaf->maxStoreableBytes(); + uint32_t dataBegin = utils::maxZeroSubtraction(beginByte, indexOfFirstLeafByte); + uint32_t dataEnd = std::min(leaf->maxStoreableBytes(), endByte - indexOfFirstLeafByte); + if (leafIndex == endLeaf-1 && writingOutside) { + // If we are traversing an area that didn't exist before, then the last leaf was just created with a wrong size. We have to fix it. + leaf->resize(dataEnd); + } + func(indexOfFirstLeafByte, leaf, dataBegin, dataEnd-dataBegin); + }); + if (writingOutside) { + ASSERT(_datatree->numStoredBytes() == endByte, "Writing didn't grow by the correct number of bytes"); + _sizeCache = endByte; + } +} + +Data BlobOnBlocks::readAll() const { + //TODO Querying size is inefficient. Is this possible without a call to size()? + uint64_t count = size(); + Data result(count); + _read(result.data(), 0, count); + return result; +} + +void BlobOnBlocks::read(void *target, uint64_t offset, uint64_t count) const { + ASSERT(offset <= size() && offset + count <= size(), "BlobOnBlocks::read() read outside blob. Use BlobOnBlocks::tryRead() if this should be allowed."); + uint64_t read = tryRead(target, offset, count); + ASSERT(read == count, "BlobOnBlocks::read() couldn't read all requested bytes. Use BlobOnBlocks::tryRead() if this should be allowed."); +} + +uint64_t BlobOnBlocks::tryRead(void *target, uint64_t offset, uint64_t count) const { + //TODO Quite inefficient to call size() here, because that has to traverse the tree + uint64_t realCount = std::max(UINT64_C(0), std::min(count, size()-offset)); + _read(target, offset, realCount); + return realCount; +} + +void BlobOnBlocks::_read(void *target, uint64_t offset, uint64_t count) const { + traverseLeaves(offset, count, [target, offset] (uint64_t indexOfFirstLeafByte, const DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) { + //TODO Simplify formula, make it easier to understand + leaf->read((uint8_t*)target + indexOfFirstLeafByte - offset + leafDataOffset, leafDataOffset, leafDataSize); + }); +} + +void BlobOnBlocks::write(const void *source, uint64_t offset, uint64_t count) { + traverseLeaves(offset, count, [source, offset] (uint64_t indexOfFirstLeafByte, DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) { + //TODO Simplify formula, make it easier to understand + leaf->write((uint8_t*)source + indexOfFirstLeafByte - offset + leafDataOffset, leafDataOffset, leafDataSize); + }); +} + +void BlobOnBlocks::flush() { + _datatree->flush(); +} + +const Key &BlobOnBlocks::key() const { + return _datatree->key(); +} + +unique_ref BlobOnBlocks::releaseTree() { + return std::move(_datatree); +} + +} +} diff --git a/src/blobstore/implementations/onblocks/BlobOnBlocks.h b/src/blobstore/implementations/onblocks/BlobOnBlocks.h new file mode 100644 index 00000000..7d73db39 --- /dev/null +++ b/src/blobstore/implementations/onblocks/BlobOnBlocks.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_BLOBONBLOCKS_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_BLOBONBLOCKS_H_ + +#include "../../interface/Blob.h" + +#include +#include + +namespace blobstore { +namespace onblocks { +namespace datanodestore { +class DataLeafNode; +} +namespace parallelaccessdatatreestore { +class DataTreeRef; +} + +class BlobOnBlocks final: public Blob { +public: + BlobOnBlocks(cpputils::unique_ref datatree); + ~BlobOnBlocks(); + + const blockstore::Key &key() const override; + + uint64_t size() const override; + void resize(uint64_t numBytes) override; + + cpputils::Data readAll() const override; + void read(void *target, uint64_t offset, uint64_t size) const override; + uint64_t tryRead(void *target, uint64_t offset, uint64_t size) const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + + void flush() override; + + cpputils::unique_ref releaseTree(); + +private: + + void _read(void *target, uint64_t offset, uint64_t count) const; + void traverseLeaves(uint64_t offsetBytes, uint64_t sizeBytes, std::function) const; + + cpputils::unique_ref _datatree; + mutable boost::optional _sizeCache; + + DISALLOW_COPY_AND_ASSIGN(BlobOnBlocks); +}; + +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/BlobStoreOnBlocks.cpp b/src/blobstore/implementations/onblocks/BlobStoreOnBlocks.cpp new file mode 100644 index 00000000..588a94cf --- /dev/null +++ b/src/blobstore/implementations/onblocks/BlobStoreOnBlocks.cpp @@ -0,0 +1,56 @@ +#include "parallelaccessdatatreestore/DataTreeRef.h" +#include "parallelaccessdatatreestore/ParallelAccessDataTreeStore.h" +#include +#include "datanodestore/DataLeafNode.h" +#include "datanodestore/DataNodeStore.h" +#include "datatreestore/DataTreeStore.h" +#include "datatreestore/DataTree.h" +#include "BlobStoreOnBlocks.h" +#include "BlobOnBlocks.h" +#include +#include + +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +using blockstore::BlockStore; +using blockstore::parallelaccess::ParallelAccessBlockStore; +using blockstore::Key; +using cpputils::dynamic_pointer_move; +using boost::optional; +using boost::none; + +namespace blobstore { +namespace onblocks { + +using datanodestore::DataNodeStore; +using datatreestore::DataTreeStore; +using parallelaccessdatatreestore::ParallelAccessDataTreeStore; + +BlobStoreOnBlocks::BlobStoreOnBlocks(unique_ref blockStore, uint32_t blocksizeBytes) +: _dataTreeStore(make_unique_ref(make_unique_ref(make_unique_ref(make_unique_ref(std::move(blockStore)), blocksizeBytes)))) { +} + +BlobStoreOnBlocks::~BlobStoreOnBlocks() { +} + +unique_ref BlobStoreOnBlocks::create() { + return make_unique_ref(_dataTreeStore->createNewTree()); +} + +optional> BlobStoreOnBlocks::load(const Key &key) { + auto tree = _dataTreeStore->load(key); + if (tree == none) { + return none; + } + return optional>(make_unique_ref(std::move(*tree))); +} + +void BlobStoreOnBlocks::remove(unique_ref blob) { + auto _blob = dynamic_pointer_move(blob); + ASSERT(_blob != none, "Passed Blob in BlobStoreOnBlocks::remove() is not a BlobOnBlocks."); + _dataTreeStore->remove((*_blob)->releaseTree()); +} + +} +} diff --git a/src/blobstore/implementations/onblocks/BlobStoreOnBlocks.h b/src/blobstore/implementations/onblocks/BlobStoreOnBlocks.h new file mode 100644 index 00000000..0dcea458 --- /dev/null +++ b/src/blobstore/implementations/onblocks/BlobStoreOnBlocks.h @@ -0,0 +1,35 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_BLOCKED_BLOBSTOREONBLOCKS_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_BLOCKED_BLOBSTOREONBLOCKS_H_ + +#include "../../interface/BlobStore.h" +#include + +namespace blobstore { +namespace onblocks { +namespace parallelaccessdatatreestore { +class ParallelAccessDataTreeStore; +} + +//TODO Make blobstore able to cope with incomplete data (some blocks missing, because they're not synchronized yet) and write test cases for that + +class BlobStoreOnBlocks final: public BlobStore { +public: + BlobStoreOnBlocks(cpputils::unique_ref blockStore, uint32_t blocksizeBytes); + ~BlobStoreOnBlocks(); + + cpputils::unique_ref create() override; + boost::optional> load(const blockstore::Key &key) override; + + void remove(cpputils::unique_ref blob) override; + +private: + cpputils::unique_ref _dataTreeStore; + + DISALLOW_COPY_AND_ASSIGN(BlobStoreOnBlocks); +}; + +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode.cpp b/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode.cpp new file mode 100644 index 00000000..dda86ece --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode.cpp @@ -0,0 +1,87 @@ +#include "DataInnerNode.h" +#include "DataNodeStore.h" +#include + +using blockstore::Block; +using cpputils::Data; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using blockstore::Key; + +namespace blobstore { +namespace onblocks { +namespace datanodestore { + +DataInnerNode::DataInnerNode(DataNodeView view) +: DataNode(std::move(view)) { + ASSERT(depth() > 0, "Inner node can't have depth 0. Is this a leaf maybe?"); +} + +DataInnerNode::~DataInnerNode() { +} + +unique_ref DataInnerNode::InitializeNewNode(unique_ref block, const DataNode &first_child) { + DataNodeView node(std::move(block)); + node.setDepth(first_child.depth() + 1); + node.setSize(1); + auto result = make_unique_ref(std::move(node)); + result->ChildrenBegin()->setKey(first_child.key()); + return result; +} + +uint32_t DataInnerNode::numChildren() const { + return node().Size(); +} + +DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() { + return const_cast(const_cast(this)->ChildrenBegin()); +} + +const DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() const { + return node().DataBegin(); +} + +DataInnerNode::ChildEntry *DataInnerNode::ChildrenEnd() { + return const_cast(const_cast(this)->ChildrenEnd()); +} + +const DataInnerNode::ChildEntry *DataInnerNode::ChildrenEnd() const { + return ChildrenBegin() + node().Size(); +} + +DataInnerNode::ChildEntry *DataInnerNode::LastChild() { + return const_cast(const_cast(this)->LastChild()); +} + +const DataInnerNode::ChildEntry *DataInnerNode::LastChild() const { + return getChild(numChildren()-1); +} + +DataInnerNode::ChildEntry *DataInnerNode::getChild(unsigned int index) { + return const_cast(const_cast(this)->getChild(index)); +} + +const DataInnerNode::ChildEntry *DataInnerNode::getChild(unsigned int index) const { + ASSERT(index < numChildren(), "Accessing child out of range"); + return ChildrenBegin()+index; +} + +void DataInnerNode::addChild(const DataNode &child) { + ASSERT(numChildren() < maxStoreableChildren(), "Adding more children than we can store"); + ASSERT(child.depth() == depth()-1, "The child that should be added has wrong depth"); + node().setSize(node().Size()+1); + LastChild()->setKey(child.key()); +} + +void DataInnerNode::removeLastChild() { + ASSERT(node().Size() > 1, "There is no child to remove"); + node().setSize(node().Size()-1); +} + +uint32_t DataInnerNode::maxStoreableChildren() const { + return node().layout().maxChildrenPerInnerNode(); +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode.h b/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode.h new file mode 100644 index 00000000..967d6746 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode.h @@ -0,0 +1,49 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATAINNERNODE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATAINNERNODE_H_ + +#include "DataNode.h" +#include "DataInnerNode_ChildEntry.h" + +namespace blobstore { +namespace onblocks { +namespace datanodestore { + +class DataInnerNode final: public DataNode { +public: + static cpputils::unique_ref InitializeNewNode(cpputils::unique_ref block, const DataNode &first_child_key); + + DataInnerNode(DataNodeView block); + ~DataInnerNode(); + + using ChildEntry = DataInnerNode_ChildEntry; + + uint32_t maxStoreableChildren() const; + + ChildEntry *getChild(unsigned int index); + const ChildEntry *getChild(unsigned int index) const; + + uint32_t numChildren() const; + + void addChild(const DataNode &child_key); + + void removeLastChild(); + + ChildEntry *LastChild(); + const ChildEntry *LastChild() const; + +private: + + ChildEntry *ChildrenBegin(); + ChildEntry *ChildrenEnd(); + const ChildEntry *ChildrenBegin() const; + const ChildEntry *ChildrenEnd() const; + + DISALLOW_COPY_AND_ASSIGN(DataInnerNode); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode_ChildEntry.h b/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode_ChildEntry.h new file mode 100644 index 00000000..51e7afc1 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataInnerNode_ChildEntry.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATAINNERNODE_CHILDENTRY_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATAINNERNODE_CHILDENTRY_H_ + +#include + +namespace blobstore{ +namespace onblocks{ +namespace datanodestore{ + +struct DataInnerNode_ChildEntry final { +public: + blockstore::Key key() const { + return blockstore::Key::FromBinary(_keydata); + } +private: + void setKey(const blockstore::Key &key) { + key.ToBinary(_keydata); + } + friend class DataInnerNode; + uint8_t _keydata[blockstore::Key::BINARY_LENGTH]; + + DISALLOW_COPY_AND_ASSIGN(DataInnerNode_ChildEntry); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataLeafNode.cpp b/src/blobstore/implementations/onblocks/datanodestore/DataLeafNode.cpp new file mode 100644 index 00000000..7e3511ea --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataLeafNode.cpp @@ -0,0 +1,67 @@ +#include "DataLeafNode.h" +#include "DataInnerNode.h" +#include + +using blockstore::Block; +using cpputils::Data; +using blockstore::Key; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +namespace blobstore { +namespace onblocks { +namespace datanodestore { + +DataLeafNode::DataLeafNode(DataNodeView view) +: DataNode(std::move(view)) { + ASSERT(node().Depth() == 0, "Leaf node must have depth 0. Is it an inner node instead?"); + ASSERT(numBytes() <= maxStoreableBytes(), "Leaf says it stores more bytes than it has space for"); +} + +DataLeafNode::~DataLeafNode() { +} + +unique_ref DataLeafNode::InitializeNewNode(unique_ref block) { + DataNodeView node(std::move(block)); + node.setDepth(0); + node.setSize(0); + //fillDataWithZeroes(); not needed, because a newly created block will be zeroed out. DataLeafNodeTest.SpaceIsZeroFilledWhenGrowing ensures this. + return make_unique_ref(std::move(node)); +} + +void DataLeafNode::read(void *target, uint64_t offset, uint64_t size) const { + ASSERT(offset <= node().Size() && offset + size <= node().Size(), "Read out of valid area"); // Also check offset, because the addition could lead to overflows + std::memcpy(target, (uint8_t*)node().data() + offset, size); +} + +void DataLeafNode::write(const void *source, uint64_t offset, uint64_t size) { + ASSERT(offset <= node().Size() && offset + size <= node().Size(), "Write out of valid area"); // Also check offset, because the addition could lead to overflows + node().write(source, offset, size); +} + +uint32_t DataLeafNode::numBytes() const { + return node().Size(); +} + +void DataLeafNode::resize(uint32_t new_size) { + ASSERT(new_size <= maxStoreableBytes(), "Trying to resize to a size larger than the maximal size"); + uint32_t old_size = node().Size(); + if (new_size < old_size) { + fillDataWithZeroesFromTo(new_size, old_size); + } + node().setSize(new_size); +} + +void DataLeafNode::fillDataWithZeroesFromTo(off_t begin, off_t end) { + Data ZEROES(end-begin); + ZEROES.FillWithZeroes(); + node().write(ZEROES.data(), begin, end-begin); +} + +uint64_t DataLeafNode::maxStoreableBytes() const { + return node().layout().maxBytesPerLeaf(); +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataLeafNode.h b/src/blobstore/implementations/onblocks/datanodestore/DataLeafNode.h new file mode 100644 index 00000000..d707eb44 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataLeafNode.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATALEAFNODE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATALEAFNODE_H_ + +#include "DataNode.h" + +namespace blobstore { +namespace onblocks { +namespace datanodestore { +class DataInnerNode; + +class DataLeafNode final: public DataNode { +public: + static cpputils::unique_ref InitializeNewNode(cpputils::unique_ref block); + + DataLeafNode(DataNodeView block); + ~DataLeafNode(); + + //Returning uint64_t, because calculations handling this probably need to be done in 64bit to support >4GB blobs. + uint64_t maxStoreableBytes() const; + + void read(void *target, uint64_t offset, uint64_t size) const; + void write(const void *source, uint64_t offset, uint64_t size); + + uint32_t numBytes() const; + + void resize(uint32_t size); + +private: + void fillDataWithZeroesFromTo(off_t begin, off_t end); + + DISALLOW_COPY_AND_ASSIGN(DataLeafNode); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataNode.cpp b/src/blobstore/implementations/onblocks/datanodestore/DataNode.cpp new file mode 100644 index 00000000..3b409e4f --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataNode.cpp @@ -0,0 +1,54 @@ +#include "DataInnerNode.h" +#include "DataLeafNode.h" +#include "DataNode.h" +#include "DataNodeStore.h" +#include + +using blockstore::Block; +using blockstore::Key; + +using std::runtime_error; +using cpputils::unique_ref; + +namespace blobstore { +namespace onblocks { +namespace datanodestore { + +DataNode::DataNode(DataNodeView node) +: _node(std::move(node)) { +} + +DataNode::~DataNode() { +} + +DataNodeView &DataNode::node() { + return const_cast(const_cast(this)->node()); +} + +const DataNodeView &DataNode::node() const { + return _node; +} + +const Key &DataNode::key() const { + return _node.key(); +} + +uint8_t DataNode::depth() const { + return _node.Depth(); +} + +unique_ref DataNode::convertToNewInnerNode(unique_ref node, const DataNode &first_child) { + Key key = node->key(); + auto block = node->_node.releaseBlock(); + blockstore::utils::fillWithZeroes(block.get()); + + return DataInnerNode::InitializeNewNode(std::move(block), first_child); +} + +void DataNode::flush() const { + _node.flush(); +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataNode.h b/src/blobstore/implementations/onblocks/datanodestore/DataNode.h new file mode 100644 index 00000000..e37c5415 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataNode.h @@ -0,0 +1,44 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODE_H_ + +#include "DataNodeView.h" +#include + +namespace blobstore { +namespace onblocks { +namespace datanodestore { +class DataNodeStore; +class DataInnerNode; + +class DataNode { +public: + virtual ~DataNode(); + + const blockstore::Key &key() const; + + uint8_t depth() const; + + static cpputils::unique_ref convertToNewInnerNode(cpputils::unique_ref node, const DataNode &first_child); + + void flush() const; + +protected: + DataNode(DataNodeView block); + + DataNodeView &node(); + const DataNodeView &node() const; + friend class DataNodeStore; + +private: + DataNodeView _node; + + DISALLOW_COPY_AND_ASSIGN(DataNode); +}; + +} +} +} + + +#endif diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataNodeStore.cpp b/src/blobstore/implementations/onblocks/datanodestore/DataNodeStore.cpp new file mode 100644 index 00000000..6849ead3 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataNodeStore.cpp @@ -0,0 +1,113 @@ +#include "DataInnerNode.h" +#include "DataLeafNode.h" +#include "DataNodeStore.h" +#include +#include +#include +#include + +using blockstore::BlockStore; +using blockstore::Block; +using blockstore::Key; +using cpputils::Data; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::runtime_error; +using boost::optional; +using boost::none; + +namespace blobstore { +namespace onblocks { +namespace datanodestore { + +DataNodeStore::DataNodeStore(unique_ref blockstore, uint32_t blocksizeBytes) +: _blockstore(std::move(blockstore)), _layout(blocksizeBytes) { +} + +DataNodeStore::~DataNodeStore() { +} + +unique_ref DataNodeStore::load(unique_ref block) { + ASSERT(block->size() == _layout.blocksizeBytes(), "Loading block of wrong size"); + DataNodeView node(std::move(block)); + + if (node.Depth() == 0) { + return make_unique_ref(std::move(node)); + } else if (node.Depth() <= MAX_DEPTH) { + return make_unique_ref(std::move(node)); + } else { + throw runtime_error("Tree is to deep. Data corruption?"); + } +} + +unique_ref DataNodeStore::createNewInnerNode(const DataNode &first_child) { + ASSERT(first_child.node().layout().blocksizeBytes() == _layout.blocksizeBytes(), "Source node has wrong layout. Is it from the same DataNodeStore?"); + //TODO Initialize block and then create it in the blockstore - this is more efficient than creating it and then writing to it + auto block = _blockstore->create(Data(_layout.blocksizeBytes()).FillWithZeroes()); + return DataInnerNode::InitializeNewNode(std::move(block), first_child); +} + +unique_ref DataNodeStore::createNewLeafNode() { + //TODO Initialize block and then create it in the blockstore - this is more efficient than creating it and then writing to it + auto block = _blockstore->create(Data(_layout.blocksizeBytes()).FillWithZeroes()); + return DataLeafNode::InitializeNewNode(std::move(block)); +} + +optional> DataNodeStore::load(const Key &key) { + auto block = _blockstore->load(key); + if (block == none) { + return none; + } else { + return load(std::move(*block)); + } +} + +unique_ref DataNodeStore::createNewNodeAsCopyFrom(const DataNode &source) { + ASSERT(source.node().layout().blocksizeBytes() == _layout.blocksizeBytes(), "Source node has wrong layout. Is it from the same DataNodeStore?"); + auto newBlock = blockstore::utils::copyToNewBlock(_blockstore.get(), source.node().block()); + return load(std::move(newBlock)); +} + +unique_ref DataNodeStore::overwriteNodeWith(unique_ref target, const DataNode &source) { + ASSERT(target->node().layout().blocksizeBytes() == _layout.blocksizeBytes(), "Target node has wrong layout. Is it from the same DataNodeStore?"); + ASSERT(source.node().layout().blocksizeBytes() == _layout.blocksizeBytes(), "Source node has wrong layout. Is it from the same DataNodeStore?"); + Key key = target->key(); + { + auto targetBlock = target->node().releaseBlock(); + cpputils::destruct(std::move(target)); // Call destructor + blockstore::utils::copyTo(targetBlock.get(), source.node().block()); + } + auto loaded = load(key); + ASSERT(loaded != none, "Couldn't load the target node after overwriting it"); + return std::move(*loaded); +} + +void DataNodeStore::remove(unique_ref node) { + auto block = node->node().releaseBlock(); + cpputils::destruct(std::move(node)); // Call destructor + _blockstore->remove(std::move(block)); +} + +uint64_t DataNodeStore::numNodes() const { + return _blockstore->numBlocks(); +} + +void DataNodeStore::removeSubtree(unique_ref node) { + DataInnerNode *inner = dynamic_cast(node.get()); + if (inner != nullptr) { + for (uint32_t i = 0; i < inner->numChildren(); ++i) { + auto child = load(inner->getChild(i)->key()); + ASSERT(child != none, "Couldn't load child node"); + removeSubtree(std::move(*child)); + } + } + remove(std::move(node)); +} + +DataNodeLayout DataNodeStore::layout() const { + return _layout; +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataNodeStore.h b/src/blobstore/implementations/onblocks/datanodestore/DataNodeStore.h new file mode 100644 index 00000000..dc2bac44 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataNodeStore.h @@ -0,0 +1,60 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODESTORE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODESTORE_H_ + +#include +#include +#include "DataNodeView.h" +#include + +namespace blockstore{ +class Block; +class BlockStore; +} + +namespace blobstore { +namespace onblocks { +namespace datanodestore { +class DataNode; +class DataLeafNode; +class DataInnerNode; + +class DataNodeStore final { +public: + DataNodeStore(cpputils::unique_ref blockstore, uint32_t blocksizeBytes); + ~DataNodeStore(); + + static constexpr uint8_t MAX_DEPTH = 10; + + DataNodeLayout layout() const; + + boost::optional> load(const blockstore::Key &key); + + cpputils::unique_ref createNewLeafNode(); + cpputils::unique_ref createNewInnerNode(const DataNode &first_child); + + cpputils::unique_ref createNewNodeAsCopyFrom(const DataNode &source); + + cpputils::unique_ref overwriteNodeWith(cpputils::unique_ref target, const DataNode &source); + + void remove(cpputils::unique_ref node); + + void removeSubtree(cpputils::unique_ref node); + + uint64_t numNodes() const; + //TODO Test overwriteNodeWith(), createNodeAsCopyFrom(), removeSubtree() + +private: + cpputils::unique_ref load(cpputils::unique_ref block); + + cpputils::unique_ref _blockstore; + const DataNodeLayout _layout; + + DISALLOW_COPY_AND_ASSIGN(DataNodeStore); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datanodestore/DataNodeView.h b/src/blobstore/implementations/onblocks/datanodestore/DataNodeView.h new file mode 100644 index 00000000..7dfcb1c4 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datanodestore/DataNodeView.h @@ -0,0 +1,140 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODEVIEW_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODEVIEW_H_ + +#include +#include "../BlobStoreOnBlocks.h" +#include "DataInnerNode_ChildEntry.h" + +#include + +#include +#include +#include + +namespace blobstore { +namespace onblocks { +namespace datanodestore { + +//TODO Move DataNodeLayout into own file +class DataNodeLayout final { +public: + constexpr DataNodeLayout(uint32_t blocksizeBytes) + :_blocksizeBytes( + (HEADERSIZE_BYTES + 2*sizeof(DataInnerNode_ChildEntry) <= blocksizeBytes) + ? blocksizeBytes + : throw std::logic_error("Blocksize too small, not enough space to store two children in an inner node")) { + } + + //Total size of the header + static constexpr uint32_t HEADERSIZE_BYTES = 8; + //Where in the header is the depth field + static constexpr uint32_t DEPTH_OFFSET_BYTES = 0; + //Where in the header is the size field (for inner nodes: number of children, for leafs: content data size) + static constexpr uint32_t SIZE_OFFSET_BYTES = 4; + + + //Size of a block (header + data region) + constexpr uint32_t blocksizeBytes() const { + return _blocksizeBytes; + } + + //Number of bytes in the data region of a node + constexpr uint32_t datasizeBytes() const { + return _blocksizeBytes - HEADERSIZE_BYTES; + } + + //Maximum number of children an inner node can store + constexpr uint32_t maxChildrenPerInnerNode() const { + return datasizeBytes() / sizeof(DataInnerNode_ChildEntry); + } + + //Maximum number of bytes a leaf can store + //We are returning uint64_t here, because calculations involving maxBytesPerLeaf most probably should use 64bit integers to support blobs >4GB. + constexpr uint64_t maxBytesPerLeaf() const { + return datasizeBytes(); + } +private: + uint32_t _blocksizeBytes; +}; + +class DataNodeView final { +public: + DataNodeView(cpputils::unique_ref block): _block(std::move(block)) { + } + ~DataNodeView() {} + + DataNodeView(DataNodeView &&rhs) = default; + + uint8_t Depth() const { + return *((uint8_t*)_block->data()+DataNodeLayout::DEPTH_OFFSET_BYTES); + } + + void setDepth(uint8_t value) { + _block->write(&value, DataNodeLayout::DEPTH_OFFSET_BYTES, sizeof(value)); + } + + uint32_t Size() const { + return *(uint32_t*)((uint8_t*)_block->data()+DataNodeLayout::SIZE_OFFSET_BYTES); + } + + void setSize(uint32_t value) { + _block->write(&value, DataNodeLayout::SIZE_OFFSET_BYTES, sizeof(value)); + } + + const void *data() const { + return (uint8_t*)_block->data() + DataNodeLayout::HEADERSIZE_BYTES; + } + + void write(const void *source, uint64_t offset, uint64_t size) { + _block->write(source, offset + DataNodeLayout::HEADERSIZE_BYTES, size); + } + + template + const Entry *DataBegin() const { + return GetOffset(); + } + + template + const Entry *DataEnd() const { + const unsigned int NUM_ENTRIES = layout().datasizeBytes() / sizeof(Entry); + return DataBegin() + NUM_ENTRIES; + } + + DataNodeLayout layout() const { + return DataNodeLayout(_block->size()); + } + + cpputils::unique_ref releaseBlock() { + return std::move(_block); + } + + const blockstore::Block &block() const { + return *_block; + } + + const blockstore::Key &key() const { + return _block->key(); + } + + void flush() const { + _block->flush(); + } + +private: + template + const Type *GetOffset() const { + return (Type*)(((const int8_t*)_block->data())+offset); + } + + cpputils::unique_ref _block; + + DISALLOW_COPY_AND_ASSIGN(DataNodeView); + +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp b/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp new file mode 100644 index 00000000..d2d87049 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp @@ -0,0 +1,334 @@ +#include "DataTree.h" + +#include "../datanodestore/DataNodeStore.h" +#include "../datanodestore/DataInnerNode.h" +#include "../datanodestore/DataLeafNode.h" +#include "../utils/Math.h" + +#include "impl/algorithms.h" + +#include +#include +#include +#include + +using blockstore::Key; +using blobstore::onblocks::datanodestore::DataNodeStore; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datanodestore::DataNodeLayout; + +using std::dynamic_pointer_cast; +using std::function; +using boost::shared_mutex; +using boost::shared_lock; +using boost::unique_lock; +using boost::none; +using std::vector; + +using cpputils::dynamic_pointer_move; +using cpputils::optional_ownership_ptr; +using cpputils::WithOwnership; +using cpputils::WithoutOwnership; +using cpputils::unique_ref; + +namespace blobstore { +namespace onblocks { +namespace datatreestore { + +DataTree::DataTree(DataNodeStore *nodeStore, unique_ref rootNode) + : _mutex(), _nodeStore(nodeStore), _rootNode(std::move(rootNode)) { +} + +DataTree::~DataTree() { +} + +void DataTree::removeLastDataLeaf() { + auto deletePosOrNull = algorithms::GetLowestRightBorderNodeWithMoreThanOneChildOrNull(_nodeStore, _rootNode.get()); + ASSERT(deletePosOrNull.get() != nullptr, "Tree has only one leaf, can't shrink it."); + + deleteLastChildSubtree(deletePosOrNull.get()); + + ifRootHasOnlyOneChildReplaceRootWithItsChild(); +} + +void DataTree::ifRootHasOnlyOneChildReplaceRootWithItsChild() { + DataInnerNode *rootNode = dynamic_cast(_rootNode.get()); + ASSERT(rootNode != nullptr, "RootNode is not an inner node"); + if (rootNode->numChildren() == 1) { + auto child = _nodeStore->load(rootNode->getChild(0)->key()); + ASSERT(child != none, "Couldn't load first child of root node"); + _rootNode = _nodeStore->overwriteNodeWith(std::move(_rootNode), **child); + _nodeStore->remove(std::move(*child)); + } +} + +void DataTree::deleteLastChildSubtree(DataInnerNode *node) { + auto lastChild = _nodeStore->load(node->LastChild()->key()); + ASSERT(lastChild != none, "Couldn't load last child"); + _nodeStore->removeSubtree(std::move(*lastChild)); + node->removeLastChild(); +} + +unique_ref DataTree::addDataLeaf() { + auto insertPosOrNull = algorithms::GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(_nodeStore, _rootNode.get()); + if (insertPosOrNull) { + return addDataLeafAt(insertPosOrNull.get()); + } else { + return addDataLeafToFullTree(); + } +} + +unique_ref DataTree::addDataLeafAt(DataInnerNode *insertPos) { + auto new_leaf = _nodeStore->createNewLeafNode(); + auto chain = createChainOfInnerNodes(insertPos->depth()-1, new_leaf.get()); + insertPos->addChild(*chain); + return new_leaf; +} + +optional_ownership_ptr DataTree::createChainOfInnerNodes(unsigned int num, DataNode *child) { + //TODO This function is implemented twice, once with optional_ownership_ptr, once with unique_ref. Redundancy! + optional_ownership_ptr chain = cpputils::WithoutOwnership(child); + for(unsigned int i=0; icreateNewInnerNode(*chain); + chain = cpputils::WithOwnership(std::move(newnode)); + } + return chain; +} + +unique_ref DataTree::createChainOfInnerNodes(unsigned int num, unique_ref child) { + unique_ref chain = std::move(child); + for(unsigned int i=0; icreateNewInnerNode(*chain); + } + return chain; +} + +DataInnerNode* DataTree::increaseTreeDepth(unsigned int levels) { + ASSERT(levels >= 1, "Parameter out of bounds: tried to increase tree depth by zero."); + auto copyOfOldRoot = _nodeStore->createNewNodeAsCopyFrom(*_rootNode); + auto chain = createChainOfInnerNodes(levels-1, copyOfOldRoot.get()); + auto newRootNode = DataNode::convertToNewInnerNode(std::move(_rootNode), *chain); + DataInnerNode *result = newRootNode.get(); + _rootNode = std::move(newRootNode); + return result; +} + +unique_ref DataTree::addDataLeafToFullTree() { + DataInnerNode *rootNode = increaseTreeDepth(1); + auto newLeaf = addDataLeafAt(rootNode); + return newLeaf; +} + +const Key &DataTree::key() const { + return _rootNode->key(); +} + +void DataTree::flush() const { + // By grabbing a lock, we ensure that all modifying functions don't run currently and are therefore flushed + unique_lock lock(_mutex); + // We also have to flush the root node + _rootNode->flush(); +} + +unique_ref DataTree::releaseRootNode() { + return std::move(_rootNode); +} + +//TODO Test numLeaves(), for example also two configurations with same number of bytes but different number of leaves (last leaf has 0 bytes) +uint32_t DataTree::numLeaves() const { + shared_lock lock(_mutex); + return _numLeaves(*_rootNode); +} + +uint32_t DataTree::_numLeaves(const DataNode &node) const { + const DataLeafNode *leaf = dynamic_cast(&node); + if (leaf != nullptr) { + return 1; + } + + const DataInnerNode &inner = dynamic_cast(node); + uint64_t numLeavesInLeftChildren = (inner.numChildren()-1) * leavesPerFullChild(inner); + auto lastChild = _nodeStore->load(inner.LastChild()->key()); + ASSERT(lastChild != none, "Couldn't load last child"); + uint64_t numLeavesInRightChild = _numLeaves(**lastChild); + + return numLeavesInLeftChildren + numLeavesInRightChild; +} + +void DataTree::traverseLeaves(uint32_t beginIndex, uint32_t endIndex, function func) { + //TODO Can we traverse in parallel? + unique_lock lock(_mutex); //TODO Only lock when resizing. Otherwise parallel read/write to a blob is not possible! + ASSERT(beginIndex <= endIndex, "Invalid parameters"); + if (0 == endIndex) { + // In this case the utils::ceilLog(_, endIndex) below would fail + return; + } + + uint8_t neededTreeDepth = utils::ceilLog(_nodeStore->layout().maxChildrenPerInnerNode(), endIndex); + uint32_t numLeaves = this->_numLeaves(*_rootNode); // TODO Querying the size causes a tree traversal down to the leaves. Possible without querying the size? + if (_rootNode->depth() < neededTreeDepth) { + //TODO Test cases that actually increase it here by 0 level / 1 level / more than 1 level + increaseTreeDepth(neededTreeDepth - _rootNode->depth()); + } + + if (numLeaves <= beginIndex) { + //TODO Test cases with numLeaves < / >= beginIndex + // There is a gap between the current size and the begin of the traversal + return _traverseLeaves(_rootNode.get(), 0, numLeaves-1, endIndex, [beginIndex, numLeaves, &func, this](DataLeafNode* node, uint32_t index) { + if (index >= beginIndex) { + func(node, index); + } else if (index == numLeaves - 1) { + // It is the old last leaf - resize it to maximum + node->resize(_nodeStore->layout().maxBytesPerLeaf()); + } + }); + } else if (numLeaves < endIndex) { + // We are starting traversal in the valid region, but traverse until after it (we grow new leaves) + return _traverseLeaves(_rootNode.get(), 0, beginIndex, endIndex, [numLeaves, &func, this] (DataLeafNode *node, uint32_t index) { + if (index == numLeaves - 1) { + // It is the old last leaf - resize it to maximum + node->resize(_nodeStore->layout().maxBytesPerLeaf()); + } + func(node, index); + }); + } else { + //We are traversing entirely inside the valid region + _traverseLeaves(_rootNode.get(), 0, beginIndex, endIndex, func); + } +} + +void DataTree::_traverseLeaves(DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, function func) { + DataLeafNode *leaf = dynamic_cast(root); + if (leaf != nullptr) { + ASSERT(beginIndex <= 1 && endIndex <= 1, "If root node is a leaf, the (sub)tree has only one leaf - access indices must be 0 or 1."); + if (beginIndex == 0 && endIndex == 1) { + func(leaf, leafOffset); + } + return; + } + + DataInnerNode *inner = dynamic_cast(root); + uint32_t leavesPerChild = leavesPerFullChild(*inner); + uint32_t beginChild = beginIndex/leavesPerChild; + uint32_t endChild = utils::ceilDivision(endIndex, leavesPerChild); + vector> children = getOrCreateChildren(inner, beginChild, endChild); + + for (uint32_t childIndex = beginChild; childIndex < endChild; ++childIndex) { + uint32_t childOffset = childIndex * leavesPerChild; + uint32_t localBeginIndex = utils::maxZeroSubtraction(beginIndex, childOffset); + uint32_t localEndIndex = std::min(leavesPerChild, endIndex - childOffset); + auto child = std::move(children[childIndex-beginChild]); + _traverseLeaves(child.get(), leafOffset + childOffset, localBeginIndex, localEndIndex, func); + } +} + +vector> DataTree::getOrCreateChildren(DataInnerNode *node, uint32_t begin, uint32_t end) { + vector> children; + children.reserve(end-begin); + for (uint32_t childIndex = begin; childIndex < std::min(node->numChildren(), end); ++childIndex) { + auto child = _nodeStore->load(node->getChild(childIndex)->key()); + ASSERT(child != none, "Couldn't load child node"); + children.emplace_back(std::move(*child)); + } + for (uint32_t childIndex = node->numChildren(); childIndex < end; ++childIndex) { + //TODO This creates each child with one chain to one leaf only, and then on the next lower level it + // has to create the children for the child. Would be faster to directly create full trees if necessary. + children.emplace_back(addChildTo(node)); + } + ASSERT(children.size() == end-begin, "Number of children in the result is wrong"); + return children; +} + +unique_ref DataTree::addChildTo(DataInnerNode *node) { + auto new_leaf = _nodeStore->createNewLeafNode(); + new_leaf->resize(_nodeStore->layout().maxBytesPerLeaf()); + auto chain = createChainOfInnerNodes(node->depth()-1, std::move(new_leaf)); + node->addChild(*chain); + return std::move(chain); +} + +uint32_t DataTree::leavesPerFullChild(const DataInnerNode &root) const { + return utils::intPow(_nodeStore->layout().maxChildrenPerInnerNode(), (uint32_t)root.depth()-1); +} + +uint64_t DataTree::numStoredBytes() const { + shared_lock lock(_mutex); + return _numStoredBytes(); +} + +uint64_t DataTree::_numStoredBytes() const { + return _numStoredBytes(*_rootNode); +} + +uint64_t DataTree::_numStoredBytes(const DataNode &root) const { + const DataLeafNode *leaf = dynamic_cast(&root); + if (leaf != nullptr) { + return leaf->numBytes(); + } + + const DataInnerNode &inner = dynamic_cast(root); + uint64_t numBytesInLeftChildren = (inner.numChildren()-1) * leavesPerFullChild(inner) * _nodeStore->layout().maxBytesPerLeaf(); + auto lastChild = _nodeStore->load(inner.LastChild()->key()); + ASSERT(lastChild != none, "Couldn't load last child"); + uint64_t numBytesInRightChild = _numStoredBytes(**lastChild); + + return numBytesInLeftChildren + numBytesInRightChild; +} + +void DataTree::resizeNumBytes(uint64_t newNumBytes) { + //TODO Can we resize in parallel? Especially creating new blocks (i.e. encrypting them) is expensive and should be done in parallel. + boost::upgrade_lock lock(_mutex); + { + boost::upgrade_to_unique_lock exclusiveLock(lock); + //TODO Faster implementation possible (no addDataLeaf()/removeLastDataLeaf() in a loop, but directly resizing) + LastLeaf(_rootNode.get())->resize(_nodeStore->layout().maxBytesPerLeaf()); + uint64_t currentNumBytes = _numStoredBytes(); + ASSERT(currentNumBytes % _nodeStore->layout().maxBytesPerLeaf() == 0, "The last leaf is not a max data leaf, although we just resized it to be one."); + uint32_t currentNumLeaves = currentNumBytes / _nodeStore->layout().maxBytesPerLeaf(); + uint32_t newNumLeaves = std::max(UINT64_C(1), utils::ceilDivision(newNumBytes, _nodeStore->layout().maxBytesPerLeaf())); + + for(uint32_t i = currentNumLeaves; i < newNumLeaves; ++i) { + addDataLeaf()->resize(_nodeStore->layout().maxBytesPerLeaf()); + } + for(uint32_t i = currentNumLeaves; i > newNumLeaves; --i) { + removeLastDataLeaf(); + } + uint32_t newLastLeafSize = newNumBytes - (newNumLeaves-1)*_nodeStore->layout().maxBytesPerLeaf(); + LastLeaf(_rootNode.get())->resize(newLastLeafSize); + } + ASSERT(newNumBytes == _numStoredBytes(), "We resized to the wrong number of bytes ("+std::to_string(numStoredBytes())+" instead of "+std::to_string(newNumBytes)+")"); +} + +optional_ownership_ptr DataTree::LastLeaf(DataNode *root) { + DataLeafNode *leaf = dynamic_cast(root); + if (leaf != nullptr) { + return WithoutOwnership(leaf); + } + + DataInnerNode *inner = dynamic_cast(root); + auto lastChild = _nodeStore->load(inner->LastChild()->key()); + ASSERT(lastChild != none, "Couldn't load last child"); + return WithOwnership(LastLeaf(std::move(*lastChild))); +} + +unique_ref DataTree::LastLeaf(unique_ref root) { + auto leaf = dynamic_pointer_move(root); + if (leaf != none) { + return std::move(*leaf); + } + auto inner = dynamic_pointer_move(root); + ASSERT(inner != none, "Root node is neither a leaf nor an inner node"); + auto child = _nodeStore->load((*inner)->LastChild()->key()); + ASSERT(child != none, "Couldn't load last child"); + return LastLeaf(std::move(*child)); +} + +uint64_t DataTree::maxBytesPerLeaf() const { + return _nodeStore->layout().maxBytesPerLeaf(); +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/datatreestore/DataTree.h b/src/blobstore/implementations/onblocks/datatreestore/DataTree.h new file mode 100644 index 00000000..ae7e6c43 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datatreestore/DataTree.h @@ -0,0 +1,79 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATATREE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATATREE_H_ + +#include +#include +#include +#include "../datanodestore/DataNodeView.h" +//TODO Replace with C++14 once std::shared_mutex is supported +#include +#include + +namespace blobstore { +namespace onblocks { +namespace datanodestore { +class DataNodeStore; +class DataInnerNode; +class DataLeafNode; +class DataNode; +} +namespace datatreestore { + +//TODO It is strange that DataLeafNode is still part in the public interface of DataTree. This should be separated somehow. +class DataTree final { +public: + DataTree(datanodestore::DataNodeStore *nodeStore, cpputils::unique_ref rootNode); + ~DataTree(); + + const blockstore::Key &key() const; + //Returning uint64_t, because calculations handling this probably need to be done in 64bit to support >4GB blobs. + uint64_t maxBytesPerLeaf() const; + + void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function func); + void resizeNumBytes(uint64_t newNumBytes); + + uint32_t numLeaves() const; + uint64_t numStoredBytes() const; + + void flush() const; + +private: + mutable boost::shared_mutex _mutex; + datanodestore::DataNodeStore *_nodeStore; + cpputils::unique_ref _rootNode; + + cpputils::unique_ref addDataLeaf(); + void removeLastDataLeaf(); + + cpputils::unique_ref releaseRootNode(); + friend class DataTreeStore; + + cpputils::unique_ref addDataLeafAt(datanodestore::DataInnerNode *insertPos); + cpputils::optional_ownership_ptr createChainOfInnerNodes(unsigned int num, datanodestore::DataNode *child); + cpputils::unique_ref createChainOfInnerNodes(unsigned int num, cpputils::unique_ref child); + cpputils::unique_ref addDataLeafToFullTree(); + + void deleteLastChildSubtree(datanodestore::DataInnerNode *node); + void ifRootHasOnlyOneChildReplaceRootWithItsChild(); + + //TODO Use underscore for private methods + void _traverseLeaves(datanodestore::DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, std::function func); + uint32_t leavesPerFullChild(const datanodestore::DataInnerNode &root) const; + uint64_t _numStoredBytes() const; + uint64_t _numStoredBytes(const datanodestore::DataNode &root) const; + uint32_t _numLeaves(const datanodestore::DataNode &node) const; + cpputils::optional_ownership_ptr LastLeaf(datanodestore::DataNode *root); + cpputils::unique_ref LastLeaf(cpputils::unique_ref root); + datanodestore::DataInnerNode* increaseTreeDepth(unsigned int levels); + std::vector> getOrCreateChildren(datanodestore::DataInnerNode *node, uint32_t begin, uint32_t end); + cpputils::unique_ref addChildTo(datanodestore::DataInnerNode *node); + + DISALLOW_COPY_AND_ASSIGN(DataTree); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datatreestore/DataTreeStore.cpp b/src/blobstore/implementations/onblocks/datatreestore/DataTreeStore.cpp new file mode 100644 index 00000000..29fddbfd --- /dev/null +++ b/src/blobstore/implementations/onblocks/datatreestore/DataTreeStore.cpp @@ -0,0 +1,46 @@ +#include "DataTreeStore.h" +#include "../datanodestore/DataNodeStore.h" +#include "../datanodestore/DataLeafNode.h" +#include "DataTree.h" + +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using boost::optional; +using boost::none; + +using blobstore::onblocks::datanodestore::DataNodeStore; +using blobstore::onblocks::datanodestore::DataNode; + +namespace blobstore { +namespace onblocks { +namespace datatreestore { + +DataTreeStore::DataTreeStore(unique_ref nodeStore) + : _nodeStore(std::move(nodeStore)) { +} + +DataTreeStore::~DataTreeStore() { +} + +optional> DataTreeStore::load(const blockstore::Key &key) { + auto node = _nodeStore->load(key); + if (node == none) { + return none; + } + return make_unique_ref(_nodeStore.get(), std::move(*node)); +} + +unique_ref DataTreeStore::createNewTree() { + auto newleaf = _nodeStore->createNewLeafNode(); + return make_unique_ref(_nodeStore.get(), std::move(newleaf)); +} + +void DataTreeStore::remove(unique_ref tree) { + auto root = tree->releaseRootNode(); + cpputils::destruct(std::move(tree)); // Destruct tree + _nodeStore->removeSubtree(std::move(root)); +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/datatreestore/DataTreeStore.h b/src/blobstore/implementations/onblocks/datatreestore/DataTreeStore.h new file mode 100644 index 00000000..1697eab8 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datatreestore/DataTreeStore.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_DATATREESTORE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_DATATREESTORE_H_ + +#include +#include +#include +#include +#include + +namespace blobstore { +namespace onblocks { +namespace datanodestore { +class DataNodeStore; +} +namespace datatreestore { +class DataTree; + +class DataTreeStore final { +public: + DataTreeStore(cpputils::unique_ref nodeStore); + ~DataTreeStore(); + + boost::optional> load(const blockstore::Key &key); + + cpputils::unique_ref createNewTree(); + + void remove(cpputils::unique_ref tree); + +private: + cpputils::unique_ref _nodeStore; + + DISALLOW_COPY_AND_ASSIGN(DataTreeStore); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/datatreestore/impl/algorithms.cpp b/src/blobstore/implementations/onblocks/datatreestore/impl/algorithms.cpp new file mode 100644 index 00000000..6f232700 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datatreestore/impl/algorithms.cpp @@ -0,0 +1,67 @@ +#include "algorithms.h" +#include +#include + +#include "../../datanodestore/DataInnerNode.h" +#include "../../datanodestore/DataNodeStore.h" +#include + +using std::function; +using cpputils::optional_ownership_ptr; +using cpputils::dynamic_pointer_move; +using cpputils::unique_ref; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataNodeStore; +using blockstore::Key; +using boost::optional; +using boost::none; + +namespace blobstore { +namespace onblocks { +namespace datatreestore { +namespace algorithms { + +optional> getLastChildAsInnerNode(DataNodeStore *nodeStore, const DataInnerNode &node) { + Key key = node.LastChild()->key(); + auto lastChild = nodeStore->load(key); + ASSERT(lastChild != none, "Couldn't load last child"); + return dynamic_pointer_move(*lastChild); +} + +//Returns the lowest right border node meeting the condition specified (exclusive the leaf). +//Returns nullptr, if no inner right border node meets the condition. +optional_ownership_ptr GetLowestInnerRightBorderNodeWithConditionOrNull(DataNodeStore *nodeStore, datanodestore::DataNode *rootNode, function condition) { + optional_ownership_ptr currentNode = cpputils::WithoutOwnership(dynamic_cast(rootNode)); + optional_ownership_ptr result = cpputils::null(); + for (unsigned int i=0; i < rootNode->depth(); ++i) { + //TODO This unnecessarily loads the leaf node in the last loop run + auto lastChild = getLastChildAsInnerNode(nodeStore, *currentNode); + if (condition(*currentNode)) { + result = std::move(currentNode); + } + ASSERT(lastChild != none || static_cast(i) == rootNode->depth()-1, "Couldn't get last child as inner node but we're not deep enough yet for the last child to be a leaf"); + if (lastChild != none) { + currentNode = cpputils::WithOwnership(std::move(*lastChild)); + } + } + + return result; +} + +optional_ownership_ptr GetLowestRightBorderNodeWithMoreThanOneChildOrNull(DataNodeStore *nodeStore, DataNode *rootNode) { + return GetLowestInnerRightBorderNodeWithConditionOrNull(nodeStore, rootNode, [] (const datanodestore::DataInnerNode &node) { + return node.numChildren() > 1; + }); +} + +optional_ownership_ptr GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(datanodestore::DataNodeStore *nodeStore, datanodestore::DataNode *rootNode) { + return GetLowestInnerRightBorderNodeWithConditionOrNull(nodeStore, rootNode, [] (const datanodestore::DataInnerNode &node) { + return node.numChildren() < node.maxStoreableChildren(); + }); +} + +} +} +} +} diff --git a/src/blobstore/implementations/onblocks/datatreestore/impl/algorithms.h b/src/blobstore/implementations/onblocks/datatreestore/impl/algorithms.h new file mode 100644 index 00000000..a04168a8 --- /dev/null +++ b/src/blobstore/implementations/onblocks/datatreestore/impl/algorithms.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_IMPL_ALGORITHMS_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_IMPL_ALGORITHMS_H_ + +#include + +namespace blobstore { +namespace onblocks { +namespace datanodestore{ +class DataNode; +class DataInnerNode; +class DataNodeStore; +} +namespace datatreestore { +namespace algorithms { + +//Returns the lowest right border node with at least two children. +//Returns nullptr, if all right border nodes have only one child (since the root is a right border node, this means that the whole tree has exactly one leaf) +cpputils::optional_ownership_ptr GetLowestRightBorderNodeWithMoreThanOneChildOrNull(datanodestore::DataNodeStore *nodeStore, datanodestore::DataNode *rootNode); + +//Returns the lowest right border node with less than k children (not considering leaves). +//Returns nullptr, if all right border nodes have k children (the tree is full) +cpputils::optional_ownership_ptr GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(datanodestore::DataNodeStore *nodeStore, datanodestore::DataNode *rootNode); + +} +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.cpp b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.cpp new file mode 100644 index 00000000..8de9dedf --- /dev/null +++ b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.cpp @@ -0,0 +1 @@ +#include "DataTreeRef.h" diff --git a/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h new file mode 100644 index 00000000..b379c91a --- /dev/null +++ b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h @@ -0,0 +1,55 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_PARALLELACCESSDATATREESTORE_DATATREEREF_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_PARALLELACCESSDATATREESTORE_DATATREEREF_H_ + +#include +#include "../datatreestore/DataTree.h" + +namespace blobstore { +namespace onblocks { +namespace parallelaccessdatatreestore { + +class DataTreeRef final: public parallelaccessstore::ParallelAccessStore::ResourceRefBase { +public: + DataTreeRef(datatreestore::DataTree *baseTree): _baseTree(baseTree) {} + + const blockstore::Key &key() const { + return _baseTree->key(); + } + + uint64_t maxBytesPerLeaf() const { + return _baseTree->maxBytesPerLeaf(); + } + + void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function func) { + return _baseTree->traverseLeaves(beginIndex, endIndex, func); + } + + uint32_t numLeaves() const { + return _baseTree->numLeaves(); + } + + void resizeNumBytes(uint64_t newNumBytes) { + return _baseTree->resizeNumBytes(newNumBytes); + } + + uint64_t numStoredBytes() const { + return _baseTree->numStoredBytes(); + } + + void flush() { + return _baseTree->flush(); + } + +private: + + datatreestore::DataTree *_baseTree; + + DISALLOW_COPY_AND_ASSIGN(DataTreeRef); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.cpp b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.cpp new file mode 100644 index 00000000..3061e359 --- /dev/null +++ b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.cpp @@ -0,0 +1,44 @@ +#include "DataTreeRef.h" +#include "ParallelAccessDataTreeStore.h" +#include "ParallelAccessDataTreeStoreAdapter.h" +#include "../datanodestore/DataNodeStore.h" +#include "../datanodestore/DataLeafNode.h" + +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using boost::optional; + +using blobstore::onblocks::datatreestore::DataTreeStore; +using blockstore::Key; + +namespace blobstore { +namespace onblocks { +using datatreestore::DataTreeStore; +using datatreestore::DataTree; +namespace parallelaccessdatatreestore { + +ParallelAccessDataTreeStore::ParallelAccessDataTreeStore(unique_ref dataTreeStore) + : _dataTreeStore(std::move(dataTreeStore)), _parallelAccessStore(make_unique_ref(_dataTreeStore.get())) { +} + +ParallelAccessDataTreeStore::~ParallelAccessDataTreeStore() { +} + +optional> ParallelAccessDataTreeStore::load(const blockstore::Key &key) { + return _parallelAccessStore.load(key); +} + +unique_ref ParallelAccessDataTreeStore::createNewTree() { + auto dataTree = _dataTreeStore->createNewTree(); + Key key = dataTree->key(); + return _parallelAccessStore.add(key, std::move(dataTree)); +} + +void ParallelAccessDataTreeStore::remove(unique_ref tree) { + Key key = tree->key(); + return _parallelAccessStore.remove(key, std::move(tree)); +} + +} +} +} diff --git a/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.h b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.h new file mode 100644 index 00000000..c0499a43 --- /dev/null +++ b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStore.h @@ -0,0 +1,43 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_PARALLELACCESSDATATREESTORE_PARALLELACCESSDATATREESTORE_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_PARALLELACCESSDATATREESTORE_PARALLELACCESSDATATREESTORE_H_ + +#include +#include +#include +#include + +namespace blobstore { +namespace onblocks { +namespace datatreestore { +class DataTreeStore; +class DataTree; +} +namespace parallelaccessdatatreestore { +class DataTreeRef; + +//TODO Test CachingDataTreeStore + +class ParallelAccessDataTreeStore final { +public: + ParallelAccessDataTreeStore(cpputils::unique_ref dataTreeStore); + ~ParallelAccessDataTreeStore(); + + boost::optional> load(const blockstore::Key &key); + + cpputils::unique_ref createNewTree(); + + void remove(cpputils::unique_ref tree); + +private: + cpputils::unique_ref _dataTreeStore; + parallelaccessstore::ParallelAccessStore _parallelAccessStore; + + DISALLOW_COPY_AND_ASSIGN(ParallelAccessDataTreeStore); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.cpp b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.cpp new file mode 100644 index 00000000..52ef9fc4 --- /dev/null +++ b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.cpp @@ -0,0 +1 @@ +#include "ParallelAccessDataTreeStoreAdapter.h" diff --git a/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.h b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.h new file mode 100644 index 00000000..6e8b1861 --- /dev/null +++ b/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/ParallelAccessDataTreeStoreAdapter.h @@ -0,0 +1,38 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_PARALLELACCESSDATATREESTORE_PARALLELACCESSDATATREESTOREADAPTER_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_PARALLELACCESSDATATREESTORE_PARALLELACCESSDATATREESTOREADAPTER_H_ + +#include +#include +#include "../datatreestore/DataTreeStore.h" +#include "../datatreestore/DataTree.h" + +namespace blobstore { +namespace onblocks { +namespace parallelaccessdatatreestore { + +class ParallelAccessDataTreeStoreAdapter final: public parallelaccessstore::ParallelAccessBaseStore { +public: + ParallelAccessDataTreeStoreAdapter(datatreestore::DataTreeStore *baseDataTreeStore) + :_baseDataTreeStore(std::move(baseDataTreeStore)) { + } + + boost::optional> loadFromBaseStore(const blockstore::Key &key) override { + return _baseDataTreeStore->load(key); + } + + void removeFromBaseStore(cpputils::unique_ref dataTree) override { + return _baseDataTreeStore->remove(std::move(dataTree)); + } + +private: + datatreestore::DataTreeStore *_baseDataTreeStore; + + DISALLOW_COPY_AND_ASSIGN(ParallelAccessDataTreeStoreAdapter); +}; + +} +} +} + +#endif diff --git a/src/blobstore/implementations/onblocks/utils/Math.cpp b/src/blobstore/implementations/onblocks/utils/Math.cpp new file mode 100644 index 00000000..b00989a1 --- /dev/null +++ b/src/blobstore/implementations/onblocks/utils/Math.cpp @@ -0,0 +1 @@ +#include "Math.h" diff --git a/src/blobstore/implementations/onblocks/utils/Math.h b/src/blobstore/implementations/onblocks/utils/Math.h new file mode 100644 index 00000000..370c1e01 --- /dev/null +++ b/src/blobstore/implementations/onblocks/utils/Math.h @@ -0,0 +1,43 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_UTILS_MATH_H_ +#define MESSMER_BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_UTILS_MATH_H_ + +#include +#include + +namespace blobstore { +namespace onblocks { +namespace utils { + +template +inline INT_TYPE intPow(INT_TYPE base, INT_TYPE exponent) { + INT_TYPE result = 1; + for(INT_TYPE i = 0; i < exponent; ++i) { + result *= base; + } + return result; +} + +template +inline INT_TYPE ceilDivision(INT_TYPE dividend, INT_TYPE divisor) { + return (dividend + divisor - 1)/divisor; +} + +template +inline INT_TYPE maxZeroSubtraction(INT_TYPE minuend, INT_TYPE subtrahend) { + if (minuend < subtrahend) { + return 0u; + } + return minuend-subtrahend; +} + +template +inline INT_TYPE ceilLog(INT_TYPE base, INT_TYPE value) { + return std::ceil((long double)std::log(value)/(long double)std::log(base)); +} + +} +} +} + +#endif diff --git a/src/blobstore/interface/Blob.h b/src/blobstore/interface/Blob.h new file mode 100644 index 00000000..972461dc --- /dev/null +++ b/src/blobstore/interface/Blob.h @@ -0,0 +1,35 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_INTERFACE_BLOB_H_ +#define MESSMER_BLOBSTORE_INTERFACE_BLOB_H_ + +#include +#include +#include +#include + +namespace blobstore { + +class Blob { +public: + virtual ~Blob() {} + + //TODO Use own Key class for blobstore + virtual const blockstore::Key &key() const = 0; + + virtual uint64_t size() const = 0; + virtual void resize(uint64_t numBytes) = 0; + + virtual cpputils::Data readAll() const = 0; + virtual void read(void *target, uint64_t offset, uint64_t size) const = 0; + virtual uint64_t tryRead(void *target, uint64_t offset, uint64_t size) const = 0; + virtual void write(const void *source, uint64_t offset, uint64_t size) = 0; + + virtual void flush() = 0; + + //TODO Test tryRead +}; + +} + + +#endif diff --git a/src/blobstore/interface/BlobStore.h b/src/blobstore/interface/BlobStore.h new file mode 100644 index 00000000..ad8b0822 --- /dev/null +++ b/src/blobstore/interface/BlobStore.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_INTERFACE_BLOBSTORE_H_ +#define MESSMER_BLOBSTORE_INTERFACE_BLOBSTORE_H_ + +#include "Blob.h" +#include +#include + +#include +#include + +namespace blobstore { + +class BlobStore { +public: + virtual ~BlobStore() {} + + virtual cpputils::unique_ref create() = 0; + virtual boost::optional> load(const blockstore::Key &key) = 0; + virtual void remove(cpputils::unique_ref blob) = 0; +}; + +} + +#endif diff --git a/src/blockstore/CMakeLists.txt b/src/blockstore/CMakeLists.txt new file mode 100644 index 00000000..e435b4cd --- /dev/null +++ b/src/blockstore/CMakeLists.txt @@ -0,0 +1,43 @@ +project (blockstore) + +set(SOURCES + utils/Key.cpp + utils/BlockStoreUtils.cpp + utils/FileDoesntExistException.cpp + interface/helpers/BlockStoreWithRandomKeys.cpp + implementations/testfake/FakeBlockStore.cpp + implementations/testfake/FakeBlock.cpp + implementations/inmemory/InMemoryBlock.cpp + implementations/inmemory/InMemoryBlockStore.cpp + implementations/parallelaccess/ParallelAccessBlockStore.cpp + implementations/parallelaccess/BlockRef.cpp + implementations/parallelaccess/ParallelAccessBlockStoreAdapter.cpp + implementations/compressing/CompressingBlockStore.cpp + implementations/compressing/CompressedBlock.cpp + implementations/compressing/compressors/RunLengthEncoding.cpp + implementations/compressing/compressors/Gzip.cpp + implementations/encrypted/EncryptedBlockStore.cpp + implementations/encrypted/EncryptedBlock.cpp + implementations/ondisk/OnDiskBlockStore.cpp + implementations/ondisk/OnDiskBlock.cpp + implementations/caching/CachingBlockStore.cpp + implementations/caching/cache/PeriodicTask.cpp + implementations/caching/cache/CacheEntry.cpp + implementations/caching/cache/Cache.cpp + implementations/caching/cache/QueueMap.cpp + implementations/caching/CachedBlock.cpp + implementations/caching/NewBlock.cpp +) + +add_library(${PROJECT_NAME} STATIC ${SOURCES}) + +# This is needed by boost thread +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries(${PROJECT_NAME} PRIVATE rt) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils) + +target_add_boost(${PROJECT_NAME} filesystem system thread) +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/src/blockstore/implementations/caching/CachedBlock.cpp b/src/blockstore/implementations/caching/CachedBlock.cpp new file mode 100644 index 00000000..8397888e --- /dev/null +++ b/src/blockstore/implementations/caching/CachedBlock.cpp @@ -0,0 +1,46 @@ +#include "CachedBlock.h" +#include "CachingBlockStore.h" + +using cpputils::unique_ref; + +namespace blockstore { +namespace caching { + +CachedBlock::CachedBlock(unique_ref baseBlock, CachingBlockStore *blockStore) + :Block(baseBlock->key()), + _blockStore(blockStore), + _baseBlock(std::move(baseBlock)) { +} + +CachedBlock::~CachedBlock() { + if (_baseBlock.get() != nullptr) { + _blockStore->release(std::move(_baseBlock)); + } +} + +const void *CachedBlock::data() const { + return _baseBlock->data(); +} + +void CachedBlock::write(const void *source, uint64_t offset, uint64_t size) { + return _baseBlock->write(source, offset, size); +} + +void CachedBlock::flush() { + return _baseBlock->flush(); +} + +size_t CachedBlock::size() const { + return _baseBlock->size(); +} + +void CachedBlock::resize(size_t newSize) { + return _baseBlock->resize(newSize); +} + +unique_ref CachedBlock::releaseBlock() { + return std::move(_baseBlock); +} + +} +} diff --git a/src/blockstore/implementations/caching/CachedBlock.h b/src/blockstore/implementations/caching/CachedBlock.h new file mode 100644 index 00000000..81f1b640 --- /dev/null +++ b/src/blockstore/implementations/caching/CachedBlock.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHEDBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHEDBLOCK_H_ + +#include "../../interface/Block.h" + +#include + +namespace blockstore { +namespace caching { +class CachingBlockStore; + +class CachedBlock final: public Block { +public: + //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. + CachedBlock(cpputils::unique_ref baseBlock, CachingBlockStore *blockStore); + ~CachedBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + void flush() override; + + size_t size() const override; + + void resize(size_t newSize) override; + + cpputils::unique_ref releaseBlock(); + +private: + CachingBlockStore *_blockStore; + cpputils::unique_ref _baseBlock; + + DISALLOW_COPY_AND_ASSIGN(CachedBlock); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/caching/CachingBlockStore.cpp b/src/blockstore/implementations/caching/CachingBlockStore.cpp new file mode 100644 index 00000000..b9afd151 --- /dev/null +++ b/src/blockstore/implementations/caching/CachingBlockStore.cpp @@ -0,0 +1,92 @@ +#include "CachedBlock.h" +#include "NewBlock.h" +#include "CachingBlockStore.h" +#include "../../interface/Block.h" + +#include +#include +#include + +using cpputils::dynamic_pointer_move; +using cpputils::Data; +using boost::optional; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using boost::none; + +namespace blockstore { +namespace caching { + +CachingBlockStore::CachingBlockStore(cpputils::unique_ref baseBlockStore) + :_baseBlockStore(std::move(baseBlockStore)), _cache(), _numNewBlocks(0) { +} + +Key CachingBlockStore::createKey() { + return _baseBlockStore->createKey(); +} + +optional> CachingBlockStore::tryCreate(const Key &key, Data data) { + ASSERT(_cache.pop(key) == none, "Key already exists in cache"); + //TODO Shouldn't we return boost::none if the key already exists? + //TODO Key can also already exist but not be in the cache right now. + ++_numNewBlocks; + return unique_ref(make_unique_ref(make_unique_ref(key, std::move(data), this), this)); +} + +optional> CachingBlockStore::load(const Key &key) { + optional> optBlock = _cache.pop(key); + //TODO an optional<> class with .getOrElse() would make this code simpler. boost::optional<>::value_or_eval didn't seem to work with unique_ptr members. + if (optBlock != none) { + return optional>(make_unique_ref(std::move(*optBlock), this)); + } else { + auto block = _baseBlockStore->load(key); + if (block == none) { + return none; + } else { + return optional>(make_unique_ref(std::move(*block), this)); + } + } +} + +void CachingBlockStore::remove(cpputils::unique_ref block) { + auto cached_block = dynamic_pointer_move(block); + ASSERT(cached_block != none, "Passed block is not a CachedBlock"); + auto baseBlock = (*cached_block)->releaseBlock(); + auto baseNewBlock = dynamic_pointer_move(baseBlock); + if (baseNewBlock != none) { + if(!(*baseNewBlock)->alreadyExistsInBaseStore()) { + --_numNewBlocks; + } + (*baseNewBlock)->remove(); + } else { + _baseBlockStore->remove(std::move(baseBlock)); + } +} + +uint64_t CachingBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks() + _numNewBlocks; +} + +void CachingBlockStore::release(unique_ref block) { + Key key = block->key(); + _cache.push(key, std::move(block)); +} + +optional> CachingBlockStore::tryCreateInBaseStore(const Key &key, Data data) { + auto block = _baseBlockStore->tryCreate(key, std::move(data)); + if (block != none) { + --_numNewBlocks; + } + return block; +} + +void CachingBlockStore::removeFromBaseStore(cpputils::unique_ref block) { + _baseBlockStore->remove(std::move(block)); +} + +void CachingBlockStore::flush() { + _cache.flush(); +} + +} +} diff --git a/src/blockstore/implementations/caching/CachingBlockStore.h b/src/blockstore/implementations/caching/CachingBlockStore.h new file mode 100644 index 00000000..7ec02af1 --- /dev/null +++ b/src/blockstore/implementations/caching/CachingBlockStore.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGBLOCKSTORE_H_ + +#include "cache/Cache.h" +#include "../../interface/BlockStore.h" + +namespace blockstore { +namespace caching { + +//TODO Check that this blockstore allows parallel destructing of blocks (otherwise we won't encrypt blocks in parallel) +class CachingBlockStore final: public BlockStore { +public: + explicit CachingBlockStore(cpputils::unique_ref baseBlockStore); + + Key createKey() override; + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + + void release(cpputils::unique_ref block); + + boost::optional> tryCreateInBaseStore(const Key &key, cpputils::Data data); + void removeFromBaseStore(cpputils::unique_ref block); + + void flush(); + +private: + cpputils::unique_ref _baseBlockStore; + Cache, 1000> _cache; + uint32_t _numNewBlocks; + + DISALLOW_COPY_AND_ASSIGN(CachingBlockStore); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/caching/NewBlock.cpp b/src/blockstore/implementations/caching/NewBlock.cpp new file mode 100644 index 00000000..8e051935 --- /dev/null +++ b/src/blockstore/implementations/caching/NewBlock.cpp @@ -0,0 +1,75 @@ +#include "NewBlock.h" +#include "CachingBlockStore.h" +#include +#include + +using cpputils::Data; +using boost::none; + +namespace blockstore { +namespace caching { + +NewBlock::NewBlock(const Key &key, Data data, CachingBlockStore *blockStore) + :Block(key), + _blockStore(blockStore), + _data(std::move(data)), + _baseBlock(none), + _dataChanged(true) { +} + +NewBlock::~NewBlock() { + writeToBaseBlockIfChanged(); +} + +const void *NewBlock::data() const { + return _data.data(); +} + +void NewBlock::write(const void *source, uint64_t offset, uint64_t size) { + ASSERT(offset <= _data.size() && offset + size <= _data.size(), "Write outside of valid area"); + std::memcpy((uint8_t*)_data.data()+offset, source, size); + _dataChanged = true; +} + +void NewBlock::writeToBaseBlockIfChanged() { + if (_dataChanged) { + if (_baseBlock == none) { + //TODO _data.copy() necessary? + auto newBase = _blockStore->tryCreateInBaseStore(key(), _data.copy()); + ASSERT(newBase != boost::none, "Couldn't create base block"); //TODO What if tryCreate fails due to a duplicate key? We should ensure we don't use duplicate keys. + _baseBlock = std::move(*newBase); + } else { + (*_baseBlock)->write(_data.data(), 0, _data.size()); + } + _dataChanged = false; + } +} + +void NewBlock::remove() { + if (_baseBlock != none) { + _blockStore->removeFromBaseStore(std::move(*_baseBlock)); + } + _dataChanged = false; +} + +void NewBlock::flush() { + writeToBaseBlockIfChanged(); + ASSERT(_baseBlock != none, "At this point, the base block should already have been created but wasn't"); + (*_baseBlock)->flush(); +} + +size_t NewBlock::size() const { + return _data.size(); +} + +void NewBlock::resize(size_t newSize) { + _data = cpputils::DataUtils::resize(std::move(_data), newSize); + _dataChanged = true; +} + +bool NewBlock::alreadyExistsInBaseStore() const { + return _baseBlock != none; +} + +} +} diff --git a/src/blockstore/implementations/caching/NewBlock.h b/src/blockstore/implementations/caching/NewBlock.h new file mode 100644 index 00000000..bdaa5172 --- /dev/null +++ b/src/blockstore/implementations/caching/NewBlock.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_NEWBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_NEWBLOCK_H_ + +#include "../../interface/BlockStore.h" +#include + +#include +#include + +namespace blockstore { +namespace caching { +class CachingBlockStore; + +//TODO Does it make sense to write a general DataBackedBlock that just stores a Data object and maps the block operations to it? +// Can we reuse that object somewhere else? +// Maybe a second abstract class for BlockRefBackedBlock? + +// This is a block that was created in CachingBlockStore, but doesn't exist in the base block store yet. +// It only exists in the cache and it is created in the base block store when destructed. +class NewBlock final: public Block { +public: + NewBlock(const Key &key, cpputils::Data data, CachingBlockStore *blockStore); + ~NewBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + void flush() override; + + size_t size() const override; + + void resize(size_t newSize) override; + + void remove(); + + bool alreadyExistsInBaseStore() const; + +private: + CachingBlockStore *_blockStore; + cpputils::Data _data; + boost::optional> _baseBlock; + bool _dataChanged; + + void writeToBaseBlockIfChanged(); + + DISALLOW_COPY_AND_ASSIGN(NewBlock); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/caching/cache/Cache.cpp b/src/blockstore/implementations/caching/cache/Cache.cpp new file mode 100644 index 00000000..2d464949 --- /dev/null +++ b/src/blockstore/implementations/caching/cache/Cache.cpp @@ -0,0 +1 @@ +#include "Cache.h" diff --git a/src/blockstore/implementations/caching/cache/Cache.h b/src/blockstore/implementations/caching/cache/Cache.h new file mode 100644 index 00000000..5b817d3c --- /dev/null +++ b/src/blockstore/implementations/caching/cache/Cache.h @@ -0,0 +1,178 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_CACHE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_CACHE_H_ + +#include "CacheEntry.h" +#include "QueueMap.h" +#include "PeriodicTask.h" +#include +#include +#include +#include +#include +#include + +namespace blockstore { +namespace caching { + +template +class Cache final { +public: + //TODO Current MAX_LIFETIME_SEC only considers time since the element was last pushed to the Cache. Also insert a real MAX_LIFETIME_SEC that forces resync of entries that have been pushed/popped often (e.g. the root blob) + //TODO Experiment with good values + static constexpr double PURGE_LIFETIME_SEC = 0.5; //When an entry has this age, it will be purged from the cache + static constexpr double PURGE_INTERVAL = 0.5; // With this interval, we check for entries to purge + static constexpr double MAX_LIFETIME_SEC = PURGE_LIFETIME_SEC + PURGE_INTERVAL; // This is the oldest age an entry can reach (given purging works in an ideal world, i.e. with the ideal interval and in zero time) + + Cache(); + ~Cache(); + + uint32_t size() const; + + void push(const Key &key, Value value); + boost::optional pop(const Key &key); + + void flush(); + +private: + void _makeSpaceForEntry(std::unique_lock *lock); + void _deleteEntry(std::unique_lock *lock); + void _deleteOldEntriesParallel(); + void _deleteAllEntriesParallel(); + void _deleteMatchingEntriesAtBeginningParallel(std::function &)> matches); + void _deleteMatchingEntriesAtBeginning(std::function &)> matches); + bool _deleteMatchingEntryAtBeginning(std::function &)> matches); + + mutable std::mutex _mutex; + cpputils::LockPool _currentlyFlushingEntries; + QueueMap> _cachedBlocks; + std::unique_ptr _timeoutFlusher; + + DISALLOW_COPY_AND_ASSIGN(Cache); +}; + +template constexpr double Cache::PURGE_LIFETIME_SEC; +template constexpr double Cache::PURGE_INTERVAL; +template constexpr double Cache::MAX_LIFETIME_SEC; + +template +Cache::Cache(): _mutex(), _currentlyFlushingEntries(), _cachedBlocks(), _timeoutFlusher(nullptr) { + //Don't initialize timeoutFlusher in the initializer list, + //because it then might already call Cache::popOldEntries() before Cache is done constructing. + _timeoutFlusher = std::make_unique(std::bind(&Cache::_deleteOldEntriesParallel, this), PURGE_INTERVAL); +} + +template +Cache::~Cache() { + _deleteAllEntriesParallel(); + ASSERT(_cachedBlocks.size() == 0, "Error in _deleteAllEntriesParallel()"); +} + +template +boost::optional Cache::pop(const Key &key) { + std::unique_lock lock(_mutex); + cpputils::MutexPoolLock lockEntryFromBeingPopped(&_currentlyFlushingEntries, key, &lock); + + auto found = _cachedBlocks.pop(key); + if (!found) { + return boost::none; + } + return found->releaseValue(); +} + +template +void Cache::push(const Key &key, Value value) { + std::unique_lock lock(_mutex); + ASSERT(_cachedBlocks.size() <= MAX_ENTRIES, "Cache too full"); + _makeSpaceForEntry(&lock); + _cachedBlocks.push(key, CacheEntry(std::move(value))); +} + +template +void Cache::_makeSpaceForEntry(std::unique_lock *lock) { + // _deleteEntry releases the lock while the Value destructor is running. + // So we can destruct multiple entries in parallel and also call pop() or push() while doing so. + // However, if another thread calls push() before we get the lock back, the cache is full again. + // That's why we need the while() loop here. + while (_cachedBlocks.size() == MAX_ENTRIES) { + _deleteEntry(lock); + } + ASSERT(_cachedBlocks.size() < MAX_ENTRIES, "Removing entry from cache didn't work"); +}; + +template +void Cache::_deleteEntry(std::unique_lock *lock) { + auto key = _cachedBlocks.peekKey(); + ASSERT(key != boost::none, "There was no entry to delete"); + cpputils::MutexPoolLock lockEntryFromBeingPopped(&_currentlyFlushingEntries, *key); + auto value = _cachedBlocks.pop(); + // Call destructor outside of the unique_lock, + // i.e. pop() and push() can be called here, except for pop() on the element in _currentlyFlushingEntries + lock->unlock(); + value = boost::none; // Call destructor + lock->lock(); +}; + +template +void Cache::_deleteAllEntriesParallel() { + return _deleteMatchingEntriesAtBeginningParallel([] (const CacheEntry &) { + return true; + }); +} + +template +void Cache::_deleteOldEntriesParallel() { + return _deleteMatchingEntriesAtBeginningParallel([] (const CacheEntry &entry) { + return entry.ageSeconds() > PURGE_LIFETIME_SEC; + }); +} + +template +void Cache::_deleteMatchingEntriesAtBeginningParallel(std::function &)> matches) { + // Twice the number of cores, so we use full CPU even if half the threads are doing I/O + unsigned int numThreads = 2 * std::max(1u, std::thread::hardware_concurrency()); + std::vector> waitHandles; + for (unsigned int i = 0; i < numThreads; ++i) { + waitHandles.push_back(std::async(std::launch::async, [this, matches] { + _deleteMatchingEntriesAtBeginning(matches); + })); + } + for (auto & waitHandle : waitHandles) { + waitHandle.wait(); + } +}; + +template +void Cache::_deleteMatchingEntriesAtBeginning(std::function &)> matches) { + while (_deleteMatchingEntryAtBeginning(matches)) {} +} + +template +bool Cache::_deleteMatchingEntryAtBeginning(std::function &)> matches) { + // This function can be called in parallel by multiple threads and will then cause the Value destructors + // to be called in parallel. The call to _deleteEntry() releases the lock while the Value destructor is running. + std::unique_lock lock(_mutex); + if (_cachedBlocks.size() > 0 && matches(*_cachedBlocks.peek())) { + _deleteEntry(&lock); + return true; + } else { + return false; + } +}; + +template +uint32_t Cache::size() const { + std::unique_lock lock(_mutex); + return _cachedBlocks.size(); +}; + +template +void Cache::flush() { + //TODO Test flush() + return _deleteAllEntriesParallel(); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/caching/cache/CacheEntry.cpp b/src/blockstore/implementations/caching/cache/CacheEntry.cpp new file mode 100644 index 00000000..a3420ac8 --- /dev/null +++ b/src/blockstore/implementations/caching/cache/CacheEntry.cpp @@ -0,0 +1 @@ +#include "CacheEntry.h" diff --git a/src/blockstore/implementations/caching/cache/CacheEntry.h b/src/blockstore/implementations/caching/cache/CacheEntry.h new file mode 100644 index 00000000..14b916e2 --- /dev/null +++ b/src/blockstore/implementations/caching/cache/CacheEntry.h @@ -0,0 +1,44 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_CACHEENTRY_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_CACHEENTRY_H_ + +#include +#include +#include +#include + +namespace blockstore { +namespace caching { + +template +class CacheEntry final { +public: + explicit CacheEntry(Value value): _lastAccess(currentTime()), _value(std::move(value)) { + } + + CacheEntry(CacheEntry &&) = default; + + double ageSeconds() const { + return ((double)(currentTime() - _lastAccess).total_nanoseconds()) / ((double)1000000000); + } + + Value releaseValue() { + return std::move(_value); + } + +private: + boost::posix_time::ptime _lastAccess; + Value _value; + + static boost::posix_time::ptime currentTime() { + return boost::posix_time::microsec_clock::local_time(); + } + + DISALLOW_COPY_AND_ASSIGN(CacheEntry); +}; + +} +} + + +#endif diff --git a/src/blockstore/implementations/caching/cache/PeriodicTask.cpp b/src/blockstore/implementations/caching/cache/PeriodicTask.cpp new file mode 100644 index 00000000..3e8f634b --- /dev/null +++ b/src/blockstore/implementations/caching/cache/PeriodicTask.cpp @@ -0,0 +1,27 @@ +#include "PeriodicTask.h" +#include + +using std::function; +using std::endl; +using namespace cpputils::logging; + +namespace blockstore { +namespace caching { + +PeriodicTask::PeriodicTask(function task, double intervalSec) : + _task(task), + _interval((uint64_t)(UINT64_C(1000000000) * intervalSec)), + _thread(std::bind(&PeriodicTask::_loopIteration, this)) { + _thread.start(); +} + +bool PeriodicTask::_loopIteration() { + //Has to be boost::this_thread::sleep_for and not std::this_thread::sleep_for, because it has to be interruptible. + //LoopThread will interrupt this method if it has to be restarted. + boost::this_thread::sleep_for(_interval); + _task(); + return true; // Run another iteration (don't terminate thread) +} + +} +} diff --git a/src/blockstore/implementations/caching/cache/PeriodicTask.h b/src/blockstore/implementations/caching/cache/PeriodicTask.h new file mode 100644 index 00000000..7663e293 --- /dev/null +++ b/src/blockstore/implementations/caching/cache/PeriodicTask.h @@ -0,0 +1,32 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_PERIODICTASK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_PERIODICTASK_H_ + +#include +#include +#include + +namespace blockstore { +namespace caching { + +class PeriodicTask final { +public: + PeriodicTask(std::function task, double intervalSec); + +private: + bool _loopIteration(); + + std::function _task; + boost::chrono::nanoseconds _interval; + + //This member has to be last, so the thread is destructed first. Otherwise the thread might access elements from a + //partly destructed PeriodicTask. + cpputils::LoopThread _thread; + + DISALLOW_COPY_AND_ASSIGN(PeriodicTask); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/caching/cache/QueueMap.cpp b/src/blockstore/implementations/caching/cache/QueueMap.cpp new file mode 100644 index 00000000..510d8ece --- /dev/null +++ b/src/blockstore/implementations/caching/cache/QueueMap.cpp @@ -0,0 +1 @@ +#include "QueueMap.h" diff --git a/src/blockstore/implementations/caching/cache/QueueMap.h b/src/blockstore/implementations/caching/cache/QueueMap.h new file mode 100644 index 00000000..8db9d646 --- /dev/null +++ b/src/blockstore/implementations/caching/cache/QueueMap.h @@ -0,0 +1,121 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_QUEUEMAP_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_QUEUEMAP_H_ + +#include +#include +#include +#include +#include +#include + +namespace blockstore { +namespace caching { + +//TODO FreeList for performance (malloc is expensive) +//TODO Single linked list with pointer to last element (for insertion) should be enough for a queue. No double linked list needed. +// But then, popping arbitrary elements needs to be rewritten so that _removeFromQueue() is _removeSuccessorFromQueue() +// and the map doesn't store the element itself, but its predecessor. That is, popping might be a bit slower. Test with experiments! + +// A class that is a queue and a map at the same time. We could also see it as an addressable queue. +template +class QueueMap final { +public: + QueueMap(): _entries(), _sentinel(&_sentinel, &_sentinel) { + } + ~QueueMap() { + for (auto &entry : _entries) { + entry.second.release(); + } + } + + void push(const Key &key, Value value) { + auto newEntry = _entries.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(_sentinel.prev, &_sentinel)); + if (!newEntry.second) { + throw std::logic_error("There is already an element with this key"); + } + newEntry.first->second.init(&newEntry.first->first, std::move(value)); + //The following is ok, because std::unordered_map never invalidates pointers to its entries + _sentinel.prev->next = &newEntry.first->second; + _sentinel.prev = &newEntry.first->second; + } + + boost::optional pop(const Key &key) { + auto found = _entries.find(key); + if (found == _entries.end()) { + return boost::none; + } + _removeFromQueue(found->second); + auto value = found->second.release(); + _entries.erase(found); + return std::move(value); + } + + boost::optional pop() { + if(_sentinel.next == &_sentinel) { + return boost::none; + } + return pop(*_sentinel.next->key); + } + + boost::optional peekKey() { + if(_sentinel.next == &_sentinel) { + return boost::none; + } + return *_sentinel.next->key; + } + + boost::optional peek() { + if(_sentinel.next == &_sentinel) { + return boost::none; + } + return _sentinel.next->value(); + } + + uint32_t size() const { + return _entries.size(); + } + +private: + class Entry final { + public: + Entry(Entry *prev_, Entry *next_): prev(prev_), next(next_), key(nullptr), __value() { + } + void init(const Key *key_, Value value_) { + key = key_; + new(__value) Value(std::move(value_)); + } + Value release() { + Value value = std::move(*_value()); + _value()->~Value(); + return value; + } + const Value &value() { + return *_value(); + } + Entry *prev; + Entry *next; + const Key *key; + private: + Value *_value() { + return reinterpret_cast(__value); + } + alignas(Value) char __value[sizeof(Value)]; + DISALLOW_COPY_AND_ASSIGN(Entry); + }; + + void _removeFromQueue(const Entry &entry) { + entry.prev->next = entry.next; + entry.next->prev = entry.prev; + } + + std::unordered_map _entries; + Entry _sentinel; + + DISALLOW_COPY_AND_ASSIGN(QueueMap); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/compressing/CompressedBlock.cpp b/src/blockstore/implementations/compressing/CompressedBlock.cpp new file mode 100644 index 00000000..e226e7e8 --- /dev/null +++ b/src/blockstore/implementations/compressing/CompressedBlock.cpp @@ -0,0 +1 @@ +#include "CompressedBlock.h" diff --git a/src/blockstore/implementations/compressing/CompressedBlock.h b/src/blockstore/implementations/compressing/CompressedBlock.h new file mode 100644 index 00000000..35f07340 --- /dev/null +++ b/src/blockstore/implementations/compressing/CompressedBlock.h @@ -0,0 +1,127 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSEDBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSEDBLOCK_H_ + +#include "../../interface/Block.h" +#include "../../interface/BlockStore.h" +#include +#include +#include + +namespace blockstore { +class BlockStore; +namespace compressing { +template class CompressingBlockStore; + +template +class CompressedBlock final: public Block { +public: + static boost::optional> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data decompressedData); + static cpputils::unique_ref Decompress(cpputils::unique_ref baseBlock); + + CompressedBlock(cpputils::unique_ref baseBlock, cpputils::Data decompressedData); + ~CompressedBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + + void flush() override; + + size_t size() const override; + void resize(size_t newSize) override; + + cpputils::unique_ref releaseBaseBlock(); + +private: + void _compressToBaseBlock(); + + cpputils::unique_ref _baseBlock; + cpputils::Data _decompressedData; + std::mutex _mutex; + bool _dataChanged; + + DISALLOW_COPY_AND_ASSIGN(CompressedBlock); +}; + +template +boost::optional>> CompressedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data decompressedData) { + cpputils::Data compressed = Compressor::Compress(decompressedData); + auto baseBlock = baseBlockStore->tryCreate(key, std::move(compressed)); + if (baseBlock == boost::none) { + //TODO Test this code branch + return boost::none; + } + + return cpputils::make_unique_ref>(std::move(*baseBlock), std::move(decompressedData)); +} + +template +cpputils::unique_ref> CompressedBlock::Decompress(cpputils::unique_ref baseBlock) { + cpputils::Data decompressed = Compressor::Decompress((byte*)baseBlock->data(), baseBlock->size()); + return cpputils::make_unique_ref>(std::move(baseBlock), std::move(decompressed)); +} + +template +CompressedBlock::CompressedBlock(cpputils::unique_ref baseBlock, cpputils::Data decompressedData) + : Block(baseBlock->key()), + _baseBlock(std::move(baseBlock)), + _decompressedData(std::move(decompressedData)), + _dataChanged(false) { +} + +template +CompressedBlock::~CompressedBlock() { + std::unique_lock lock(_mutex); + _compressToBaseBlock(); +} + +template +const void *CompressedBlock::data() const { + return _decompressedData.data(); +} + +template +void CompressedBlock::write(const void *source, uint64_t offset, uint64_t size) { + std::memcpy((uint8_t*)_decompressedData.dataOffset(offset), source, size); + _dataChanged = true; +} + +template +void CompressedBlock::flush() { + std::unique_lock lock(_mutex); + _compressToBaseBlock(); + return _baseBlock->flush(); +} + +template +size_t CompressedBlock::size() const { + return _decompressedData.size(); +} + +template +void CompressedBlock::resize(size_t newSize) { + _decompressedData = cpputils::DataUtils::resize(std::move(_decompressedData), newSize); + _dataChanged = true; +} + +template +cpputils::unique_ref CompressedBlock::releaseBaseBlock() { + std::unique_lock lock(_mutex); + _compressToBaseBlock(); + return std::move(_baseBlock); +} + +template +void CompressedBlock::_compressToBaseBlock() { + if (_dataChanged) { + cpputils::Data compressed = Compressor::Compress(_decompressedData); + _baseBlock->resize(compressed.size()); + _baseBlock->write(compressed.data(), 0, compressed.size()); + _dataChanged = false; + } +} + +} +} + +#endif diff --git a/src/blockstore/implementations/compressing/CompressingBlockStore.cpp b/src/blockstore/implementations/compressing/CompressingBlockStore.cpp new file mode 100644 index 00000000..ab79d260 --- /dev/null +++ b/src/blockstore/implementations/compressing/CompressingBlockStore.cpp @@ -0,0 +1 @@ +#include "CompressingBlockStore.h" diff --git a/src/blockstore/implementations/compressing/CompressingBlockStore.h b/src/blockstore/implementations/compressing/CompressingBlockStore.h new file mode 100644 index 00000000..06b22c66 --- /dev/null +++ b/src/blockstore/implementations/compressing/CompressingBlockStore.h @@ -0,0 +1,77 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSINGBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSINGBLOCKSTORE_H_ + +#include "../../interface/BlockStore.h" +#include "CompressedBlock.h" + +namespace blockstore { +namespace compressing { + +template +class CompressingBlockStore final: public BlockStore { +public: + CompressingBlockStore(cpputils::unique_ref baseBlockStore); + ~CompressingBlockStore(); + + Key createKey() override; + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + +private: + cpputils::unique_ref _baseBlockStore; + + DISALLOW_COPY_AND_ASSIGN(CompressingBlockStore); +}; + +template +CompressingBlockStore::CompressingBlockStore(cpputils::unique_ref baseBlockStore) + : _baseBlockStore(std::move(baseBlockStore)) { +} + +template +CompressingBlockStore::~CompressingBlockStore() { +} + +template +Key CompressingBlockStore::createKey() { + return _baseBlockStore->createKey(); +} + +template +boost::optional> CompressingBlockStore::tryCreate(const Key &key, cpputils::Data data) { + auto result = CompressedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data)); + if (result == boost::none) { + return boost::none; + } + return cpputils::unique_ref(std::move(*result)); +} + +template +boost::optional> CompressingBlockStore::load(const Key &key) { + auto loaded = _baseBlockStore->load(key); + if (loaded == boost::none) { + return boost::none; + } + return boost::optional>(CompressedBlock::Decompress(std::move(*loaded))); +} + +template +void CompressingBlockStore::remove(cpputils::unique_ref block) { + auto _block = cpputils::dynamic_pointer_move>(block); + ASSERT(_block != boost::none, "Wrong block type"); + auto baseBlock = (*_block)->releaseBaseBlock(); + return _baseBlockStore->remove(std::move(baseBlock)); +} + +template +uint64_t CompressingBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks(); +} + +} +} + +#endif diff --git a/src/blockstore/implementations/compressing/compressors/Gzip.cpp b/src/blockstore/implementations/compressing/compressors/Gzip.cpp new file mode 100644 index 00000000..3a8f5705 --- /dev/null +++ b/src/blockstore/implementations/compressing/compressors/Gzip.cpp @@ -0,0 +1,29 @@ +#include "Gzip.h" +#include + +using cpputils::Data; + +namespace blockstore { + namespace compressing { + + Data Gzip::Compress(const Data &data) { + CryptoPP::Gzip zipper; + zipper.Put((byte *) data.data(), data.size()); + zipper.MessageEnd(); + Data compressed(zipper.MaxRetrievable()); + zipper.Get((byte *) compressed.data(), compressed.size()); + return compressed; + } + + Data Gzip::Decompress(const void *data, size_t size) { + //TODO Change interface to taking cpputils::Data objects (needs changing blockstore so we can read their "class Data", because this is called from CompressedBlock::Decompress()). + CryptoPP::Gunzip zipper; + zipper.Put((byte *) data, size); + zipper.MessageEnd(); + Data decompressed(zipper.MaxRetrievable()); + zipper.Get((byte *) decompressed.data(), decompressed.size()); + return decompressed; + } + + } +} \ No newline at end of file diff --git a/src/blockstore/implementations/compressing/compressors/Gzip.h b/src/blockstore/implementations/compressing/compressors/Gzip.h new file mode 100644 index 00000000..12ac23ac --- /dev/null +++ b/src/blockstore/implementations/compressing/compressors/Gzip.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSORS_GZIP_H +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSORS_GZIP_H + +#include + +namespace blockstore { + namespace compressing { + class Gzip { + public: + static cpputils::Data Compress(const cpputils::Data &data); + + static cpputils::Data Decompress(const void *data, size_t size); + }; + } +} + +#endif diff --git a/src/blockstore/implementations/compressing/compressors/RunLengthEncoding.cpp b/src/blockstore/implementations/compressing/compressors/RunLengthEncoding.cpp new file mode 100644 index 00000000..3a20d759 --- /dev/null +++ b/src/blockstore/implementations/compressing/compressors/RunLengthEncoding.cpp @@ -0,0 +1,139 @@ +#include "RunLengthEncoding.h" +#include +#include + +using cpputils::Data; +using std::string; +using std::ostringstream; +using std::istringstream; + +namespace blockstore { + namespace compressing { + + // Alternatively store a run of arbitrary bytes and a run of identical bytes. + // Each run is preceded by its length. Length fields are uint16_t. + // Example: 2 - 5 - 8 - 10 - 3 - 0 - 2 - 0 + // Length 2 arbitrary bytes (values: 5, 8), the next 10 bytes store "3" each, + // then 0 arbitrary bytes and 2x "0". + + Data RunLengthEncoding::Compress(const Data &data) { + ostringstream compressed; + uint8_t *current = (uint8_t*)data.data(); + uint8_t *end = (uint8_t*)data.data()+data.size(); + while (current < end) { + _encodeArbitraryWords(¤t, end, &compressed); + ASSERT(current <= end, "Overflow"); + if (current == end) { + break; + } + _encodeIdenticalWords(¤t, end, &compressed); + ASSERT(current <= end, "Overflow"); + } + return _extractData(&compressed); + } + + void RunLengthEncoding::_encodeArbitraryWords(uint8_t **current, uint8_t* end, ostringstream *output) { + uint16_t size = _arbitraryRunLength(*current, end); + output->write((const char*)&size, sizeof(uint16_t)); + output->write((const char*)*current, size); + *current += size; + } + + uint16_t RunLengthEncoding::_arbitraryRunLength(uint8_t *start, uint8_t* end) { + // Each stopping of an arbitrary bytes run costs us 5 byte, because we have to store the length + // for the identical bytes run (2 byte), the identical byte itself (1 byte) and the length for the next arbitrary bytes run (2 byte). + // So to get an advantage from stopping an arbitrary bytes run, at least 6 bytes have to be identical. + + // realEnd avoids an overflow of the 16bit counter + uint8_t *realEnd = std::min(end, start + std::numeric_limits::max()); + + // Count the number of identical bytes and return if it finds a run of more than 6 identical bytes. + uint8_t lastByte = *start + 1; // Something different from the first byte + uint8_t numIdenticalBytes = 1; + for(uint8_t *current = start; current != realEnd; ++current) { + if (*current == lastByte) { + ++numIdenticalBytes; + if (numIdenticalBytes == 6) { + return current - start - 5; //-5, because the end pointer for the arbitrary byte run should point to the first identical byte, not the one before. + } + } else { + numIdenticalBytes = 1; + } + lastByte = *current; + } + //It wasn't worth stopping the arbitrary bytes run anywhere. The whole region should be an arbitrary run. + return realEnd-start; + } + + void RunLengthEncoding::_encodeIdenticalWords(uint8_t **current, uint8_t* end, ostringstream *output) { + uint16_t size = _countIdenticalBytes(*current, end); + output->write((const char*)&size, sizeof(uint16_t)); + output->write((const char*)*current, 1); + *current += size; + } + + uint16_t RunLengthEncoding::_countIdenticalBytes(uint8_t *start, uint8_t *end) { + uint8_t *realEnd = std::min(end, start + std::numeric_limits::max()); // This prevents overflow of the 16bit counter + for (uint8_t *current = start+1; current != realEnd; ++current) { + if (*current != *start) { + return current-start; + } + } + // All bytes have been identical + return realEnd - start; + } + + Data RunLengthEncoding::_extractData(ostringstream *stream) { + string str = stream->str(); + Data data(str.size()); + std::memcpy(data.data(), str.c_str(), str.size()); + return data; + } + + Data RunLengthEncoding::Decompress(const void *data, size_t size) { + istringstream stream; + _parseData((uint8_t*)data, size, &stream); + ostringstream decompressed; + while(_hasData(&stream)) { + _decodeArbitraryWords(&stream, &decompressed); + if (!_hasData(&stream)) { + break; + } + _decodeIdenticalWords(&stream, &decompressed); + } + return _extractData(&decompressed); + } + + bool RunLengthEncoding::_hasData(istringstream *str) { + str->peek(); + return !str->eof(); + } + + void RunLengthEncoding::_parseData(const uint8_t *data, size_t size, istringstream *result) { + result->str(string((const char*)data, size)); + } + + void RunLengthEncoding::_decodeArbitraryWords(istringstream *stream, ostringstream *decompressed) { + uint16_t size; + stream->read((char*)&size, sizeof(uint16_t)); + ASSERT(stream->good(), "Premature end of stream"); + Data run(size); + stream->read((char*)run.data(), size); + ASSERT(stream->good(), "Premature end of stream"); + decompressed->write((const char*)run.data(), run.size()); + } + + void RunLengthEncoding::_decodeIdenticalWords(istringstream *stream, ostringstream *decompressed) { + uint16_t size; + stream->read((char*)&size, sizeof(uint16_t)); + ASSERT(stream->good(), "Premature end of stream"); + uint8_t value; + stream->read((char*)&value, 1); + ASSERT(stream->good(), "Premature end of stream"); + Data run(size); + std::memset(run.data(), value, run.size()); + decompressed->write((const char*)run.data(), run.size()); + } + + } +} diff --git a/src/blockstore/implementations/compressing/compressors/RunLengthEncoding.h b/src/blockstore/implementations/compressing/compressors/RunLengthEncoding.h new file mode 100644 index 00000000..2342f61e --- /dev/null +++ b/src/blockstore/implementations/compressing/compressors/RunLengthEncoding.h @@ -0,0 +1,29 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSORS_RUNLENGTHENCODING_H +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_COMPRESSING_COMPRESSORS_RUNLENGTHENCODING_H + +#include + +namespace blockstore { + namespace compressing { + class RunLengthEncoding { + public: + static cpputils::Data Compress(const cpputils::Data &data); + + static cpputils::Data Decompress(const void *data, size_t size); + + private: + static void _encodeArbitraryWords(uint8_t **current, uint8_t* end, std::ostringstream *output); + static uint16_t _arbitraryRunLength(uint8_t *start, uint8_t* end); + static void _encodeIdenticalWords(uint8_t **current, uint8_t* end, std::ostringstream *output); + static uint16_t _countIdenticalBytes(uint8_t *start, uint8_t *end); + static bool _hasData(std::istringstream *stream); + static cpputils::Data _extractData(std::ostringstream *stream); + static void _parseData(const uint8_t *data, size_t size, std::istringstream *result); + static void _decodeArbitraryWords(std::istringstream *stream, std::ostringstream *decompressed); + static void _decodeIdenticalWords(std::istringstream *stream, std::ostringstream *decompressed); + }; + } +} + +#endif diff --git a/src/blockstore/implementations/encrypted/EncryptedBlock.cpp b/src/blockstore/implementations/encrypted/EncryptedBlock.cpp new file mode 100644 index 00000000..5f66d9c9 --- /dev/null +++ b/src/blockstore/implementations/encrypted/EncryptedBlock.cpp @@ -0,0 +1 @@ +#include "EncryptedBlock.h" diff --git a/src/blockstore/implementations/encrypted/EncryptedBlock.h b/src/blockstore/implementations/encrypted/EncryptedBlock.h new file mode 100644 index 00000000..1cb40110 --- /dev/null +++ b/src/blockstore/implementations/encrypted/EncryptedBlock.h @@ -0,0 +1,177 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_ + +#include "../../interface/Block.h" +#include +#include "../../interface/BlockStore.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace blockstore { +namespace encrypted { +template class EncryptedBlockStore; + +//TODO Test EncryptedBlock + +//TODO Fix mutexes & locks (basically true for all blockstores) + +template +class EncryptedBlock final: public Block { +public: + BOOST_CONCEPT_ASSERT((cpputils::CipherConcept)); + static boost::optional> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, const typename Cipher::EncryptionKey &encKey); + static boost::optional> TryDecrypt(cpputils::unique_ref baseBlock, const typename Cipher::EncryptionKey &key); + + //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. + EncryptedBlock(cpputils::unique_ref baseBlock, const typename Cipher::EncryptionKey &key, cpputils::Data plaintextWithHeader); + ~EncryptedBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t count) override; + void flush() override; + + size_t size() const override; + void resize(size_t newSize) override; + + cpputils::unique_ref releaseBlock(); + +private: + cpputils::unique_ref _baseBlock; // TODO Do I need the ciphertext block in memory or is the key enough? + cpputils::Data _plaintextWithHeader; + typename Cipher::EncryptionKey _encKey; + bool _dataChanged; + + static constexpr unsigned int HEADER_LENGTH = Key::BINARY_LENGTH; + + void _encryptToBaseBlock(); + static cpputils::Data _prependKeyHeaderToData(const Key &key, cpputils::Data data); + static bool _keyHeaderIsCorrect(const Key &key, const cpputils::Data &data); + + std::mutex _mutex; + + DISALLOW_COPY_AND_ASSIGN(EncryptedBlock); +}; + +template +constexpr unsigned int EncryptedBlock::HEADER_LENGTH; + + +template +boost::optional>> EncryptedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, const typename Cipher::EncryptionKey &encKey) { + cpputils::Data plaintextWithHeader = _prependKeyHeaderToData(key, std::move(data)); + cpputils::Data encrypted = Cipher::encrypt((byte*)plaintextWithHeader.data(), plaintextWithHeader.size(), encKey); + auto baseBlock = baseBlockStore->tryCreate(key, std::move(encrypted)); + if (baseBlock == boost::none) { + //TODO Test this code branch + return boost::none; + } + + return cpputils::make_unique_ref(std::move(*baseBlock), encKey, std::move(plaintextWithHeader)); +} + +template +boost::optional>> EncryptedBlock::TryDecrypt(cpputils::unique_ref baseBlock, const typename Cipher::EncryptionKey &encKey) { + //TODO Change BlockStore so we can read their "class Data" objects instead of "void *data()", and then we can change the Cipher interface to take Data objects instead of "byte *" + size + boost::optional plaintextWithHeader = Cipher::decrypt((byte*)baseBlock->data(), baseBlock->size(), encKey); + if(plaintextWithHeader == boost::none) { + //Decryption failed (e.g. an authenticated cipher detected modifications to the ciphertext) + cpputils::logging::LOG(cpputils::logging::WARN) << "Decrypting block " << baseBlock->key().ToString() << " failed. Was the block modified by an attacker?"; + return boost::none; + } + if(!_keyHeaderIsCorrect(baseBlock->key(), *plaintextWithHeader)) { + //The stored key in the block data is incorrect - an attacker might have exchanged the contents with the encrypted data from a different block + cpputils::logging::LOG(cpputils::logging::WARN) << "Decrypting block " << baseBlock->key().ToString() << " failed due to invalid block key. Was the block modified by an attacker?"; + return boost::none; + } + return cpputils::make_unique_ref>(std::move(baseBlock), encKey, std::move(*plaintextWithHeader)); +} + +template +cpputils::Data EncryptedBlock::_prependKeyHeaderToData(const Key &key, cpputils::Data data) { + static_assert(HEADER_LENGTH >= Key::BINARY_LENGTH, "Key doesn't fit into the header"); + cpputils::Data result(data.size() + HEADER_LENGTH); + std::memcpy(result.data(), key.data(), Key::BINARY_LENGTH); + std::memcpy((uint8_t*)result.data() + Key::BINARY_LENGTH, data.data(), data.size()); + return result; +} + +template +bool EncryptedBlock::_keyHeaderIsCorrect(const Key &key, const cpputils::Data &data) { + return 0 == std::memcmp(key.data(), data.data(), Key::BINARY_LENGTH); +} + +template +EncryptedBlock::EncryptedBlock(cpputils::unique_ref baseBlock, const typename Cipher::EncryptionKey &encKey, cpputils::Data plaintextWithHeader) + :Block(baseBlock->key()), + _baseBlock(std::move(baseBlock)), + _plaintextWithHeader(std::move(plaintextWithHeader)), + _encKey(encKey), + _dataChanged(false), + _mutex() { +} + +template +EncryptedBlock::~EncryptedBlock() { + std::unique_lock lock(_mutex); + _encryptToBaseBlock(); +} + +template +const void *EncryptedBlock::data() const { + return (uint8_t*)_plaintextWithHeader.data() + HEADER_LENGTH; +} + +template +void EncryptedBlock::write(const void *source, uint64_t offset, uint64_t count) { + ASSERT(offset <= size() && offset + count <= size(), "Write outside of valid area"); //Also check offset < size() because of possible overflow in the addition + std::memcpy((uint8_t*)_plaintextWithHeader.data()+HEADER_LENGTH+offset, source, count); + _dataChanged = true; +} + +template +void EncryptedBlock::flush() { + std::unique_lock lock(_mutex); + _encryptToBaseBlock(); + return _baseBlock->flush(); +} + +template +size_t EncryptedBlock::size() const { + return _plaintextWithHeader.size() - HEADER_LENGTH; +} + +template +void EncryptedBlock::resize(size_t newSize) { + _plaintextWithHeader = cpputils::DataUtils::resize(std::move(_plaintextWithHeader), newSize + HEADER_LENGTH); + _dataChanged = true; +} + +template +void EncryptedBlock::_encryptToBaseBlock() { + if (_dataChanged) { + cpputils::Data encrypted = Cipher::encrypt((byte*)_plaintextWithHeader.data(), _plaintextWithHeader.size(), _encKey); + _baseBlock->write(encrypted.data(), 0, encrypted.size()); + _dataChanged = false; + } +} + +template +cpputils::unique_ref EncryptedBlock::releaseBlock() { + std::unique_lock lock(_mutex); + _encryptToBaseBlock(); + return std::move(_baseBlock); +} + +} +} + +#endif diff --git a/src/blockstore/implementations/encrypted/EncryptedBlockStore.cpp b/src/blockstore/implementations/encrypted/EncryptedBlockStore.cpp new file mode 100644 index 00000000..5ab7ca32 --- /dev/null +++ b/src/blockstore/implementations/encrypted/EncryptedBlockStore.cpp @@ -0,0 +1 @@ +#include "EncryptedBlockStore.h" diff --git a/src/blockstore/implementations/encrypted/EncryptedBlockStore.h b/src/blockstore/implementations/encrypted/EncryptedBlockStore.h new file mode 100644 index 00000000..30943521 --- /dev/null +++ b/src/blockstore/implementations/encrypted/EncryptedBlockStore.h @@ -0,0 +1,90 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCKSTORE_H_ + +#include "../../interface/BlockStore.h" +#include +#include +#include "EncryptedBlock.h" +#include + +namespace blockstore { +namespace encrypted { + +template +class EncryptedBlockStore final: public BlockStore { +public: + EncryptedBlockStore(cpputils::unique_ref baseBlockStore, const typename Cipher::EncryptionKey &encKey); + + //TODO Are createKey() tests included in generic BlockStoreTest? If not, add it! + Key createKey() override; + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + + //This function should only be used by test cases + void __setKey(const typename Cipher::EncryptionKey &encKey); + +private: + cpputils::unique_ref _baseBlockStore; + typename Cipher::EncryptionKey _encKey; + + DISALLOW_COPY_AND_ASSIGN(EncryptedBlockStore); +}; + + + +template +EncryptedBlockStore::EncryptedBlockStore(cpputils::unique_ref baseBlockStore, const typename Cipher::EncryptionKey &encKey) + : _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) { +} + +template +Key EncryptedBlockStore::createKey() { + return _baseBlockStore->createKey(); +} + +template +boost::optional> EncryptedBlockStore::tryCreate(const Key &key, cpputils::Data data) { + //TODO Test that this returns boost::none when base blockstore returns nullptr (for all pass-through-blockstores) + //TODO Easier implementation? This is only so complicated because of the case EncryptedBlock -> Block + auto result = EncryptedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), _encKey); + if (result == boost::none) { + return boost::none; + } + return cpputils::unique_ref(std::move(*result)); +} + +template +boost::optional> EncryptedBlockStore::load(const Key &key) { + auto block = _baseBlockStore->load(key); + if (block == boost::none) { + //TODO Test this path (for all pass-through-blockstores) + return boost::none; + } + return boost::optional>(EncryptedBlock::TryDecrypt(std::move(*block), _encKey)); +} + +template +void EncryptedBlockStore::remove(cpputils::unique_ref block) { + auto encryptedBlock = cpputils::dynamic_pointer_move>(block); + ASSERT(encryptedBlock != boost::none, "Block is not an EncryptedBlock"); + auto baseBlock = (*encryptedBlock)->releaseBlock(); + return _baseBlockStore->remove(std::move(baseBlock)); +} + +template +uint64_t EncryptedBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks(); +} + +template +void EncryptedBlockStore::__setKey(const typename Cipher::EncryptionKey &encKey) { + _encKey = encKey; +} + +} +} + +#endif diff --git a/src/blockstore/implementations/inmemory/InMemoryBlock.cpp b/src/blockstore/implementations/inmemory/InMemoryBlock.cpp new file mode 100644 index 00000000..6ebd50fa --- /dev/null +++ b/src/blockstore/implementations/inmemory/InMemoryBlock.cpp @@ -0,0 +1,50 @@ +#include "InMemoryBlock.h" +#include "InMemoryBlockStore.h" +#include +#include +#include + +using std::make_shared; +using std::istream; +using std::ostream; +using std::ifstream; +using std::ofstream; +using std::ios; +using cpputils::Data; + +namespace blockstore { +namespace inmemory { + +InMemoryBlock::InMemoryBlock(const Key &key, Data data) + : Block(key), _data(make_shared(std::move(data))) { +} + +InMemoryBlock::InMemoryBlock(const InMemoryBlock &rhs) + : Block(rhs), _data(rhs._data) { +} + +InMemoryBlock::~InMemoryBlock() { +} + +const void *InMemoryBlock::data() const { + return _data->data(); +} + +void InMemoryBlock::write(const void *source, uint64_t offset, uint64_t size) { + ASSERT(offset <= _data->size() && offset + size <= _data->size(), "Write outside of valid area"); //Also check offset < _data->size() because of possible overflow in the addition + std::memcpy((uint8_t*)_data->data()+offset, source, size); +} + +size_t InMemoryBlock::size() const { + return _data->size(); +} + +void InMemoryBlock::resize(size_t newSize) { + *_data = cpputils::DataUtils::resize(std::move(*_data), newSize); +} + +void InMemoryBlock::flush() { +} + +} +} diff --git a/src/blockstore/implementations/inmemory/InMemoryBlock.h b/src/blockstore/implementations/inmemory/InMemoryBlock.h new file mode 100644 index 00000000..3f688637 --- /dev/null +++ b/src/blockstore/implementations/inmemory/InMemoryBlock.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCK_H_ + +#include "../../interface/Block.h" +#include + +namespace blockstore { +namespace inmemory { +class InMemoryBlockStore; + +class InMemoryBlock final: public Block { +public: + InMemoryBlock(const Key &key, cpputils::Data size); + InMemoryBlock(const InMemoryBlock &rhs); + ~InMemoryBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + + void flush() override; + + size_t size() const override; + void resize(size_t newSize) override; + +private: + std::shared_ptr _data; +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/inmemory/InMemoryBlockStore.cpp b/src/blockstore/implementations/inmemory/InMemoryBlockStore.cpp new file mode 100644 index 00000000..17124fea --- /dev/null +++ b/src/blockstore/implementations/inmemory/InMemoryBlockStore.cpp @@ -0,0 +1,56 @@ +#include "InMemoryBlock.h" +#include "InMemoryBlockStore.h" +#include +#include + +using std::make_unique; +using std::string; +using std::mutex; +using std::lock_guard; +using std::piecewise_construct; +using std::make_tuple; +using cpputils::Data; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using boost::optional; +using boost::none; + +namespace blockstore { +namespace inmemory { + +InMemoryBlockStore::InMemoryBlockStore() + : _blocks() {} + +optional> InMemoryBlockStore::tryCreate(const Key &key, Data data) { + auto insert_result = _blocks.emplace(piecewise_construct, make_tuple(key.ToString()), make_tuple(key, std::move(data))); + + if (!insert_result.second) { + return none; + } + + //Return a pointer to the stored InMemoryBlock + return optional>(make_unique_ref(insert_result.first->second)); +} + +optional> InMemoryBlockStore::load(const Key &key) { + //Return a pointer to the stored InMemoryBlock + try { + return optional>(make_unique_ref(_blocks.at(key.ToString()))); + } catch (const std::out_of_range &e) { + return none; + } +} + +void InMemoryBlockStore::remove(unique_ref block) { + Key key = block->key(); + cpputils::destruct(std::move(block)); + int numRemoved = _blocks.erase(key.ToString()); + ASSERT(1==numRemoved, "Didn't find block to remove"); +} + +uint64_t InMemoryBlockStore::numBlocks() const { + return _blocks.size(); +} + +} +} diff --git a/src/blockstore/implementations/inmemory/InMemoryBlockStore.h b/src/blockstore/implementations/inmemory/InMemoryBlockStore.h new file mode 100644 index 00000000..6fbfe7ae --- /dev/null +++ b/src/blockstore/implementations/inmemory/InMemoryBlockStore.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE_H_ + +#include "../../interface/helpers/BlockStoreWithRandomKeys.h" +#include + +#include +#include + +namespace blockstore { +namespace inmemory { +class InMemoryBlock; + +class InMemoryBlockStore final: public BlockStoreWithRandomKeys { +public: + InMemoryBlockStore(); + + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + +private: + std::map _blocks; + + DISALLOW_COPY_AND_ASSIGN(InMemoryBlockStore); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/ondisk/OnDiskBlock.cpp b/src/blockstore/implementations/ondisk/OnDiskBlock.cpp new file mode 100644 index 00000000..ffb789bb --- /dev/null +++ b/src/blockstore/implementations/ondisk/OnDiskBlock.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include "OnDiskBlock.h" +#include "OnDiskBlockStore.h" +#include "../../utils/FileDoesntExistException.h" +#include +#include + +using std::istream; +using std::ostream; +using std::ifstream; +using std::ofstream; +using std::ios; +using cpputils::Data; +using cpputils::make_unique_ref; +using cpputils::unique_ref; +using boost::optional; +using boost::none; + +namespace bf = boost::filesystem; + +namespace blockstore { +namespace ondisk { + +OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, Data data) + : Block(key), _filepath(filepath), _data(std::move(data)), _dataChanged(false), _mutex() { +} + +OnDiskBlock::~OnDiskBlock() { + flush(); +} + +const void *OnDiskBlock::data() const { + return _data.data(); +} + +void OnDiskBlock::write(const void *source, uint64_t offset, uint64_t size) { + ASSERT(offset <= _data.size() && offset + size <= _data.size(), "Write outside of valid area"); //Also check offset < _data->size() because of possible overflow in the addition + std::memcpy((uint8_t*)_data.data()+offset, source, size); + _dataChanged = true; +} + +size_t OnDiskBlock::size() const { + return _data.size(); +} + +void OnDiskBlock::resize(size_t newSize) { + _data = cpputils::DataUtils::resize(std::move(_data), newSize); + _dataChanged = true; +} + +optional> OnDiskBlock::LoadFromDisk(const bf::path &rootdir, const Key &key) { + auto filepath = rootdir / key.ToString(); + try { + //If it isn't a file, Data::LoadFromFile() would usually also crash. We still need this extra check + //upfront, because Data::LoadFromFile() doesn't crash if we give it the path of a directory + //instead the path of a file. + //TODO Data::LoadFromFile now returns boost::optional. Do we then still need this? + if(!bf::is_regular_file(filepath)) { + return none; + } + boost::optional data = Data::LoadFromFile(filepath); + if (!data) { + return none; + } + return make_unique_ref(key, filepath, std::move(*data)); + } catch (const FileDoesntExistException &e) { + return none; + } +} + +optional> OnDiskBlock::CreateOnDisk(const bf::path &rootdir, const Key &key, Data data) { + auto filepath = rootdir / key.ToString(); + if (bf::exists(filepath)) { + return none; + } + + auto block = make_unique_ref(key, filepath, std::move(data)); + block->_storeToDisk(); + return std::move(block); +} + +void OnDiskBlock::RemoveFromDisk(const bf::path &rootdir, const Key &key) { + auto filepath = rootdir / key.ToString(); + ASSERT(bf::is_regular_file(filepath), "Block not found on disk"); + bf::remove(filepath); +} + +void OnDiskBlock::_fillDataWithZeroes() { + _data.FillWithZeroes(); + _dataChanged = true; +} + +void OnDiskBlock::_storeToDisk() const { + _data.StoreToFile(_filepath); +} + +void OnDiskBlock::flush() { + std::unique_lock lock(_mutex); + if (_dataChanged) { + _storeToDisk(); + _dataChanged = false; + } +} + +} +} diff --git a/src/blockstore/implementations/ondisk/OnDiskBlock.h b/src/blockstore/implementations/ondisk/OnDiskBlock.h new file mode 100644 index 00000000..03f958dc --- /dev/null +++ b/src/blockstore/implementations/ondisk/OnDiskBlock.h @@ -0,0 +1,50 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ONDISK_ONDISKBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ONDISK_ONDISKBLOCK_H_ + +#include +#include "../../interface/Block.h" +#include +#include + +#include +#include + +namespace blockstore { +namespace ondisk { +class OnDiskBlockStore; + +class OnDiskBlock final: public Block { +public: + OnDiskBlock(const Key &key, const boost::filesystem::path &filepath, cpputils::Data data); + ~OnDiskBlock(); + + static boost::optional> LoadFromDisk(const boost::filesystem::path &rootdir, const Key &key); + static boost::optional> CreateOnDisk(const boost::filesystem::path &rootdir, const Key &key, cpputils::Data data); + static void RemoveFromDisk(const boost::filesystem::path &rootdir, const Key &key); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + + void flush() override; + + size_t size() const override; + void resize(size_t newSize) override; + +private: + const boost::filesystem::path _filepath; + cpputils::Data _data; + bool _dataChanged; + + void _fillDataWithZeroes(); + void _storeToDisk() const; + + std::mutex _mutex; + + DISALLOW_COPY_AND_ASSIGN(OnDiskBlock); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/ondisk/OnDiskBlockStore.cpp b/src/blockstore/implementations/ondisk/OnDiskBlockStore.cpp new file mode 100644 index 00000000..9ef83446 --- /dev/null +++ b/src/blockstore/implementations/ondisk/OnDiskBlockStore.cpp @@ -0,0 +1,52 @@ +#include "OnDiskBlock.h" +#include "OnDiskBlockStore.h" + +using std::string; +using cpputils::Data; +using cpputils::unique_ref; +using boost::optional; +using boost::none; + +namespace bf = boost::filesystem; + +namespace blockstore { +namespace ondisk { + +OnDiskBlockStore::OnDiskBlockStore(const boost::filesystem::path &rootdir) + : _rootdir(rootdir) { + if (!bf::exists(rootdir)) { + throw std::runtime_error("Base directory not found"); + } + if (!bf::is_directory(rootdir)) { + throw std::runtime_error("Base directory is not a directory"); + } + //TODO Test for read access, write access, enter (x) access, and throw runtime_error in case +} + +//TODO Do I have to lock tryCreate/remove and/or load? Or does ParallelAccessBlockStore take care of that? + +optional> OnDiskBlockStore::tryCreate(const Key &key, Data data) { + //TODO Easier implementation? This is only so complicated because of the cast OnDiskBlock -> Block + auto result = std::move(OnDiskBlock::CreateOnDisk(_rootdir, key, std::move(data))); + if (result == boost::none) { + return boost::none; + } + return unique_ref(std::move(*result)); +} + +optional> OnDiskBlockStore::load(const Key &key) { + return optional>(OnDiskBlock::LoadFromDisk(_rootdir, key)); +} + +void OnDiskBlockStore::remove(unique_ref block) { + Key key = block->key(); + cpputils::destruct(std::move(block)); + OnDiskBlock::RemoveFromDisk(_rootdir, key); +} + +uint64_t OnDiskBlockStore::numBlocks() const { + return std::distance(bf::directory_iterator(_rootdir), bf::directory_iterator()); +} + +} +} diff --git a/src/blockstore/implementations/ondisk/OnDiskBlockStore.h b/src/blockstore/implementations/ondisk/OnDiskBlockStore.h new file mode 100644 index 00000000..59bf449d --- /dev/null +++ b/src/blockstore/implementations/ondisk/OnDiskBlockStore.h @@ -0,0 +1,31 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ONDISK_ONDISKBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ONDISK_ONDISKBLOCKSTORE_H_ + +#include +#include "../../interface/helpers/BlockStoreWithRandomKeys.h" + +#include + +namespace blockstore { +namespace ondisk { + +class OnDiskBlockStore final: public BlockStoreWithRandomKeys { +public: + explicit OnDiskBlockStore(const boost::filesystem::path &rootdir); + + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + +private: + const boost::filesystem::path _rootdir; + + DISALLOW_COPY_AND_ASSIGN(OnDiskBlockStore); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/parallelaccess/BlockRef.cpp b/src/blockstore/implementations/parallelaccess/BlockRef.cpp new file mode 100644 index 00000000..f35230b4 --- /dev/null +++ b/src/blockstore/implementations/parallelaccess/BlockRef.cpp @@ -0,0 +1 @@ +#include "BlockRef.h" diff --git a/src/blockstore/implementations/parallelaccess/BlockRef.h b/src/blockstore/implementations/parallelaccess/BlockRef.h new file mode 100644 index 00000000..a4f742bb --- /dev/null +++ b/src/blockstore/implementations/parallelaccess/BlockRef.h @@ -0,0 +1,48 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_PARALLELACCESS_BLOCKREF_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_PARALLELACCESS_BLOCKREF_H_ + +#include +#include "../../interface/Block.h" +#include +#include + +namespace blockstore { +namespace parallelaccess { +class ParallelAccessBlockStore; + +class BlockRef final: public Block, public parallelaccessstore::ParallelAccessStore::ResourceRefBase { +public: + //TODO Unneccessarily storing Key twice here (in parent class and in _baseBlock). + explicit BlockRef(Block *baseBlock): Block(baseBlock->key()), _baseBlock(baseBlock) {} + + const void *data() const override { + return _baseBlock->data(); + } + + void write(const void *source, uint64_t offset, uint64_t size) override { + return _baseBlock->write(source, offset, size); + } + + void flush() override { + return _baseBlock->flush(); + } + + size_t size() const override { + return _baseBlock->size(); + } + + void resize(size_t newSize) override { + return _baseBlock->resize(newSize); + } + +private: + Block *_baseBlock; + + DISALLOW_COPY_AND_ASSIGN(BlockRef); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStore.cpp b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStore.cpp new file mode 100644 index 00000000..308352c0 --- /dev/null +++ b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStore.cpp @@ -0,0 +1,60 @@ +#include "BlockRef.h" +#include "ParallelAccessBlockStore.h" +#include "ParallelAccessBlockStoreAdapter.h" +#include +#include +#include + +using std::string; +using std::promise; +using cpputils::dynamic_pointer_move; +using cpputils::make_unique_ref; +using boost::none; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using boost::optional; +using boost::none; + +namespace blockstore { +namespace parallelaccess { + +ParallelAccessBlockStore::ParallelAccessBlockStore(unique_ref baseBlockStore) + : _baseBlockStore(std::move(baseBlockStore)), _parallelAccessStore(make_unique_ref(_baseBlockStore.get())) { +} + +Key ParallelAccessBlockStore::createKey() { + return _baseBlockStore->createKey(); +} + +optional> ParallelAccessBlockStore::tryCreate(const Key &key, cpputils::Data data) { + ASSERT(!_parallelAccessStore.isOpened(key), ("Key "+key.ToString()+"already exists").c_str()); + auto block = _baseBlockStore->tryCreate(key, std::move(data)); + if (block == none) { + //TODO Test this code branch + return none; + } + return unique_ref(_parallelAccessStore.add(key, std::move(*block))); +} + +optional> ParallelAccessBlockStore::load(const Key &key) { + auto block = _parallelAccessStore.load(key); + if (block == none) { + return none; + } + return unique_ref(std::move(*block)); +} + + +void ParallelAccessBlockStore::remove(unique_ref block) { + Key key = block->key(); + auto block_ref = dynamic_pointer_move(block); + ASSERT(block_ref != none, "Block is not a BlockRef"); + return _parallelAccessStore.remove(key, std::move(*block_ref)); +} + +uint64_t ParallelAccessBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks(); +} + +} +} diff --git a/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStore.h b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStore.h new file mode 100644 index 00000000..b87bd57a --- /dev/null +++ b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStore.h @@ -0,0 +1,34 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_PARALLELACCESS_PARALLELACCESSBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_PARALLELACCESS_PARALLELACCESSBLOCKSTORE_H_ + +#include +#include "BlockRef.h" +#include "../../interface/BlockStore.h" +#include + +namespace blockstore { +namespace parallelaccess { + +//TODO Check that this blockstore allows parallel destructing of blocks (otherwise we won't encrypt blocks in parallel) +class ParallelAccessBlockStore final: public BlockStore { +public: + explicit ParallelAccessBlockStore(cpputils::unique_ref baseBlockStore); + + Key createKey() override; + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + +private: + cpputils::unique_ref _baseBlockStore; + parallelaccessstore::ParallelAccessStore _parallelAccessStore; + + DISALLOW_COPY_AND_ASSIGN(ParallelAccessBlockStore); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.cpp b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.cpp new file mode 100644 index 00000000..49b896b0 --- /dev/null +++ b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.cpp @@ -0,0 +1 @@ +#include "ParallelAccessBlockStoreAdapter.h" diff --git a/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h new file mode 100644 index 00000000..679b30d5 --- /dev/null +++ b/src/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h @@ -0,0 +1,35 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_PARALLELACCESS_PARALLELACCESSBLOCKSTOREADAPTER_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_PARALLELACCESS_PARALLELACCESSBLOCKSTOREADAPTER_H_ + +#include +#include +#include "../../interface/BlockStore.h" + +namespace blockstore { +namespace parallelaccess { + +class ParallelAccessBlockStoreAdapter final: public parallelaccessstore::ParallelAccessBaseStore { +public: + explicit ParallelAccessBlockStoreAdapter(BlockStore *baseBlockStore) + :_baseBlockStore(std::move(baseBlockStore)) { + } + + boost::optional> loadFromBaseStore(const Key &key) override { + return _baseBlockStore->load(key); + } + + void removeFromBaseStore(cpputils::unique_ref block) override { + return _baseBlockStore->remove(std::move(block)); + } + +private: + BlockStore *_baseBlockStore; + + DISALLOW_COPY_AND_ASSIGN(ParallelAccessBlockStoreAdapter); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/testfake/FakeBlock.cpp b/src/blockstore/implementations/testfake/FakeBlock.cpp new file mode 100644 index 00000000..8ce97fe3 --- /dev/null +++ b/src/blockstore/implementations/testfake/FakeBlock.cpp @@ -0,0 +1,54 @@ +#include "FakeBlock.h" +#include "FakeBlockStore.h" +#include +#include +#include + +using std::shared_ptr; +using std::istream; +using std::ostream; +using std::ifstream; +using std::ofstream; +using std::ios; +using std::string; +using cpputils::Data; + +namespace blockstore { +namespace testfake { + +FakeBlock::FakeBlock(FakeBlockStore *store, const Key &key, shared_ptr data, bool dirty) + : Block(key), _store(store), _data(data), _dataChanged(dirty) { +} + +FakeBlock::~FakeBlock() { + flush(); +} + +const void *FakeBlock::data() const { + return _data->data(); +} + +void FakeBlock::write(const void *source, uint64_t offset, uint64_t size) { + ASSERT(offset <= _data->size() && offset + size <= _data->size(), "Write outside of valid area"); //Also check offset < _data->size() because of possible overflow in the addition + std::memcpy((uint8_t*)_data->data()+offset, source, size); + _dataChanged = true; +} + +size_t FakeBlock::size() const { + return _data->size(); +} + +void FakeBlock::resize(size_t newSize) { + *_data = cpputils::DataUtils::resize(std::move(*_data), newSize); + _dataChanged = true; +} + +void FakeBlock::flush() { + if(_dataChanged) { + _store->updateData(key(), *_data); + _dataChanged = false; + } +} + +} +} diff --git a/src/blockstore/implementations/testfake/FakeBlock.h b/src/blockstore/implementations/testfake/FakeBlock.h new file mode 100644 index 00000000..e025d3e5 --- /dev/null +++ b/src/blockstore/implementations/testfake/FakeBlock.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_TESTFAKE_FAKEBLOCK_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_TESTFAKE_FAKEBLOCK_H_ + +#include "../../interface/Block.h" +#include + +#include + +namespace blockstore { +namespace testfake { +class FakeBlockStore; + +class FakeBlock final: public Block { +public: + FakeBlock(FakeBlockStore *store, const Key &key, std::shared_ptr data, bool dirty); + ~FakeBlock(); + + const void *data() const override; + void write(const void *source, uint64_t offset, uint64_t size) override; + + void flush() override; + + size_t size() const override; + + void resize(size_t newSize) override; + +private: + FakeBlockStore *_store; + std::shared_ptr _data; + bool _dataChanged; + + DISALLOW_COPY_AND_ASSIGN(FakeBlock); +}; + +} +} + +#endif diff --git a/src/blockstore/implementations/testfake/FakeBlockStore.cpp b/src/blockstore/implementations/testfake/FakeBlockStore.cpp new file mode 100644 index 00000000..2a0ddfc7 --- /dev/null +++ b/src/blockstore/implementations/testfake/FakeBlockStore.cpp @@ -0,0 +1,80 @@ +#include "FakeBlock.h" +#include "FakeBlockStore.h" +#include + +using std::make_shared; +using std::string; +using std::mutex; +using std::lock_guard; +using cpputils::Data; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using boost::optional; +using boost::none; + +namespace blockstore { +namespace testfake { + +FakeBlockStore::FakeBlockStore() + : _blocks(), _used_dataregions_for_blocks(), _mutex() {} + +optional> FakeBlockStore::tryCreate(const Key &key, Data data) { + std::unique_lock lock(_mutex); + auto insert_result = _blocks.emplace(key.ToString(), std::move(data)); + + if (!insert_result.second) { + return none; + } + + //Return a copy of the stored data + return _load(key); +} + +optional> FakeBlockStore::load(const Key &key) { + std::unique_lock lock(_mutex); + return _load(key); +} + +optional> FakeBlockStore::_load(const Key &key) { + //Return a copy of the stored data + string key_string = key.ToString(); + try { + return makeFakeBlockFromData(key, _blocks.at(key_string), false); + } catch (const std::out_of_range &e) { + return none; + } +} + +void FakeBlockStore::remove(unique_ref block) { + Key key = block->key(); + cpputils::destruct(std::move(block)); + std::unique_lock lock(_mutex); + int numRemoved = _blocks.erase(key.ToString()); + ASSERT(numRemoved == 1, "Block not found"); +} + +unique_ref FakeBlockStore::makeFakeBlockFromData(const Key &key, const Data &data, bool dirty) { + auto newdata = make_shared(data.copy()); + _used_dataregions_for_blocks.push_back(newdata); + return make_unique_ref(this, key, newdata, dirty); +} + +void FakeBlockStore::updateData(const Key &key, const Data &data) { + std::unique_lock lock(_mutex); + auto found = _blocks.find(key.ToString()); + if (found == _blocks.end()) { + auto insertResult = _blocks.emplace(key.ToString(), data.copy()); + ASSERT(true == insertResult.second, "Inserting didn't work"); + found = insertResult.first; + } + Data &stored_data = found->second; + stored_data = data.copy(); +} + +uint64_t FakeBlockStore::numBlocks() const { + std::unique_lock lock(_mutex); + return _blocks.size(); +} + +} +} diff --git a/src/blockstore/implementations/testfake/FakeBlockStore.h b/src/blockstore/implementations/testfake/FakeBlockStore.h new file mode 100644 index 00000000..38d9da7e --- /dev/null +++ b/src/blockstore/implementations/testfake/FakeBlockStore.h @@ -0,0 +1,62 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_TESTFAKE_FAKEBLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_TESTFAKE_FAKEBLOCKSTORE_H_ + +#include "../../interface/helpers/BlockStoreWithRandomKeys.h" +#include +#include + +#include +#include + +namespace blockstore { +namespace testfake { +class FakeBlock; + +/** + * This blockstore is meant to be used for unit tests when the module under test needs a blockstore to work with. + * It basically is the same as the InMemoryBlockStore, but much less forgiving for programming mistakes. + * + * InMemoryBlockStore for example simply ignores flushing and gives you access to the same data region each time + * you request a block. This is very performant, but also forgiving to mistakes. Say you write over the boundaries + * of a block, then you wouldn't notice, since the next time you access the block, the overflow data is (probably) + * still there. Or say an application is relying on flushing the block store in the right moment. Since flushing + * is a no-op in InMemoryBlockStore, you wouldn't notice either. + * + * So this FakeBlockStore has a background copy of each block. When you request a block, you will get a copy of + * the data (instead of a direct pointer as InMemoryBlockStore does) and flushing will copy the data back to the + * background. This way, tests are more likely to fail if they use the blockstore wrongly. + */ +class FakeBlockStore final: public BlockStoreWithRandomKeys { +public: + FakeBlockStore(); + + boost::optional> tryCreate(const Key &key, cpputils::Data data) override; + boost::optional> load(const Key &key) override; + void remove(cpputils::unique_ref block) override; + uint64_t numBlocks() const override; + + void updateData(const Key &key, const cpputils::Data &data); + +private: + std::map _blocks; + + //This vector keeps a handle of the data regions for all created FakeBlock objects. + //This way, it is ensured that no two created FakeBlock objects will work on the + //same data region. Without this, it could happen that a test case creates a FakeBlock, + //destructs it, creates another one, and the new one gets the same memory region. + //We want to avoid this for the reasons mentioned above (overflow data). + std::vector> _used_dataregions_for_blocks; + + mutable std::mutex _mutex; + + cpputils::unique_ref makeFakeBlockFromData(const Key &key, const cpputils::Data &data, bool dirty); + boost::optional> _load(const Key &key); + + DISALLOW_COPY_AND_ASSIGN(FakeBlockStore); +}; + +} +} + +#endif diff --git a/src/blockstore/interface/Block.h b/src/blockstore/interface/Block.h new file mode 100644 index 00000000..0261a097 --- /dev/null +++ b/src/blockstore/interface/Block.h @@ -0,0 +1,41 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_INTERFACE_BLOCK_H_ +#define MESSMER_BLOCKSTORE_INTERFACE_BLOCK_H_ + +#include "../utils/Key.h" +#include + +namespace blockstore { + +//TODO Make Block non-virtual class that stores ptr to its blockstore and writes itself back to the blockstore who is offering a corresponding function. +// Then ondisk blockstore can be actually create the file on disk in blockstore::create() and cachingblockstore will delay that call to its base block store. + +class Block { +public: + virtual ~Block() {} + + virtual const void *data() const = 0; + virtual void write(const void *source, uint64_t offset, uint64_t size) = 0; + + virtual void flush() = 0; + + virtual size_t size() const = 0; + + //TODO Test resize() + virtual void resize(size_t newSize) = 0; + + const Key &key() const { + return _key; + } + +protected: + Block(const Key &key) : _key(key) {} + +private: + const Key _key; +}; + +} + + +#endif diff --git a/src/blockstore/interface/BlockStore.h b/src/blockstore/interface/BlockStore.h new file mode 100644 index 00000000..4130d4d7 --- /dev/null +++ b/src/blockstore/interface/BlockStore.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_INTERFACE_BLOCKSTORE_H_ +#define MESSMER_BLOCKSTORE_INTERFACE_BLOCKSTORE_H_ + +#include "Block.h" +#include +#include +#include +#include + +namespace blockstore { + +class BlockStore { +public: + virtual ~BlockStore() {} + + virtual Key createKey() = 0; + //Returns boost::none if key already exists + virtual boost::optional> tryCreate(const Key &key, cpputils::Data data) = 0; + //TODO Use boost::optional (if key doesn't exist) + // Return nullptr if block with this key doesn't exists + virtual boost::optional> load(const Key &key) = 0; + virtual void remove(cpputils::unique_ref block) = 0; + virtual uint64_t numBlocks() const = 0; + + cpputils::unique_ref create(const cpputils::Data &data) { + while(true) { + //TODO Copy (data.copy()) necessary? + auto block = tryCreate(createKey(), data.copy()); + if (block != boost::none) { + return std::move(*block); + } + } + } +}; + +} + +#endif diff --git a/src/blockstore/interface/helpers/BlockStoreWithRandomKeys.cpp b/src/blockstore/interface/helpers/BlockStoreWithRandomKeys.cpp new file mode 100644 index 00000000..f9d7c120 --- /dev/null +++ b/src/blockstore/interface/helpers/BlockStoreWithRandomKeys.cpp @@ -0,0 +1 @@ +#include "BlockStoreWithRandomKeys.h" diff --git a/src/blockstore/interface/helpers/BlockStoreWithRandomKeys.h b/src/blockstore/interface/helpers/BlockStoreWithRandomKeys.h new file mode 100644 index 00000000..3a9cad8d --- /dev/null +++ b/src/blockstore/interface/helpers/BlockStoreWithRandomKeys.h @@ -0,0 +1,23 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_INTERFACE_HELPERS_BLOCKSTOREWITHRANDOMKEYS_H_ +#define MESSMER_BLOCKSTORE_INTERFACE_HELPERS_BLOCKSTOREWITHRANDOMKEYS_H_ + +#include "../BlockStore.h" +#include "../Block.h" +#include + +namespace blockstore { + +// This is an implementation helpers for BlockStores that use random block keys. +// You should never give this static type to the client. The client should always +// work with the BlockStore interface instead. +class BlockStoreWithRandomKeys: public BlockStore { +public: + Key createKey() final { + return cpputils::Random::PseudoRandom().getFixedSize(); + } +}; + +} + +#endif diff --git a/src/blockstore/utils/BlockStoreUtils.cpp b/src/blockstore/utils/BlockStoreUtils.cpp new file mode 100644 index 00000000..77cafc02 --- /dev/null +++ b/src/blockstore/utils/BlockStoreUtils.cpp @@ -0,0 +1,31 @@ +#include "../interface/BlockStore.h" +#include "BlockStoreUtils.h" +#include +#include +#include + +using cpputils::Data; +using cpputils::unique_ref; + +namespace blockstore { +namespace utils { + +unique_ref copyToNewBlock(BlockStore *blockStore, const Block &block) { + Data data(block.size()); + std::memcpy(data.data(), block.data(), block.size()); + return blockStore->create(data); +} + +void copyTo(Block *target, const Block &source) { + ASSERT(target->size() == source.size(), "Can't copy block data when blocks have different sizes"); + target->write(source.data(), 0, source.size()); +} + +void fillWithZeroes(Block *target) { + Data zeroes(target->size()); + zeroes.FillWithZeroes(); + target->write(zeroes.data(), 0, target->size()); +} + +} +} diff --git a/src/blockstore/utils/BlockStoreUtils.h b/src/blockstore/utils/BlockStoreUtils.h new file mode 100644 index 00000000..0e7b6328 --- /dev/null +++ b/src/blockstore/utils/BlockStoreUtils.h @@ -0,0 +1,19 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_UTILS_BLOCKSTOREUTILS_H_ +#define MESSMER_BLOCKSTORE_UTILS_BLOCKSTOREUTILS_H_ + +#include + +namespace blockstore { +class BlockStore; +class Block; +namespace utils { + +cpputils::unique_ref copyToNewBlock(BlockStore *blockStore, const Block &block); +void copyTo(Block *target, const Block &source); +void fillWithZeroes(Block *target); + +} +} + +#endif diff --git a/src/blockstore/utils/FileDoesntExistException.cpp b/src/blockstore/utils/FileDoesntExistException.cpp new file mode 100644 index 00000000..d8479e6b --- /dev/null +++ b/src/blockstore/utils/FileDoesntExistException.cpp @@ -0,0 +1,17 @@ +#include "FileDoesntExistException.h" + +namespace bf = boost::filesystem; + +using std::runtime_error; +using std::string; + +namespace blockstore { + +FileDoesntExistException::FileDoesntExistException(const bf::path &filepath) +: runtime_error(string("The file ")+filepath.c_str()+" doesn't exist") { +} + +FileDoesntExistException::~FileDoesntExistException() { +} + +} diff --git a/src/blockstore/utils/FileDoesntExistException.h b/src/blockstore/utils/FileDoesntExistException.h new file mode 100644 index 00000000..d03f57ea --- /dev/null +++ b/src/blockstore/utils/FileDoesntExistException.h @@ -0,0 +1,19 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_UTILS_FILEDOESNTEXISTEXCEPTION_H_ +#define MESSMER_BLOCKSTORE_UTILS_FILEDOESNTEXISTEXCEPTION_H_ + +#include + +#include + +namespace blockstore { + +class FileDoesntExistException final: public std::runtime_error { +public: + explicit FileDoesntExistException(const boost::filesystem::path &filepath); + ~FileDoesntExistException(); +}; + +} + +#endif diff --git a/src/blockstore/utils/Key.cpp b/src/blockstore/utils/Key.cpp new file mode 100644 index 00000000..f22451aa --- /dev/null +++ b/src/blockstore/utils/Key.cpp @@ -0,0 +1 @@ +#include "Key.h" diff --git a/src/blockstore/utils/Key.h b/src/blockstore/utils/Key.h new file mode 100644 index 00000000..ae9286ba --- /dev/null +++ b/src/blockstore/utils/Key.h @@ -0,0 +1,32 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_UTILS_KEY_H_ +#define MESSMER_BLOCKSTORE_UTILS_KEY_H_ + +#include +#include + +namespace blockstore { + + // A key here is NOT a key for encryption, but a key as used in key->value mappings ("access handle for a block"). + //TODO Rename to BlockId/BlobId and make it a class containing a FixedSizeData<> member + using Key = cpputils::FixedSizeData<16>; +} + +namespace std { + //Allow using blockstore::Key in std::unordered_map / std::unordered_set + template <> struct hash { + size_t operator()(const blockstore::Key &key) const { + //Keys are random, so it is enough to use the first few bytes as a hash + return *(size_t*)(key.data()); + } + }; + + //Allow using blockstore::Key in std::map / std::set + template <> struct less { + bool operator()(const blockstore::Key &lhs, const blockstore::Key &rhs) const { + return 0 > std::memcmp(lhs.data(), rhs.data(), blockstore::Key::BINARY_LENGTH); + } + }; +} + +#endif diff --git a/src/cpp-utils/CMakeLists.txt b/src/cpp-utils/CMakeLists.txt new file mode 100644 index 00000000..ab6a513e --- /dev/null +++ b/src/cpp-utils/CMakeLists.txt @@ -0,0 +1,51 @@ +project (cpp-utils) + +set(SOURCES + crypto/symmetric/ciphers.cpp + crypto/kdf/DerivedKey.cpp + crypto/kdf/Scrypt.cpp + crypto/kdf/DerivedKeyConfig.cpp + crypto/RandomPadding.cpp + process/daemonize.cpp + process/subprocess.cpp + tempfile/TempFile.cpp + tempfile/TempDir.cpp + network/HttpClient.cpp + network/CurlHttpClient.cpp + network/FakeHttpClient.cpp + io/Console.cpp + io/pipestream.cpp + thread/LoopThread.cpp + thread/ThreadSystem.cpp + random/Random.cpp + random/RandomGeneratorThread.cpp + random/OSRandomGenerator.cpp + random/PseudoRandomPool.cpp + random/RandomDataBuffer.cpp + random/RandomGenerator.cpp + lock/LockPool.cpp + data/Serializer.cpp + data/Deserializer.cpp + data/DataFixture.cpp + data/DataUtils.cpp + data/Data.cpp + assert/backtrace.cpp + assert/AssertFailed.cpp +) + +add_library(${PROJECT_NAME} STATIC ${SOURCES}) + +# This is needed by boost thread +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries(${PROJECT_NAME} PRIVATE rt) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +target_link_libraries(${PROJECT_NAME} PRIVATE pthread curl) + +# TODO From Crypto++ 5.7 on, it should support cmake with find_package(). +find_library(CryptoPP cryptopp) +target_link_libraries(${PROJECT_NAME} PUBLIC ${CryptoPP} scrypt spdlog) + +target_add_boost(${PROJECT_NAME} filesystem system thread) +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/src/cpp-utils/assert/AssertFailed.cpp b/src/cpp-utils/assert/AssertFailed.cpp new file mode 100644 index 00000000..226ef4b4 --- /dev/null +++ b/src/cpp-utils/assert/AssertFailed.cpp @@ -0,0 +1 @@ +#include "AssertFailed.h" diff --git a/src/cpp-utils/assert/AssertFailed.h b/src/cpp-utils/assert/AssertFailed.h new file mode 100644 index 00000000..3d9b8978 --- /dev/null +++ b/src/cpp-utils/assert/AssertFailed.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_ASSERT_ASSERTFAILED_H +#define MESSMER_CPPUTILS_ASSERT_ASSERTFAILED_H + +#include +#include +#include "../macros.h" + +namespace cpputils { + + class AssertFailed final: public std::exception { + public: + AssertFailed(const std::string &message) : _message(message) { } + + const char *what() const throw() override { + return _message.c_str(); + } + + private: + std::string _message; + }; + +} + +#endif diff --git a/src/cpp-utils/assert/assert.h b/src/cpp-utils/assert/assert.h new file mode 100644 index 00000000..52f1b868 --- /dev/null +++ b/src/cpp-utils/assert/assert.h @@ -0,0 +1,45 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_ASSERT_ASSERT_H +#define MESSMER_CPPUTILS_ASSERT_ASSERT_H + +/** + * This implements an ASSERT(expr, msg) macro. + * In a debug build, it will crash and halt the program on an assert failure. + * In a release build, it will throw an AssertFailed exception instead, which can then be caught. + */ + +#include "AssertFailed.h" +#include +#include "backtrace.h" +#include "../logging/logging.h" + +namespace cpputils { + namespace _assert { + inline std::string format(const char *expr, const std::string &message, const char *file, int line) { + std::string result = std::string()+"Assertion ["+expr+"] failed in "+file+":"+std::to_string(line)+": "+message+"\n\n" + backtrace(); + return result; + } + + inline void assert_fail_release [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { + auto msg = format(expr, message, file, line); + using namespace logging; + LOG(ERROR) << msg; + throw AssertFailed(msg); + } + + inline void assert_fail_debug [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { + using namespace logging; + LOG(ERROR) << format(expr, message, file, line); + abort(); + } + } +} + +#ifdef NDEBUG +//TODO Check whether disabling assertions in prod affects speed. +# define ASSERT(expr, msg) (void)((expr) || (cpputils::_assert::assert_fail_release(#expr, msg, __FILE__, __LINE__),0)) +#else +# define ASSERT(expr, msg) (void)((expr) || (cpputils::_assert::assert_fail_debug(#expr, msg, __FILE__, __LINE__),0)) +#endif + +#endif diff --git a/src/cpp-utils/assert/backtrace.cpp b/src/cpp-utils/assert/backtrace.cpp new file mode 100644 index 00000000..24885380 --- /dev/null +++ b/src/cpp-utils/assert/backtrace.cpp @@ -0,0 +1,68 @@ +#include "backtrace.h" +#include +#include +#include +#include +#include +#include +#include +#include "../logging/logging.h" + +using std::string; +using std::ostringstream; +using namespace cpputils::logging; + +//TODO Use the following? https://github.com/bombela/backward-cpp + +namespace cpputils { + + //TODO Refactor (for example: RAII or at least try{}finally{} instead of free()) + + std::string demangle(const string &mangledName) { + string result; + int status = -10; + char *demangledName = abi::__cxa_demangle(mangledName.c_str(), NULL, NULL, &status); + if (status == 0) { + result = demangledName; + } else { + result = mangledName; + } + free(demangledName); + return result; + } + + std::string pretty(const string &backtraceLine) { + size_t startMangledName = backtraceLine.find('('); + size_t endMangledName = backtraceLine.find('+'); + if (startMangledName == string::npos || endMangledName == string::npos) { + return backtraceLine; + } + return demangle(backtraceLine.substr(startMangledName+1, endMangledName-startMangledName-1)) + ": (" + backtraceLine.substr(0, startMangledName) + backtraceLine.substr(endMangledName); + } + + string backtrace_to_string(void *array[], size_t size) { + ostringstream result; + char **ptr = backtrace_symbols(array, size); + for (size_t i = 0; i < size; ++i) { + result << pretty(ptr[i]) << "\n"; + } + free(ptr); + return result.str(); + } + + string backtrace() { + constexpr unsigned int MAX_SIZE = 100; + void *array[MAX_SIZE]; + size_t size = ::backtrace(array, MAX_SIZE); + return backtrace_to_string(array, size); + } + + void sigsegv_handler(int) { + LOG(ERROR) << "SIGSEGV\n" << backtrace(); + exit(1); + } + + void showBacktraceOnSigSegv() { + signal(SIGSEGV, sigsegv_handler); + } +} diff --git a/src/cpp-utils/assert/backtrace.h b/src/cpp-utils/assert/backtrace.h new file mode 100644 index 00000000..4d7ca1a7 --- /dev/null +++ b/src/cpp-utils/assert/backtrace.h @@ -0,0 +1,12 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_ASSERT_BACKTRACE_H +#define MESSMER_CPPUTILS_ASSERT_BACKTRACE_H + +#include + +namespace cpputils { + std::string backtrace(); + void showBacktraceOnSigSegv(); +} + +#endif diff --git a/src/cpp-utils/crypto/RandomPadding.cpp b/src/cpp-utils/crypto/RandomPadding.cpp new file mode 100644 index 00000000..8852ea9d --- /dev/null +++ b/src/cpp-utils/crypto/RandomPadding.cpp @@ -0,0 +1,34 @@ +#include "RandomPadding.h" +#include "../logging/logging.h" +#include "../random/Random.h" + +using boost::optional; +using namespace cpputils::logging; + +namespace cpputils { + Data RandomPadding::add(const Data &data, size_t targetSize) { + uint32_t size = data.size(); + if (size >= targetSize - sizeof(size)) { + throw std::runtime_error("Data too large. We should increase padding target size."); + } + Data randomData = Random::PseudoRandom().get(targetSize-sizeof(size)-size); + ASSERT(sizeof(size) + size + randomData.size() == targetSize, "Calculated size of randomData incorrectly"); + Data result(targetSize); + std::memcpy(reinterpret_cast(result.data()), &size, sizeof(size)); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(size))), reinterpret_cast(data.data()), size); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(size)+size)), reinterpret_cast(randomData.data()), randomData.size()); + return result; + } + + optional RandomPadding::remove(const Data &data) { + uint32_t size; + std::memcpy(&size, reinterpret_cast(data.data()), sizeof(size)); + if(sizeof(size) + size >= data.size()) { + LOG(ERROR) << "Config file is invalid: Invalid padding."; + return boost::none; + }; + Data result(size); + std::memcpy(reinterpret_cast(result.data()), reinterpret_cast(data.dataOffset(sizeof(size))), size); + return std::move(result); + } +} diff --git a/src/cpp-utils/crypto/RandomPadding.h b/src/cpp-utils/crypto/RandomPadding.h new file mode 100644 index 00000000..2dd2a427 --- /dev/null +++ b/src/cpp-utils/crypto/RandomPadding.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_RANDOMPADDING_H +#define MESSMER_CPPUTILS_CRYPTO_RANDOMPADDING_H + +#include "../data/Data.h" +#include + +namespace cpputils { + //TODO Test + class RandomPadding final { + public: + static Data add(const Data &data, size_t targetSize); + static boost::optional remove(const Data &data); + }; +} + +#endif diff --git a/src/cpp-utils/crypto/kdf/DerivedKey.cpp b/src/cpp-utils/crypto/kdf/DerivedKey.cpp new file mode 100644 index 00000000..7968d456 --- /dev/null +++ b/src/cpp-utils/crypto/kdf/DerivedKey.cpp @@ -0,0 +1 @@ +#include "DerivedKey.h" diff --git a/src/cpp-utils/crypto/kdf/DerivedKey.h b/src/cpp-utils/crypto/kdf/DerivedKey.h new file mode 100644 index 00000000..2f6eb582 --- /dev/null +++ b/src/cpp-utils/crypto/kdf/DerivedKey.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_DERIVEDKEY_H +#define MESSMER_CPPUTILS_CRYPTO_KDF_DERIVEDKEY_H + +#include "../../data/FixedSizeData.h" +#include "DerivedKeyConfig.h" + +namespace cpputils { + + template + class DerivedKey final { + public: + DerivedKey(DerivedKeyConfig config, const FixedSizeData &key): _config(std::move(config)), _key(key) {} + DerivedKey(DerivedKey &&rhs) = default; + + const DerivedKeyConfig &config() const { + return _config; + } + + DerivedKeyConfig moveOutConfig() { + return std::move(_config); + } + + const FixedSizeData &key() const { + return _key; + } + + FixedSizeData moveOutKey() { + return std::move(_key); + } + private: + DerivedKeyConfig _config; + FixedSizeData _key; + + DISALLOW_COPY_AND_ASSIGN(DerivedKey); + }; +} + +#endif diff --git a/src/cpp-utils/crypto/kdf/DerivedKeyConfig.cpp b/src/cpp-utils/crypto/kdf/DerivedKeyConfig.cpp new file mode 100644 index 00000000..5883cf0b --- /dev/null +++ b/src/cpp-utils/crypto/kdf/DerivedKeyConfig.cpp @@ -0,0 +1,27 @@ +#include "DerivedKeyConfig.h" + +using std::istream; +using std::ostream; +using boost::optional; +using boost::none; + +namespace cpputils { + void DerivedKeyConfig::serialize(Serializer *target) const { + target->writeUint64(_N); + target->writeUint32(_r); + target->writeUint32(_p); + target->writeData(_salt); + } + + size_t DerivedKeyConfig::serializedSize() const { + return Serializer::DataSize(_salt) + sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t); + } + + DerivedKeyConfig DerivedKeyConfig::deserialize(Deserializer *source) { + uint64_t N = source->readUint64(); + uint32_t r = source->readUint32(); + uint32_t p = source->readUint32(); + Data salt = source->readData(); + return DerivedKeyConfig(std::move(salt), N, r, p); + } +} diff --git a/src/cpp-utils/crypto/kdf/DerivedKeyConfig.h b/src/cpp-utils/crypto/kdf/DerivedKeyConfig.h new file mode 100644 index 00000000..045ed0eb --- /dev/null +++ b/src/cpp-utils/crypto/kdf/DerivedKeyConfig.h @@ -0,0 +1,77 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_KEYCONFIG_H +#define MESSMER_CPPUTILS_CRYPTO_KDF_KEYCONFIG_H + +#include "../../data/Data.h" +#include "../../data/Serializer.h" +#include "../../data/Deserializer.h" +#include + +namespace cpputils { + + //TODO Test Copy/move constructor and assignment + //TODO Test operator==/!= + //TODO Use SCryptSettings as a member here instead of storing _N, _r, _p. + + class DerivedKeyConfig final { + public: + DerivedKeyConfig(Data salt, uint64_t N, uint32_t r, uint32_t p) + : _salt(std::move(salt)), + _N(N), _r(r), _p(p) { } + + DerivedKeyConfig(const DerivedKeyConfig &rhs) + :_salt(rhs._salt.copy()), + _N(rhs._N), _r(rhs._r), _p(rhs._p) { } + + DerivedKeyConfig(DerivedKeyConfig &&rhs) = default; + + DerivedKeyConfig &operator=(const DerivedKeyConfig &rhs) { + _salt = rhs._salt.copy(); + _N = rhs._N; + _r = rhs._r; + _p = rhs._p; + return *this; + } + + DerivedKeyConfig &operator=(DerivedKeyConfig &&rhs) = default; + + const Data &salt() const { + return _salt; + } + + size_t N() const { + return _N; + } + + size_t r() const { + return _r; + } + + size_t p() const { + return _p; + } + + void serialize(Serializer *destination) const; + + size_t serializedSize() const; + + static DerivedKeyConfig deserialize(Deserializer *source); + + private: + Data _salt; + uint64_t _N; + uint32_t _r; + uint32_t _p; + }; + + inline bool operator==(const DerivedKeyConfig &lhs, const DerivedKeyConfig &rhs) { + return lhs.salt() == rhs.salt() && lhs.N() == rhs.N() && lhs.r() == rhs.r() && lhs.p() == rhs.p(); + } + + inline bool operator!=(const DerivedKeyConfig &lhs, const DerivedKeyConfig &rhs) { + return !operator==(lhs, rhs); + } + +} + +#endif diff --git a/src/cpp-utils/crypto/kdf/Scrypt.cpp b/src/cpp-utils/crypto/kdf/Scrypt.cpp new file mode 100644 index 00000000..edf14e62 --- /dev/null +++ b/src/cpp-utils/crypto/kdf/Scrypt.cpp @@ -0,0 +1,7 @@ +#include "Scrypt.h" + +namespace cpputils { + constexpr SCryptSettings SCrypt::ParanoidSettings; + constexpr SCryptSettings SCrypt::DefaultSettings; + constexpr SCryptSettings SCrypt::TestSettings; +} \ No newline at end of file diff --git a/src/cpp-utils/crypto/kdf/Scrypt.h b/src/cpp-utils/crypto/kdf/Scrypt.h new file mode 100644 index 00000000..14f838bf --- /dev/null +++ b/src/cpp-utils/crypto/kdf/Scrypt.h @@ -0,0 +1,56 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_SCRYPT_H +#define MESSMER_CPPUTILS_CRYPTO_KDF_SCRYPT_H + +#include "../../macros.h" +#include "../../random/Random.h" +extern "C" { + #include +} +#include +#include "DerivedKey.h" + +namespace cpputils { + + struct SCryptSettings { + size_t SALT_LEN; + uint64_t N; + uint32_t r; + uint32_t p; + }; + + class SCrypt final { + public: + static constexpr SCryptSettings ParanoidSettings = SCryptSettings {32, 1048576, 8, 16}; + static constexpr SCryptSettings DefaultSettings = SCryptSettings {32, 524288, 1, 1}; + static constexpr SCryptSettings TestSettings = SCryptSettings {32, 1024, 1, 1}; + + SCrypt() {} + + template + DerivedKey generateKey(const std::string &password, const SCryptSettings &settings) { + auto salt = Random::PseudoRandom().get(settings.SALT_LEN); + auto config = DerivedKeyConfig(std::move(salt), settings.N, settings.r, settings.p); + auto key = generateKeyFromConfig(password, config); + return DerivedKey(std::move(config), key); + } + + template + FixedSizeData generateKeyFromConfig(const std::string &password, const DerivedKeyConfig &config) { + auto key = FixedSizeData::Null(); + int errorcode = crypto_scrypt(reinterpret_cast(password.c_str()), password.size(), + reinterpret_cast(config.salt().data()), config.salt().size(), + config.N(), config.r(), config.p(), + static_cast(key.data()), KEYSIZE); + if (errorcode != 0) { + throw std::runtime_error("Error running scrypt key derivation."); + } + return key; + } + + private: + DISALLOW_COPY_AND_ASSIGN(SCrypt); + }; +} + +#endif diff --git a/src/cpp-utils/crypto/symmetric/CFB_Cipher.h b/src/cpp-utils/crypto/symmetric/CFB_Cipher.h new file mode 100644 index 00000000..c99b50c5 --- /dev/null +++ b/src/cpp-utils/crypto/symmetric/CFB_Cipher.h @@ -0,0 +1,64 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CFBCIPHER_H_ +#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CFBCIPHER_H_ + +#include "../../data/FixedSizeData.h" +#include "../../data/Data.h" +#include "../../random/Random.h" +#include +#include +#include "Cipher.h" + +namespace cpputils { + +template +class CFB_Cipher { +public: + using EncryptionKey = FixedSizeData; + + static EncryptionKey CreateKey(RandomGenerator &randomGenerator) { + return randomGenerator.getFixedSize(); + } + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + IV_SIZE; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - IV_SIZE; + } + + static Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey); + static boost::optional decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey); + +private: + static constexpr unsigned int IV_SIZE = BlockCipher::BLOCKSIZE; +}; + +template +Data CFB_Cipher::encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + FixedSizeData iv = Random::PseudoRandom().getFixedSize(); + auto encryption = typename CryptoPP::CFB_Mode::Encryption(encKey.data(), encKey.BINARY_LENGTH, iv.data()); + Data ciphertext(ciphertextSize(plaintextSize)); + std::memcpy(ciphertext.data(), iv.data(), IV_SIZE); + encryption.ProcessData((byte*)ciphertext.data() + IV_SIZE, plaintext, plaintextSize); + return ciphertext; +} + +template +boost::optional CFB_Cipher::decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) { + if (ciphertextSize < IV_SIZE) { + return boost::none; + } + + const byte *ciphertextIV = ciphertext; + const byte *ciphertextData = ciphertext + IV_SIZE; + auto decryption = typename CryptoPP::CFB_Mode::Decryption((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV); + Data plaintext(plaintextSize(ciphertextSize)); + decryption.ProcessData((byte*)plaintext.data(), ciphertextData, plaintext.size()); + return std::move(plaintext); +} + +} + +#endif diff --git a/src/cpp-utils/crypto/symmetric/Cipher.h b/src/cpp-utils/crypto/symmetric/Cipher.h new file mode 100644 index 00000000..196dbd92 --- /dev/null +++ b/src/cpp-utils/crypto/symmetric/Cipher.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHER_H_ +#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHER_H_ + +#include +#include +#include "../../data/Data.h" +#include "../../random/Random.h" + +using std::string; + +namespace cpputils { + +template +struct CipherConcept { +public: + BOOST_CONCEPT_USAGE(CipherConcept) { + same_type(UINT32_C(0), X::ciphertextSize(UINT32_C(5))); + same_type(UINT32_C(0), X::plaintextSize(UINT32_C(5))); + typename X::EncryptionKey key = X::CreateKey(Random::OSRandom()); + same_type(Data(0), X::encrypt((uint8_t*)nullptr, UINT32_C(0), key)); + same_type(boost::optional(Data(0)), X::decrypt((uint8_t*)nullptr, UINT32_C(0), key)); + string name = X::NAME; + } + +private: + // Type deduction will fail unless the arguments have the same type. + template void same_type(T const&, T const&); +}; + +} + +#endif diff --git a/src/cpp-utils/crypto/symmetric/GCM_Cipher.h b/src/cpp-utils/crypto/symmetric/GCM_Cipher.h new file mode 100644 index 00000000..023fa536 --- /dev/null +++ b/src/cpp-utils/crypto/symmetric/GCM_Cipher.h @@ -0,0 +1,82 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_GCMCIPHER_H_ +#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_GCMCIPHER_H_ + +#include "../../data/FixedSizeData.h" +#include "../../data/Data.h" +#include "../../random/Random.h" +#include +#include "Cipher.h" + +namespace cpputils { + +template +class GCM_Cipher { +public: + using EncryptionKey = FixedSizeData; + + static EncryptionKey CreateKey(RandomGenerator &randomGenerator) { + return randomGenerator.getFixedSize(); + } + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + IV_SIZE + TAG_SIZE; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - IV_SIZE - TAG_SIZE; + } + + static Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey); + static boost::optional decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey); + +private: + static constexpr unsigned int IV_SIZE = BlockCipher::BLOCKSIZE; + static constexpr unsigned int TAG_SIZE = 16; +}; + +template +Data GCM_Cipher::encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + FixedSizeData iv = Random::PseudoRandom().getFixedSize(); + typename CryptoPP::GCM::Encryption encryption; + encryption.SetKeyWithIV(encKey.data(), encKey.BINARY_LENGTH, iv.data(), IV_SIZE); + Data ciphertext(ciphertextSize(plaintextSize)); + + std::memcpy(ciphertext.data(), iv.data(), IV_SIZE); + CryptoPP::ArraySource(plaintext, plaintextSize, true, + new CryptoPP::AuthenticatedEncryptionFilter(encryption, + new CryptoPP::ArraySink((byte*)ciphertext.data() + IV_SIZE, ciphertext.size() - IV_SIZE), + false, TAG_SIZE + ) + ); + return ciphertext; +} + +template +boost::optional GCM_Cipher::decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) { + if (ciphertextSize < IV_SIZE + TAG_SIZE) { + return boost::none; + } + + const byte *ciphertextIV = ciphertext; + const byte *ciphertextData = ciphertext + IV_SIZE; + typename CryptoPP::GCM::Decryption decryption; + decryption.SetKeyWithIV((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV, IV_SIZE); + Data plaintext(plaintextSize(ciphertextSize)); + + try { + CryptoPP::ArraySource((byte*)ciphertextData, ciphertextSize - IV_SIZE, true, + new CryptoPP::AuthenticatedDecryptionFilter(decryption, + new CryptoPP::ArraySink((byte*)plaintext.data(), plaintext.size()), + CryptoPP::AuthenticatedDecryptionFilter::DEFAULT_FLAGS, TAG_SIZE + ) + ); + return std::move(plaintext); + } catch (const CryptoPP::HashVerificationFilter::HashVerificationFailed &e) { + return boost::none; + } +} + +} + +#endif diff --git a/src/cpp-utils/crypto/symmetric/ciphers.cpp b/src/cpp-utils/crypto/symmetric/ciphers.cpp new file mode 100644 index 00000000..f0f5017f --- /dev/null +++ b/src/cpp-utils/crypto/symmetric/ciphers.cpp @@ -0,0 +1,33 @@ +#include "ciphers.h" + +#define DEFINE_CIPHER(InstanceName) \ + constexpr const char *InstanceName::NAME; \ + +namespace cpputils { + + DEFINE_CIPHER(AES256_GCM); + DEFINE_CIPHER(AES256_CFB); + DEFINE_CIPHER(AES128_GCM); + DEFINE_CIPHER(AES128_CFB); + + DEFINE_CIPHER(Twofish256_GCM); + DEFINE_CIPHER(Twofish256_CFB); + DEFINE_CIPHER(Twofish128_GCM); + DEFINE_CIPHER(Twofish128_CFB); + + DEFINE_CIPHER(Serpent256_GCM); + DEFINE_CIPHER(Serpent256_CFB); + DEFINE_CIPHER(Serpent128_GCM); + DEFINE_CIPHER(Serpent128_CFB); + + DEFINE_CIPHER(Cast256_GCM); + DEFINE_CIPHER(Cast256_CFB); + + DEFINE_CIPHER(Mars448_GCM); + DEFINE_CIPHER(Mars448_CFB); + DEFINE_CIPHER(Mars256_GCM); + DEFINE_CIPHER(Mars256_CFB); + DEFINE_CIPHER(Mars128_GCM); + DEFINE_CIPHER(Mars128_CFB); + +} diff --git a/src/cpp-utils/crypto/symmetric/ciphers.h b/src/cpp-utils/crypto/symmetric/ciphers.h new file mode 100644 index 00000000..284aff0d --- /dev/null +++ b/src/cpp-utils/crypto/symmetric/ciphers.h @@ -0,0 +1,54 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHERS_H_ +#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHERS_H_ + +#include +#include +#include +#include +#include +#include "GCM_Cipher.h" +#include "CFB_Cipher.h" + +#define DECLARE_CIPHER(InstanceName, StringName, Mode, Base, Keysize) \ + class InstanceName final: public Mode { \ + public: \ + BOOST_CONCEPT_ASSERT((CipherConcept)); \ + static constexpr const char *NAME = StringName; \ + } \ + +namespace cpputils { + +static_assert(32 == CryptoPP::AES::MAX_KEYLENGTH, "If AES offered larger keys, we should offer a variant with it"); +DECLARE_CIPHER(AES256_GCM, "aes-256-gcm", GCM_Cipher, CryptoPP::AES, 32); +DECLARE_CIPHER(AES256_CFB, "aes-256-cfb", CFB_Cipher, CryptoPP::AES, 32); +DECLARE_CIPHER(AES128_GCM, "aes-128-gcm", GCM_Cipher, CryptoPP::AES, 16); +DECLARE_CIPHER(AES128_CFB, "aes-128-cfb", CFB_Cipher, CryptoPP::AES, 16); + +static_assert(32 == CryptoPP::Twofish::MAX_KEYLENGTH, "If Twofish offered larger keys, we should offer a variant with it"); +DECLARE_CIPHER(Twofish256_GCM, "twofish-256-gcm", GCM_Cipher, CryptoPP::Twofish, 32); +DECLARE_CIPHER(Twofish256_CFB, "twofish-256-cfb", CFB_Cipher, CryptoPP::Twofish, 32); +DECLARE_CIPHER(Twofish128_GCM, "twofish-128-gcm", GCM_Cipher, CryptoPP::Twofish, 16); +DECLARE_CIPHER(Twofish128_CFB, "twofish-128-cfb", CFB_Cipher, CryptoPP::Twofish, 16); + +static_assert(32 == CryptoPP::Serpent::MAX_KEYLENGTH, "If Serpent offered larger keys, we should offer a variant with it"); +DECLARE_CIPHER(Serpent256_GCM, "serpent-256-gcm", GCM_Cipher, CryptoPP::Serpent, 32); +DECLARE_CIPHER(Serpent256_CFB, "serpent-256-cfb", CFB_Cipher, CryptoPP::Serpent, 32); +DECLARE_CIPHER(Serpent128_GCM, "serpent-128-gcm", GCM_Cipher, CryptoPP::Serpent, 16); +DECLARE_CIPHER(Serpent128_CFB, "serpent-128-cfb", CFB_Cipher, CryptoPP::Serpent, 16); + +static_assert(32 == CryptoPP::CAST256::MAX_KEYLENGTH, "If Cast offered larger keys, we should offer a variant with it"); +DECLARE_CIPHER(Cast256_GCM, "cast-256-gcm", GCM_Cipher, CryptoPP::CAST256, 32); +DECLARE_CIPHER(Cast256_CFB, "cast-256-cfb", CFB_Cipher, CryptoPP::CAST256, 32); + +static_assert(56 == CryptoPP::MARS::MAX_KEYLENGTH, "If Mars offered larger keys, we should offer a variant with it"); +DECLARE_CIPHER(Mars448_GCM, "mars-448-gcm", GCM_Cipher, CryptoPP::MARS, 56); +DECLARE_CIPHER(Mars448_CFB, "mars-448-cfb", CFB_Cipher, CryptoPP::MARS, 56); +DECLARE_CIPHER(Mars256_GCM, "mars-256-gcm", GCM_Cipher, CryptoPP::MARS, 32); +DECLARE_CIPHER(Mars256_CFB, "mars-256-cfb", CFB_Cipher, CryptoPP::MARS, 32); +DECLARE_CIPHER(Mars128_GCM, "mars-128-gcm", GCM_Cipher, CryptoPP::MARS, 16); +DECLARE_CIPHER(Mars128_CFB, "mars-128-cfb", CFB_Cipher, CryptoPP::MARS, 16); + +} + +#endif diff --git a/src/cpp-utils/data/Data.cpp b/src/cpp-utils/data/Data.cpp new file mode 100644 index 00000000..cd37683c --- /dev/null +++ b/src/cpp-utils/data/Data.cpp @@ -0,0 +1,40 @@ +#include "Data.h" +#include + +using std::istream; +using std::ofstream; +using std::ifstream; +using std::ios; + +namespace bf = boost::filesystem; + +namespace cpputils { + +boost::optional Data::LoadFromFile(const bf::path &filepath) { + ifstream file(filepath.c_str(), ios::binary); + if (!file.good()) { + return boost::none; + } + return LoadFromStream(file); +} + +std::streampos Data::_getStreamSize(istream &stream) { + auto current_pos = stream.tellg(); + + //Retrieve length + stream.seekg(0, stream.end); + auto endpos = stream.tellg(); + + //Restore old position + stream.seekg(current_pos, stream.beg); + + return endpos - current_pos; +} + +Data Data::LoadFromStream(istream &stream, size_t size) { + Data result(size); + stream.read(static_cast(result.data()), result.size()); + return std::move(result); +} + +} diff --git a/src/cpp-utils/data/Data.h b/src/cpp-utils/data/Data.h new file mode 100644 index 00000000..ff3916a1 --- /dev/null +++ b/src/cpp-utils/data/Data.h @@ -0,0 +1,147 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_DATA_H_ +#define MESSMER_CPPUTILS_DATA_DATA_H_ + +#include +#include +#include +#include "../macros.h" +#include +#include + +namespace cpputils { + +class Data final { +public: + explicit Data(size_t size); + ~Data(); + + Data(Data &&rhs); // move constructor + Data &operator=(Data &&rhs); // move assignment + + Data copy() const; + + void *data(); + const void *data() const; + + //TODO Test dataOffset + void *dataOffset(size_t offset); + const void *dataOffset(size_t offset) const; + + size_t size() const; + + Data &FillWithZeroes(); + + void StoreToFile(const boost::filesystem::path &filepath) const; + static boost::optional LoadFromFile(const boost::filesystem::path &filepath); + + //TODO Test LoadFromStream/StoreToStream + static Data LoadFromStream(std::istream &stream); + static Data LoadFromStream(std::istream &stream, size_t size); + void StoreToStream(std::ostream &stream) const; + +private: + size_t _size; + void *_data; + + static std::streampos _getStreamSize(std::istream &stream); + void _readFromStream(std::istream &stream); + + DISALLOW_COPY_AND_ASSIGN(Data); +}; + +bool operator==(const Data &lhs, const Data &rhs); +bool operator!=(const Data &lhs, const Data &rhs); + + +// --------------------------- +// Inline function definitions +// --------------------------- + +inline Data::Data(size_t size) + : _size(size), _data(std::malloc(size)) { + if (nullptr == _data) { + throw std::bad_alloc(); + } +} + +inline Data::Data(Data &&rhs) + : _size(rhs._size), _data(rhs._data) { + // Make rhs invalid, so the memory doesn't get freed in its destructor. + rhs._data = nullptr; + rhs._size = 0; +} + +inline Data &Data::operator=(Data &&rhs) { + std::free(_data); + _data = rhs._data; + _size = rhs._size; + rhs._data = nullptr; + rhs._size = 0; + + return *this; +} + +inline Data::~Data() { + std::free(_data); + _data = nullptr; +} + +inline Data Data::copy() const { + Data copy(_size); + std::memcpy(copy._data, _data, _size); + return copy; +} + +inline void *Data::data() { + return const_cast(const_cast(this)->data()); +} + +inline const void *Data::data() const { + return _data; +} + +inline void *Data::dataOffset(size_t offset) { + return const_cast(const_cast(this)->dataOffset(offset)); +} + +inline const void *Data::dataOffset(size_t offset) const { + return static_cast(data()) + offset; +} + +inline size_t Data::size() const { + return _size; +} + +inline Data &Data::FillWithZeroes() { + std::memset(_data, 0, _size); + return *this; +} + +inline void Data::StoreToFile(const boost::filesystem::path &filepath) const { + std::ofstream file(filepath.c_str(), std::ios::binary | std::ios::trunc); + if (!file.good()) { + throw std::runtime_error("Could not open file for writing"); + } + StoreToStream(file); +} + +inline void Data::StoreToStream(std::ostream &stream) const { + stream.write(static_cast(_data), _size); +} + +inline Data Data::LoadFromStream(std::istream &stream) { + return LoadFromStream(stream, _getStreamSize(stream)); +} + +inline bool operator==(const Data &lhs, const Data &rhs) { + return lhs.size() == rhs.size() && 0 == memcmp(lhs.data(), rhs.data(), lhs.size()); +} + +inline bool operator!=(const Data &lhs, const Data &rhs) { + return !operator==(lhs, rhs); +} + +} + +#endif diff --git a/src/cpp-utils/data/DataFixture.cpp b/src/cpp-utils/data/DataFixture.cpp new file mode 100644 index 00000000..b9918846 --- /dev/null +++ b/src/cpp-utils/data/DataFixture.cpp @@ -0,0 +1,23 @@ +#include "DataFixture.h" + +namespace cpputils { + Data DataFixture::generate(size_t size, long long int seed) { + Data result(size); + long long int val = seed; + for(size_t i=0; i(result.data())[i] = val; + } + uint64_t alreadyWritten = (size/sizeof(long long int))*sizeof(long long int); + val *= 6364136223846793005L; + val += 1442695040888963407; + char *remainingBytes = reinterpret_cast(&val); + //Fill remaining bytes + for(size_t i=0; i(result.data())[alreadyWritten + i] = remainingBytes[i]; + } + return result; + } +} diff --git a/src/cpp-utils/data/DataFixture.h b/src/cpp-utils/data/DataFixture.h new file mode 100644 index 00000000..a2ac705f --- /dev/null +++ b/src/cpp-utils/data/DataFixture.h @@ -0,0 +1,27 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_DATAFIXTURE_H_ +#define MESSMER_CPPUTILS_DATA_DATAFIXTURE_H_ + +#include "Data.h" +#include "FixedSizeData.h" + +namespace cpputils { + +class DataFixture final { +public: + static Data generate(size_t size, long long int seed = 1); + + //TODO Test + template static FixedSizeData generateFixedSize(long long int seed = 1); +}; + +template FixedSizeData DataFixture::generateFixedSize(long long int seed) { + Data data = generate(SIZE, seed); + auto result = FixedSizeData::Null(); + std::memcpy(result.data(), data.data(), SIZE); + return result; +} + +} + +#endif diff --git a/src/cpp-utils/data/DataUtils.cpp b/src/cpp-utils/data/DataUtils.cpp new file mode 100644 index 00000000..66a1819a --- /dev/null +++ b/src/cpp-utils/data/DataUtils.cpp @@ -0,0 +1,12 @@ +#include "DataUtils.h" + +namespace cpputils { + namespace DataUtils { + Data resize(Data data, size_t newSize) { + Data newData(newSize); + newData.FillWithZeroes(); // TODO Only fill region after copied old data with zeroes + std::memcpy(newData.data(), data.data(), std::min(newData.size(), data.size())); + return newData; + } + } +} \ No newline at end of file diff --git a/src/cpp-utils/data/DataUtils.h b/src/cpp-utils/data/DataUtils.h new file mode 100644 index 00000000..e61abc6f --- /dev/null +++ b/src/cpp-utils/data/DataUtils.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_DATAUTILS_H +#define MESSMER_CPPUTILS_DATA_DATAUTILS_H + +#include "Data.h" + +namespace cpputils { + namespace DataUtils { + //TODO Test + + //Return a new data object with the given size and initialize as much as possible with the given input data. + //If the new data object is larger, then the remaining bytes will be zero filled. + Data resize(Data data, size_t newSize); + } +} + +#endif diff --git a/src/cpp-utils/data/Deserializer.cpp b/src/cpp-utils/data/Deserializer.cpp new file mode 100644 index 00000000..b7963481 --- /dev/null +++ b/src/cpp-utils/data/Deserializer.cpp @@ -0,0 +1 @@ +#include "Deserializer.h" diff --git a/src/cpp-utils/data/Deserializer.h b/src/cpp-utils/data/Deserializer.h new file mode 100644 index 00000000..a51a92a6 --- /dev/null +++ b/src/cpp-utils/data/Deserializer.h @@ -0,0 +1,123 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_DESERIALIZER_H +#define MESSMER_CPPUTILS_DATA_DESERIALIZER_H + +#include "Data.h" +#include "../macros.h" +#include "../assert/assert.h" + +namespace cpputils { + class Deserializer final { + public: + Deserializer(const Data *source); + + uint8_t readUint8(); + int8_t readInt8(); + uint16_t readUint16(); + int16_t readInt16(); + uint32_t readUint32(); + int32_t readInt32(); + uint64_t readUint64(); + int64_t readInt64(); + std::string readString(); + Data readData(); + Data readTailData(); + + void finished(); + + private: + template DataType _read(); + Data _readData(size_t size); + + size_t _pos; + const Data *_source; + + DISALLOW_COPY_AND_ASSIGN(Deserializer); + }; + + inline Deserializer::Deserializer(const Data *source): _pos(0), _source(source) { + } + + inline uint8_t Deserializer::readUint8() { + return _read(); + } + + inline int8_t Deserializer::readInt8() { + return _read(); + } + + inline uint16_t Deserializer::readUint16() { + return _read(); + } + + inline int16_t Deserializer::readInt16() { + return _read(); + } + + inline uint32_t Deserializer::readUint32() { + return _read(); + } + + inline int32_t Deserializer::readInt32() { + return _read(); + } + + inline uint64_t Deserializer::readUint64() { + return _read(); + } + + inline int64_t Deserializer::readInt64() { + return _read(); + } + + template + inline DataType Deserializer::_read() { + static_assert(std::is_pod::value, "Can only deserialize PODs"); + if (_pos + sizeof(DataType) > _source->size()) { + throw std::runtime_error("Deserialization failed - size overflow"); + } + DataType result = *reinterpret_cast(_source->dataOffset(_pos)); + _pos += sizeof(DataType); + return result; + } + + inline Data Deserializer::readData() { + uint64_t size = readUint64(); + if (_pos + size > _source->size()) { + throw std::runtime_error("Deserialization failed - size overflow"); + } + return _readData(size); + } + + inline Data Deserializer::readTailData() { + uint64_t size = _source->size() - _pos; + return _readData(size); + } + + inline Data Deserializer::_readData(size_t size) { + Data result(size); + std::memcpy(static_cast(result.data()), static_cast(_source->dataOffset(_pos)), size); + _pos += size; + return result; + } + + inline std::string Deserializer::readString() { + //TODO Test whether that works when string ends (a) at beginning (b) in middle (c) at end of data region + const void *nullbytepos = std::memchr(_source->dataOffset(_pos), '\0', _source->size()-_pos); + if (nullbytepos == nullptr) { + throw std::runtime_error("Deserialization failed - missing nullbyte for string termination"); + } + uint64_t size = static_cast(nullbytepos) - static_cast(_source->dataOffset(_pos)); + std::string result(reinterpret_cast(_source->dataOffset(_pos)), size); + _pos += size + 1; + return result; + } + + inline void Deserializer::finished() { + if (_pos != _source->size()) { + throw std::runtime_error("Deserialization failed - size not fully used."); + } + } +} + +#endif diff --git a/src/cpp-utils/data/FixedSizeData.h b/src/cpp-utils/data/FixedSizeData.h new file mode 100644 index 00000000..c62fa11b --- /dev/null +++ b/src/cpp-utils/data/FixedSizeData.h @@ -0,0 +1,132 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_FIXEDSIZEDATA_H_ +#define MESSMER_CPPUTILS_DATA_FIXEDSIZEDATA_H_ + +#include +#include +#include +#include "../assert/assert.h" + +namespace cpputils { + +template +class FixedSizeData final { +public: + //Non-virtual destructor because we want objects to be small + ~FixedSizeData() {} + + static constexpr size_t BINARY_LENGTH = SIZE; + static constexpr size_t STRING_LENGTH = 2 * BINARY_LENGTH; // Hex encoding + + //TODO Test Null() + static FixedSizeData Null(); + + static FixedSizeData FromString(const std::string &data); + std::string ToString() const; + + static FixedSizeData FromBinary(const void *source); + void ToBinary(void *target) const; + + const unsigned char *data() const; + unsigned char *data(); + + template FixedSizeData take() const; + template FixedSizeData drop() const; + +private: + FixedSizeData() {} + template friend class FixedSizeData; + + unsigned char _data[BINARY_LENGTH]; +}; + +template bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs); +template bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs); + +// ----- Implementation ----- + +template constexpr size_t FixedSizeData::BINARY_LENGTH; +template constexpr size_t FixedSizeData::STRING_LENGTH; + +template +FixedSizeData FixedSizeData::Null() { + FixedSizeData result; + std::memset(result._data, 0, BINARY_LENGTH); + return result; +} + +template +FixedSizeData FixedSizeData::FromString(const std::string &data) { + ASSERT(data.size() == STRING_LENGTH, "Wrong string size for parsing FixedSizeData"); + FixedSizeData result; + CryptoPP::StringSource(data, true, + new CryptoPP::HexDecoder( + new CryptoPP::ArraySink(result._data, BINARY_LENGTH) + ) + ); + return result; +} + +template +std::string FixedSizeData::ToString() const { + std::string result; + CryptoPP::ArraySource(_data, BINARY_LENGTH, true, + new CryptoPP::HexEncoder( + new CryptoPP::StringSink(result) + ) + ); + ASSERT(result.size() == STRING_LENGTH, "Created wrongly sized string"); + return result; +} + +template +const unsigned char *FixedSizeData::data() const { + return _data; +} + +template +unsigned char *FixedSizeData::data() { + return const_cast(const_cast*>(this)->data()); +} + +template +void FixedSizeData::ToBinary(void *target) const { + std::memcpy(target, _data, BINARY_LENGTH); +} + +template +FixedSizeData FixedSizeData::FromBinary(const void *source) { + FixedSizeData result; + std::memcpy(result._data, source, BINARY_LENGTH); + return result; +} + +template template +FixedSizeData FixedSizeData::take() const { + static_assert(size <= SIZE, "Out of bounds"); + FixedSizeData result; + std::memcpy(result._data, _data, size); + return result; +} + +template template +FixedSizeData FixedSizeData::drop() const { + static_assert(size <= SIZE, "Out of bounds"); + FixedSizeData result; + std::memcpy(result._data, _data+size, SIZE-size); + return result; +} + +template +bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs) { + return 0 == std::memcmp(lhs.data(), rhs.data(), FixedSizeData::BINARY_LENGTH); +} + +template +bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs) { + return !operator==(lhs, rhs); +} + +} + +#endif diff --git a/src/cpp-utils/data/Serializer.cpp b/src/cpp-utils/data/Serializer.cpp new file mode 100644 index 00000000..03ed1de3 --- /dev/null +++ b/src/cpp-utils/data/Serializer.cpp @@ -0,0 +1 @@ +#include "Serializer.h" diff --git a/src/cpp-utils/data/Serializer.h b/src/cpp-utils/data/Serializer.h new file mode 100644 index 00000000..f1f8b42c --- /dev/null +++ b/src/cpp-utils/data/Serializer.h @@ -0,0 +1,135 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_DATA_SERIALIZER_H +#define MESSMER_CPPUTILS_DATA_SERIALIZER_H + +#include "Data.h" +#include "../macros.h" +#include "../assert/assert.h" +#include + +namespace cpputils { + //TODO Test Serializer/Deserializer + //TODO Also test system (big endian/little endian) by adding a serialized data file to the repository and (a) reading it and (b) rewriting and comparing it + class Serializer final { + public: + Serializer(size_t size); + + void writeUint8(uint8_t value); + void writeInt8(int8_t value); + void writeUint16(uint16_t value); + void writeInt16(int16_t value); + void writeUint32(uint32_t value); + void writeInt32(int32_t value); + void writeUint64(uint64_t value); + void writeInt64(int64_t value); + void writeString(const std::string &value); + void writeData(const Data &value); + + // Write the data as last element when serializing. + // It does not store a data size but limits the size by the size of the serialization result + void writeTailData(const Data &value); + + static size_t DataSize(const Data &value); + static size_t StringSize(const std::string &value); + + Data finished(); + + private: + template void _write(DataType obj); + void _writeData(const Data &value); + + size_t _pos; + Data _result; + + DISALLOW_COPY_AND_ASSIGN(Serializer); + }; + + inline Serializer::Serializer(size_t size): _pos(0), _result(size) { + } + + inline void Serializer::writeUint8(uint8_t value) { + _write(value); + } + + inline void Serializer::writeInt8(int8_t value) { + _write(value); + } + + inline void Serializer::writeUint16(uint16_t value) { + _write(value); + } + + inline void Serializer::writeInt16(int16_t value) { + _write(value); + } + + inline void Serializer::writeUint32(uint32_t value) { + _write(value); + } + + inline void Serializer::writeInt32(int32_t value) { + _write(value); + } + + inline void Serializer::writeUint64(uint64_t value) { + _write(value); + } + + inline void Serializer::writeInt64(int64_t value) { + _write(value); + } + + template + inline void Serializer::_write(DataType obj) { + static_assert(std::is_pod::value, "Can only serialize PODs"); + if (_pos + sizeof(DataType) > _result.size()) { + throw std::runtime_error("Serialization failed - size overflow"); + } + *reinterpret_cast(_result.dataOffset(_pos)) = obj; + _pos += sizeof(DataType); + } + + inline void Serializer::writeData(const Data &data) { + writeUint64(data.size()); + _writeData(data); + } + + inline size_t Serializer::DataSize(const Data &data) { + return sizeof(uint64_t) + data.size(); + } + + inline void Serializer::writeTailData(const Data &data) { + ASSERT(_pos + data.size() == _result.size(), "Not enough data given to write until the end of the stream"); + _writeData(data); + } + + inline void Serializer::_writeData(const Data &data) { + if (_pos + data.size() > _result.size()) { + throw std::runtime_error("Serialization failed - size overflow"); + } + std::memcpy(static_cast(_result.dataOffset(_pos)), static_cast(data.data()), data.size()); + _pos += data.size(); + } + + inline void Serializer::writeString(const std::string &value) { + size_t size = value.size() + 1; // +1 for the nullbyte + if (_pos + size > _result.size()) { + throw std::runtime_error("Serialization failed - size overflow"); + } + std::memcpy(static_cast(_result.dataOffset(_pos)), value.c_str(), size); + _pos += size; + } + + inline size_t Serializer::StringSize(const std::string &value) { + return value.size() + 1; // +1 for nullbyte + } + + inline Data Serializer::finished() { + if (_pos != _result.size()) { + throw std::runtime_error("Serialization failed - size not fully used."); + } + return std::move(_result); + } +} + +#endif diff --git a/src/cpp-utils/either.h b/src/cpp-utils/either.h new file mode 100644 index 00000000..23da49d4 --- /dev/null +++ b/src/cpp-utils/either.h @@ -0,0 +1,218 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_EITHER_H +#define MESSMER_CPPUTILS_EITHER_H + +#include +#include + +namespace cpputils { + + template + class either final { + public: + //TODO Try allowing construction with any type that std::is_convertible to Left or Right. + either(const Left &left): _side(Side::left) { + _construct_left(left); + } + either(Left &&left): _side(Side::left) { + _construct_left(std::move(left)); + } + either(const Right &right): _side(Side::right) { + _construct_right(right); + } + either(Right &&right): _side(Side::right) { + _construct_right(std::move(right)); + } + //TODO Try allowing copy-construction when Left/Right types are std::is_convertible + either(const either &rhs): _side(rhs._side) { + if(_side == Side::left) { + _construct_left(rhs._left); + } else { + _construct_right(rhs._right); + } + } + either(either &&rhs): _side(rhs._side) { + if(_side == Side::left) { + _construct_left(std::move(rhs._left)); + } else { + _construct_right(std::move(rhs._right)); + } + } + + ~either() { + _destruct(); + } + + //TODO Try allowing copy-assignment when Left/Right types are std::is_convertible + either &operator=(const either &rhs) { + _destruct(); + _side = rhs._side; + if (_side == Side::left) { + _construct_left(rhs._left); + } else { + _construct_right(rhs._right); + } + return *this; + } + + either &operator=(either &&rhs) { + _destruct(); + _side = rhs._side; + if (_side == Side::left) { + _construct_left(std::move(rhs._left)); + } else { + _construct_right(std::move(rhs._right)); + } + return *this; + } + + //TODO fold, map_left, map_right, left_or_else(val), right_or_else(val), left_or_else(func), right_or_else(func) + + bool is_left() const { + return _side == Side::left; + } + + bool is_right() const { + return _side == Side::right; + } + + const Left &left() const& { + return _left; + } + Left &left() & { + return const_cast(const_cast*>(this)->left()); + } + Left &&left() && { + return std::move(left()); + } + + const Right &right() const& { + return _right; + } + Right &right() & { + return const_cast(const_cast*>(this)->right()); + } + Right &&right() && { + return std::move(right()); + } + + boost::optional left_opt() const& { + if (_side == Side::left) { + return _left; + } else { + return boost::none; + } + } + boost::optional left_opt() & { + if (_side == Side::left) { + return _left; + } else { + return boost::none; + } + } + boost::optional left_opt() && { + if (_side == Side::left) { + return std::move(_left); + } else { + return boost::none; + } + } + + boost::optional right_opt() const& { + if (_side == Side::right) { + return _right; + } else { + return boost::none; + } + } + boost::optional right_opt() & { + if (_side == Side::right) { + return _right; + } else { + return boost::none; + } + } + boost::optional right_opt() && { + if (_side == Side::right) { + return std::move(_right); + } else { + return boost::none; + } + } + + private: + union { + Left _left; + Right _right; + }; + enum class Side : unsigned char {left, right} _side; + + either(Side side): _side(side) {} + + template + void _construct_left(Args&&... args) { + new(&_left)Left(std::forward(args)...); + } + template + void _construct_right(Args&&... args) { + new(&_right)Right(std::forward(args)...); + } + void _destruct() { + if (_side == Side::left) { + _left.~Left(); + } else { + _right.~Right(); + } + } + + template + friend either make_left(Args&&... args); + + template + friend either make_right(Args&&... args); + }; + + template + bool operator==(const either &lhs, const either &rhs) { + if (lhs.is_left() != rhs.is_left()) { + return false; + } + if (lhs.is_left()) { + return lhs.left() == rhs.left(); + } else { + return lhs.right() == rhs.right(); + } + } + + template + bool operator!=(const either &lhs, const either &rhs) { + return !operator==(lhs, rhs); + } + + template + std::ostream &operator<<(std::ostream &stream, const either &value) { + if (value.is_left()) { + stream << "Left(" << value.left() << ")"; + } else { + stream << "Right(" << value.right() << ")"; + } + return stream; + } + + template + either make_left(Args&&... args) { + either result(either::Side::left); + result._construct_left(std::forward(args)...); + return result; + } + + template + either make_right(Args&&... args) { + either result(either::Side::right); + result._construct_right(std::forward(args)...); + return result; + } +} + + +#endif diff --git a/src/cpp-utils/io/Console.cpp b/src/cpp-utils/io/Console.cpp new file mode 100644 index 00000000..61ed3533 --- /dev/null +++ b/src/cpp-utils/io/Console.cpp @@ -0,0 +1,99 @@ +#include "Console.h" + +#include +#include + +using std::string; +using std::vector; +using std::ostream; +using std::istream; +using std::flush; +using std::getline; +using boost::optional; +using boost::none; +using std::function; + +using namespace cpputils; + +IOStreamConsole::IOStreamConsole(): IOStreamConsole(std::cout, std::cin) { +} + +IOStreamConsole::IOStreamConsole(ostream &output, istream &input): _output(output), _input(input) { +} + +optional parseInt(const string &str) { + try { + string trimmed = str; + boost::algorithm::trim(trimmed); + int parsed = std::stoi(str); + if (std::to_string(parsed) != trimmed) { + return none; + } + return parsed; + } catch (const std::invalid_argument &e) { + return none; + } catch (const std::out_of_range &e) { + return none; + } +} + +function(const std::string &input)> parseUIntWithMinMax(unsigned int min, unsigned int max) { + return [min, max] (const string &input) { + optional parsed = parseInt(input); + if (parsed == none) { + return optional(none); + } + unsigned int value = static_cast(*parsed); + if (value < min || value > max) { + return optional(none); + } + return optional(value); + }; +} + +template +Return IOStreamConsole::_askForChoice(const string &question, function (const string&)> parse) { + optional choice = none; + do { + _output << question << flush; + string choiceStr; + getline(_input, choiceStr); + choice = parse(choiceStr); + } while(choice == none); + return *choice; +} + +unsigned int IOStreamConsole::ask(const string &question, const vector &options) { + if(options.size() == 0) { + throw std::invalid_argument("options should have at least one entry"); + } + _output << question << "\n"; + for (unsigned int i = 0; i < options.size(); ++i) { + _output << " [" << (i+1) << "] " << options[i] << "\n"; + } + int choice = _askForChoice("Your choice [1-" + std::to_string(options.size()) + "]: ", parseUIntWithMinMax(1, options.size())); + return choice-1; +} + +function(const string &input)> parseYesNo() { + return [] (const string &input) { + string trimmed = input; + boost::algorithm::trim(trimmed); + if(trimmed == "Y" || trimmed == "y" || trimmed == "Yes" || trimmed == "yes") { + return optional(true); + } else if (trimmed == "N" || trimmed == "n" || trimmed == "No" || trimmed == "no") { + return optional(false); + } else { + return optional(none); + } + }; +} + +bool IOStreamConsole::askYesNo(const string &question) { + _output << question << "\n"; + return _askForChoice("Your choice [y/n]: ", parseYesNo()); +} + +void IOStreamConsole::print(const std::string &output) { + _output << output << std::flush; +} diff --git a/src/cpp-utils/io/Console.h b/src/cpp-utils/io/Console.h new file mode 100644 index 00000000..882b0e82 --- /dev/null +++ b/src/cpp-utils/io/Console.h @@ -0,0 +1,41 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_IO_CONSOLE_H +#define MESSMER_CPPUTILS_IO_CONSOLE_H + +#include +#include +#include +#include +#include "../macros.h" + +namespace cpputils { + +class Console { +public: + virtual ~Console() {} + virtual unsigned int ask(const std::string &question, const std::vector &options) = 0; + virtual bool askYesNo(const std::string &question) = 0; + virtual void print(const std::string &output) = 0; +}; + +class IOStreamConsole final: public Console { +public: + IOStreamConsole(); + IOStreamConsole(std::ostream &output, std::istream &input); + unsigned int ask(const std::string &question, const std::vector &options) override; + bool askYesNo(const std::string &question) override; + void print(const std::string &output) override; +private: + template + Return _askForChoice(const std::string &question, std::function (const std::string&)> parse); + + std::ostream &_output; + std::istream &_input; + + DISALLOW_COPY_AND_ASSIGN(IOStreamConsole); +}; + +} + + +#endif diff --git a/src/cpp-utils/io/pipestream.cpp b/src/cpp-utils/io/pipestream.cpp new file mode 100644 index 00000000..e07c89b9 --- /dev/null +++ b/src/cpp-utils/io/pipestream.cpp @@ -0,0 +1 @@ +#include "pipestream.h" diff --git a/src/cpp-utils/io/pipestream.h b/src/cpp-utils/io/pipestream.h new file mode 100644 index 00000000..fa71c373 --- /dev/null +++ b/src/cpp-utils/io/pipestream.h @@ -0,0 +1,156 @@ +// Original version taken under MIT licence from http://stackoverflow.com/a/12413298/829568 and modified. +// ---------------------------------------------------------------------------- +// Copyright (C) 2013 Dietmar Kuehl http://www.dietmar-kuehl.de +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// ---------------------------------------------------------------------------- +#pragma once +#ifndef MESSMER_CPPUTILS_PIPESTREAM_H +#define MESSMER_CPPUTILS_PIPESTREAM_H + +/** + * This is a class that implements a pipe for std::ostream/std::istream. + * You can in one thread write to std::ostream and read that data (blocking) from an std::istream. + * Reading and writing can happen in different threads. + * + * Use as follows: + * pipestream pipe; + * std::istream istream(&pipe); + * std::ostream ostream(&pipe); + * istream << "Data"; + * ostream >> ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../macros.h" + +//TODO Add test cases + +namespace cpputils { + + class pipestream final : public std::streambuf { + private: + typedef std::streambuf::traits_type traits_type; + typedef std::string::size_type string_size_t; + + std::mutex d_mutex; + std::condition_variable d_condition; + std::string d_out; + std::string d_in; + std::string d_tmp; + char *d_current; + bool d_closed; + + public: + pipestream(string_size_t out_size = 16, string_size_t in_size = 64) + : d_mutex() + , d_condition() + , d_out(std::max(string_size_t(1), out_size), ' ') + , d_in(std::max(string_size_t(1), in_size), ' ') + , d_tmp(std::max(string_size_t(1), in_size), ' ') + , d_current(&this->d_tmp[0]) + , d_closed(false) + { + this->setp(&this->d_out[0], &this->d_out[0] + this->d_out.size() - 1); + this->setg(&this->d_in[0], &this->d_in[0], &this->d_in[0]); + } + + void close() { + { + std::unique_lock lock(this->d_mutex); + this->d_closed = true; + while (this->pbase() != this->pptr()) { + this->internal_sync(lock); + } + } + this->d_condition.notify_all(); + } + + private: + int_type underflow() override { + if (this->gptr() == this->egptr()) { + std::unique_lock lock(this->d_mutex); + while (&this->d_tmp[0] == this->d_current && !this->d_closed) { + this->d_condition.wait(lock); + } + if (&this->d_tmp[0] != this->d_current) { + std::streamsize size(this->d_current - &this->d_tmp[0]); + traits_type::copy(this->eback(), &this->d_tmp[0], + this->d_current - &this->d_tmp[0]); + this->setg(this->eback(), this->eback(), this->eback() + size); + this->d_current = &this->d_tmp[0]; + this->d_condition.notify_one(); + } + } + return this->gptr() == this->egptr() + ? traits_type::eof() + : traits_type::to_int_type(*this->gptr()); + } + + int_type overflow(int_type c) override { + std::unique_lock lock(this->d_mutex); + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *this->pptr() = traits_type::to_char_type(c); + this->pbump(1); + } + return this->internal_sync(lock) + ? traits_type::eof() + : traits_type::not_eof(c); + } + + int sync() override { + std::unique_lock lock(this->d_mutex); + return this->internal_sync(lock); + } + + int internal_sync(std::unique_lock &lock) { + char *end(&this->d_tmp[0] + this->d_tmp.size()); + while (this->d_current == end && !this->d_closed) { + this->d_condition.wait(lock); + } + if (this->d_current != end) { + std::streamsize size(std::min(end - d_current, + this->pptr() - this->pbase())); + traits_type::copy(d_current, this->pbase(), size); + this->d_current += size; + std::streamsize remain((this->pptr() - this->pbase()) - size); + traits_type::move(this->pbase(), this->pptr(), remain); + this->setp(this->pbase(), this->epptr()); + this->pbump(remain); + this->d_condition.notify_one(); + return 0; + } + return traits_type::eof(); + } + + DISALLOW_COPY_AND_ASSIGN(pipestream); + }; + +} + +#endif diff --git a/src/cpp-utils/lock/ConditionBarrier.h b/src/cpp-utils/lock/ConditionBarrier.h new file mode 100644 index 00000000..459718a8 --- /dev/null +++ b/src/cpp-utils/lock/ConditionBarrier.h @@ -0,0 +1,42 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_LOCK_CONDITIONBARRIER_H +#define MESSMER_CPPUTILS_LOCK_CONDITIONBARRIER_H + +#include +#include +#include "../macros.h" + +//TODO Test +//TODO Merge lock folder with thread folder + +namespace cpputils { + // Like a condition variable, but without spurious wakeups. + // The waiting threads are only woken, when notify() is called. + // After a call to release(), future calls to wait() will not block anymore. + class ConditionBarrier final { + public: + ConditionBarrier() :_mutex(), _cv(), _triggered(false) { + } + + void wait() { + std::unique_lock lock(_mutex); + _cv.wait(lock, [this] { + return _triggered; + }); + } + + void release() { + std::unique_lock lock(_mutex); + _triggered = true; + _cv.notify_all(); + } + private: + std::mutex _mutex; + std::condition_variable _cv; + bool _triggered; + + DISALLOW_COPY_AND_ASSIGN(ConditionBarrier); + }; +} + +#endif diff --git a/src/cpp-utils/lock/LockPool.cpp b/src/cpp-utils/lock/LockPool.cpp new file mode 100644 index 00000000..460417c7 --- /dev/null +++ b/src/cpp-utils/lock/LockPool.cpp @@ -0,0 +1 @@ +#include "LockPool.h" diff --git a/src/cpp-utils/lock/LockPool.h b/src/cpp-utils/lock/LockPool.h new file mode 100644 index 00000000..bbcee468 --- /dev/null +++ b/src/cpp-utils/lock/LockPool.h @@ -0,0 +1,73 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_LOCK_LOCKPOOL_H +#define MESSMER_CPPUTILS_LOCK_LOCKPOOL_H + +#include +#include +#include +#include +#include "../assert/assert.h" +#include "../macros.h" + +//TODO Test +//TODO Rename package to synchronization +//TODO Rename to MutexPool +namespace cpputils { + template + class LockPool final { + public: + LockPool(); + ~LockPool(); + void lock(const LockName &lock, std::unique_lock *lockToFreeWhileWaiting = nullptr); + void release(const LockName &lock); + + private: + bool _isLocked(const LockName &lock) const; + + std::vector _lockedLocks; + std::mutex _mutex; + std::condition_variable _cv; + + DISALLOW_COPY_AND_ASSIGN(LockPool); + }; + template + inline LockPool::LockPool(): _lockedLocks(), _mutex(), _cv() {} + + template + inline LockPool::~LockPool() { + ASSERT(_lockedLocks.size() == 0, "Still locks open"); + } + + template + inline void LockPool::lock(const LockName &lock, std::unique_lock *lockToFreeWhileWaiting) { + std::unique_lock mutexLock(_mutex); // TODO Is shared_lock enough here? + if (_isLocked(lock)) { + if(lockToFreeWhileWaiting != nullptr) { + lockToFreeWhileWaiting->unlock(); + } + _cv.wait(mutexLock, [this, &lock]{ + return !_isLocked(lock); + }); + if(lockToFreeWhileWaiting != nullptr) { + lockToFreeWhileWaiting->lock(); + } + } + _lockedLocks.push_back(lock); + } + + template + inline bool LockPool::_isLocked(const LockName &lock) const { + return std::find(_lockedLocks.begin(), _lockedLocks.end(), lock) != _lockedLocks.end(); + } + + template + inline void LockPool::release(const LockName &lock) { + std::unique_lock mutexLock(_mutex); + auto found = std::find(_lockedLocks.begin(), _lockedLocks.end(), lock); + ASSERT(found != _lockedLocks.end(), "Lock given to release() was not locked"); + _lockedLocks.erase(found); + _cv.notify_all(); + } +} + +#endif diff --git a/src/cpp-utils/lock/MutexPoolLock.h b/src/cpp-utils/lock/MutexPoolLock.h new file mode 100644 index 00000000..43cbc228 --- /dev/null +++ b/src/cpp-utils/lock/MutexPoolLock.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_LOCK_MUTEXPOOLLOCK_H +#define MESSMER_CPPUTILS_LOCK_MUTEXPOOLLOCK_H + +#include "LockPool.h" + +namespace cpputils { + template + class MutexPoolLock final { + public: + MutexPoolLock(LockPool *pool, const LockName &lockName): _pool(pool), _lockName(lockName) { + _pool->lock(_lockName); + } + + MutexPoolLock(LockPool *pool, const LockName &lockName, std::unique_lock *lockToFreeWhileWaiting) + : _pool(pool), _lockName(lockName) { + _pool->lock(_lockName, lockToFreeWhileWaiting); + } + + MutexPoolLock(MutexPoolLock &&rhs): _pool(rhs._pool), _lockName(rhs._lockName) { + rhs._pool = nullptr; + } + + ~MutexPoolLock() { + if (_pool != nullptr) { + _pool->release(_lockName); + _pool = nullptr; + } + } + + private: + LockPool *_pool; + LockName _lockName; + + DISALLOW_COPY_AND_ASSIGN(MutexPoolLock); + }; +} + +#endif diff --git a/src/cpp-utils/logging/Logger.h b/src/cpp-utils/logging/Logger.h new file mode 100644 index 00000000..a6fa0e34 --- /dev/null +++ b/src/cpp-utils/logging/Logger.h @@ -0,0 +1,56 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_LOGGING_LOGGER_H +#define MESSMER_CPPUTILS_LOGGING_LOGGER_H + +#include +#include "../macros.h" + +namespace cpputils { +namespace logging { + class Logger final { + public: + void setLogger(std::shared_ptr logger) { + _logger = logger; + _logger->set_level(_level); + } + + void reset() { + _level = spdlog::level::info; + setLogger(_defaultLogger()); + } + + void setLevel(spdlog::level::level_enum level) { + _level = level; + _logger->set_level(_level); + } + + spdlog::logger *operator->() { + return _logger.get(); + } + + private: + + static std::shared_ptr _defaultLogger() { + static auto singleton = spdlog::stderr_logger_mt("Log"); + return singleton; + } + + Logger() : _logger(), _level() { + reset(); + } + friend Logger &logger(); + + std::shared_ptr _logger; + spdlog::level::level_enum _level; + + DISALLOW_COPY_AND_ASSIGN(Logger); + }; + + inline Logger &logger() { + static Logger singleton; + return singleton; + } +} +} + +#endif diff --git a/src/cpp-utils/logging/logging.h b/src/cpp-utils/logging/logging.h new file mode 100644 index 00000000..4ffcc376 --- /dev/null +++ b/src/cpp-utils/logging/logging.h @@ -0,0 +1,58 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_LOGGING_LOGGING_H +#define MESSMER_CPPUTILS_LOGGING_LOGGING_H + +#include "Logger.h" +#include + +namespace cpputils { + namespace logging { + + extern struct ERROR_TYPE {} ERROR; + extern struct WARN_TYPE {} WARN; + extern struct INFO_TYPE {} INFO; + extern struct DEBUG_TYPE {} DEBUG; + + inline void setLogger(std::shared_ptr newLogger) { + logger().setLogger(newLogger); + } + + inline void reset() { + logger().reset(); + } + + inline void setLevel(ERROR_TYPE) { + logger().setLevel(spdlog::level::err); + } + + inline void setLevel(WARN_TYPE) { + logger().setLevel(spdlog::level::warn); + } + + inline void setLevel(INFO_TYPE) { + logger().setLevel(spdlog::level::info); + } + + inline void setLevel(DEBUG_TYPE) { + logger().setLevel(spdlog::level::debug); + } + + inline spdlog::details::line_logger LOG(ERROR_TYPE) { + return logger()->error(); + } + + inline spdlog::details::line_logger LOG(WARN_TYPE) { + return logger()->warn(); + } + + inline spdlog::details::line_logger LOG(INFO_TYPE) { + return logger()->info(); + } + + inline spdlog::details::line_logger LOG(DEBUG_TYPE) { + return logger()->debug(); + } + } +} + +#endif diff --git a/src/cpp-utils/macros.h b/src/cpp-utils/macros.h new file mode 100644 index 00000000..a07baca5 --- /dev/null +++ b/src/cpp-utils/macros.h @@ -0,0 +1,20 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_MACROS_H_ +#define MESSMER_CPPUTILS_MACROS_H_ + +//TODO If possible, make classes final and destructors non-virtual or delete destructors +//TODO Use DISALLOW_COPY_AND_ASSIGN where possible + +/** + * Disallow the copy and assignment constructors of a class + */ +#define DISALLOW_COPY_AND_ASSIGN(Class) \ + Class(const Class &rhs) = delete; \ + Class &operator=(const Class &rhs) = delete; + +/** + * Declare a function parameter as intentionally unused to get rid of the compiler warning + */ +#define UNUSED(expr) (void)(expr) + +#endif diff --git a/src/cpp-utils/network/CurlHttpClient.cpp b/src/cpp-utils/network/CurlHttpClient.cpp new file mode 100644 index 00000000..c80a0a62 --- /dev/null +++ b/src/cpp-utils/network/CurlHttpClient.cpp @@ -0,0 +1,50 @@ +// Base version taken from https://techoverflow.net/blog/2013/03/15/c-simple-http-download-using-libcurl-easy-api/ + +#include "CurlHttpClient.h" +#include +#include +#include +#include + +using boost::none; +using boost::optional; +using std::string; +using std::ostringstream; + +namespace cpputils { + + size_t CurlHttpClient::write_data(void *ptr, size_t size, size_t nmemb, ostringstream *stream) { + stream->write((const char *) ptr, size * nmemb); + return size * nmemb; + } + + CurlHttpClient::CurlHttpClient() { + curl = curl_easy_init(); + } + + CurlHttpClient::~CurlHttpClient() { + curl_easy_cleanup(curl); + } + + optional CurlHttpClient::get(const string &url, optional timeoutMsec) { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + // example.com is redirected, so we tell libcurl to follow redirection + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate"); + ostringstream out; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlHttpClient::write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out); + if (timeoutMsec != none) { + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, *timeoutMsec); + } + // Perform the request, res will get the return code + CURLcode res = curl_easy_perform(curl); + // Check for errors + if (res != CURLE_OK) { + return none; + } + return out.str(); + } + +} diff --git a/src/cpp-utils/network/CurlHttpClient.h b/src/cpp-utils/network/CurlHttpClient.h new file mode 100644 index 00000000..d86831b7 --- /dev/null +++ b/src/cpp-utils/network/CurlHttpClient.h @@ -0,0 +1,28 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_HPP +#define MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_HPP + +#include "HttpClient.h" +#include "../macros.h" + +namespace cpputils { + + class CurlHttpClient final : public HttpClient { + public: + CurlHttpClient(); + + ~CurlHttpClient(); + + boost::optional get(const std::string &url, boost::optional timeoutMsec = boost::none) override; + + private: + void *curl; + + static size_t write_data(void *ptr, size_t size, size_t nmemb, std::ostringstream *stream); + + DISALLOW_COPY_AND_ASSIGN(CurlHttpClient); + }; + +} + +#endif diff --git a/src/cpp-utils/network/FakeHttpClient.cpp b/src/cpp-utils/network/FakeHttpClient.cpp new file mode 100644 index 00000000..098ffa3d --- /dev/null +++ b/src/cpp-utils/network/FakeHttpClient.cpp @@ -0,0 +1,23 @@ +#include "FakeHttpClient.h" + +using std::string; +using boost::optional; +using boost::none; + +namespace cpputils { + FakeHttpClient::FakeHttpClient(): _sites() { + } + + void FakeHttpClient::addWebsite(const string &url, const string &content) { + _sites[url] = content; + } + + optional FakeHttpClient::get(const string &url, optional timeoutMsec) { + UNUSED(timeoutMsec); + auto found = _sites.find(url); + if (found == _sites.end()) { + return none; + } + return found->second; + } +} diff --git a/src/cpp-utils/network/FakeHttpClient.h b/src/cpp-utils/network/FakeHttpClient.h new file mode 100644 index 00000000..eb63d956 --- /dev/null +++ b/src/cpp-utils/network/FakeHttpClient.h @@ -0,0 +1,26 @@ +#ifndef MESSMER_CPPUTILS_NETWORK_FAKEHTTPCLIENT_H +#define MESSMER_CPPUTILS_NETWORK_FAKEHTTPCLIENT_H + +#include "HttpClient.h" +#include "../macros.h" +#include + +namespace cpputils { + + class FakeHttpClient final : public HttpClient { + public: + FakeHttpClient(); + + void addWebsite(const std::string &url, const std::string &content); + + boost::optional get(const std::string &url, boost::optional timeoutMsec = boost::none) override; + + private: + std::map _sites; + + DISALLOW_COPY_AND_ASSIGN(FakeHttpClient); + }; + +} + +#endif diff --git a/src/cpp-utils/network/HttpClient.cpp b/src/cpp-utils/network/HttpClient.cpp new file mode 100644 index 00000000..1c7ca709 --- /dev/null +++ b/src/cpp-utils/network/HttpClient.cpp @@ -0,0 +1 @@ +#include "HttpClient.h" diff --git a/src/cpp-utils/network/HttpClient.h b/src/cpp-utils/network/HttpClient.h new file mode 100644 index 00000000..6fc3b82b --- /dev/null +++ b/src/cpp-utils/network/HttpClient.h @@ -0,0 +1,16 @@ +#ifndef MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_H +#define MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_H + +#include +#include + +namespace cpputils { + class HttpClient { + public: + virtual ~HttpClient() {} + + virtual boost::optional get(const std::string& url, boost::optional timeoutMsec = boost::none) = 0; + }; +}; + +#endif diff --git a/src/cpp-utils/pointer/cast.h b/src/cpp-utils/pointer/cast.h new file mode 100644 index 00000000..be45ab87 --- /dev/null +++ b/src/cpp-utils/pointer/cast.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_POINTER_CAST_H_ +#define MESSMER_CPPUTILS_POINTER_CAST_H_ + +#include + +namespace cpputils { + +/** + * dynamic_cast implementation for unique_ptr (moving unique_ptr into a unique_ptr of different type) + */ +//TODO Also allow passing a rvalue reference, otherwise dynamic_pointer_move(func()) won't work +template +inline std::unique_ptr dynamic_pointer_move(std::unique_ptr &source) { + //TODO Deleter + DST *casted = dynamic_cast(source.get()); + if (casted != nullptr) { + source.release(); + } + return std::unique_ptr(casted); +} + +} + +#endif diff --git a/src/cpp-utils/pointer/gcc_4_8_compatibility.h b/src/cpp-utils/pointer/gcc_4_8_compatibility.h new file mode 100644 index 00000000..bf228c13 --- /dev/null +++ b/src/cpp-utils/pointer/gcc_4_8_compatibility.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_GCC48COMPATIBILITY_H +#define MESSMER_CPPUTILS_GCC48COMPATIBILITY_H + +#include + +#if __GNUC__ == 4 && __GNUC_MINOR__ == 8 +// Add std::make_unique +namespace std { + template + inline unique_ptr make_unique(Args&&... args) { + return unique_ptr(new T(std::forward(args)...)); + } +} + +#endif + +#endif diff --git a/src/cpp-utils/pointer/optional_ownership_ptr.h b/src/cpp-utils/pointer/optional_ownership_ptr.h new file mode 100644 index 00000000..fb90d639 --- /dev/null +++ b/src/cpp-utils/pointer/optional_ownership_ptr.h @@ -0,0 +1,49 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_POINTER_OPTIONALOWNERSHIPPOINTER_H_ +#define MESSMER_CPPUTILS_POINTER_OPTIONALOWNERSHIPPOINTER_H_ + +#include "unique_ref.h" +#include + +/** + * optional_ownership_ptr can be used to hold a pointer to an instance of an object. + * The pointer might or might not have ownership of the object. + * + * If it has ownership, it will delete the stored object in its destructor. + * If it doesn't have ownership, it won't. + * + * You can create such pointers with + * - WithOwnership(ptr) + * - WithoutOwnership(ptr) + * - null() + */ + +namespace cpputils { + +template +using optional_ownership_ptr = std::unique_ptr>; + +template +optional_ownership_ptr WithOwnership(std::unique_ptr obj) { + auto deleter = obj.get_deleter(); + return optional_ownership_ptr(obj.release(), [deleter](T* obj){deleter(obj);}); +} + +template +optional_ownership_ptr WithOwnership(unique_ref obj) { + return WithOwnership(to_unique_ptr(std::move(obj))); +} + +template +optional_ownership_ptr WithoutOwnership(T *obj) { + return optional_ownership_ptr(obj, [](T*){}); +} + +template +optional_ownership_ptr null() { + return WithoutOwnership(nullptr); +} + +} + +#endif diff --git a/src/cpp-utils/pointer/unique_ref.h b/src/cpp-utils/pointer/unique_ref.h new file mode 100644 index 00000000..1cc2120f --- /dev/null +++ b/src/cpp-utils/pointer/unique_ref.h @@ -0,0 +1,149 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_POINTER_UNIQUE_REF_H +#define MESSMER_CPPUTILS_POINTER_UNIQUE_REF_H + +#include +#include +#include "../macros.h" +#include "gcc_4_8_compatibility.h" +#include "cast.h" + +namespace cpputils { + +/** + * unique_ref behaves like unique_ptr, but guarantees that the pointer points to a valid object. + * You can create objects using make_unique_ref (works like make_unique for unique_ptr). + * + * If you happen to already have a unique_ptr, you can call nullcheck(unique_ptr), + * which returns optional>. + * Take care that this should be used very rarely, since it circumvents parts of the guarantee. + * It still protects against null pointers, but it does not guarantee anymore that the pointer points + * to a valid object. It might hold an arbitrary non-null memory location. + * + * Caution: There is one way a unique_ref can actually hold a nullptr. + * It will hold a nullptr after its value was moved to another unique_ref. + * Never use the old instance after moving! + */ +template +class unique_ref final { +public: + + unique_ref(unique_ref&& from): _target(std::move(from._target)) {} + // TODO Test this upcast-allowing move constructor + template unique_ref(unique_ref&& from): _target(std::move(from._target)) {} + + unique_ref& operator=(unique_ref&& from) { + _target = std::move(from._target); + return *this; + } + // TODO Test this upcast-allowing assignment + template unique_ref& operator=(unique_ref&& from) { + _target = std::move(from._target); + return *this; + } + + typename std::add_lvalue_reference::type operator*() const& { + return *_target; + } + typename std::add_rvalue_reference::type operator*() && { + return std::move(*_target); + } + + T* operator->() const { + return get(); + } + + T* get() const { + return _target.get(); + } + + void swap(unique_ref& rhs) { + std::swap(_target, rhs._target); + } + +private: + unique_ref(std::unique_ptr target): _target(std::move(target)) {} + template friend unique_ref make_unique_ref(Args&&... args); + template friend boost::optional> nullcheck(std::unique_ptr ptr); + template friend class unique_ref; + template friend boost::optional> dynamic_pointer_move(unique_ref &source); + template friend std::unique_ptr to_unique_ptr(unique_ref ref); + + std::unique_ptr _target; + + DISALLOW_COPY_AND_ASSIGN(unique_ref); +}; + +template +inline unique_ref make_unique_ref(Args&&... args) { + return unique_ref(std::make_unique(std::forward(args)...)); +} + +template +inline boost::optional> nullcheck(std::unique_ptr ptr) { + if (ptr.get() != nullptr) { + return unique_ref(std::move(ptr)); + } + return boost::none; +} + +template inline void destruct(unique_ref ptr) { + to_unique_ptr(std::move(ptr)).reset(); +} + +//TODO Also allow passing a rvalue reference, otherwise dynamic_pointer_move(func()) won't work +template +inline boost::optional> dynamic_pointer_move(unique_ref &source) { + return nullcheck(dynamic_pointer_move(source._target)); +} + +//TODO Write test cases for to_unique_ptr +template +inline std::unique_ptr to_unique_ptr(unique_ref ref) { + return std::move(ref._target); +} + +template +inline bool operator==(const unique_ref &lhs, const unique_ref &rhs) { + return lhs.get() == rhs.get(); +} + +template +inline bool operator!=(const unique_ref &lhs, const unique_ref &rhs) { + return !operator==(lhs, rhs); +} + +} + +namespace std { + template + inline void swap(cpputils::unique_ref& lhs, cpputils::unique_ref& rhs) { + lhs.swap(rhs); + } + + template + inline void swap(cpputils::unique_ref&& lhs, cpputils::unique_ref& rhs) { + lhs.swap(rhs); + } + + template + inline void swap(cpputils::unique_ref& lhs, cpputils::unique_ref&& rhs) { + lhs.swap(rhs); + } + + // Allow using it in std::unordered_set / std::unordered_map + template struct hash> { + size_t operator()(const cpputils::unique_ref &ref) const { + return (size_t)ref.get(); + } + }; + + // Allow using it in std::map / std::set + template struct less> { + bool operator()(const cpputils::unique_ref &lhs, const cpputils::unique_ref &rhs) const { + return lhs.get() < rhs.get(); + } + }; +} + +#endif diff --git a/src/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h b/src/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h new file mode 100644 index 00000000..62442452 --- /dev/null +++ b/src/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h @@ -0,0 +1,23 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_POINTER_UNIQUE_REF_BOOST_OPTIONAL_GTEST_WORKAROUND_H +#define MESSMER_CPPUTILS_POINTER_UNIQUE_REF_BOOST_OPTIONAL_GTEST_WORKAROUND_H + +/** + * This is a workaround for using boost::optional> in gtest. + * Without including this file, the linker will fail. + */ + +//TODO Test that this solves the problem (add test unit file that doesn't compile without) + +#include "unique_ref.h" +//gtest/boost::optional workaround for working with optional> +namespace cpputils { + template + inline std::ostream& operator<<(std::ostream& out, const cpputils::unique_ref &ref) { + out << ref.get(); + return out; + } +} +#include + +#endif diff --git a/src/cpp-utils/process/daemonize.cpp b/src/cpp-utils/process/daemonize.cpp new file mode 100644 index 00000000..5207f803 --- /dev/null +++ b/src/cpp-utils/process/daemonize.cpp @@ -0,0 +1,52 @@ +#include "daemonize.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../logging/logging.h" + +using namespace cpputils::logging; + +namespace cpputils { + + //TODO Test daemonize() + + void daemonize() { + pid_t pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + if (pid > 0) { + //We're the parent process. Exit. + exit(EXIT_SUCCESS); + } + + // We're the child process. + umask(0); + + // Create a new SID for the child process + pid_t sid = setsid(); + if (sid < 0) { + LOG(ERROR) << "Failed to get SID for daemon process"; + exit(EXIT_FAILURE); + } + + // Change the current working directory to a directory that's always existin + if ((chdir("/")) < 0) { + LOG(ERROR) << "Failed to change working directory for daemon process"; + exit(EXIT_FAILURE); + } + + // Close out the standard file descriptors. The process can't use them anyhow. + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + }; +} diff --git a/src/cpp-utils/process/daemonize.h b/src/cpp-utils/process/daemonize.h new file mode 100644 index 00000000..b3cfc704 --- /dev/null +++ b/src/cpp-utils/process/daemonize.h @@ -0,0 +1,9 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_PROCESS_DAEMONIZE_H +#define MESSMER_CPPUTILS_PROCESS_DAEMONIZE_H + +namespace cpputils { + void daemonize(); +} + +#endif diff --git a/src/cpp-utils/process/subprocess.cpp b/src/cpp-utils/process/subprocess.cpp new file mode 100644 index 00000000..fcf740b0 --- /dev/null +++ b/src/cpp-utils/process/subprocess.cpp @@ -0,0 +1,43 @@ +#include "subprocess.h" +#include +#include +#include + +using std::string; + +namespace cpputils { + //TODO Exception safety + + string Subprocess::call(const string &command) { + FILE *subprocessOutput = _call(command); + + string result; + char buffer[1024]; + while(fgets(buffer, sizeof(buffer), subprocessOutput) != nullptr) { + result += buffer; + } + + auto returncode = pclose(subprocessOutput); + if(WEXITSTATUS(returncode) != 0) { + throw std::runtime_error("Subprocess \""+command+"\" exited with code "+std::to_string(WEXITSTATUS(returncode))); + } + + return result; + } + + int Subprocess::callAndGetReturnCode(const string &command) { + FILE *subprocess = _call(command); + + auto returncode = pclose(subprocess); + return WEXITSTATUS(returncode); + } + + FILE *Subprocess::_call(const string &command) { + FILE *subprocess = popen(command.c_str(), "r"); + if (!subprocess) + { + throw std::runtime_error("Error starting subprocess "+command); + } + return subprocess; + } +} diff --git a/src/cpp-utils/process/subprocess.h b/src/cpp-utils/process/subprocess.h new file mode 100644 index 00000000..907aab76 --- /dev/null +++ b/src/cpp-utils/process/subprocess.h @@ -0,0 +1,21 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_PROCESS_SUBPROCESS_H +#define MESSMER_CPPUTILS_PROCESS_SUBPROCESS_H + +#include +#include "../macros.h" + +namespace cpputils { + //TODO Test + class Subprocess final { + public: + static std::string call(const std::string &command); + static int callAndGetReturnCode(const std::string &command); + private: + static FILE* _call(const std::string &command); + + DISALLOW_COPY_AND_ASSIGN(Subprocess); + }; +} + +#endif diff --git a/src/cpp-utils/random/OSRandomGenerator.cpp b/src/cpp-utils/random/OSRandomGenerator.cpp new file mode 100644 index 00000000..02916ad5 --- /dev/null +++ b/src/cpp-utils/random/OSRandomGenerator.cpp @@ -0,0 +1 @@ +#include "OSRandomGenerator.h" diff --git a/src/cpp-utils/random/OSRandomGenerator.h b/src/cpp-utils/random/OSRandomGenerator.h new file mode 100644 index 00000000..0b4c9e73 --- /dev/null +++ b/src/cpp-utils/random/OSRandomGenerator.h @@ -0,0 +1,27 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_RANDOM_OSRANDOMGENERATOR_H +#define MESSMER_CPPUTILS_RANDOM_OSRANDOMGENERATOR_H + +#include "RandomGenerator.h" +#include + +namespace cpputils { + class OSRandomGenerator final : public RandomGenerator { + public: + OSRandomGenerator(); + + protected: + void _get(void *target, size_t bytes) override; + + private: + DISALLOW_COPY_AND_ASSIGN(OSRandomGenerator); + }; + + inline OSRandomGenerator::OSRandomGenerator() {} + + inline void OSRandomGenerator::_get(void *target, size_t bytes) { + CryptoPP::OS_GenerateRandomBlock(true, (byte*)target, bytes); + } +} + +#endif diff --git a/src/cpp-utils/random/PseudoRandomPool.cpp b/src/cpp-utils/random/PseudoRandomPool.cpp new file mode 100644 index 00000000..c7115685 --- /dev/null +++ b/src/cpp-utils/random/PseudoRandomPool.cpp @@ -0,0 +1,6 @@ +#include "PseudoRandomPool.h" + +namespace cpputils { + constexpr size_t PseudoRandomPool::MIN_BUFFER_SIZE; + constexpr size_t PseudoRandomPool::MAX_BUFFER_SIZE; +} \ No newline at end of file diff --git a/src/cpp-utils/random/PseudoRandomPool.h b/src/cpp-utils/random/PseudoRandomPool.h new file mode 100644 index 00000000..f440fccf --- /dev/null +++ b/src/cpp-utils/random/PseudoRandomPool.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_RANDOM_PSEUDORANDOMPOOL_H +#define MESSMER_CPPUTILS_RANDOM_PSEUDORANDOMPOOL_H + +#include +#include "RandomGenerator.h" +#include "ThreadsafeRandomDataBuffer.h" +#include "RandomGeneratorThread.h" +#include + +namespace cpputils { + //TODO Test + class PseudoRandomPool final : public RandomGenerator { + public: + PseudoRandomPool(); + + protected: + void _get(void *target, size_t bytes) override; + + private: + static constexpr size_t MIN_BUFFER_SIZE = 1*1024*1024; // 1MB + static constexpr size_t MAX_BUFFER_SIZE = 2*1024*1024; // 2MB + + ThreadsafeRandomDataBuffer _buffer; + RandomGeneratorThread _refillThread; + DISALLOW_COPY_AND_ASSIGN(PseudoRandomPool); + }; + + + inline void PseudoRandomPool::_get(void *target, size_t bytes) { + _buffer.get(target, bytes); + } + + inline PseudoRandomPool::PseudoRandomPool(): _buffer(), _refillThread(&_buffer, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE) { + _refillThread.start(); + } +} + +#endif diff --git a/src/cpp-utils/random/Random.cpp b/src/cpp-utils/random/Random.cpp new file mode 100644 index 00000000..85c98814 --- /dev/null +++ b/src/cpp-utils/random/Random.cpp @@ -0,0 +1,5 @@ +#include "Random.h" + +namespace cpputils { + std::mutex Random::_mutex; +} \ No newline at end of file diff --git a/src/cpp-utils/random/Random.h b/src/cpp-utils/random/Random.h new file mode 100644 index 00000000..49610460 --- /dev/null +++ b/src/cpp-utils/random/Random.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_RANDOM_RANDOM_H +#define MESSMER_CPPUTILS_RANDOM_RANDOM_H + +#include "PseudoRandomPool.h" +#include "OSRandomGenerator.h" +#include "../data/FixedSizeData.h" +#include "../data/Data.h" +#include + +namespace cpputils { + class Random final { + public: + static PseudoRandomPool &PseudoRandom() { + std::unique_lock lock(_mutex); + static PseudoRandomPool random; + return random; + } + + static OSRandomGenerator &OSRandom() { + std::unique_lock lock(_mutex); + static OSRandomGenerator random; + return random; + } + + private: + static std::mutex _mutex; + + DISALLOW_COPY_AND_ASSIGN(Random); + }; +} + +#endif diff --git a/src/cpp-utils/random/RandomDataBuffer.cpp b/src/cpp-utils/random/RandomDataBuffer.cpp new file mode 100644 index 00000000..879b3394 --- /dev/null +++ b/src/cpp-utils/random/RandomDataBuffer.cpp @@ -0,0 +1 @@ +#include "RandomDataBuffer.h" diff --git a/src/cpp-utils/random/RandomDataBuffer.h b/src/cpp-utils/random/RandomDataBuffer.h new file mode 100644 index 00000000..89c5fbe9 --- /dev/null +++ b/src/cpp-utils/random/RandomDataBuffer.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_RANDOM_RANDOMDATABUFFER_H +#define MESSMER_CPPUTILS_RANDOM_RANDOMDATABUFFER_H + +#include "../data/Data.h" +#include "../assert/assert.h" + +namespace cpputils { + //TODO Test + class RandomDataBuffer final { + public: + RandomDataBuffer(); + + size_t size() const; + + void get(void *target, size_t bytes); + + void add(Data data); + + private: + size_t _usedUntil; + Data _data; + + DISALLOW_COPY_AND_ASSIGN(RandomDataBuffer); + }; + + inline RandomDataBuffer::RandomDataBuffer() : _usedUntil(0), _data(0) { + } + + inline size_t RandomDataBuffer::size() const { + return _data.size() - _usedUntil; + } + + inline void RandomDataBuffer::get(void *target, size_t numBytes) { + ASSERT(size() >= numBytes, "Too many bytes requested. Buffer is smaller."); + std::memcpy(target, _data.dataOffset(_usedUntil), numBytes); + _usedUntil += numBytes; + } + + inline void RandomDataBuffer::add(Data newData) { + // Concatenate old and new random data + size_t oldSize = size(); + Data combined(oldSize + newData.size()); + get(combined.data(), oldSize); + std::memcpy(combined.dataOffset(oldSize), newData.data(), newData.size()); + _data = std::move(combined); + _usedUntil = 0; + } + +} + +#endif diff --git a/src/cpp-utils/random/RandomGenerator.cpp b/src/cpp-utils/random/RandomGenerator.cpp new file mode 100644 index 00000000..d809d03b --- /dev/null +++ b/src/cpp-utils/random/RandomGenerator.cpp @@ -0,0 +1 @@ +#include "RandomGenerator.h" \ No newline at end of file diff --git a/src/cpp-utils/random/RandomGenerator.h b/src/cpp-utils/random/RandomGenerator.h new file mode 100644 index 00000000..b0d0ba97 --- /dev/null +++ b/src/cpp-utils/random/RandomGenerator.h @@ -0,0 +1,39 @@ +#ifndef MESSMER_CPPUTILS_RANDOM_RANDOMGENERATOR_H +#define MESSMER_CPPUTILS_RANDOM_RANDOMGENERATOR_H + +#include "../data/FixedSizeData.h" +#include "../data/Data.h" + +namespace cpputils { + class RandomGenerator { + public: + RandomGenerator(); + + template FixedSizeData getFixedSize(); + Data get(size_t size); + + protected: + virtual void _get(void *target, size_t bytes) = 0; + private: + static std::mutex _mutex; + + DISALLOW_COPY_AND_ASSIGN(RandomGenerator); + }; + + inline RandomGenerator::RandomGenerator() { + } + + template inline FixedSizeData RandomGenerator::getFixedSize() { + FixedSizeData result = FixedSizeData::Null(); + _get(result.data(), SIZE); + return result; + } + + inline Data RandomGenerator::get(size_t size) { + Data result(size); + _get(result.data(), size); + return result; + } +} + +#endif diff --git a/src/cpp-utils/random/RandomGeneratorThread.cpp b/src/cpp-utils/random/RandomGeneratorThread.cpp new file mode 100644 index 00000000..45fc74e4 --- /dev/null +++ b/src/cpp-utils/random/RandomGeneratorThread.cpp @@ -0,0 +1,33 @@ +#include "RandomGeneratorThread.h" + +namespace cpputils { + + RandomGeneratorThread::RandomGeneratorThread(ThreadsafeRandomDataBuffer *buffer, size_t minSize, size_t maxSize) + : _randomGenerator(), + _buffer(buffer), + _minSize(minSize), + _maxSize(maxSize), + _thread(std::bind(&RandomGeneratorThread::_loopIteration, this)) { + ASSERT(_maxSize >= _minSize, "Invalid parameters"); + } + + void RandomGeneratorThread::start() { + return _thread.start(); + } + + bool RandomGeneratorThread::_loopIteration() { + _buffer->waitUntilSizeIsLessThan(_minSize); + size_t neededRandomDataSize = _maxSize - _buffer->size(); + ASSERT(_maxSize > _buffer->size(), "This could theoretically fail if another thread refilled the buffer. But we should be the only refilling thread."); + Data randomData = _generateRandomData(neededRandomDataSize); + _buffer->add(std::move(randomData)); + return true; // Run another iteration (don't terminate thread) + } + + Data RandomGeneratorThread::_generateRandomData(size_t size) { + Data newRandom(size); + _randomGenerator.GenerateBlock(static_cast(newRandom.data()), size); + return newRandom; + } + +} diff --git a/src/cpp-utils/random/RandomGeneratorThread.h b/src/cpp-utils/random/RandomGeneratorThread.h new file mode 100644 index 00000000..103c00d7 --- /dev/null +++ b/src/cpp-utils/random/RandomGeneratorThread.h @@ -0,0 +1,34 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_RANDOM_RANDOMGENERATORTHREAD_H +#define MESSMER_CPPUTILS_RANDOM_RANDOMGENERATORTHREAD_H + +#include "../thread/LoopThread.h" +#include "ThreadsafeRandomDataBuffer.h" +#include + +namespace cpputils { + //TODO Test + class RandomGeneratorThread final { + public: + RandomGeneratorThread(ThreadsafeRandomDataBuffer *buffer, size_t minSize, size_t maxSize); + + void start(); + + private: + bool _loopIteration(); + Data _generateRandomData(size_t size); + + CryptoPP::AutoSeededRandomPool _randomGenerator; + ThreadsafeRandomDataBuffer *_buffer; + size_t _minSize; + size_t _maxSize; + + //This has to be the last member, because it has to be destructed first - otherwise the thread could still be + //running while the RandomGeneratorThread object is invalid. + LoopThread _thread; + + DISALLOW_COPY_AND_ASSIGN(RandomGeneratorThread); + }; +} + +#endif diff --git a/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h b/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h new file mode 100644 index 00000000..a1047aa7 --- /dev/null +++ b/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h @@ -0,0 +1,80 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_RANDOM_THREADSAFERANDOMDATABUFFER_H +#define MESSMER_CPPUTILS_RANDOM_THREADSAFERANDOMDATABUFFER_H + +#include "../data/Data.h" +#include "../assert/assert.h" +#include "RandomDataBuffer.h" +#include + +namespace cpputils { + //TODO Test + class ThreadsafeRandomDataBuffer final { + public: + ThreadsafeRandomDataBuffer(); + + size_t size() const; + + void get(void *target, size_t numBytes); + + void add(Data data); + + void waitUntilSizeIsLessThan(size_t numBytes); + + private: + size_t _get(void *target, size_t bytes); + + RandomDataBuffer _buffer; + mutable boost::mutex _mutex; + boost::condition_variable _dataAddedCv; + // _dataGottenCv needs to be boost::condition_variable and not std::condition_variable, because the + // RandomGeneratorThread calling ThreadsafeRandomDataBuffer::waitUntilSizeIsLessThan() needs the waiting to be + // interruptible to stop the thread in RandomGeneratorThread::stop() or in the RandomGeneratorThread destructor. + boost::condition_variable _dataGottenCv; + + DISALLOW_COPY_AND_ASSIGN(ThreadsafeRandomDataBuffer); + }; + + inline ThreadsafeRandomDataBuffer::ThreadsafeRandomDataBuffer(): _buffer(), _mutex(), _dataAddedCv(), _dataGottenCv() { + } + + inline size_t ThreadsafeRandomDataBuffer::size() const { + boost::unique_lock lock(_mutex); + return _buffer.size(); + } + + inline void ThreadsafeRandomDataBuffer::get(void *target, size_t numBytes) { + size_t alreadyGotten = 0; + while (alreadyGotten < numBytes) { + size_t got = _get(static_cast(target)+alreadyGotten, numBytes); + alreadyGotten += got; + ASSERT(alreadyGotten <= numBytes, "Got too many bytes"); + } + } + + inline size_t ThreadsafeRandomDataBuffer::_get(void *target, size_t numBytes) { + boost::unique_lock lock(_mutex); + _dataAddedCv.wait(lock, [this, numBytes] { + return _buffer.size() > 0; + }); + size_t gettableBytes = std::min(_buffer.size(), numBytes); + _buffer.get(target, gettableBytes); + _dataGottenCv.notify_all(); + return gettableBytes; + } + + inline void ThreadsafeRandomDataBuffer::add(Data data) { + boost::unique_lock lock(_mutex); + _buffer.add(std::move(data)); + _dataAddedCv.notify_all(); + } + + inline void ThreadsafeRandomDataBuffer::waitUntilSizeIsLessThan(size_t numBytes) { + boost::unique_lock lock(_mutex); + _dataGottenCv.wait(lock, [this, numBytes] { + return _buffer.size() < numBytes; + }); + } +} + +#endif diff --git a/src/cpp-utils/tempfile/TempDir.cpp b/src/cpp-utils/tempfile/TempDir.cpp new file mode 100644 index 00000000..7d4d0838 --- /dev/null +++ b/src/cpp-utils/tempfile/TempDir.cpp @@ -0,0 +1,32 @@ +#include "TempDir.h" +#include "../logging/logging.h" + +namespace bf = boost::filesystem; +using namespace cpputils::logging; + +namespace cpputils { + +TempDir::TempDir() + : _path(bf::unique_path(bf::temp_directory_path() / "%%%%-%%%%-%%%%-%%%%")) { + bf::create_directory(_path); +} + +TempDir::~TempDir() { + remove(); +} + +void TempDir::remove() { + try { + if (bf::exists(_path)) { + bf::remove_all(_path); + } + } catch (const boost::filesystem::filesystem_error &e) { + LOG(ERROR) << "Could not delete tempfile."; + } +} + +const bf::path &TempDir::path() const { + return _path; +} + +} diff --git a/src/cpp-utils/tempfile/TempDir.h b/src/cpp-utils/tempfile/TempDir.h new file mode 100644 index 00000000..361788b1 --- /dev/null +++ b/src/cpp-utils/tempfile/TempDir.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_TEMPFILE_TEMPDIR_H_ +#define MESSMER_CPPUTILS_TEMPFILE_TEMPDIR_H_ + +#include +#include "../macros.h" + +namespace cpputils { + +class TempDir final { +public: + TempDir(); + ~TempDir(); + const boost::filesystem::path &path() const; + void remove(); + +private: + const boost::filesystem::path _path; + + DISALLOW_COPY_AND_ASSIGN(TempDir); +}; + +} + +#endif diff --git a/src/cpp-utils/tempfile/TempFile.cpp b/src/cpp-utils/tempfile/TempFile.cpp new file mode 100644 index 00000000..ea2e3cf4 --- /dev/null +++ b/src/cpp-utils/tempfile/TempFile.cpp @@ -0,0 +1,43 @@ +#include "TempFile.h" +#include "../logging/logging.h" +#include + +namespace bf = boost::filesystem; +using std::ofstream; +using namespace cpputils::logging; + +namespace cpputils { + +TempFile::TempFile(const bf::path &path, bool create) + : _path(path) { + if (create) { + ofstream file(_path.c_str()); + if (!file.good()) { + throw std::runtime_error("Could not create tempfile"); + } + } +} + +TempFile::TempFile(bool create) + : TempFile(bf::unique_path(bf::temp_directory_path() / "%%%%-%%%%-%%%%-%%%%"), create) { +} + +TempFile::~TempFile() { + try { + if (exists()) { + bf::remove(_path); + } + } catch (const boost::filesystem::filesystem_error &e) { + LOG(ERROR) << "Could not delete tempfile."; + } +} + +bool TempFile::exists() const { + return bf::exists(_path); +} + +const bf::path &TempFile::path() const { + return _path; +} + +} diff --git a/src/cpp-utils/tempfile/TempFile.h b/src/cpp-utils/tempfile/TempFile.h new file mode 100644 index 00000000..bf7641a1 --- /dev/null +++ b/src/cpp-utils/tempfile/TempFile.h @@ -0,0 +1,27 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_TEMPFILE_TEMPFILE_H_ +#define MESSMER_CPPUTILS_TEMPFILE_TEMPFILE_H_ + +#include +#include "../macros.h" + +namespace cpputils { + +class TempFile final { +public: + explicit TempFile(const boost::filesystem::path &path, bool create = true); + explicit TempFile(bool create = true); + ~TempFile(); + const boost::filesystem::path &path() const; + //TODO Test exists() + bool exists() const; + +private: + const boost::filesystem::path _path; + + DISALLOW_COPY_AND_ASSIGN(TempFile); +}; + +} + +#endif diff --git a/src/cpp-utils/thread/LoopThread.cpp b/src/cpp-utils/thread/LoopThread.cpp new file mode 100644 index 00000000..6573d902 --- /dev/null +++ b/src/cpp-utils/thread/LoopThread.cpp @@ -0,0 +1,29 @@ +#include "LoopThread.h" +#include "../logging/logging.h" + +using std::function; +using boost::none; + +namespace cpputils { + + LoopThread::LoopThread(function loopIteration): _loopIteration(loopIteration), _runningHandle(none) { + } + + LoopThread::~LoopThread() { + if (_runningHandle != none) { + stop(); + } + } + + void LoopThread::start() { + _runningHandle = ThreadSystem::singleton().start(_loopIteration); + } + + void LoopThread::stop() { + if (_runningHandle == none) { + throw std::runtime_error("LoopThread is not running"); + } + ThreadSystem::singleton().stop(*_runningHandle); + _runningHandle = none; + } +} diff --git a/src/cpp-utils/thread/LoopThread.h b/src/cpp-utils/thread/LoopThread.h new file mode 100644 index 00000000..8f7bb5c5 --- /dev/null +++ b/src/cpp-utils/thread/LoopThread.h @@ -0,0 +1,28 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_THREAD_LOOPTHREAD_H +#define MESSMER_CPPUTILS_THREAD_LOOPTHREAD_H + +#include "ThreadSystem.h" +#include + +namespace cpputils { + //TODO Test + //TODO Test that fork() doesn't destroy anything (e.g. no deadlock on stop() because thread is not running anymore) + + // Has to be final, because otherwise there could be a race condition where LoopThreadForkHandler calls a LoopThread + // where the child class destructor already ran. + class LoopThread final { + public: + // The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated. + LoopThread(std::function loopIteration); + ~LoopThread(); + void start(); + void stop(); + + private: + std::function _loopIteration; + boost::optional _runningHandle; + }; +} + +#endif diff --git a/src/cpp-utils/thread/ThreadSystem.cpp b/src/cpp-utils/thread/ThreadSystem.cpp new file mode 100644 index 00000000..02b75b97 --- /dev/null +++ b/src/cpp-utils/thread/ThreadSystem.cpp @@ -0,0 +1,84 @@ +#include "ThreadSystem.h" +#include "../logging/logging.h" + +using std::function; +using namespace cpputils::logging; + +namespace cpputils { + + ThreadSystem &ThreadSystem::singleton() { + static ThreadSystem system; + return system; + } + + ThreadSystem::ThreadSystem(): _runningThreads(), _mutex() { + //Stopping the thread before fork() (and then also restarting it in the parent thread after fork()) is important, + //because as a running thread it might hold locks or condition variables that won't play well when forked. + pthread_atfork(&ThreadSystem::_onBeforeFork, &ThreadSystem::_onAfterFork, &ThreadSystem::_onAfterFork); + } + + ThreadSystem::Handle ThreadSystem::start(function loopIteration) { + boost::unique_lock lock(_mutex); + auto thread = _startThread(loopIteration); + _runningThreads.push_back(RunningThread{loopIteration, std::move(thread)}); + return std::prev(_runningThreads.end()); + } + + void ThreadSystem::stop(Handle handle) { + boost::unique_lock lock(_mutex); + boost::thread thread = std::move(handle->thread); + thread.interrupt(); + _runningThreads.erase(handle); + + //It's fine if another thread gets the mutex while we still wait for the join. Joining doesn't change any internal state. + lock.unlock(); + thread.join(); + } + + void ThreadSystem::_onBeforeFork() { + singleton()._stopAllThreadsForRestart(); + } + + void ThreadSystem::_onAfterFork() { + singleton()._restartAllThreads(); + } + + void ThreadSystem::_stopAllThreadsForRestart() { + _mutex.lock(); // Is unlocked in the after-fork handler. This way, the whole fork() is protected. + for (RunningThread &thread : _runningThreads) { + thread.thread.interrupt(); + } + for (RunningThread &thread : _runningThreads) { + thread.thread.join(); + } + } + + void ThreadSystem::_restartAllThreads() { + for (RunningThread &thread : _runningThreads) { + thread.thread = _startThread(thread.loopIteration); + } + _mutex.unlock(); // Was locked in the before-fork handler + } + + boost::thread ThreadSystem::_startThread(function loopIteration) { + return boost::thread(std::bind(&ThreadSystem::_runThread, loopIteration)); + } + + void ThreadSystem::_runThread(function loopIteration) { + try { + bool cont = true; + while(cont) { + boost::this_thread::interruption_point(); + cont = loopIteration(); // This might also be interrupted. + } + //The thread is terminated gracefully. + } catch (const boost::thread_interrupted &e) { + //Do nothing, exit thread. + } catch (const std::exception &e) { + LOG(ERROR) << "LoopThread crashed: " << e.what(); + } catch (...) { + LOG(ERROR) << "LoopThread crashed"; + } + //TODO We should remove the thread from _runningThreads here, not in stop(). + } +} diff --git a/src/cpp-utils/thread/ThreadSystem.h b/src/cpp-utils/thread/ThreadSystem.h new file mode 100644 index 00000000..b9a7b419 --- /dev/null +++ b/src/cpp-utils/thread/ThreadSystem.h @@ -0,0 +1,46 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_THREAD_THREADSYSTEM_H +#define MESSMER_CPPUTILS_THREAD_THREADSYSTEM_H + +#include "../macros.h" +#include +#include +#include + +namespace cpputils { + //TODO Test + + class ThreadSystem final { + private: + struct RunningThread { + std::function loopIteration; // The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated. + boost::thread thread; // boost::thread because we need it to be interruptible. + }; + public: + using Handle = std::list::iterator; + + static ThreadSystem &singleton(); + + Handle start(std::function loopIteration); + void stop(Handle handle); + + private: + ThreadSystem(); + + static void _runThread(std::function loopIteration); + + static void _onBeforeFork(); + static void _onAfterFork(); + //TODO Rename to _doOnBeforeFork and _doAfterFork or similar, because they also handle locking _mutex for fork(). + void _stopAllThreadsForRestart(); + void _restartAllThreads(); + boost::thread _startThread(std::function loopIteration); + + std::list _runningThreads; // std::list, because we give out iterators as handles + boost::mutex _mutex; + + DISALLOW_COPY_AND_ASSIGN(ThreadSystem); + }; +} + +#endif diff --git a/src/cryfs/CMakeLists.txt b/src/cryfs/CMakeLists.txt new file mode 100644 index 00000000..3ba0ad95 --- /dev/null +++ b/src/cryfs/CMakeLists.txt @@ -0,0 +1,61 @@ +#TODO gitversion + +project (cryfs) + +set(SOURCES + cli/Cli.cpp + cli/VersionChecker.cpp + cli/CallAfterTimeout.cpp + cli/program_options/utils.cpp + cli/program_options/ProgramOptions.cpp + cli/program_options/Parser.cpp + config/crypto/outer/OuterConfig.cpp + config/crypto/outer/OuterEncryptor.cpp + config/crypto/CryConfigEncryptorFactory.cpp + config/crypto/inner/ConcreteInnerEncryptor.cpp + config/crypto/inner/InnerConfig.cpp + config/crypto/inner/InnerEncryptor.cpp + config/crypto/CryConfigEncryptor.cpp + config/CryConfigConsole.cpp + config/CryConfigLoader.cpp + config/CryConfig.cpp + config/CryConfigFile.cpp + config/CryCipher.cpp + config/CryConfigCreator.cpp + filesystem/CryOpenFile.cpp + filesystem/fsblobstore/utils/DirEntry.cpp + filesystem/fsblobstore/utils/DirEntryList.cpp + filesystem/fsblobstore/FsBlobStore.cpp + filesystem/fsblobstore/FileBlob.cpp + filesystem/fsblobstore/FsBlob.cpp + filesystem/fsblobstore/SymlinkBlob.cpp + filesystem/fsblobstore/DirBlob.cpp + filesystem/CryNode.cpp + filesystem/parallelaccessfsblobstore/DirBlobRef.cpp + filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.cpp + filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.cpp + filesystem/parallelaccessfsblobstore/FsBlobRef.cpp + filesystem/parallelaccessfsblobstore/FileBlobRef.cpp + filesystem/parallelaccessfsblobstore/SymlinkBlobRef.cpp + filesystem/CrySymlink.cpp + filesystem/CryDir.cpp + filesystem/cachingfsblobstore/DirBlobRef.cpp + filesystem/cachingfsblobstore/CachingFsBlobStore.cpp + filesystem/cachingfsblobstore/FsBlobRef.cpp + filesystem/cachingfsblobstore/FileBlobRef.cpp + filesystem/cachingfsblobstore/SymlinkBlobRef.cpp + filesystem/CryFile.cpp + filesystem/CryDevice.cpp +) + +add_library(${PROJECT_NAME}_lib STATIC ${SOURCES}) +target_link_libraries(${PROJECT_NAME}_lib PUBLIC cpp-utils blockstore blobstore fspp) +target_add_boost(${PROJECT_NAME}_lib program_options chrono) +target_enable_style_warnings(${PROJECT_NAME}_lib) +target_activate_cpp14(${PROJECT_NAME}_lib) +target_git_version_init(${PROJECT_NAME}_lib) + +add_executable(${PROJECT_NAME} main.cpp) +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_lib) +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/src/cli/CallAfterTimeout.cpp b/src/cryfs/cli/CallAfterTimeout.cpp similarity index 100% rename from src/cli/CallAfterTimeout.cpp rename to src/cryfs/cli/CallAfterTimeout.cpp diff --git a/src/cli/CallAfterTimeout.h b/src/cryfs/cli/CallAfterTimeout.h similarity index 97% rename from src/cli/CallAfterTimeout.h rename to src/cryfs/cli/CallAfterTimeout.h index 97c6e93d..09db6195 100644 --- a/src/cli/CallAfterTimeout.h +++ b/src/cryfs/cli/CallAfterTimeout.h @@ -4,7 +4,7 @@ #include #include -#include +#include namespace cryfs { class CallAfterTimeout final { diff --git a/src/cli/Cli.cpp b/src/cryfs/cli/Cli.cpp similarity index 96% rename from src/cli/Cli.cpp rename to src/cryfs/cli/Cli.cpp index 099e5eed..77a29e7b 100644 --- a/src/cli/Cli.cpp +++ b/src/cryfs/cli/Cli.cpp @@ -1,16 +1,16 @@ #include "Cli.h" -#include -#include -#include +#include +#include +#include #include #include #include -#include +#include -#include "messmer/fspp/fuse/Fuse.h" -#include "messmer/fspp/impl/FilesystemImpl.h" -#include +#include +#include +#include #include "../filesystem/CryDevice.h" #include "../config/CryConfigLoader.h" #include "program_options/Parser.h" diff --git a/src/cli/Cli.h b/src/cryfs/cli/Cli.h similarity index 92% rename from src/cli/Cli.h rename to src/cryfs/cli/Cli.h index 86ee8811..b5ffa216 100644 --- a/src/cli/Cli.h +++ b/src/cryfs/cli/Cli.h @@ -5,10 +5,10 @@ #include "program_options/ProgramOptions.h" #include "../config/CryConfigFile.h" #include -#include -#include -#include -#include +#include +#include +#include +#include #include "CallAfterTimeout.h" namespace cryfs { diff --git a/src/cli/VersionChecker.cpp b/src/cryfs/cli/VersionChecker.cpp similarity index 95% rename from src/cli/VersionChecker.cpp rename to src/cryfs/cli/VersionChecker.cpp index a58cc84a..6e0f81a3 100644 --- a/src/cli/VersionChecker.cpp +++ b/src/cryfs/cli/VersionChecker.cpp @@ -1,8 +1,8 @@ #include "VersionChecker.h" #include -#include +#include #include -#include +#include #include using boost::optional; diff --git a/src/cli/VersionChecker.h b/src/cryfs/cli/VersionChecker.h similarity index 91% rename from src/cli/VersionChecker.h rename to src/cryfs/cli/VersionChecker.h index f94e6778..e3ea2d7f 100644 --- a/src/cli/VersionChecker.h +++ b/src/cryfs/cli/VersionChecker.h @@ -1,11 +1,11 @@ #ifndef MESSMER_CRYFS_SRC_CLI_VERSIONCHECKER_H #define MESSMER_CRYFS_SRC_CLI_VERSIONCHECKER_H -#include +#include #include #include #include -#include +#include namespace cryfs { class VersionChecker final { diff --git a/src/cli/program_options/Parser.cpp b/src/cryfs/cli/program_options/Parser.cpp similarity index 100% rename from src/cli/program_options/Parser.cpp rename to src/cryfs/cli/program_options/Parser.cpp diff --git a/src/cli/program_options/Parser.h b/src/cryfs/cli/program_options/Parser.h similarity index 100% rename from src/cli/program_options/Parser.h rename to src/cryfs/cli/program_options/Parser.h diff --git a/src/cli/program_options/ProgramOptions.cpp b/src/cryfs/cli/program_options/ProgramOptions.cpp similarity index 98% rename from src/cli/program_options/ProgramOptions.cpp rename to src/cryfs/cli/program_options/ProgramOptions.cpp index ec145aae..8ec8aa94 100644 --- a/src/cli/program_options/ProgramOptions.cpp +++ b/src/cryfs/cli/program_options/ProgramOptions.cpp @@ -1,6 +1,6 @@ #include "ProgramOptions.h" #include -#include +#include using namespace cryfs::program_options; using std::string; diff --git a/src/cli/program_options/ProgramOptions.h b/src/cryfs/cli/program_options/ProgramOptions.h similarity index 98% rename from src/cli/program_options/ProgramOptions.h rename to src/cryfs/cli/program_options/ProgramOptions.h index 32a86fe7..bf4170fa 100644 --- a/src/cli/program_options/ProgramOptions.h +++ b/src/cryfs/cli/program_options/ProgramOptions.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include namespace cryfs { diff --git a/src/cli/program_options/utils.cpp b/src/cryfs/cli/program_options/utils.cpp similarity index 100% rename from src/cli/program_options/utils.cpp rename to src/cryfs/cli/program_options/utils.cpp diff --git a/src/cli/program_options/utils.h b/src/cryfs/cli/program_options/utils.h similarity index 100% rename from src/cli/program_options/utils.h rename to src/cryfs/cli/program_options/utils.h diff --git a/src/config/CryCipher.cpp b/src/cryfs/config/CryCipher.cpp similarity index 96% rename from src/config/CryCipher.cpp rename to src/cryfs/config/CryCipher.cpp index 70b81d52..573ea8dc 100644 --- a/src/config/CryCipher.cpp +++ b/src/cryfs/config/CryCipher.cpp @@ -1,7 +1,7 @@ #include "CryCipher.h" -#include -#include +#include +#include #include "crypto/inner/ConcreteInnerEncryptor.h" using std::vector; diff --git a/src/config/CryCipher.h b/src/cryfs/config/CryCipher.h similarity index 89% rename from src/config/CryCipher.h rename to src/cryfs/config/CryCipher.h index c5958f2f..36ce5873 100644 --- a/src/config/CryCipher.h +++ b/src/cryfs/config/CryCipher.h @@ -4,9 +4,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include "crypto/inner/InnerEncryptor.h" namespace cryfs { diff --git a/src/config/CryConfig.cpp b/src/cryfs/config/CryConfig.cpp similarity index 100% rename from src/config/CryConfig.cpp rename to src/cryfs/config/CryConfig.cpp diff --git a/src/config/CryConfig.h b/src/cryfs/config/CryConfig.h similarity index 95% rename from src/config/CryConfig.h rename to src/cryfs/config/CryConfig.h index 89cf31c1..3c9767fd 100644 --- a/src/config/CryConfig.h +++ b/src/cryfs/config/CryConfig.h @@ -4,7 +4,7 @@ #include -#include +#include #include namespace cryfs { diff --git a/src/config/CryConfigConsole.cpp b/src/cryfs/config/CryConfigConsole.cpp similarity index 100% rename from src/config/CryConfigConsole.cpp rename to src/cryfs/config/CryConfigConsole.cpp diff --git a/src/config/CryConfigConsole.h b/src/cryfs/config/CryConfigConsole.h similarity index 89% rename from src/config/CryConfigConsole.h rename to src/cryfs/config/CryConfigConsole.h index 53e01266..686516ff 100644 --- a/src/config/CryConfigConsole.h +++ b/src/cryfs/config/CryConfigConsole.h @@ -2,8 +2,8 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYCONFIGCONSOLE_H #define MESSMER_CRYFS_SRC_CONFIG_CRYCONFIGCONSOLE_H -#include -#include +#include +#include #include namespace cryfs { diff --git a/src/config/CryConfigCreator.cpp b/src/cryfs/config/CryConfigCreator.cpp similarity index 100% rename from src/config/CryConfigCreator.cpp rename to src/cryfs/config/CryConfigCreator.cpp diff --git a/src/config/CryConfigCreator.h b/src/cryfs/config/CryConfigCreator.h similarity index 86% rename from src/config/CryConfigCreator.h rename to src/cryfs/config/CryConfigCreator.h index 605c0f2d..3b714d5e 100644 --- a/src/config/CryConfigCreator.h +++ b/src/cryfs/config/CryConfigCreator.h @@ -2,9 +2,9 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYCONFIGCREATOR_H #define MESSMER_CRYFS_SRC_CONFIG_CRYCONFIGCREATOR_H -#include -#include -#include +#include +#include +#include #include "CryConfig.h" #include "CryConfigConsole.h" diff --git a/src/config/CryConfigFile.cpp b/src/cryfs/config/CryConfigFile.cpp similarity index 98% rename from src/config/CryConfigFile.cpp rename to src/cryfs/config/CryConfigFile.cpp index d6abedf3..553f6449 100644 --- a/src/config/CryConfigFile.cpp +++ b/src/cryfs/config/CryConfigFile.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include using boost::optional; using boost::none; diff --git a/src/config/CryConfigFile.h b/src/cryfs/config/CryConfigFile.h similarity index 95% rename from src/config/CryConfigFile.h rename to src/cryfs/config/CryConfigFile.h index 49978797..4d256994 100644 --- a/src/config/CryConfigFile.h +++ b/src/cryfs/config/CryConfigFile.h @@ -5,7 +5,7 @@ #include #include #include "CryConfig.h" -#include +#include #include "crypto/CryConfigEncryptorFactory.h" namespace cryfs { diff --git a/src/config/CryConfigLoader.cpp b/src/cryfs/config/CryConfigLoader.cpp similarity index 96% rename from src/config/CryConfigLoader.cpp rename to src/cryfs/config/CryConfigLoader.cpp index 9797d4e9..28498e77 100644 --- a/src/config/CryConfigLoader.cpp +++ b/src/cryfs/config/CryConfigLoader.cpp @@ -1,8 +1,8 @@ #include "CryConfigLoader.h" #include "CryConfigFile.h" #include -#include -#include +#include +#include namespace bf = boost::filesystem; using cpputils::unique_ref; diff --git a/src/config/CryConfigLoader.h b/src/cryfs/config/CryConfigLoader.h similarity index 92% rename from src/config/CryConfigLoader.h rename to src/cryfs/config/CryConfigLoader.h index 417d9cb6..fe02fc65 100644 --- a/src/config/CryConfigLoader.h +++ b/src/cryfs/config/CryConfigLoader.h @@ -2,12 +2,12 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYCONFIGLOADER_H_ #define MESSMER_CRYFS_SRC_CONFIG_CRYCONFIGLOADER_H_ -#include +#include #include #include "CryConfigFile.h" #include "CryCipher.h" #include "CryConfigCreator.h" -#include +#include namespace cryfs { diff --git a/src/config/crypto/CryConfigEncryptor.cpp b/src/cryfs/config/crypto/CryConfigEncryptor.cpp similarity index 97% rename from src/config/crypto/CryConfigEncryptor.cpp rename to src/cryfs/config/crypto/CryConfigEncryptor.cpp index 6f6c494c..a27b0f03 100644 --- a/src/config/crypto/CryConfigEncryptor.cpp +++ b/src/cryfs/config/crypto/CryConfigEncryptor.cpp @@ -1,5 +1,5 @@ #include "CryConfigEncryptor.h" -#include +#include using std::string; using cpputils::unique_ref; diff --git a/src/config/crypto/CryConfigEncryptor.h b/src/cryfs/config/crypto/CryConfigEncryptor.h similarity index 81% rename from src/config/crypto/CryConfigEncryptor.h rename to src/cryfs/config/crypto/CryConfigEncryptor.h index af1a0976..cbcfb06f 100644 --- a/src/config/crypto/CryConfigEncryptor.h +++ b/src/cryfs/config/crypto/CryConfigEncryptor.h @@ -2,12 +2,12 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_CRYCONFIGENCRYPTOR_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_CRYCONFIGENCRYPTOR_H -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "inner/InnerEncryptor.h" #include "outer/OuterEncryptor.h" #include "../CryCipher.h" diff --git a/src/config/crypto/CryConfigEncryptorFactory.cpp b/src/cryfs/config/crypto/CryConfigEncryptorFactory.cpp similarity index 97% rename from src/config/crypto/CryConfigEncryptorFactory.cpp rename to src/cryfs/config/crypto/CryConfigEncryptorFactory.cpp index 4df79519..3b33f42f 100644 --- a/src/config/crypto/CryConfigEncryptorFactory.cpp +++ b/src/cryfs/config/crypto/CryConfigEncryptorFactory.cpp @@ -1,5 +1,5 @@ #include "CryConfigEncryptorFactory.h" -#include +#include #include "outer/OuterConfig.h" using namespace cpputils::logging; diff --git a/src/config/crypto/CryConfigEncryptorFactory.h b/src/cryfs/config/crypto/CryConfigEncryptorFactory.h similarity index 91% rename from src/config/crypto/CryConfigEncryptorFactory.h rename to src/cryfs/config/crypto/CryConfigEncryptorFactory.h index f71ccc26..b76c13ce 100644 --- a/src/config/crypto/CryConfigEncryptorFactory.h +++ b/src/cryfs/config/crypto/CryConfigEncryptorFactory.h @@ -4,8 +4,8 @@ #include "inner/ConcreteInnerEncryptor.h" #include "CryConfigEncryptor.h" -#include -#include +#include +#include #include "../CryCipher.h" namespace cryfs { diff --git a/src/config/crypto/inner/ConcreteInnerEncryptor.cpp b/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.cpp similarity index 100% rename from src/config/crypto/inner/ConcreteInnerEncryptor.cpp rename to src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.cpp diff --git a/src/config/crypto/inner/ConcreteInnerEncryptor.h b/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h similarity index 95% rename from src/config/crypto/inner/ConcreteInnerEncryptor.h rename to src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h index 58ee3faa..ea6d03fa 100644 --- a/src/config/crypto/inner/ConcreteInnerEncryptor.h +++ b/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h @@ -2,8 +2,8 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_CONCRETECRYCONFIGENCRYPTOR_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_CONCRETECRYCONFIGENCRYPTOR_H -#include -#include +#include +#include #include "InnerEncryptor.h" #include "InnerConfig.h" diff --git a/src/config/crypto/inner/InnerConfig.cpp b/src/cryfs/config/crypto/inner/InnerConfig.cpp similarity index 97% rename from src/config/crypto/inner/InnerConfig.cpp rename to src/cryfs/config/crypto/inner/InnerConfig.cpp index 38cb4ee3..1bea763a 100644 --- a/src/config/crypto/inner/InnerConfig.cpp +++ b/src/cryfs/config/crypto/inner/InnerConfig.cpp @@ -1,5 +1,5 @@ #include "InnerConfig.h" -#include +#include using std::string; using std::exception; diff --git a/src/config/crypto/inner/InnerConfig.h b/src/cryfs/config/crypto/inner/InnerConfig.h similarity index 81% rename from src/config/crypto/inner/InnerConfig.h rename to src/cryfs/config/crypto/inner/InnerConfig.h index 3afdb7c0..2bba75a3 100644 --- a/src/config/crypto/inner/InnerConfig.h +++ b/src/cryfs/config/crypto/inner/InnerConfig.h @@ -2,9 +2,9 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_INNERCONFIG_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_INNERCONFIG_H -#include -#include -#include +#include +#include +#include namespace cryfs { struct InnerConfig final { diff --git a/src/config/crypto/inner/InnerEncryptor.cpp b/src/cryfs/config/crypto/inner/InnerEncryptor.cpp similarity index 100% rename from src/config/crypto/inner/InnerEncryptor.cpp rename to src/cryfs/config/crypto/inner/InnerEncryptor.cpp diff --git a/src/config/crypto/inner/InnerEncryptor.h b/src/cryfs/config/crypto/inner/InnerEncryptor.h similarity index 70% rename from src/config/crypto/inner/InnerEncryptor.h rename to src/cryfs/config/crypto/inner/InnerEncryptor.h index 07edcd61..800dd994 100644 --- a/src/config/crypto/inner/InnerEncryptor.h +++ b/src/cryfs/config/crypto/inner/InnerEncryptor.h @@ -2,11 +2,11 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_INNERENCRYPTOR_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_INNERENCRYPTOR_H -#include -#include +#include +#include #include -#include -#include +#include +#include #include "InnerConfig.h" namespace cryfs { diff --git a/src/config/crypto/outer/OuterConfig.cpp b/src/cryfs/config/crypto/outer/OuterConfig.cpp similarity index 100% rename from src/config/crypto/outer/OuterConfig.cpp rename to src/cryfs/config/crypto/outer/OuterConfig.cpp diff --git a/src/config/crypto/outer/OuterConfig.h b/src/cryfs/config/crypto/outer/OuterConfig.h similarity index 75% rename from src/config/crypto/outer/OuterConfig.h rename to src/cryfs/config/crypto/outer/OuterConfig.h index aded5cbc..2d1b85e0 100644 --- a/src/config/crypto/outer/OuterConfig.h +++ b/src/cryfs/config/crypto/outer/OuterConfig.h @@ -2,10 +2,10 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H -#include -#include -#include -#include +#include +#include +#include +#include namespace cryfs { struct OuterConfig final { diff --git a/src/config/crypto/outer/OuterEncryptor.cpp b/src/cryfs/config/crypto/outer/OuterEncryptor.cpp similarity index 96% rename from src/config/crypto/outer/OuterEncryptor.cpp rename to src/cryfs/config/crypto/outer/OuterEncryptor.cpp index 33dbd315..e37e8220 100644 --- a/src/config/crypto/outer/OuterEncryptor.cpp +++ b/src/cryfs/config/crypto/outer/OuterEncryptor.cpp @@ -1,5 +1,5 @@ #include "OuterEncryptor.h" -#include +#include #include "OuterConfig.h" using std::string; diff --git a/src/config/crypto/outer/OuterEncryptor.h b/src/cryfs/config/crypto/outer/OuterEncryptor.h similarity index 80% rename from src/config/crypto/outer/OuterEncryptor.h rename to src/cryfs/config/crypto/outer/OuterEncryptor.h index 0a8a62a7..a7477488 100644 --- a/src/config/crypto/outer/OuterEncryptor.h +++ b/src/cryfs/config/crypto/outer/OuterEncryptor.h @@ -2,10 +2,10 @@ #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERENCRYPTOR_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERENCRYPTOR_H -#include -#include -#include -#include +#include +#include +#include +#include #include "OuterConfig.h" namespace cryfs { diff --git a/src/filesystem/CryDevice.cpp b/src/cryfs/filesystem/CryDevice.cpp similarity index 94% rename from src/filesystem/CryDevice.cpp rename to src/cryfs/filesystem/CryDevice.cpp index 94f194d0..b5d5b653 100644 --- a/src/filesystem/CryDevice.cpp +++ b/src/cryfs/filesystem/CryDevice.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include "parallelaccessfsblobstore/DirBlobRef.h" #include "CryDevice.h" @@ -7,10 +7,10 @@ #include "CryFile.h" #include "CrySymlink.h" -#include "messmer/fspp/fuse/FuseErrnoException.h" -#include "messmer/blobstore/implementations/onblocks/BlobStoreOnBlocks.h" -#include "messmer/blobstore/implementations/onblocks/BlobOnBlocks.h" -#include "messmer/blockstore/implementations/encrypted/EncryptedBlockStore.h" +#include +#include +#include +#include #include "parallelaccessfsblobstore/ParallelAccessFsBlobStore.h" #include "cachingfsblobstore/CachingFsBlobStore.h" #include "../config/CryCipher.h" diff --git a/src/filesystem/CryDevice.h b/src/cryfs/filesystem/CryDevice.h similarity index 95% rename from src/filesystem/CryDevice.h rename to src/cryfs/filesystem/CryDevice.h index 4730f36a..72123e8f 100644 --- a/src/filesystem/CryDevice.h +++ b/src/cryfs/filesystem/CryDevice.h @@ -2,11 +2,11 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_CRYDEVICE_H_ #define MESSMER_CRYFS_FILESYSTEM_CRYDEVICE_H_ -#include +#include #include "../config/CryConfigFile.h" #include -#include +#include #include "parallelaccessfsblobstore/ParallelAccessFsBlobStore.h" #include "parallelaccessfsblobstore/DirBlobRef.h" diff --git a/src/filesystem/CryDir.cpp b/src/cryfs/filesystem/CryDir.cpp similarity index 97% rename from src/filesystem/CryDir.cpp rename to src/cryfs/filesystem/CryDir.cpp index 0308d8c5..87fa08ba 100644 --- a/src/filesystem/CryDir.cpp +++ b/src/cryfs/filesystem/CryDir.cpp @@ -5,7 +5,7 @@ #include #include -#include "messmer/fspp/fuse/FuseErrnoException.h" +#include #include "CryDevice.h" #include "CryFile.h" #include "CryOpenFile.h" diff --git a/src/filesystem/CryDir.h b/src/cryfs/filesystem/CryDir.h similarity index 96% rename from src/filesystem/CryDir.h rename to src/cryfs/filesystem/CryDir.h index a314911b..807dd58a 100644 --- a/src/filesystem/CryDir.h +++ b/src/cryfs/filesystem/CryDir.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_CRYDIR_H_ #define MESSMER_CRYFS_FILESYSTEM_CRYDIR_H_ -#include +#include #include "CryNode.h" #include "parallelaccessfsblobstore/DirBlobRef.h" diff --git a/src/filesystem/CryFile.cpp b/src/cryfs/filesystem/CryFile.cpp similarity index 96% rename from src/filesystem/CryFile.cpp rename to src/cryfs/filesystem/CryFile.cpp index 48636dc1..2816feaf 100644 --- a/src/filesystem/CryFile.cpp +++ b/src/cryfs/filesystem/CryFile.cpp @@ -2,7 +2,7 @@ #include "CryDevice.h" #include "CryOpenFile.h" -#include "messmer/fspp/fuse/FuseErrnoException.h" +#include namespace bf = boost::filesystem; diff --git a/src/filesystem/CryFile.h b/src/cryfs/filesystem/CryFile.h similarity index 94% rename from src/filesystem/CryFile.h rename to src/cryfs/filesystem/CryFile.h index bc43dd08..a9aaf4e7 100644 --- a/src/filesystem/CryFile.h +++ b/src/cryfs/filesystem/CryFile.h @@ -4,7 +4,7 @@ #include "parallelaccessfsblobstore/FileBlobRef.h" #include "parallelaccessfsblobstore/DirBlobRef.h" -#include +#include #include "CryNode.h" namespace cryfs { diff --git a/src/filesystem/CryNode.cpp b/src/cryfs/filesystem/CryNode.cpp similarity index 97% rename from src/filesystem/CryNode.cpp rename to src/cryfs/filesystem/CryNode.cpp index 06caad7f..617dbe46 100644 --- a/src/filesystem/CryNode.cpp +++ b/src/cryfs/filesystem/CryNode.cpp @@ -5,8 +5,8 @@ #include "CryDevice.h" #include "CryDir.h" #include "CryFile.h" -#include "messmer/fspp/fuse/FuseErrnoException.h" -#include +#include +#include namespace bf = boost::filesystem; diff --git a/src/filesystem/CryNode.h b/src/cryfs/filesystem/CryNode.h similarity index 90% rename from src/filesystem/CryNode.h rename to src/cryfs/filesystem/CryNode.h index f77d7bf4..0fc439c6 100644 --- a/src/filesystem/CryNode.h +++ b/src/cryfs/filesystem/CryNode.h @@ -2,9 +2,9 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_CRYNODE_H_ #define MESSMER_CRYFS_FILESYSTEM_CRYNODE_H_ -#include -#include "messmer/cpp-utils/macros.h" -#include +#include +#include +#include #include "parallelaccessfsblobstore/DirBlobRef.h" #include "CryDevice.h" diff --git a/src/filesystem/CryOpenFile.cpp b/src/cryfs/filesystem/CryOpenFile.cpp similarity index 96% rename from src/filesystem/CryOpenFile.cpp rename to src/cryfs/filesystem/CryOpenFile.cpp index 98d467c8..70585874 100644 --- a/src/filesystem/CryOpenFile.cpp +++ b/src/cryfs/filesystem/CryOpenFile.cpp @@ -4,7 +4,7 @@ #include #include "CryDevice.h" -#include "messmer/fspp/fuse/FuseErrnoException.h" +#include namespace bf = boost::filesystem; diff --git a/src/filesystem/CryOpenFile.h b/src/cryfs/filesystem/CryOpenFile.h similarity index 94% rename from src/filesystem/CryOpenFile.h rename to src/cryfs/filesystem/CryOpenFile.h index 136f469a..7fdc906a 100644 --- a/src/filesystem/CryOpenFile.h +++ b/src/cryfs/filesystem/CryOpenFile.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_CRYOPENFILE_H_ #define MESSMER_CRYFS_FILESYSTEM_CRYOPENFILE_H_ -#include "messmer/fspp/fs_interface/OpenFile.h" +#include #include "parallelaccessfsblobstore/FileBlobRef.h" namespace cryfs { diff --git a/src/filesystem/CrySymlink.cpp b/src/cryfs/filesystem/CrySymlink.cpp similarity index 96% rename from src/filesystem/CrySymlink.cpp rename to src/cryfs/filesystem/CrySymlink.cpp index f40cfd4b..5af99832 100644 --- a/src/filesystem/CrySymlink.cpp +++ b/src/cryfs/filesystem/CrySymlink.cpp @@ -1,6 +1,6 @@ #include "CrySymlink.h" -#include "messmer/fspp/fuse/FuseErrnoException.h" +#include #include "CryDevice.h" #include "CrySymlink.h" #include "parallelaccessfsblobstore/SymlinkBlobRef.h" diff --git a/src/filesystem/CrySymlink.h b/src/cryfs/filesystem/CrySymlink.h similarity index 93% rename from src/filesystem/CrySymlink.h rename to src/cryfs/filesystem/CrySymlink.h index 58721286..2a030800 100644 --- a/src/filesystem/CrySymlink.h +++ b/src/cryfs/filesystem/CrySymlink.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_CRYSYMLINK_H_ #define MESSMER_CRYFS_FILESYSTEM_CRYSYMLINK_H_ -#include +#include #include "CryNode.h" #include "parallelaccessfsblobstore/SymlinkBlobRef.h" #include "parallelaccessfsblobstore/DirBlobRef.h" diff --git a/src/filesystem/cachingfsblobstore/CachingFsBlobStore.cpp b/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.cpp similarity index 100% rename from src/filesystem/cachingfsblobstore/CachingFsBlobStore.cpp rename to src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.cpp diff --git a/src/filesystem/cachingfsblobstore/CachingFsBlobStore.h b/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h similarity index 93% rename from src/filesystem/cachingfsblobstore/CachingFsBlobStore.h rename to src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h index 9c9f853a..777f00f3 100644 --- a/src/filesystem/cachingfsblobstore/CachingFsBlobStore.h +++ b/src/cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h @@ -2,9 +2,9 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_CACHINGFSBLOBSTORE_CACHINGFSBLOBSTORE_H #define MESSMER_CRYFS_FILESYSTEM_CACHINGFSBLOBSTORE_CACHINGFSBLOBSTORE_H -#include +#include #include "../fsblobstore/FsBlobStore.h" -#include +#include #include "FileBlobRef.h" #include "DirBlobRef.h" #include "SymlinkBlobRef.h" diff --git a/src/filesystem/cachingfsblobstore/DirBlobRef.cpp b/src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.cpp similarity index 100% rename from src/filesystem/cachingfsblobstore/DirBlobRef.cpp rename to src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.cpp diff --git a/src/filesystem/cachingfsblobstore/DirBlobRef.h b/src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.h similarity index 100% rename from src/filesystem/cachingfsblobstore/DirBlobRef.h rename to src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.h diff --git a/src/filesystem/cachingfsblobstore/FileBlobRef.cpp b/src/cryfs/filesystem/cachingfsblobstore/FileBlobRef.cpp similarity index 100% rename from src/filesystem/cachingfsblobstore/FileBlobRef.cpp rename to src/cryfs/filesystem/cachingfsblobstore/FileBlobRef.cpp diff --git a/src/filesystem/cachingfsblobstore/FileBlobRef.h b/src/cryfs/filesystem/cachingfsblobstore/FileBlobRef.h similarity index 100% rename from src/filesystem/cachingfsblobstore/FileBlobRef.h rename to src/cryfs/filesystem/cachingfsblobstore/FileBlobRef.h diff --git a/src/filesystem/cachingfsblobstore/FsBlobRef.cpp b/src/cryfs/filesystem/cachingfsblobstore/FsBlobRef.cpp similarity index 100% rename from src/filesystem/cachingfsblobstore/FsBlobRef.cpp rename to src/cryfs/filesystem/cachingfsblobstore/FsBlobRef.cpp diff --git a/src/filesystem/cachingfsblobstore/FsBlobRef.h b/src/cryfs/filesystem/cachingfsblobstore/FsBlobRef.h similarity index 100% rename from src/filesystem/cachingfsblobstore/FsBlobRef.h rename to src/cryfs/filesystem/cachingfsblobstore/FsBlobRef.h diff --git a/src/filesystem/cachingfsblobstore/SymlinkBlobRef.cpp b/src/cryfs/filesystem/cachingfsblobstore/SymlinkBlobRef.cpp similarity index 100% rename from src/filesystem/cachingfsblobstore/SymlinkBlobRef.cpp rename to src/cryfs/filesystem/cachingfsblobstore/SymlinkBlobRef.cpp diff --git a/src/filesystem/cachingfsblobstore/SymlinkBlobRef.h b/src/cryfs/filesystem/cachingfsblobstore/SymlinkBlobRef.h similarity index 100% rename from src/filesystem/cachingfsblobstore/SymlinkBlobRef.h rename to src/cryfs/filesystem/cachingfsblobstore/SymlinkBlobRef.h diff --git a/src/filesystem/fsblobstore/DirBlob.cpp b/src/cryfs/filesystem/fsblobstore/DirBlob.cpp similarity index 97% rename from src/filesystem/fsblobstore/DirBlob.cpp rename to src/cryfs/filesystem/fsblobstore/DirBlob.cpp index b9c00f9e..e602913c 100644 --- a/src/filesystem/fsblobstore/DirBlob.cpp +++ b/src/cryfs/filesystem/fsblobstore/DirBlob.cpp @@ -2,10 +2,10 @@ #include //TODO Remove and replace with exception hierarchy -#include "messmer/fspp/fuse/FuseErrnoException.h" +#include -#include -#include +#include +#include #include "MagicNumbers.h" #include "../CryDevice.h" #include "FileBlob.h" diff --git a/src/filesystem/fsblobstore/DirBlob.h b/src/cryfs/filesystem/fsblobstore/DirBlob.h similarity index 95% rename from src/filesystem/fsblobstore/DirBlob.h rename to src/cryfs/filesystem/fsblobstore/DirBlob.h index 957d917f..cb0ea449 100644 --- a/src/filesystem/fsblobstore/DirBlob.h +++ b/src/cryfs/filesystem/fsblobstore/DirBlob.h @@ -2,9 +2,9 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_DIRBLOB_H_ #define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_DIRBLOB_H_ -#include -#include -#include +#include +#include +#include #include "FsBlob.h" #include "utils/DirEntryList.h" #include diff --git a/src/filesystem/fsblobstore/FileBlob.cpp b/src/cryfs/filesystem/fsblobstore/FileBlob.cpp similarity index 95% rename from src/filesystem/fsblobstore/FileBlob.cpp rename to src/cryfs/filesystem/fsblobstore/FileBlob.cpp index c8d3834e..22e20cbe 100644 --- a/src/filesystem/fsblobstore/FileBlob.cpp +++ b/src/cryfs/filesystem/fsblobstore/FileBlob.cpp @@ -1,7 +1,7 @@ #include "FileBlob.h" #include "MagicNumbers.h" -#include +#include #include using blobstore::Blob; diff --git a/src/filesystem/fsblobstore/FileBlob.h b/src/cryfs/filesystem/fsblobstore/FileBlob.h similarity index 100% rename from src/filesystem/fsblobstore/FileBlob.h rename to src/cryfs/filesystem/fsblobstore/FileBlob.h diff --git a/src/filesystem/fsblobstore/FsBlob.cpp b/src/cryfs/filesystem/fsblobstore/FsBlob.cpp similarity index 100% rename from src/filesystem/fsblobstore/FsBlob.cpp rename to src/cryfs/filesystem/fsblobstore/FsBlob.cpp diff --git a/src/filesystem/fsblobstore/FsBlob.h b/src/cryfs/filesystem/fsblobstore/FsBlob.h similarity index 95% rename from src/filesystem/fsblobstore/FsBlob.h rename to src/cryfs/filesystem/fsblobstore/FsBlob.h index 1a8b399b..a9b38071 100644 --- a/src/filesystem/fsblobstore/FsBlob.h +++ b/src/cryfs/filesystem/fsblobstore/FsBlob.h @@ -2,8 +2,8 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_FSBLOB_H #define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_FSBLOB_H -#include -#include +#include +#include namespace cryfs { namespace fsblobstore { diff --git a/src/filesystem/fsblobstore/FsBlobStore.cpp b/src/cryfs/filesystem/fsblobstore/FsBlobStore.cpp similarity index 100% rename from src/filesystem/fsblobstore/FsBlobStore.cpp rename to src/cryfs/filesystem/fsblobstore/FsBlobStore.cpp diff --git a/src/filesystem/fsblobstore/FsBlobStore.h b/src/cryfs/filesystem/fsblobstore/FsBlobStore.h similarity index 87% rename from src/filesystem/fsblobstore/FsBlobStore.h rename to src/cryfs/filesystem/fsblobstore/FsBlobStore.h index ca73644a..0d2e1ba5 100644 --- a/src/filesystem/fsblobstore/FsBlobStore.h +++ b/src/cryfs/filesystem/fsblobstore/FsBlobStore.h @@ -2,9 +2,9 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_FSBLOBSTORE_H #define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_FSBLOBSTORE_H -#include -#include -#include +#include +#include +#include #include "FileBlob.h" #include "DirBlob.h" #include "SymlinkBlob.h" diff --git a/src/filesystem/fsblobstore/MagicNumbers.h b/src/cryfs/filesystem/fsblobstore/MagicNumbers.h similarity index 100% rename from src/filesystem/fsblobstore/MagicNumbers.h rename to src/cryfs/filesystem/fsblobstore/MagicNumbers.h diff --git a/src/filesystem/fsblobstore/SymlinkBlob.cpp b/src/cryfs/filesystem/fsblobstore/SymlinkBlob.cpp similarity index 97% rename from src/filesystem/fsblobstore/SymlinkBlob.cpp rename to src/cryfs/filesystem/fsblobstore/SymlinkBlob.cpp index 44298da3..459b7d5f 100644 --- a/src/filesystem/fsblobstore/SymlinkBlob.cpp +++ b/src/cryfs/filesystem/fsblobstore/SymlinkBlob.cpp @@ -1,7 +1,7 @@ #include "SymlinkBlob.h" #include "MagicNumbers.h" -#include +#include #include using std::string; diff --git a/src/filesystem/fsblobstore/SymlinkBlob.h b/src/cryfs/filesystem/fsblobstore/SymlinkBlob.h similarity index 100% rename from src/filesystem/fsblobstore/SymlinkBlob.h rename to src/cryfs/filesystem/fsblobstore/SymlinkBlob.h diff --git a/src/filesystem/fsblobstore/utils/DirEntry.cpp b/src/cryfs/filesystem/fsblobstore/utils/DirEntry.cpp similarity index 100% rename from src/filesystem/fsblobstore/utils/DirEntry.cpp rename to src/cryfs/filesystem/fsblobstore/utils/DirEntry.cpp diff --git a/src/filesystem/fsblobstore/utils/DirEntry.h b/src/cryfs/filesystem/fsblobstore/utils/DirEntry.h similarity index 96% rename from src/filesystem/fsblobstore/utils/DirEntry.h rename to src/cryfs/filesystem/fsblobstore/utils/DirEntry.h index 3f65541a..a92ee9d3 100644 --- a/src/filesystem/fsblobstore/utils/DirEntry.h +++ b/src/cryfs/filesystem/fsblobstore/utils/DirEntry.h @@ -2,8 +2,8 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRY_H #define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRY_H -#include -#include +#include +#include namespace cryfs { namespace fsblobstore { diff --git a/src/filesystem/fsblobstore/utils/DirEntryList.cpp b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp similarity index 99% rename from src/filesystem/fsblobstore/utils/DirEntryList.cpp rename to src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp index 56a5d1f9..0902e3be 100644 --- a/src/filesystem/fsblobstore/utils/DirEntryList.cpp +++ b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp @@ -2,7 +2,7 @@ #include //TODO Get rid of that in favor of better error handling -#include +#include using cpputils::Data; using std::string; diff --git a/src/filesystem/fsblobstore/utils/DirEntryList.h b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h similarity index 98% rename from src/filesystem/fsblobstore/utils/DirEntryList.h rename to src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h index b2b5d26a..31d2bbc8 100644 --- a/src/filesystem/fsblobstore/utils/DirEntryList.h +++ b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRYLIST_H #define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRYLIST_H -#include +#include #include "DirEntry.h" #include #include diff --git a/src/filesystem/parallelaccessfsblobstore/DirBlobRef.cpp b/src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.cpp similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/DirBlobRef.cpp rename to src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.cpp diff --git a/src/filesystem/parallelaccessfsblobstore/DirBlobRef.h b/src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.h similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/DirBlobRef.h rename to src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.h diff --git a/src/filesystem/parallelaccessfsblobstore/FileBlobRef.cpp b/src/cryfs/filesystem/parallelaccessfsblobstore/FileBlobRef.cpp similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/FileBlobRef.cpp rename to src/cryfs/filesystem/parallelaccessfsblobstore/FileBlobRef.cpp diff --git a/src/filesystem/parallelaccessfsblobstore/FileBlobRef.h b/src/cryfs/filesystem/parallelaccessfsblobstore/FileBlobRef.h similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/FileBlobRef.h rename to src/cryfs/filesystem/parallelaccessfsblobstore/FileBlobRef.h diff --git a/src/filesystem/parallelaccessfsblobstore/FsBlobRef.cpp b/src/cryfs/filesystem/parallelaccessfsblobstore/FsBlobRef.cpp similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/FsBlobRef.cpp rename to src/cryfs/filesystem/parallelaccessfsblobstore/FsBlobRef.cpp diff --git a/src/filesystem/parallelaccessfsblobstore/FsBlobRef.h b/src/cryfs/filesystem/parallelaccessfsblobstore/FsBlobRef.h similarity index 91% rename from src/filesystem/parallelaccessfsblobstore/FsBlobRef.h rename to src/cryfs/filesystem/parallelaccessfsblobstore/FsBlobRef.h index 96dc5787..0f77a863 100644 --- a/src/filesystem/parallelaccessfsblobstore/FsBlobRef.h +++ b/src/cryfs/filesystem/parallelaccessfsblobstore/FsBlobRef.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_PARALLELACCESSFSBLOBSTORE_FSBLOBREF_H #define MESSMER_CRYFS_FILESYSTEM_PARALLELACCESSFSBLOBSTORE_FSBLOBREF_H -#include +#include #include "../cachingfsblobstore/FsBlobRef.h" namespace cryfs { diff --git a/src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.cpp b/src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.cpp similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.cpp rename to src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.cpp diff --git a/src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.h b/src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.h similarity index 96% rename from src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.h rename to src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.h index 8ba42228..bcfa5a94 100644 --- a/src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.h +++ b/src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStore.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_PARALLELACCESSFSBLOBSTORE_PARALLELACCESSFSBLOBSTORE_H #define MESSMER_CRYFS_FILESYSTEM_PARALLELACCESSFSBLOBSTORE_PARALLELACCESSFSBLOBSTORE_H -#include +#include #include "FileBlobRef.h" #include "DirBlobRef.h" #include "SymlinkBlobRef.h" diff --git a/src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.cpp b/src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.cpp similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.cpp rename to src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.cpp diff --git a/src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.h b/src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.h similarity index 91% rename from src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.h rename to src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.h index abd1a725..46c7dcd3 100644 --- a/src/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.h +++ b/src/cryfs/filesystem/parallelaccessfsblobstore/ParallelAccessFsBlobStoreAdapter.h @@ -2,8 +2,8 @@ #ifndef MESSMER_CRYFS_FILESYSTEM_PARALLELACCESSFSBLOBSTORE_PARALLELACCESSFSBLOBSTOREADAPTER_H_ #define MESSMER_CRYFS_FILESYSTEM_PARALLELACCESSFSBLOBSTORE_PARALLELACCESSFSBLOBSTOREADAPTER_H_ -#include -#include +#include +#include #include "../cachingfsblobstore/CachingFsBlobStore.h" namespace cryfs { diff --git a/src/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.cpp b/src/cryfs/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.cpp similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.cpp rename to src/cryfs/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.cpp diff --git a/src/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h b/src/cryfs/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h similarity index 100% rename from src/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h rename to src/cryfs/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h diff --git a/src/main.cpp b/src/cryfs/main.cpp similarity index 72% rename from src/main.cpp rename to src/cryfs/main.cpp index d65fd467..8df6740d 100644 --- a/src/main.cpp +++ b/src/cryfs/main.cpp @@ -1,7 +1,7 @@ #include "cli/Cli.h" -#include -#include -#include +#include +#include +#include using namespace cryfs; using cpputils::Random; diff --git a/src/fspp/CMakeLists.txt b/src/fspp/CMakeLists.txt new file mode 100644 index 00000000..9e9c9349 --- /dev/null +++ b/src/fspp/CMakeLists.txt @@ -0,0 +1,32 @@ +project (fspp) + +set(SOURCES + impl/FilesystemImpl.cpp + impl/Profiler.cpp + fuse/Fuse.cpp +) + +add_library(${PROJECT_NAME} STATIC ${SOURCES}) + +# This is needed by boost thread +#if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") +# target_link_libraries(${PROJECT_NAME} PRIVATE rt) +#endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +target_compile_definitions(${PROJECT_NAME} PUBLIC _FILE_OFFSET_BITS=64) +target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils) + +target_add_boost(${PROJECT_NAME} filesystem system thread chrono) +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + if(EXISTS "/usr/local/include/osxfuse") + target_include_directories(${PROJECT_NAME} PRIVATE /usr/local/include/osxfuse) + target_link_libraries(${PROJECT_NAME} PRIVATE osxfuse) + else() + message(FATAL_ERROR "Osxfuse not found in /usr/local/include/osxfuse. Please install osxfuse.") + endif(EXISTS "/usr/local/include/osxfuse") +else(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + target_link_libraries(${PROJECT_NAME} PRIVATE fuse) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/src/fspp/fs_interface/Device.h b/src/fspp/fs_interface/Device.h new file mode 100644 index 00000000..50c3eadc --- /dev/null +++ b/src/fspp/fs_interface/Device.h @@ -0,0 +1,22 @@ +#pragma once +#ifndef MESSMER_FSPP_FSINTERFACE_DEVICE_H_ +#define MESSMER_FSPP_FSINTERFACE_DEVICE_H_ + +#include +#include +#include + +namespace fspp { +class Node; + +class Device { +public: + virtual ~Device() {} + + virtual void statfs(const boost::filesystem::path &path, struct ::statvfs *fsstat) = 0; + virtual boost::optional> Load(const boost::filesystem::path &path) = 0; +}; + +} + +#endif diff --git a/src/fspp/fs_interface/Dir.h b/src/fspp/fs_interface/Dir.h new file mode 100644 index 00000000..338dcf2b --- /dev/null +++ b/src/fspp/fs_interface/Dir.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef MESSMER_FSPP_FSINTERFACE_DIR_H_ +#define MESSMER_FSPP_FSINTERFACE_DIR_H_ + +#include "Node.h" +#include +#include + +namespace fspp { +class Device; +class OpenFile; + +class Dir: public virtual Node { +public: + virtual ~Dir() {} + + enum class EntryType: uint8_t { + DIR = 0x00, + FILE = 0x01, + SYMLINK = 0x02 + }; + + struct Entry { + Entry(EntryType type_, const std::string &name_): type(type_), name(name_) {} + EntryType type; + std::string name; + }; + + virtual cpputils::unique_ref createAndOpenFile(const std::string &name, mode_t mode, uid_t uid, gid_t gid) = 0; + virtual void createDir(const std::string &name, mode_t mode, uid_t uid, gid_t gid) = 0; + virtual void createSymlink(const std::string &name, const boost::filesystem::path &target, uid_t uid, gid_t gid) = 0; + + //TODO Allow alternative implementation returning only children names without more information + //virtual std::unique_ptr> children() const = 0; + virtual cpputils::unique_ref> children() const = 0; +}; + +} + +#endif diff --git a/src/fspp/fs_interface/File.h b/src/fspp/fs_interface/File.h new file mode 100644 index 00000000..74084303 --- /dev/null +++ b/src/fspp/fs_interface/File.h @@ -0,0 +1,22 @@ +#pragma once +#ifndef MESSMER_FSPP_FSINTERFACE_FILE_H_ +#define MESSMER_FSPP_FSINTERFACE_FILE_H_ + +#include "Node.h" +#include + +namespace fspp { +class Device; +class OpenFile; + +class File: public virtual Node { +public: + virtual ~File() {} + + virtual cpputils::unique_ref open(int flags) const = 0; + virtual void truncate(off_t size) const = 0; +}; + +} + +#endif diff --git a/src/fspp/fs_interface/Node.h b/src/fspp/fs_interface/Node.h new file mode 100644 index 00000000..650f247a --- /dev/null +++ b/src/fspp/fs_interface/Node.h @@ -0,0 +1,26 @@ +#pragma once +#ifndef MESSMER_FSPP_FSINTERFACE_NODE_H_ +#define MESSMER_FSPP_FSINTERFACE_NODE_H_ + +#include + +#include + +namespace fspp { + +class Node { +public: + virtual ~Node() {} + + virtual void stat(struct ::stat *result) const = 0; + virtual void chmod(mode_t mode) = 0; + virtual void chown(uid_t uid, gid_t gid) = 0; + virtual void access(int mask) const = 0; + virtual void rename(const boost::filesystem::path &to) = 0; + virtual void utimens(const timespec lastAccessTime, const timespec lastModificationTime) = 0; + virtual void remove() = 0; +}; + +} + +#endif diff --git a/src/fspp/fs_interface/OpenFile.h b/src/fspp/fs_interface/OpenFile.h new file mode 100644 index 00000000..4f1b464c --- /dev/null +++ b/src/fspp/fs_interface/OpenFile.h @@ -0,0 +1,26 @@ +#pragma once +#ifndef MESSMER_FSPP_FSINTERFACE_OPENFILE_H_ +#define MESSMER_FSPP_FSINTERFACE_OPENFILE_H_ + +#include +#include + +namespace fspp { +class Device; + +class OpenFile { +public: + virtual ~OpenFile() {} + + virtual void stat(struct ::stat *result) const = 0; + virtual void truncate(off_t size) const = 0; + virtual size_t read(void *buf, size_t count, off_t offset) const = 0; + virtual void write(const void *buf, size_t count, off_t offset) = 0; + virtual void flush() = 0; + virtual void fsync() = 0; + virtual void fdatasync() = 0; +}; + +} + +#endif diff --git a/src/fspp/fs_interface/Symlink.h b/src/fspp/fs_interface/Symlink.h new file mode 100644 index 00000000..591e8a06 --- /dev/null +++ b/src/fspp/fs_interface/Symlink.h @@ -0,0 +1,21 @@ +#pragma once +#ifndef MESSMER_FSPP_FSINTERFACE_SYMLINK_H_ +#define MESSMER_FSPP_FSINTERFACE_SYMLINK_H_ + +#include "Node.h" +#include +#include + +namespace fspp { +class Device; + +class Symlink: public virtual Node { +public: + virtual ~Symlink() {} + + virtual boost::filesystem::path target() const = 0; +}; + +} + +#endif diff --git a/src/fspp/fstest/FsTest.h b/src/fspp/fstest/FsTest.h new file mode 100644 index 00000000..c403c0d3 --- /dev/null +++ b/src/fspp/fstest/FsTest.h @@ -0,0 +1,19 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSTEST_H_ +#define MESSMER_FSPP_FSTEST_FSTEST_H_ + +#include "testutils/FileSystemTest.h" +#include "FsppDeviceTest.h" +#include "FsppDirTest.h" +#include "FsppFileTest.h" +#include "FsppSymlinkTest.h" +#include "FsppOpenFileTest.h" + +#define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \ + +#endif diff --git a/src/fspp/fstest/FsppDeviceTest.h b/src/fspp/fstest/FsppDeviceTest.h new file mode 100644 index 00000000..117e63f9 --- /dev/null +++ b/src/fspp/fstest/FsppDeviceTest.h @@ -0,0 +1,105 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPDEVICETEST_H_ +#define MESSMER_FSPP_FSTEST_FSPPDEVICETEST_H_ + +template +class FsppDeviceTest: public FileSystemTest { +public: + void InitDirStructure() { + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/")->createDir("myemptydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createAndOpenFile("myfile2", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createDir("mysubdir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir/mysubdir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir/mysubdir")->createDir("mysubsubdir", this->MODE_PUBLIC, 0, 0); + } +}; + +TYPED_TEST_CASE_P(FsppDeviceTest); + +TYPED_TEST_P(FsppDeviceTest, InitFilesystem) { + //fixture->createDevice() is called in the FileSystemTest constructor +} + +TYPED_TEST_P(FsppDeviceTest, LoadRootDir) { + this->LoadDir("/"); +} + +TYPED_TEST_P(FsppDeviceTest, LoadFileFromRootDir) { + this->InitDirStructure(); + this->LoadFile("/myfile"); +} + +TYPED_TEST_P(FsppDeviceTest, LoadDirFromRootDir) { + this->InitDirStructure(); + this->LoadDir("/mydir"); +} + +TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromEmptyRootDir) { + EXPECT_EQ(boost::none, this->device->Load("/nonexisting")); +} + +TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromRootDir) { + this->InitDirStructure(); + EXPECT_EQ(boost::none, this->device->Load("/nonexisting")); +} + +TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromNonexistingDir) { + this->InitDirStructure(); + //TODO Change as soon as we have a concept of how to handle filesystem errors in the interface + EXPECT_ANY_THROW( + this->device->Load("/nonexisting/nonexisting2") + ); +} + +TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromExistingDir) { + this->InitDirStructure(); + EXPECT_EQ(boost::none, this->device->Load("/mydir/nonexisting")); +} + +TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromExistingEmptyDir) { + this->InitDirStructure(); + EXPECT_EQ(boost::none, this->device->Load("/myemptydir/nonexisting")); +} + +TYPED_TEST_P(FsppDeviceTest, LoadFileFromDir_Nesting1) { + this->InitDirStructure(); + this->LoadFile("/mydir/myfile"); +} + +TYPED_TEST_P(FsppDeviceTest, LoadDirFromDir_Nesting1) { + this->InitDirStructure(); + this->LoadDir("/mydir/mysubdir"); +} + +TYPED_TEST_P(FsppDeviceTest, LoadFileFromDir_Nesting2) { + this->InitDirStructure(); + this->LoadFile("/mydir/mysubdir/myfile"); +} + +TYPED_TEST_P(FsppDeviceTest, LoadDirFromDir_Nesting2) { + this->InitDirStructure(); + this->LoadDir("/mydir/mysubdir/mysubsubdir"); +} + +//TODO Test statfs + +REGISTER_TYPED_TEST_CASE_P(FsppDeviceTest, + InitFilesystem, + LoadRootDir, + LoadFileFromRootDir, + LoadDirFromRootDir, + LoadNonexistingFromEmptyRootDir, + LoadNonexistingFromRootDir, + LoadNonexistingFromNonexistingDir, + LoadNonexistingFromExistingDir, + LoadNonexistingFromExistingEmptyDir, + LoadFileFromDir_Nesting1, + LoadDirFromDir_Nesting1, + LoadFileFromDir_Nesting2, + LoadDirFromDir_Nesting2 +); + +#endif diff --git a/src/fspp/fstest/FsppDirTest.h b/src/fspp/fstest/FsppDirTest.h new file mode 100644 index 00000000..ee55c0f6 --- /dev/null +++ b/src/fspp/fstest/FsppDirTest.h @@ -0,0 +1,289 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPDIRTEST_H_ +#define MESSMER_FSPP_FSTEST_FSPPDIRTEST_H_ + +template +class FsppDirTest: public FileSystemTest { +public: + void InitDirStructure() { + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/")->createDir("myemptydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createAndOpenFile("myfile2", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createDir("mysubdir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir/mysubdir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir/mysubdir")->createDir("mysubsubdir", this->MODE_PUBLIC, 0, 0); + } + + void EXPECT_CHILDREN_ARE(const boost::filesystem::path &path, const std::initializer_list expected) { + EXPECT_CHILDREN_ARE(*this->LoadDir(path), expected); + } + + void EXPECT_CHILDREN_ARE(const fspp::Dir &dir, const std::initializer_list expected) { + std::vector expectedChildren = expected; + expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".")); + expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "..")); + EXPECT_UNORDERED_EQ(expectedChildren, *dir.children()); + } + + template + void EXPECT_UNORDERED_EQ(const std::vector &expected, std::vector actual) { + EXPECT_EQ(expected.size(), actual.size()); + for (const Entry &expectedEntry : expected) { + removeOne(&actual, expectedEntry); + } + } + + template + void removeOne(std::vector *entries, const Entry &toRemove) { + for (auto iter = entries->begin(); iter != entries->end(); ++iter) { + if (iter->type == toRemove.type && iter->name == toRemove.name) { + entries->erase(iter); + return; + } + } + EXPECT_TRUE(false); + } +}; +TYPED_TEST_CASE_P(FsppDirTest); + +fspp::Dir::Entry DirEntry(const std::string &name) { + return fspp::Dir::Entry(fspp::Dir::EntryType::DIR, name); +} + +fspp::Dir::Entry FileEntry(const std::string &name) { + return fspp::Dir::Entry(fspp::Dir::EntryType::FILE, name); +} + +TYPED_TEST_P(FsppDirTest, Children_RootDir_Empty) { + this->EXPECT_CHILDREN_ARE("/", {}); +} + +TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_Directly) { + auto rootdir = this->LoadDir("/"); + rootdir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE(*rootdir, { + FileEntry("myfile") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_AfterReloadingDir) { + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/", { + FileEntry("myfile") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_Directly) { + auto rootdir = this->LoadDir("/"); + rootdir->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE(*rootdir, { + DirEntry("mydir") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_AfterReloadingDir) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/", { + DirEntry("mydir") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_RootDir_LargerStructure) { + this->InitDirStructure(); + this->EXPECT_CHILDREN_ARE("/", { + FileEntry("myfile"), + DirEntry("mydir"), + DirEntry("myemptydir") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_Empty) { + this->LoadDir("/")->createDir("myemptydir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/myemptydir", {}); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_OneFile_Directly) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + auto dir = this->LoadDir("/mydir"); + dir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE(*dir, { + FileEntry("myfile") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_OneFile_AfterReloadingDir) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/mydir", { + FileEntry("myfile") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_OneDir_Directly) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + auto dir = this->LoadDir("/mydir"); + dir->createDir("mysubdir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE(*dir, { + DirEntry("mysubdir") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_OneDir_AfterReloadingDir) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createDir("mysubdir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/mydir", { + DirEntry("mysubdir") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_LargerStructure_Empty) { + this->InitDirStructure(); + this->EXPECT_CHILDREN_ARE("/myemptydir", {}); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested_LargerStructure) { + this->InitDirStructure(); + this->EXPECT_CHILDREN_ARE("/mydir", { + FileEntry("myfile"), + FileEntry("myfile2"), + DirEntry("mysubdir") + }); +} + +TYPED_TEST_P(FsppDirTest, Children_Nested2_LargerStructure) { + this->InitDirStructure(); + this->EXPECT_CHILDREN_ARE("/mydir/mysubdir", { + FileEntry("myfile"), + DirEntry("mysubsubdir") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InEmptyRoot) { + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + this->LoadFile("/myfile"); +} + +TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InNonemptyRoot) { + this->InitDirStructure(); + this->LoadDir("/")->createAndOpenFile("mynewfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/", { + FileEntry("myfile"), + DirEntry("mydir"), + DirEntry("myemptydir"), + FileEntry("mynewfile") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InEmptyNestedDir) { + this->InitDirStructure(); + this->LoadDir("/myemptydir")->createAndOpenFile("mynewfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/myemptydir", { + FileEntry("mynewfile") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InNonemptyNestedDir) { + this->InitDirStructure(); + this->LoadDir("/mydir")->createAndOpenFile("mynewfile", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/mydir", { + FileEntry("myfile"), + FileEntry("myfile2"), + DirEntry("mysubdir"), + FileEntry("mynewfile") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_AlreadyExisting) { + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + //TODO Change, once we know which way of error reporting we want for such errors + EXPECT_ANY_THROW( + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + ); +} + +TYPED_TEST_P(FsppDirTest, CreateDir_InEmptyRoot) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir"); +} + +TYPED_TEST_P(FsppDirTest, CreateDir_InNonemptyRoot) { + this->InitDirStructure(); + this->LoadDir("/")->createDir("mynewdir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/", { + FileEntry("myfile"), + DirEntry("mydir"), + DirEntry("myemptydir"), + DirEntry("mynewdir") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateDir_InEmptyNestedDir) { + this->InitDirStructure(); + this->LoadDir("/myemptydir")->createDir("mynewdir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/myemptydir", { + DirEntry("mynewdir") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateDir_InNonemptyNestedDir) { + this->InitDirStructure(); + this->LoadDir("/mydir")->createDir("mynewdir", this->MODE_PUBLIC, 0, 0); + this->EXPECT_CHILDREN_ARE("/mydir", { + FileEntry("myfile"), + FileEntry("myfile2"), + DirEntry("mysubdir"), + DirEntry("mynewdir") + }); +} + +TYPED_TEST_P(FsppDirTest, CreateDir_AlreadyExisting) { + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + //TODO Change, once we know which way of error reporting we want for such errors + EXPECT_ANY_THROW( + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + ); +} + + + +REGISTER_TYPED_TEST_CASE_P(FsppDirTest, + Children_RootDir_Empty, + Children_RootDir_OneFile_Directly, + Children_RootDir_OneFile_AfterReloadingDir, + Children_RootDir_OneDir_Directly, + Children_RootDir_OneDir_AfterReloadingDir, + Children_RootDir_LargerStructure, + Children_Nested_Empty, + Children_Nested_OneFile_Directly, + Children_Nested_OneFile_AfterReloadingDir, + Children_Nested_OneDir_Directly, + Children_Nested_OneDir_AfterReloadingDir, + Children_Nested_LargerStructure, + Children_Nested_LargerStructure_Empty, + Children_Nested2_LargerStructure, + CreateAndOpenFile_InEmptyRoot, + CreateAndOpenFile_InNonemptyRoot, + CreateAndOpenFile_InEmptyNestedDir, + CreateAndOpenFile_InNonemptyNestedDir, + CreateAndOpenFile_AlreadyExisting, + CreateDir_InEmptyRoot, + CreateDir_InNonemptyRoot, + CreateDir_InEmptyNestedDir, + CreateDir_InNonemptyNestedDir, + CreateDir_AlreadyExisting +); + +//TODO stat +//TODO access +//TODO rename +//TODO utimens +//TODO rmdir +//TODO chmod +//TODO chown +//TODO mkdir with uid/gid + +//TODO Test permission flags + +#endif diff --git a/src/fspp/fstest/FsppFileTest.h b/src/fspp/fstest/FsppFileTest.h new file mode 100644 index 00000000..ac6df30d --- /dev/null +++ b/src/fspp/fstest/FsppFileTest.h @@ -0,0 +1,179 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPFILETEST_H_ +#define MESSMER_FSPP_FSTEST_FSPPFILETEST_H_ + +#include +#include + +#include "testutils/FileTest.h" + +template +class FsppFileTest: public FileTest { +public: + void Test_Open_RDONLY(fspp::File *file) { + file->open(O_RDONLY); + } + + void Test_Open_WRONLY(fspp::File *file) { + file->open(O_WRONLY); + } + + void Test_Open_RDWR(fspp::File *file) { + file->open(O_RDONLY); + } + + void Test_Truncate_DontChange1(fspp::File *file) { + file->truncate(0); + this->EXPECT_SIZE(0, *file); + } + + void Test_Truncate_GrowTo1(fspp::File *file) { + file->truncate(1); + this->EXPECT_SIZE(1, *file); + } + + void Test_Truncate_Grow(fspp::File *file) { + file->truncate(10*1024*1024); + this->EXPECT_SIZE(10*1024*1024, *file); + } + + void Test_Truncate_DontChange2(fspp::File *file) { + file->truncate(10*1024*1024); + file->truncate(10*1024*1024); + this->EXPECT_SIZE(10*1024*1024, *file); + } + + void Test_Truncate_Shrink(fspp::File *file) { + file->truncate(10*1024*1024); + file->truncate(5*1024*1024); + this->EXPECT_SIZE(5*1024*1024, *file); + } + + void Test_Truncate_ShrinkTo0(fspp::File *file) { + file->truncate(10*1024*1024); + file->truncate(0); + this->EXPECT_SIZE(0, *file); + } + + void Test_Stat_CreatedFileIsEmpty(fspp::File *file) { + this->EXPECT_SIZE(0, *file); + } +}; + +TYPED_TEST_CASE_P(FsppFileTest); + +TYPED_TEST_P(FsppFileTest, Open_RDONLY) { + this->Test_Open_RDONLY(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Open_RDONLY_Nested) { + this->Test_Open_RDONLY(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Open_WRONLY) { + this->Test_Open_WRONLY(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Open_WRONLY_Nested) { + this->Test_Open_WRONLY(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Open_RDWR) { + this->Test_Open_RDWR(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Open_RDWR_Nested) { + this->Test_Open_RDWR(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_DontChange1) { + this->Test_Truncate_DontChange1(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_DontChange1_Nested) { + this->Test_Truncate_DontChange1(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_GrowTo1) { + this->Test_Truncate_GrowTo1(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_GrowTo1_Nested) { + this->Test_Truncate_GrowTo1(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_Grow) { + this->Test_Truncate_Grow(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_Grow_Nested) { + this->Test_Truncate_Grow(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_DontChange2) { + this->Test_Truncate_DontChange2(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_DontChange2_Nested) { + this->Test_Truncate_DontChange2(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_Shrink) { + this->Test_Truncate_Shrink(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_Shrink_Nested) { + this->Test_Truncate_Shrink(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_ShrinkTo0) { + this->Test_Truncate_ShrinkTo0(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Truncate_ShrinkTo0_Nested) { + this->Test_Truncate_ShrinkTo0(this->file_nested.get()); +} + +TYPED_TEST_P(FsppFileTest, Stat_CreatedFileIsEmpty) { + this->Test_Stat_CreatedFileIsEmpty(this->file_root.get()); +} + +TYPED_TEST_P(FsppFileTest, Stat_CreatedFileIsEmpty_Nested) { + this->Test_Stat_CreatedFileIsEmpty(this->file_nested.get()); +} + +REGISTER_TYPED_TEST_CASE_P(FsppFileTest, + Open_RDONLY, + Open_RDONLY_Nested, + Open_WRONLY, + Open_WRONLY_Nested, + Open_RDWR, + Open_RDWR_Nested, + Truncate_DontChange1, + Truncate_DontChange1_Nested, + Truncate_GrowTo1, + Truncate_GrowTo1_Nested, + Truncate_Grow, + Truncate_Grow_Nested, + Truncate_DontChange2, + Truncate_DontChange2_Nested, + Truncate_Shrink, + Truncate_Shrink_Nested, + Truncate_ShrinkTo0, + Truncate_ShrinkTo0_Nested, + Stat_CreatedFileIsEmpty, + Stat_CreatedFileIsEmpty_Nested +); + +//TODO stat +//TODO access +//TODO rename +//TODO utimens +//TODO unlink +//TODO chmod +//TODO chown +//TODO createAndOpenFile with uid/gid + +//TODO Test permission flags + +#endif diff --git a/src/fspp/fstest/FsppOpenFileTest.h b/src/fspp/fstest/FsppOpenFileTest.h new file mode 100644 index 00000000..7dc39f67 --- /dev/null +++ b/src/fspp/fstest/FsppOpenFileTest.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPOPENFILETEST_H_ +#define MESSMER_FSPP_FSTEST_FSPPOPENFILETEST_H_ + +#include "testutils/FileTest.h" + +template +class FsppOpenFileTest: public FileTest { +public: +}; + +TYPED_TEST_CASE_P(FsppOpenFileTest); + +TYPED_TEST_P(FsppOpenFileTest, Bla) { + //TODO +} + +REGISTER_TYPED_TEST_CASE_P(FsppOpenFileTest, + Bla +); + +//TODO Test stat +//TODO Test truncate +//TODO Test read +//TODO Test write +//TODO Test flush +//TODO Test fsync +//TODO Test fdatasync + +#endif diff --git a/src/fspp/fstest/FsppSymlinkTest.h b/src/fspp/fstest/FsppSymlinkTest.h new file mode 100644 index 00000000..9dc624fa --- /dev/null +++ b/src/fspp/fstest/FsppSymlinkTest.h @@ -0,0 +1,78 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPSYMLINKTEST_H_ +#define MESSMER_FSPP_FSTEST_FSPPSYMLINKTEST_H_ + +#include +#include + +#include "testutils/FileSystemTest.h" + +template +class FsppSymlinkTest: public FileSystemTest { +public: + void CreateSymlink(const std::string &source, const boost::filesystem::path &target) { + this->LoadDir("/")->createSymlink(source, target, 0, 0); + } + + void Test_Create_AbsolutePath() { + CreateSymlink("mysymlink", "/my/symlink/target"); + } + + void Test_Create_RelativePath() { + CreateSymlink("mysymlink", "../target"); + } + + void Test_Read_AbsolutePath() { + CreateSymlink("mysymlink", "/my/symlink/target"); + EXPECT_EQ("/my/symlink/target", this->LoadSymlink("/mysymlink")->target()); + } + + void Test_Read_RelativePath() { + CreateSymlink("mysymlink", "../target"); + EXPECT_EQ("../target", this->LoadSymlink("/mysymlink")->target()); + } + + void Test_Delete() { + CreateSymlink("mysymlink", "/my/symlink/target"); + std::cerr << "1" << std::endl; + EXPECT_NE(boost::none, this->device->Load("/mysymlink")); + std::cerr << "2" << std::endl; + this->LoadSymlink("/mysymlink")->remove(); + std::cerr << "3" << std::endl; + EXPECT_EQ(boost::none, this->device->Load("/mysymlink")); + } +}; + +TYPED_TEST_CASE_P(FsppSymlinkTest); + +TYPED_TEST_P(FsppSymlinkTest, Create_AbsolutePath) { + this->Test_Create_AbsolutePath(); +} + +TYPED_TEST_P(FsppSymlinkTest, Create_RelativePath) { + this->Test_Create_RelativePath(); +} + +TYPED_TEST_P(FsppSymlinkTest, Read_AbsolutePath) { + this->Test_Read_AbsolutePath(); +} + +TYPED_TEST_P(FsppSymlinkTest, Read_RelativePath) { + this->Test_Read_RelativePath(); +} + +TYPED_TEST_P(FsppSymlinkTest, Delete) { + this->Test_Delete(); +} + +REGISTER_TYPED_TEST_CASE_P(FsppSymlinkTest, + Create_AbsolutePath, + Create_RelativePath, + Read_AbsolutePath, + Read_RelativePath, + Delete +); + +//TODO Other tests? + +#endif diff --git a/src/fspp/fstest/testutils/FileSystemTest.h b/src/fspp/fstest/testutils/FileSystemTest.h new file mode 100644 index 00000000..f8b03a98 --- /dev/null +++ b/src/fspp/fstest/testutils/FileSystemTest.h @@ -0,0 +1,64 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_TESTUTILS_FILESYSTEMTEST_H_ +#define MESSMER_FSPP_FSTEST_TESTUTILS_FILESYSTEMTEST_H_ + +#include +#include +#include +#include +#include + +#include "../../fs_interface/Device.h" +#include "../../fs_interface/Dir.h" +#include "../../fs_interface/File.h" +#include "../../fs_interface/Symlink.h" +#include "../../fs_interface/OpenFile.h" + +class FileSystemTestFixture { +public: + virtual ~FileSystemTestFixture() {} + virtual cpputils::unique_ref createDevice() = 0; +}; + +template +class FileSystemTest: public ::testing::Test { +public: + BOOST_STATIC_ASSERT_MSG( + (std::is_base_of::value), + "Given test fixture for instantiating the (type parameterized) FileSystemTest must inherit from FileSystemTestFixture" + ); + + FileSystemTest(): fixture(), device(fixture.createDevice()) {} + + ConcreteFileSystemTestFixture fixture; + cpputils::unique_ref device; + + static constexpr mode_t MODE_PUBLIC = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; + + cpputils::unique_ref LoadDir(const boost::filesystem::path &path) { + auto loaded = device->Load(path); + EXPECT_NE(boost::none, loaded); + auto dir = cpputils::dynamic_pointer_move(*loaded); + EXPECT_NE(boost::none, dir); + return std::move(*dir); + } + + cpputils::unique_ref LoadFile(const boost::filesystem::path &path) { + auto loaded = device->Load(path); + EXPECT_NE(boost::none, loaded); + auto file = cpputils::dynamic_pointer_move(*loaded); + EXPECT_NE(boost::none, file); + return std::move(*file); + } + + cpputils::unique_ref LoadSymlink(const boost::filesystem::path &path) { + auto loaded = device->Load(path); + EXPECT_NE(boost::none, loaded); + auto symlink = cpputils::dynamic_pointer_move(*loaded); + EXPECT_NE(boost::none, symlink); + return std::move(*symlink); + } +}; + + +#endif diff --git a/src/fspp/fstest/testutils/FileTest.h b/src/fspp/fstest/testutils/FileTest.h new file mode 100644 index 00000000..773219d1 --- /dev/null +++ b/src/fspp/fstest/testutils/FileTest.h @@ -0,0 +1,51 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_TESTUTILS_FILETEST_H_ +#define MESSMER_FSPP_FSTEST_TESTUTILS_FILETEST_H_ + +#include "FileSystemTest.h" +#include +#include + +template +class FileTest: public FileSystemTest { +public: + FileTest(): file_root(), file_nested() { + this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); + file_root = cpputils::to_unique_ptr(this->LoadFile("/myfile")); + + this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); + this->LoadDir("/mydir")->createAndOpenFile("mynestedfile", this->MODE_PUBLIC, 0, 0); + file_nested = cpputils::to_unique_ptr(this->LoadFile("/mydir/mynestedfile")); + } + std::unique_ptr file_root; + std::unique_ptr file_nested; + + void EXPECT_SIZE(uint64_t expectedSize, const fspp::File &file) { + EXPECT_SIZE_IN_FILE(expectedSize, file); + auto openFile = file.open(O_RDONLY); + EXPECT_SIZE_IN_OPEN_FILE(expectedSize, *openFile); + EXPECT_NUMBYTES_READABLE(expectedSize, *openFile); + } + + void EXPECT_SIZE_IN_FILE(uint64_t expectedSize, const fspp::File &file) { + struct stat st; + file.stat(&st); + EXPECT_EQ(expectedSize, (uint64_t)st.st_size); + } + + void EXPECT_SIZE_IN_OPEN_FILE(uint64_t expectedSize, const fspp::OpenFile &file) { + struct stat st; + file.stat(&st); + EXPECT_EQ(expectedSize, (uint64_t)st.st_size); + } + + void EXPECT_NUMBYTES_READABLE(uint64_t expectedSize, const fspp::OpenFile &file) { + cpputils::Data data(expectedSize); + //Try to read one byte more than the expected size + ssize_t readBytes = file.read(data.data(), expectedSize+1, 0); + //and check that it only read the expected size (but also not less) + EXPECT_EQ(expectedSize, (uint64_t)readBytes); + } +}; + +#endif diff --git a/src/fspp/fuse/Filesystem.h b/src/fspp/fuse/Filesystem.h new file mode 100644 index 00000000..770cda6f --- /dev/null +++ b/src/fspp/fuse/Filesystem.h @@ -0,0 +1,53 @@ +#pragma once +#ifndef MESSMER_FSPP_FUSE_FILESYSTEM_H_ +#define MESSMER_FSPP_FUSE_FILESYSTEM_H_ + +#include +#include +#include +#include +#include "../fs_interface/Dir.h" + +namespace fspp { +namespace fuse { +class Filesystem { +public: + virtual ~Filesystem() {} + + //TODO Test uid/gid parameters of createAndOpenFile + virtual int createAndOpenFile(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) = 0; + virtual int openFile(const boost::filesystem::path &path, int flags) = 0; + virtual void flush(int descriptor) = 0; + virtual void closeFile(int descriptor) = 0; + virtual void lstat(const boost::filesystem::path &path, struct ::stat *stbuf) = 0; + virtual void fstat(int descriptor, struct ::stat *stbuf) = 0; + //TODO Test chmod + virtual void chmod(const boost::filesystem::path &path, mode_t mode) = 0; + //TODO Test chown + virtual void chown(const boost::filesystem::path &path, uid_t uid, gid_t gid) = 0; + virtual void truncate(const boost::filesystem::path &path, off_t size) = 0; + virtual void ftruncate(int descriptor, off_t size) = 0; + virtual size_t read(int descriptor, void *buf, size_t count, off_t offset) = 0; + virtual void write(int descriptor, const void *buf, size_t count, off_t offset) = 0; + virtual void fsync(int descriptor) = 0; + virtual void fdatasync(int descriptor) = 0; + virtual void access(const boost::filesystem::path &path, int mask) = 0; + //TODO Test uid/gid parameters of mkdir + virtual void mkdir(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) = 0; + virtual void rmdir(const boost::filesystem::path &path) = 0; + virtual void unlink(const boost::filesystem::path &path) = 0; + virtual void rename(const boost::filesystem::path &from, const boost::filesystem::path &to) = 0; + virtual void utimens(const boost::filesystem::path &path, timespec lastAccessTime, timespec lastModificationTime) = 0; + virtual void statfs(const boost::filesystem::path &path, struct statvfs *fsstat) = 0; + //TODO We shouldn't use Dir::Entry here, that's in another layer + virtual cpputils::unique_ref> readDir(const boost::filesystem::path &path) = 0; + //TODO Test createSymlink + virtual void createSymlink(const boost::filesystem::path &to, const boost::filesystem::path &from, uid_t uid, gid_t gid) = 0; + //TODO Test readSymlink + virtual void readSymlink(const boost::filesystem::path &path, char *buf, size_t size) = 0; +}; + +} +} + +#endif diff --git a/src/fspp/fuse/Fuse.cpp b/src/fspp/fuse/Fuse.cpp new file mode 100644 index 00000000..68c07198 --- /dev/null +++ b/src/fspp/fuse/Fuse.cpp @@ -0,0 +1,831 @@ +#include "Fuse.h" +#include +#include + +#include "FuseErrnoException.h" +#include "Filesystem.h" +#include +#include +#include +#include + +using std::vector; +using std::string; + +namespace bf = boost::filesystem; +using namespace cpputils::logging; +using namespace fspp::fuse; + +#define FUSE_OBJ ((Fuse *) fuse_get_context()->private_data) + +// Remove the following line, if you don't want to output each fuse operation on the console +//#define FSPP_LOG 1 + +namespace { +int fusepp_getattr(const char *path, struct stat *stbuf) { + int rs = FUSE_OBJ->getattr(bf::path(path), stbuf); + return rs; +} + +int fusepp_fgetattr(const char *path, struct stat *stbuf, fuse_file_info *fileinfo) { + return FUSE_OBJ->fgetattr(bf::path(path), stbuf, fileinfo); +} + +int fusepp_readlink(const char *path, char *buf, size_t size) { + return FUSE_OBJ->readlink(bf::path(path), buf, size); +} + +int fusepp_mknod(const char *path, mode_t mode, dev_t rdev) { + return FUSE_OBJ->mknod(bf::path(path), mode, rdev); +} + +int fusepp_mkdir(const char *path, mode_t mode) { + return FUSE_OBJ->mkdir(bf::path(path), mode); +} + +int fusepp_unlink(const char *path) { + return FUSE_OBJ->unlink(bf::path(path)); +} + +int fusepp_rmdir(const char *path) { + return FUSE_OBJ->rmdir(bf::path(path)); +} + +int fusepp_symlink(const char *to, const char *from) { + return FUSE_OBJ->symlink(bf::path(to), bf::path(from)); +} + +int fusepp_rename(const char *from, const char *to) { + return FUSE_OBJ->rename(bf::path(from), bf::path(to)); +} + +int fusepp_link(const char *from, const char *to) { + return FUSE_OBJ->link(bf::path(from), bf::path(to)); +} + +int fusepp_chmod(const char *path, mode_t mode) { + return FUSE_OBJ->chmod(bf::path(path), mode); +} + +int fusepp_chown(const char *path, uid_t uid, gid_t gid) { + return FUSE_OBJ->chown(bf::path(path), uid, gid); +} + +int fusepp_truncate(const char *path, off_t size) { + return FUSE_OBJ->truncate(bf::path(path), size); +} + +int fusepp_ftruncate(const char *path, off_t size, fuse_file_info *fileinfo) { + return FUSE_OBJ->ftruncate(bf::path(path), size, fileinfo); +} + +int fusepp_utimens(const char *path, const timespec times[2]) { + return FUSE_OBJ->utimens(bf::path(path), times); +} + +int fusepp_open(const char *path, fuse_file_info *fileinfo) { + return FUSE_OBJ->open(bf::path(path), fileinfo); +} + +int fusepp_release(const char *path, fuse_file_info *fileinfo) { + return FUSE_OBJ->release(bf::path(path), fileinfo); +} + +int fusepp_read(const char *path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { + return FUSE_OBJ->read(bf::path(path), buf, size, offset, fileinfo); +} + +int fusepp_write(const char *path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { + return FUSE_OBJ->write(bf::path(path), buf, size, offset, fileinfo); +} + +int fusepp_statfs(const char *path, struct statvfs *fsstat) { + return FUSE_OBJ->statfs(bf::path(path), fsstat); +} + +int fusepp_flush(const char *path, fuse_file_info *fileinfo) { + return FUSE_OBJ->flush(bf::path(path), fileinfo); +} + +int fusepp_fsync(const char *path, int datasync, fuse_file_info *fileinfo) { + return FUSE_OBJ->fsync(bf::path(path), datasync, fileinfo); +} + +//int fusepp_setxattr(const char*, const char*, const char*, size_t, int) +//int fusepp_getxattr(const char*, const char*, char*, size_t) +//int fusepp_listxattr(const char*, char*, size_t) +//int fusepp_removexattr(const char*, const char*) + +int fusepp_opendir(const char *path, fuse_file_info *fileinfo) { + return FUSE_OBJ->opendir(bf::path(path), fileinfo); +} + +int fusepp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) { + return FUSE_OBJ->readdir(bf::path(path), buf, filler, offset, fileinfo); +} + +int fusepp_releasedir(const char *path, fuse_file_info *fileinfo) { + return FUSE_OBJ->releasedir(bf::path(path), fileinfo); +} + +int fusepp_fsyncdir(const char *path, int datasync, fuse_file_info *fileinfo) { + return FUSE_OBJ->fsyncdir(bf::path(path), datasync, fileinfo); +} + +void* fusepp_init(fuse_conn_info *conn) { + auto f = FUSE_OBJ; + f->init(conn); + return f; +} + +void fusepp_destroy(void *userdata) { + auto f = FUSE_OBJ; + ASSERT(userdata == f, "Wrong userdata set"); + UNUSED(userdata); //In case the assert is disabled + f->destroy(); +} + +int fusepp_access(const char *path, int mask) { + return FUSE_OBJ->access(bf::path(path), mask); +} + +int fusepp_create(const char *path, mode_t mode, fuse_file_info *fileinfo) { + return FUSE_OBJ->create(bf::path(path), mode, fileinfo); +} + +/*int fusepp_lock(const char*, fuse_file_info*, int cmd, flock*) +int fusepp_bmap(const char*, size_t blocksize, uint64_t *idx) +int fusepp_ioctl(const char*, int cmd, void *arg, fuse_file_info*, unsigned int flags, void *data) +int fusepp_poll(const char*, fuse_file_info*, fuse_pollhandle *ph, unsigned *reventsp) +int fusepp_write_buf(const char*, fuse_bufvec *buf, off_t off, fuse_file_info*) +int fusepp_read_buf(const chas*, struct fuse_bufvec **bufp, size_t size, off_T off, fuse_file_info*) +int fusepp_flock(const char*, fuse_file_info*, int op) +int fusepp_fallocate(const char*, int, off_t, off_t, fuse_file_info*)*/ + +fuse_operations *operations() { + static std::unique_ptr singleton(nullptr); + + if (!singleton) { + singleton = std::make_unique(); + singleton->getattr = &fusepp_getattr; + singleton->fgetattr = &fusepp_fgetattr; + singleton->readlink = &fusepp_readlink; + singleton->mknod = &fusepp_mknod; + singleton->mkdir = &fusepp_mkdir; + singleton->unlink = &fusepp_unlink; + singleton->rmdir = &fusepp_rmdir; + singleton->symlink = &fusepp_symlink; + singleton->rename = &fusepp_rename; + singleton->link = &fusepp_link; + singleton->chmod = &fusepp_chmod; + singleton->chown = &fusepp_chown; + singleton->truncate = &fusepp_truncate; + singleton->utimens = &fusepp_utimens; + singleton->open = &fusepp_open; + singleton->read = &fusepp_read; + singleton->write = &fusepp_write; + singleton->statfs = &fusepp_statfs; + singleton->flush = &fusepp_flush; + singleton->release = &fusepp_release; + singleton->fsync = &fusepp_fsync; + /*#ifdef HAVE_SYS_XATTR_H + singleton->setxattr = &fusepp_setxattr; + singleton->getxattr = &fusepp_getxattr; + singleton->listxattr = &fusepp_listxattr; + singleton->removexattr = &fusepp_removexattr; + #endif*/ + singleton->opendir = &fusepp_opendir; + singleton->readdir = &fusepp_readdir; + singleton->releasedir = &fusepp_releasedir; + singleton->fsyncdir = &fusepp_fsyncdir; + singleton->init = &fusepp_init; + singleton->destroy = &fusepp_destroy; + singleton->access = &fusepp_access; + singleton->create = &fusepp_create; + singleton->ftruncate = &fusepp_ftruncate; + } + + return singleton.get(); +} +} + +Fuse::~Fuse() { +} + +Fuse::Fuse(Filesystem *fs) + :_fs(fs), _mountdir(), _running(false) { +} + +void Fuse::_logException(const std::exception &e) { + LOG(ERROR) << "Exception thrown: " << e.what(); +} + +void Fuse::_logUnknownException() { + LOG(ERROR) << "Unknown exception thrown"; +} + +void Fuse::run(int argc, char **argv) { + vector _argv(argv, argv + argc); + _mountdir = argv[1]; + fuse_main(_argv.size(), _argv.data(), operations(), (void*)this); +} + +bool Fuse::running() const { + return _running; +} + +void Fuse::stop() { + //TODO Find better way to unmount (i.e. don't use external fusermount). Unmounting by kill(getpid(), SIGINT) worked, but left the mount directory transport endpoint as not connected. + int ret = system(("fusermount -z -u " + _mountdir.native()).c_str()); // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be. + if (ret != 0) { + LOG(ERROR) << "Could not unmount filesystem"; + } +} + +int Fuse::getattr(const bf::path &path, struct stat *stbuf) { +#ifdef FSPP_LOG + LOG(DEBUG) << "getattr(" << path << ", _, _)"; +#endif + try { + _fs->lstat(path, stbuf); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::getattr: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "fgetattr(" << path << ", _, _)\n"; +#endif + + // On FreeBSD, trying to do anything with the mountpoint ends up + // opening it, and then using the FD for an fgetattr. So in the + // special case of a path of "/", I need to do a getattr on the + // underlying root directory instead of doing the fgetattr(). + // TODO Check if necessary + if (path.native() == "/") { + return getattr(path, stbuf); + } + + try { + _fs->fstat(fileinfo->fh, stbuf); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::fgetattr: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::readlink(const bf::path &path, char *buf, size_t size) { +#ifdef FSPP_LOG + LOG(DEBUG) << "readlink(" << path << ", _, " << size << ")"; +#endif + try { + _fs->readSymlink(path, buf, size); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::readlink: " << e.what(); + return -EIO; + } catch (fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::mknod(const bf::path &path, mode_t mode, dev_t rdev) { + UNUSED(rdev); + UNUSED(mode); + UNUSED(path); + LOG(WARN) << "Called non-implemented mknod(" << path << ", " << mode << ", _)"; + return ENOSYS; +} + +int Fuse::mkdir(const bf::path &path, mode_t mode) { +#ifdef FSPP_LOG + LOG(DEBUG) << "mkdir(" << path << ", " << mode << ")"; +#endif + try { + auto context = fuse_get_context(); + _fs->mkdir(path, mode, context->uid, context->gid); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::mkdir: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::unlink(const bf::path &path) { +#ifdef FSPP_LOG + LOG(DEBUG) << "unlink(" << path << ")"; +#endif + try { + _fs->unlink(path); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::unlink: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::rmdir(const bf::path &path) { +#ifdef FSPP_LOG + LOG(DEBUG) << "rmdir(" << path << ")"; +#endif + try { + _fs->rmdir(path); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::rmdir: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::symlink(const bf::path &from, const bf::path &to) { +#ifdef FSPP_LOG + LOG(DEBUG) << "symlink(" << from << ", " << to << ")"; +#endif + try { + auto context = fuse_get_context(); + _fs->createSymlink(from, to, context->uid, context->gid); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::symlink: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::rename(const bf::path &from, const bf::path &to) { +#ifdef FSPP_LOG + LOG(DEBUG) << "rename(" << from << ", " << to << ")"; +#endif + try { + _fs->rename(from, to); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::rename: " << e.what(); + return -EIO; + } catch(fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +//TODO +int Fuse::link(const bf::path &from, const bf::path &to) { + LOG(WARN) << "NOT IMPLEMENTED: link(" << from << ", " << to << ")"; + //auto real_from = _impl->RootDir() / from; + //auto real_to = _impl->RootDir() / to; + //int retstat = ::link(real_from.c_str(), real_to.c_str()); + //return errcode_map(retstat); + return ENOSYS; +} + +int Fuse::chmod(const bf::path &path, mode_t mode) { +#ifdef FSPP_LOG + LOG(DEBUG) << "chmod(" << path << ", " << mode << ")"; +#endif + try { + _fs->chmod(path, mode); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::chmod: " << e.what(); + return -EIO; + } catch (fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) { +#ifdef FSPP_LOG + LOG(DEBUG) << "chown(" << path << ", " << uid << ", " << gid << ")"; +#endif + try { + _fs->chown(path, uid, gid); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::chown: " << e.what(); + return -EIO; + } catch (fspp::fuse::FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::truncate(const bf::path &path, off_t size) { +#ifdef FSPP_LOG + LOG(DEBUG) << "truncate(" << path << ", " << size << ")"; +#endif + try { + _fs->truncate(path, size); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::truncate: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "ftruncate(" << path << ", " << size << ")"; +#endif + UNUSED(path); + try { + _fs->ftruncate(fileinfo->fh, size); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::ftruncate: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::utimens(const bf::path &path, const timespec times[2]) { +#ifdef FSPP_LOG + LOG(DEBUG) << "utimens(" << path << ", _)"; +#endif + try { + _fs->utimens(path, times[0], times[1]); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::utimens: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "open(" << path << ", _)"; +#endif + try { + fileinfo->fh = _fs->openFile(path, fileinfo->flags); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::open: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "release(" << path << ", _)"; +#endif + UNUSED(path); + try { + _fs->closeFile(fileinfo->fh); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::release: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "read(" << path << ", _, " << size << ", " << offset << ", _ )"; +#endif + UNUSED(path); + try { + return _fs->read(fileinfo->fh, buf, size, offset); + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::read: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "write(" << path << ", _, " << size << ", " << offset << ", _)"; +#endif + UNUSED(path); + try { + _fs->write(fileinfo->fh, buf, size, offset); + return size; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::write: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +//TODO +int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) { +#ifdef FSPP_LOG + LOG(DEBUG) << "statfs(" << path << ", _)"; +#endif + try { + _fs->statfs(path, fsstat); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::statfs: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +//TODO +int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + //TODO Implement it + LOG(WARN) << "Called non-implemented flush(" << path << ", _)"; +#endif + UNUSED(path); + try { + _fs->flush(fileinfo->fh); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::flush: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "fsync(" << path << ", " << datasync << ", _)"; +#endif + UNUSED(path); + try { + if (datasync) { + _fs->fdatasync(fileinfo->fh); + } else { + _fs->fsync(fileinfo->fh); + } + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::fsync: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) { + UNUSED(path); + UNUSED(fileinfo); + //LOG(DEBUG) << "opendir(" << path << ", _)"; + //We don't need opendir, because readdir works directly on the path + return 0; +} + +int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "readdir(" << path << ", _, _, " << offset << ", _)"; +#endif + UNUSED(fileinfo); + UNUSED(offset); + try { + auto entries = _fs->readDir(path); + struct stat stbuf; + for (const auto &entry : *entries) { + //We could pass more file metadata to filler() in its third parameter, + //but it doesn't help performance since fuse ignores everything in stbuf + //except for file-type bits in st_mode and (if used) st_ino. + //It does getattr() calls on all entries nevertheless. + if (entry.type == Dir::EntryType::DIR) { + stbuf.st_mode = S_IFDIR; + } else if (entry.type == Dir::EntryType::FILE) { + stbuf.st_mode = S_IFREG; + } else if (entry.type == Dir::EntryType::SYMLINK) { + stbuf.st_mode = S_IFLNK; + } else { + ASSERT(false, "Unknown entry type"); + } + if (filler(buf, entry.name.c_str(), &stbuf, 0) != 0) { + return -ENOMEM; + } + } + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::readdir: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) { + UNUSED(path); + UNUSED(fileinfo); + //LOG(DEBUG) << "releasedir(" << path << ", _)"; + //We don't need releasedir, because readdir works directly on the path + return 0; +} + +//TODO +int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo) { + UNUSED(fileinfo); + UNUSED(datasync); + UNUSED(path); + //LOG(WARN) << "Called non-implemented fsyncdir(" << path << ", " << datasync << ", _)"; + return 0; +} + +void Fuse::init(fuse_conn_info *conn) { + UNUSED(conn); + _running = true; + +#ifdef FSPP_LOG + cpputils::logging::setLevel(DEBUG); +#endif + + LOG(INFO) << "Filesystem started."; +} + +void Fuse::destroy() { + _running = false; + LOG(INFO) << "Filesystem stopped."; +} + +int Fuse::access(const bf::path &path, int mask) { +#ifdef FSPP_LOG + LOG(DEBUG) << "access(" << path << ", " << mask << ")"; +#endif + try { + _fs->access(path, mask); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::access: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} + +int Fuse::create(const bf::path &path, mode_t mode, fuse_file_info *fileinfo) { +#ifdef FSPP_LOG + LOG(DEBUG) << "create(" << path << ", " << mode << ", _)"; +#endif + try { + auto context = fuse_get_context(); + fileinfo->fh = _fs->createAndOpenFile(path, mode, context->uid, context->gid); + return 0; + } catch(const cpputils::AssertFailed &e) { + LOG(ERROR) << "AssertFailed in Fuse::create: " << e.what(); + return -EIO; + } catch (FuseErrnoException &e) { + return -e.getErrno(); + } catch(const std::exception &e) { + _logException(e); + return -EIO; + } catch(...) { + _logUnknownException(); + return -EIO; + } +} diff --git a/src/fspp/fuse/Fuse.h b/src/fspp/fuse/Fuse.h new file mode 100644 index 00000000..6c62f9d4 --- /dev/null +++ b/src/fspp/fuse/Fuse.h @@ -0,0 +1,72 @@ +#pragma once +#ifndef MESSMER_FSPP_FUSE_FUSE_H_ +#define MESSMER_FSPP_FUSE_FUSE_H_ + +#include "params.h" +#include +#include +#include +#include +#include +#include + +namespace fspp { +class Device; + +namespace fuse { +class Filesystem; + +class Fuse final { +public: + explicit Fuse(Filesystem *fs); + ~Fuse(); + + void run(int argc, char **argv); + bool running() const; + void stop(); + + int getattr(const boost::filesystem::path &path, struct stat *stbuf); + int fgetattr(const boost::filesystem::path &path, struct stat *stbuf, fuse_file_info *fileinfo); + int readlink(const boost::filesystem::path &path, char *buf, size_t size); + int mknod(const boost::filesystem::path &path, mode_t mode, dev_t rdev); + int mkdir(const boost::filesystem::path &path, mode_t mode); + int unlink(const boost::filesystem::path &path); + int rmdir(const boost::filesystem::path &path); + int symlink(const boost::filesystem::path &from, const boost::filesystem::path &to); + int rename(const boost::filesystem::path &from, const boost::filesystem::path &to); + int link(const boost::filesystem::path &from, const boost::filesystem::path &to); + int chmod(const boost::filesystem::path &path, mode_t mode); + int chown(const boost::filesystem::path &path, uid_t uid, gid_t gid); + int truncate(const boost::filesystem::path &path, off_t size); + int ftruncate(const boost::filesystem::path &path, off_t size, fuse_file_info *fileinfo); + int utimens(const boost::filesystem::path &path, const timespec times[2]); + int open(const boost::filesystem::path &path, fuse_file_info *fileinfo); + int release(const boost::filesystem::path &path, fuse_file_info *fileinfo); + int read(const boost::filesystem::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo); + int write(const boost::filesystem::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo); + int statfs(const boost::filesystem::path &path, struct statvfs *fsstat); + int flush(const boost::filesystem::path &path, fuse_file_info *fileinfo); + int fsync(const boost::filesystem::path &path, int flags, fuse_file_info *fileinfo); + int opendir(const boost::filesystem::path &path, fuse_file_info *fileinfo); + int readdir(const boost::filesystem::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo); + int releasedir(const boost::filesystem::path &path, fuse_file_info *fileinfo); + int fsyncdir(const boost::filesystem::path &path, int datasync, fuse_file_info *fileinfo); + void init(fuse_conn_info *conn); + void destroy(); + int access(const boost::filesystem::path &path, int mask); + int create(const boost::filesystem::path &path, mode_t mode, fuse_file_info *fileinfo); + +private: + static void _logException(const std::exception &e); + static void _logUnknownException(); + + Filesystem *_fs; + boost::filesystem::path _mountdir; + bool _running; + + DISALLOW_COPY_AND_ASSIGN(Fuse); +}; +} +} + +#endif diff --git a/src/fspp/fuse/FuseErrnoException.h b/src/fspp/fuse/FuseErrnoException.h new file mode 100644 index 00000000..c100a8c4 --- /dev/null +++ b/src/fspp/fuse/FuseErrnoException.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_FSPP_FUSE_FUSEERRNOEXCEPTION_H_ +#define MESSMER_FSPP_FUSE_FUSEERRNOEXCEPTION_H_ + +#include +#include +#include + +namespace fspp { +namespace fuse{ + +class FuseErrnoException final: public std::runtime_error { +public: + explicit FuseErrnoException(int errno_); + + int getErrno() const; +private: + int _errno; +}; + +inline void CHECK_RETVAL(int retval) { + if (retval < 0) { + throw FuseErrnoException(errno); + } +} + +inline FuseErrnoException::FuseErrnoException(int errno_) + :runtime_error(strerror(errno_)), _errno(errno_) { + ASSERT(_errno != 0, "Errno shouldn't be zero"); +} + +inline int FuseErrnoException::getErrno() const { + return _errno; +} + +} +} + +#endif diff --git a/src/fspp/fuse/params.h b/src/fspp/fuse/params.h new file mode 100644 index 00000000..9903ac82 --- /dev/null +++ b/src/fspp/fuse/params.h @@ -0,0 +1,8 @@ +#pragma once +#ifndef MESSMER_FSPP_FUSE_PARAMS_H_ +#define MESSMER_FSPP_FUSE_PARAMS_H_ + +#define FUSE_USE_VERSION 26 +#include + +#endif diff --git a/src/fspp/impl/FilesystemImpl.cpp b/src/fspp/impl/FilesystemImpl.cpp new file mode 100644 index 00000000..1be269de --- /dev/null +++ b/src/fspp/impl/FilesystemImpl.cpp @@ -0,0 +1,321 @@ +#include "FilesystemImpl.h" + +#include +#include "../fs_interface/Device.h" +#include "../fs_interface/Dir.h" +#include "../fs_interface/Symlink.h" + +#include "../fuse/FuseErrnoException.h" +#include "../fs_interface/File.h" + +#include +#include +#include + +using namespace fspp; +using cpputils::dynamic_pointer_move; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::vector; +using std::string; +using boost::none; + +namespace bf = boost::filesystem; +using namespace cpputils::logging; + +#ifdef FSPP_PROFILE +#include "Profiler.h" +#include +#include +#define PROFILE(name) Profiler profiler_##name(&name); +#else +#define PROFILE(name) +#endif + +FilesystemImpl::FilesystemImpl(Device *device) + : +#ifdef FSPP_PROFILE + _loadFileNanosec(0), _loadDirNanosec(0), _loadSymlinkNanosec(0), _openFileNanosec(0), _flushNanosec(0), + _closeFileNanosec(0), _lstatNanosec(0), _fstatNanosec(0), _chmodNanosec(0), _chownNanosec(0), _truncateNanosec(0), + _ftruncateNanosec(0), _readNanosec(0), _writeNanosec(0), _fsyncNanosec(0), _fdatasyncNanosec(0), _accessNanosec(0), + _createAndOpenFileNanosec(0), _createAndOpenFileNanosec_withoutLoading(0), _mkdirNanosec(0), + _mkdirNanosec_withoutLoading(0), _rmdirNanosec(0), _rmdirNanosec_withoutLoading(0), _unlinkNanosec(0), + _unlinkNanosec_withoutLoading(0), _renameNanosec(0), _readDirNanosec(0), _readDirNanosec_withoutLoading(0), + _utimensNanosec(0), _statfsNanosec(0), _createSymlinkNanosec(0), _createSymlinkNanosec_withoutLoading(0), + _readSymlinkNanosec(0), _readSymlinkNanosec_withoutLoading(0), +#endif + _device(device), _open_files() +{ +} + +FilesystemImpl::~FilesystemImpl() { +#ifdef FSPP_PROFILE + std::ostringstream profilerInformation; + profilerInformation << "Profiler Information\n" + << std::fixed << std::setprecision(6) + << std::setw(40) << "LoadFile: " << static_cast(_loadFileNanosec)/1000000000 << "\n" + << std::setw(40) << "LoadDir: " << static_cast(_loadDirNanosec)/1000000000 << "\n" + << std::setw(40) << "LoadSymlink: " << static_cast(_loadSymlinkNanosec)/1000000000 << "\n" + << std::setw(40) << "OpenFile: " << static_cast(_openFileNanosec)/1000000000 << "\n" + << std::setw(40) << "Flush: " << static_cast(_flushNanosec)/1000000000 << "\n" + << std::setw(40) << "CloseFile: " << static_cast(_closeFileNanosec)/1000000000 << "\n" + << std::setw(40) << "Lstat: " << static_cast(_lstatNanosec)/1000000000 << "\n" + << std::setw(40) << "Fstat: " << static_cast(_fstatNanosec)/1000000000 << "\n" + << std::setw(40) << "Chmod: " << static_cast(_chmodNanosec)/1000000000 << "\n" + << std::setw(40) << "Chown: " << static_cast(_chownNanosec)/1000000000 << "\n" + << std::setw(40) << "Truncate: " << static_cast(_truncateNanosec)/1000000000 << "\n" + << std::setw(40) << "Ftruncate: " << static_cast(_ftruncateNanosec)/1000000000 << "\n" + << std::setw(40) << "Read: " << static_cast(_readNanosec)/1000000000 << "\n" + << std::setw(40) << "Write: " << static_cast(_writeNanosec)/1000000000 << "\n" + << std::setw(40) << "Fsync: " << static_cast(_fsyncNanosec)/1000000000 << "\n" + << std::setw(40) << "Fdatasync: " << static_cast(_fdatasyncNanosec)/1000000000 << "\n" + << std::setw(40) << "Access: " << static_cast(_accessNanosec)/1000000000 << "\n" + << std::setw(40) << "CreateAndOpenFile: " << static_cast(_createAndOpenFileNanosec)/1000000000 << "\n" + << std::setw(40) << "CreateAndOpenFile (without loading): " << static_cast(_createAndOpenFileNanosec_withoutLoading)/1000000000 << "\n" + << std::setw(40) << "Mkdir: " << static_cast(_mkdirNanosec)/1000000000 << "\n" + << std::setw(40) << "Mkdir (without loading): " << static_cast(_mkdirNanosec_withoutLoading)/1000000000 << "\n" + << std::setw(40) << "Rmdir: " << static_cast(_rmdirNanosec)/1000000000 << "\n" + << std::setw(40) << "Rmdir (without loading): " << static_cast(_rmdirNanosec_withoutLoading)/1000000000 << "\n" + << std::setw(40) << "Unlink: " << static_cast(_unlinkNanosec)/1000000000 << "\n" + << std::setw(40) << "Unlink (without loading): " << static_cast(_unlinkNanosec_withoutLoading)/1000000000 << "\n" + << std::setw(40) << "Rename: " << static_cast(_renameNanosec)/1000000000 << "\n" + << std::setw(40) << "ReadDir: " << static_cast(_readDirNanosec)/1000000000 << "\n" + << std::setw(40) << "ReadDir (without loading): " << static_cast(_readDirNanosec_withoutLoading)/1000000000 << "\n" + << std::setw(40) << "Utimens: " << static_cast(_utimensNanosec)/1000000000 << "\n" + << std::setw(40) << "Statfs: " << static_cast(_statfsNanosec)/1000000000 << "\n" + << std::setw(40) << "CreateSymlink: " << static_cast(_createSymlinkNanosec)/1000000000 << "\n" + << std::setw(40) << "CreateSymlink (without loading): " << static_cast(_createSymlinkNanosec_withoutLoading)/1000000000 << "\n" + << std::setw(40) << "ReadSymlink: " << static_cast(_readSymlinkNanosec)/1000000000 << "\n" + << std::setw(40) << "ReadSymlink (without loading): " << static_cast(_readSymlinkNanosec_withoutLoading)/1000000000 << "\n"; + LOG(INFO) << profilerInformation.str(); +#endif +} + +unique_ref FilesystemImpl::LoadFile(const bf::path &path) { + PROFILE(_loadFileNanosec); + auto node = _device->Load(path); + if (node == none) { + throw fuse::FuseErrnoException(EIO); + } + auto file = dynamic_pointer_move(*node); + if (file == none) { + throw fuse::FuseErrnoException(EISDIR); + } + return std::move(*file); +} + +unique_ref FilesystemImpl::LoadDir(const bf::path &path) { + PROFILE(_loadDirNanosec); + auto node = _device->Load(path); + if (node == none) { + throw fuse::FuseErrnoException(EIO); + } + auto dir = dynamic_pointer_move(*node); + if (dir == none) { + throw fuse::FuseErrnoException(ENOTDIR); + } + return std::move(*dir); +} + +unique_ref FilesystemImpl::LoadSymlink(const bf::path &path) { + PROFILE(_loadSymlinkNanosec); + auto node = _device->Load(path); + if (node == none) { + throw fuse::FuseErrnoException(EIO); + } + auto lnk = dynamic_pointer_move(*node); + if (lnk == none) { + throw fuse::FuseErrnoException(ENOTDIR); + } + return std::move(*lnk); +} + +unique_ref FilesystemImpl::LoadFileOrSymlink(const bf::path &path) { + PROFILE(_loadFileOrSymlinkNanosec); + auto node = _device->Load(path); + if (node == none) { + throw fuse::FuseErrnoException(EIO); + } + auto file = dynamic_pointer_move(*node); + if (file != none) { + return std::move(*file); + } + + auto symlink = dynamic_pointer_move(*node); + if (symlink != none) { + return std::move(*symlink); + } + + throw fuse::FuseErrnoException(EISDIR); +} + +int FilesystemImpl::openFile(const bf::path &path, int flags) { + auto file = LoadFile(path); + return openFile(*file, flags); +} + +int FilesystemImpl::openFile(const File &file, int flags) { + PROFILE(_openFileNanosec); + return _open_files.open(file.open(flags)); +} + +void FilesystemImpl::flush(int descriptor) { + PROFILE(_flushNanosec); + _open_files.get(descriptor)->flush(); +} + +void FilesystemImpl::closeFile(int descriptor) { + PROFILE(_closeFileNanosec); + _open_files.close(descriptor); +} + +void FilesystemImpl::lstat(const bf::path &path, struct ::stat *stbuf) { + PROFILE(_lstatNanosec); + auto node = _device->Load(path); + if(node == none) { + throw fuse::FuseErrnoException(ENOENT); + } else { + (*node)->stat(stbuf); + } +} + +void FilesystemImpl::fstat(int descriptor, struct ::stat *stbuf) { + PROFILE(_fstatNanosec); + _open_files.get(descriptor)->stat(stbuf); +} + +void FilesystemImpl::chmod(const boost::filesystem::path &path, mode_t mode) { + PROFILE(_chmodNanosec); + auto node = _device->Load(path); + if(node == none) { + throw fuse::FuseErrnoException(ENOENT); + } else { + (*node)->chmod(mode); + } +} + +void FilesystemImpl::chown(const boost::filesystem::path &path, uid_t uid, gid_t gid) { + PROFILE(_chownNanosec); + auto node = _device->Load(path); + if(node == none) { + throw fuse::FuseErrnoException(ENOENT); + } else { + (*node)->chown(uid, gid); + } +} + +void FilesystemImpl::truncate(const bf::path &path, off_t size) { + PROFILE(_truncateNanosec); + LoadFile(path)->truncate(size); +} + +void FilesystemImpl::ftruncate(int descriptor, off_t size) { + PROFILE(_ftruncateNanosec); + _open_files.get(descriptor)->truncate(size); +} + +size_t FilesystemImpl::read(int descriptor, void *buf, size_t count, off_t offset) { + PROFILE(_readNanosec); + return _open_files.get(descriptor)->read(buf, count, offset); +} + +void FilesystemImpl::write(int descriptor, const void *buf, size_t count, off_t offset) { + PROFILE(_writeNanosec); + _open_files.get(descriptor)->write(buf, count, offset); +} + +void FilesystemImpl::fsync(int descriptor) { + PROFILE(_fsyncNanosec); + _open_files.get(descriptor)->fsync(); +} + +void FilesystemImpl::fdatasync(int descriptor) { + PROFILE(_fdatasyncNanosec); + _open_files.get(descriptor)->fdatasync(); +} + +void FilesystemImpl::access(const bf::path &path, int mask) { + PROFILE(_accessNanosec); + auto node = _device->Load(path); + if(node == none) { + throw fuse::FuseErrnoException(ENOENT); + } else { + (*node)->access(mask); + } +} + +int FilesystemImpl::createAndOpenFile(const bf::path &path, mode_t mode, uid_t uid, gid_t gid) { + PROFILE(_createAndOpenFileNanosec); + auto dir = LoadDir(path.parent_path()); + PROFILE(_createAndOpenFileNanosec_withoutLoading); + auto file = dir->createAndOpenFile(path.filename().native(), mode, uid, gid); + return _open_files.open(std::move(file)); +} + +void FilesystemImpl::mkdir(const bf::path &path, mode_t mode, uid_t uid, gid_t gid) { + PROFILE(_mkdirNanosec); + auto dir = LoadDir(path.parent_path()); + PROFILE(_mkdirNanosec_withoutLoading); + dir->createDir(path.filename().native(), mode, uid, gid); +} + +void FilesystemImpl::rmdir(const bf::path &path) { + PROFILE(_rmdirNanosec); + auto dir = LoadDir(path); + PROFILE(_rmdirNanosec_withoutLoading); + dir->remove(); +} + +void FilesystemImpl::unlink(const bf::path &path) { + PROFILE(_unlinkNanosec); + auto node = LoadFileOrSymlink(path); + PROFILE(_unlinkNanosec_withoutLoading); + node->remove(); +} + +void FilesystemImpl::rename(const bf::path &from, const bf::path &to) { + PROFILE(_renameNanosec); + auto node = _device->Load(from); + if(node == none) { + throw fuse::FuseErrnoException(ENOENT); + } else { + (*node)->rename(to); + } +} + +unique_ref> FilesystemImpl::readDir(const bf::path &path) { + PROFILE(_readDirNanosec); + auto dir = LoadDir(path); + PROFILE(_readDirNanosec_withoutLoading); + return dir->children(); +} + +void FilesystemImpl::utimens(const bf::path &path, timespec lastAccessTime, timespec lastModificationTime) { + PROFILE(_utimensNanosec); + auto node = _device->Load(path); + if(node == none) { + throw fuse::FuseErrnoException(ENOENT); + } else { + (*node)->utimens(lastAccessTime, lastModificationTime); + } +} + +void FilesystemImpl::statfs(const bf::path &path, struct statvfs *fsstat) { + PROFILE(_statfsNanosec); + _device->statfs(path, fsstat); +} + +void FilesystemImpl::createSymlink(const bf::path &to, const bf::path &from, uid_t uid, gid_t gid) { + PROFILE(_createSymlinkNanosec); + auto parent = LoadDir(from.parent_path()); + PROFILE(_createSymlinkNanosec_withoutLoading); + parent->createSymlink(from.filename().native(), to, uid, gid); +} + +void FilesystemImpl::readSymlink(const bf::path &path, char *buf, size_t size) { + PROFILE(_readSymlinkNanosec); + string target = LoadSymlink(path)->target().native(); + PROFILE(_readSymlinkNanosec_withoutLoading); + std::memcpy(buf, target.c_str(), std::min(target.size()+1, size)); + buf[size-1] = '\0'; +} diff --git a/src/fspp/impl/FilesystemImpl.h b/src/fspp/impl/FilesystemImpl.h new file mode 100644 index 00000000..8af1fab1 --- /dev/null +++ b/src/fspp/impl/FilesystemImpl.h @@ -0,0 +1,105 @@ +#pragma once +#ifndef MESSMER_FSPP_IMPL_FILESYSTEMIMPL_H_ +#define MESSMER_FSPP_IMPL_FILESYSTEMIMPL_H_ + +#include "FuseOpenFileList.h" +#include "../fuse/Filesystem.h" + +#include +#include + +//Remove this line if you don't want profiling +//#define FSPP_PROFILE 1 + +//TODO Test + +namespace fspp { +class Node; +class File; +class Symlink; +class OpenFile; + +class FilesystemImpl final: public fuse::Filesystem { +public: + explicit FilesystemImpl(Device *device); + virtual ~FilesystemImpl(); + + int openFile(const boost::filesystem::path &path, int flags) override; + void flush(int descriptor) override; + void closeFile(int descriptor) override; + void lstat(const boost::filesystem::path &path, struct ::stat *stbuf) override; + void fstat(int descriptor, struct ::stat *stbuf) override; + void chmod(const boost::filesystem::path &path, mode_t mode) override; + void chown(const boost::filesystem::path &path, uid_t uid, gid_t gid) override; + void truncate(const boost::filesystem::path &path, off_t size) override; + void ftruncate(int descriptor, off_t size) override; + size_t read(int descriptor, void *buf, size_t count, off_t offset) override; + void write(int descriptor, const void *buf, size_t count, off_t offset) override; + void fsync(int descriptor) override; + void fdatasync(int descriptor) override; + void access(const boost::filesystem::path &path, int mask) override; + int createAndOpenFile(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) override; + void mkdir(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) override; + void rmdir(const boost::filesystem::path &path) override; + void unlink(const boost::filesystem::path &path) override; + void rename(const boost::filesystem::path &from, const boost::filesystem::path &to) override; + cpputils::unique_ref> readDir(const boost::filesystem::path &path) override; + void utimens(const boost::filesystem::path &path, timespec lastAccessTime, timespec lastModificationTime) override; + void statfs(const boost::filesystem::path &path, struct statvfs *fsstat) override; + void createSymlink(const boost::filesystem::path &to, const boost::filesystem::path &from, uid_t uid, gid_t gid) override; + void readSymlink(const boost::filesystem::path &path, char *buf, size_t size) override; + +private: + cpputils::unique_ref LoadFile(const boost::filesystem::path &path); + cpputils::unique_ref LoadDir(const boost::filesystem::path &path); + cpputils::unique_ref LoadSymlink(const boost::filesystem::path &path); + cpputils::unique_ref LoadFileOrSymlink(const boost::filesystem::path &path); + int openFile(const File &file, int flags); + +#ifdef FSPP_PROFILE + std::atomic _loadFileNanosec; + std::atomic _loadDirNanosec; + std::atomic _loadSymlinkNanosec; + std::atomic _loadFileOrSymlinkNanosec; + std::atomic _openFileNanosec; + std::atomic _flushNanosec; + std::atomic _closeFileNanosec; + std::atomic _lstatNanosec; + std::atomic _fstatNanosec; + std::atomic _chmodNanosec; + std::atomic _chownNanosec; + std::atomic _truncateNanosec; + std::atomic _ftruncateNanosec; + std::atomic _readNanosec; + std::atomic _writeNanosec; + std::atomic _fsyncNanosec; + std::atomic _fdatasyncNanosec; + std::atomic _accessNanosec; + std::atomic _createAndOpenFileNanosec; + std::atomic _createAndOpenFileNanosec_withoutLoading; + std::atomic _mkdirNanosec; + std::atomic _mkdirNanosec_withoutLoading; + std::atomic _rmdirNanosec; + std::atomic _rmdirNanosec_withoutLoading; + std::atomic _unlinkNanosec; + std::atomic _unlinkNanosec_withoutLoading; + std::atomic _renameNanosec; + std::atomic _readDirNanosec; + std::atomic _readDirNanosec_withoutLoading; + std::atomic _utimensNanosec; + std::atomic _statfsNanosec; + std::atomic _createSymlinkNanosec; + std::atomic _createSymlinkNanosec_withoutLoading; + std::atomic _readSymlinkNanosec; + std::atomic _readSymlinkNanosec_withoutLoading; +#endif + + Device *_device; + FuseOpenFileList _open_files; + + DISALLOW_COPY_AND_ASSIGN(FilesystemImpl); +}; + +} + +#endif diff --git a/src/fspp/impl/FuseOpenFileList.h b/src/fspp/impl/FuseOpenFileList.h new file mode 100644 index 00000000..ec1d1d18 --- /dev/null +++ b/src/fspp/impl/FuseOpenFileList.h @@ -0,0 +1,49 @@ +#pragma once +#ifndef MESSMER_FSPP_IMPL_FUSEOPENFILELIST_H_ +#define MESSMER_FSPP_IMPL_FUSEOPENFILELIST_H_ + +#include "../fs_interface/File.h" +#include "../fs_interface/OpenFile.h" +#include +#include "IdList.h" + +namespace fspp { + +class FuseOpenFileList final { +public: + FuseOpenFileList(); + ~FuseOpenFileList(); + + int open(cpputils::unique_ref file); + OpenFile *get(int descriptor); + void close(int descriptor); + +private: + IdList _open_files; + + DISALLOW_COPY_AND_ASSIGN(FuseOpenFileList); +}; + +inline FuseOpenFileList::FuseOpenFileList() + :_open_files() { +} + +inline FuseOpenFileList::~FuseOpenFileList() { +} + +inline int FuseOpenFileList::open(cpputils::unique_ref file) { + return _open_files.add(std::move(file)); +} + +inline OpenFile *FuseOpenFileList::get(int descriptor) { + return _open_files.get(descriptor); +} + +inline void FuseOpenFileList::close(int descriptor) { + //The destructor of the stored FuseOpenFile closes the file + _open_files.remove(descriptor); +} + +} + +#endif diff --git a/src/fspp/impl/IdList.h b/src/fspp/impl/IdList.h new file mode 100644 index 00000000..41c9308e --- /dev/null +++ b/src/fspp/impl/IdList.h @@ -0,0 +1,72 @@ +#pragma once +#ifndef MESSMER_FSPP_IMPL_IDLIST_H_ +#define MESSMER_FSPP_IMPL_IDLIST_H_ + +#include +#include +#include +#include + +namespace fspp { + +template +class IdList final { +public: + IdList(); + virtual ~IdList(); + + int add(cpputils::unique_ref entry); + Entry *get(int id); + const Entry *get(int id) const; + void remove(int id); +private: + std::map> _entries; + int _id_counter; + mutable std::mutex _mutex; + + DISALLOW_COPY_AND_ASSIGN(IdList); +}; + +template +IdList::IdList() + : _entries(), _id_counter(0), _mutex() { +} + +template +IdList::~IdList() { +} + +template +int IdList::add(cpputils::unique_ref entry) { + std::lock_guard lock(_mutex); + //TODO Reuse IDs (ids = descriptors) + int new_id = ++_id_counter; + _entries.insert(std::make_pair(new_id, std::move(entry))); + return new_id; +} + +template +Entry *IdList::get(int id) { + return const_cast(const_cast*>(this)->get(id)); +} + +template +const Entry *IdList::get(int id) const { + std::lock_guard lock(_mutex); + const Entry *result = _entries.at(id).get(); + return result; +} + +template +void IdList::remove(int id) { + std::lock_guard lock(_mutex); + auto found_iter = _entries.find(id); + if (found_iter == _entries.end()) { + throw std::out_of_range("Called IdList::remove() with an invalid ID"); + } + _entries.erase(found_iter); +} + +} + +#endif diff --git a/src/fspp/impl/Profiler.cpp b/src/fspp/impl/Profiler.cpp new file mode 100644 index 00000000..ebab91c1 --- /dev/null +++ b/src/fspp/impl/Profiler.cpp @@ -0,0 +1 @@ +#include "Profiler.h" diff --git a/src/fspp/impl/Profiler.h b/src/fspp/impl/Profiler.h new file mode 100644 index 00000000..66282714 --- /dev/null +++ b/src/fspp/impl/Profiler.h @@ -0,0 +1,32 @@ +#pragma once +#ifndef MESSMER_FSPP_IMPL_PROFILER_H +#define MESSMER_FSPP_IMPL_PROFILER_H + +#include +#include +#include + +namespace fspp { + class Profiler final { + public: + Profiler(std::atomic_uint_fast64_t *targetForAddingNanosec); + ~Profiler(); + + private: + std::atomic_uint_fast64_t *_targetForAddingNanosec; + std::chrono::high_resolution_clock::time_point _beginTime; + + DISALLOW_COPY_AND_ASSIGN(Profiler); + }; + + inline Profiler::Profiler(std::atomic_uint_fast64_t *targetForAddingNanosec) + : _targetForAddingNanosec(targetForAddingNanosec), _beginTime(std::chrono::high_resolution_clock::now()) { + } + + inline Profiler::~Profiler() { + uint64_t timeDiff = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _beginTime).count(); + *_targetForAddingNanosec += timeDiff; + } +} + +#endif diff --git a/src/parallelaccessstore/ParallelAccessBaseStore.h b/src/parallelaccessstore/ParallelAccessBaseStore.h new file mode 100644 index 00000000..4a215fb4 --- /dev/null +++ b/src/parallelaccessstore/ParallelAccessBaseStore.h @@ -0,0 +1,20 @@ +#pragma once +#ifndef MESSMER_PARALLELACCESSSTORE_PARALLELACCESSBASESTORE_H_ +#define MESSMER_PARALLELACCESSSTORE_PARALLELACCESSBASESTORE_H_ + +#include +#include + +namespace parallelaccessstore { + +template +class ParallelAccessBaseStore { +public: + virtual ~ParallelAccessBaseStore() {} + virtual boost::optional> loadFromBaseStore(const Key &key) = 0; + virtual void removeFromBaseStore(cpputils::unique_ref block) = 0; +}; + +} + +#endif diff --git a/src/parallelaccessstore/ParallelAccessStore.h b/src/parallelaccessstore/ParallelAccessStore.h new file mode 100644 index 00000000..28946e8d --- /dev/null +++ b/src/parallelaccessstore/ParallelAccessStore.h @@ -0,0 +1,204 @@ +#pragma once +#ifndef MESSMER_PARALLELACCESSSTORE_PARALLELACCESSSTORE_H_ +#define MESSMER_PARALLELACCESSSTORE_PARALLELACCESSSTORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ParallelAccessBaseStore.h" +#include + + +//TODO Refactor +//TODO Test cases + +namespace parallelaccessstore { + +template +class ParallelAccessStore final { +public: + explicit ParallelAccessStore(cpputils::unique_ref> baseStore); + ~ParallelAccessStore() { + ASSERT(_openResources.size() == 0, "Still resources open when trying to destruct"); + ASSERT(_resourcesToRemove.size() == 0, "Still resources to remove when trying to destruct"); + }; + + class ResourceRefBase { + public: + //TODO Better way to initialize + ResourceRefBase(): _parallelAccessStore(nullptr), _key(Key::Null()) {} + void init(ParallelAccessStore *parallelAccessStore, const Key &key) { + _parallelAccessStore = parallelAccessStore; + _key = key; + } + virtual ~ResourceRefBase() { + _parallelAccessStore->release(_key); + } + private: + ParallelAccessStore *_parallelAccessStore; + //TODO We're storing Key twice (here and in the base resource). Rather use getKey() on the base resource if possible somehow. + Key _key; + + DISALLOW_COPY_AND_ASSIGN(ResourceRefBase); + }; + + bool isOpened(const Key &key) const; + cpputils::unique_ref add(const Key &key, cpputils::unique_ref resource); + template + cpputils::unique_ref add(const Key &key, cpputils::unique_ref resource, std::function(Resource*)> createResourceRef); + boost::optional> load(const Key &key); + boost::optional> load(const Key &key, std::function(Resource*)> createResourceRef); + void remove(const Key &key, cpputils::unique_ref block); + +private: + class OpenResource final { + public: + OpenResource(cpputils::unique_ref resource): _resource(std::move(resource)), _refCount(0) {} + OpenResource(OpenResource &&rhs) = default; + + Resource *getReference() { + ++_refCount; + return _resource.get(); + } + + void releaseReference() { + --_refCount; + } + + bool refCountIsZero() const { + return 0 == _refCount; + } + + cpputils::unique_ref moveResourceOut() { + return std::move(_resource); + } + private: + cpputils::unique_ref _resource; + uint32_t _refCount; + + DISALLOW_COPY_AND_ASSIGN(OpenResource); + }; + + mutable std::mutex _mutex; + cpputils::unique_ref> _baseStore; + + std::unordered_map _openResources; + std::map>> _resourcesToRemove; + + template + cpputils::unique_ref _add(const Key &key, cpputils::unique_ref resource, std::function(Resource*)> createResourceRef); + + void release(const Key &key); + friend class CachedResource; + + DISALLOW_COPY_AND_ASSIGN(ParallelAccessStore); +}; + +template +ParallelAccessStore::ParallelAccessStore(cpputils::unique_ref> baseStore) + : _mutex(), + _baseStore(std::move(baseStore)), + _openResources(), + _resourcesToRemove() { + static_assert(std::is_base_of::value, "ResourceRef must inherit from ResourceRefBase"); +} + +template +bool ParallelAccessStore::isOpened(const Key &key) const { + std::lock_guard lock(_mutex); + return _openResources.find(key) != _openResources.end(); +}; + +template +cpputils::unique_ref ParallelAccessStore::add(const Key &key, cpputils::unique_ref resource) { + return add(key, std::move(resource), [] (Resource *resource) { + return cpputils::make_unique_ref(resource); + }); +} + +template +template +cpputils::unique_ref ParallelAccessStore::add(const Key &key, cpputils::unique_ref resource, std::function(Resource*)> createResourceRef) { + static_assert(std::is_base_of::value, "Wrong ResourceRef type"); + std::lock_guard lock(_mutex); + return _add(key, std::move(resource), createResourceRef); +} + +template +template +cpputils::unique_ref ParallelAccessStore::_add(const Key &key, cpputils::unique_ref resource, std::function(Resource*)> createResourceRef) { + static_assert(std::is_base_of::value, "Wrong ResourceRef type"); + auto insertResult = _openResources.emplace(key, std::move(resource)); + ASSERT(true == insertResult.second, "Inserting failed. Already exists."); + auto resourceRef = createResourceRef(insertResult.first->second.getReference()); + resourceRef->init(this, key); + return resourceRef; +} + +template +boost::optional> ParallelAccessStore::load(const Key &key) { + return load(key, [] (Resource *res) { + return cpputils::make_unique_ref(res); + }); +}; + +template +boost::optional> ParallelAccessStore::load(const Key &key, std::function(Resource*)> createResourceRef) { + //TODO This lock doesn't allow loading different blocks in parallel. Can we only lock the requested key? + std::lock_guard lock(_mutex); + auto found = _openResources.find(key); + if (found == _openResources.end()) { + auto resource = _baseStore->loadFromBaseStore(key); + if (resource == boost::none) { + return boost::none; + } + return _add(key, std::move(*resource), createResourceRef); + } else { + auto resourceRef = createResourceRef(found->second.getReference()); + resourceRef->init(this, key); + return std::move(resourceRef); + } +} + +template +void ParallelAccessStore::remove(const Key &key, cpputils::unique_ref resource) { + std::future> resourceToRemoveFuture; + { + std::lock_guard lock(_mutex); // TODO Lock needed for _resourcesToRemove? + auto insertResult = _resourcesToRemove.emplace(key, std::promise < cpputils::unique_ref < Resource >> ()); + ASSERT(true == insertResult.second, "Inserting failed"); + resourceToRemoveFuture = insertResult.first->second.get_future(); + } + cpputils::destruct(std::move(resource)); + //Wait for last resource user to release it + auto resourceToRemove = resourceToRemoveFuture.get(); + + std::lock_guard lock(_mutex); // TODO Just added this as a precaution on a whim, but I seriously need to rethink locking here. + _resourcesToRemove.erase(key); //TODO Is this erase causing a race condition? + + _baseStore->removeFromBaseStore(std::move(resourceToRemove)); +} + +template +void ParallelAccessStore::release(const Key &key) { + std::lock_guard lock(_mutex); + auto found = _openResources.find(key); + ASSERT(found != _openResources.end(), "Didn't find key"); + found->second.releaseReference(); + if (found->second.refCountIsZero()) { + auto foundToRemove = _resourcesToRemove.find(key); + if (foundToRemove != _resourcesToRemove.end()) { + foundToRemove->second.set_value(found->second.moveResourceOut()); + } + _openResources.erase(found); + } +} + +} + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..3c7840f2 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,12 @@ +include(CTest) + +if (BUILD_TESTING) + include_directories(../src) + + add_subdirectory(cpp-utils) + add_subdirectory(fspp) + add_subdirectory(parallelaccessstore) + add_subdirectory(blockstore) + add_subdirectory(blobstore) + add_subdirectory(cryfs) +endif(BUILD_TESTING) diff --git a/test/blobstore/CMakeLists.txt b/test/blobstore/CMakeLists.txt new file mode 100644 index 00000000..93ff3a70 --- /dev/null +++ b/test/blobstore/CMakeLists.txt @@ -0,0 +1,33 @@ +project (blobstore-test) + +set(SOURCES + implementations/onblocks/utils/MaxZeroSubtractionTest.cpp + implementations/onblocks/utils/CeilDivisionTest.cpp + implementations/onblocks/utils/IntPowTest.cpp + implementations/onblocks/utils/CeilLogTest.cpp + implementations/onblocks/testutils/BlobStoreTest.cpp + implementations/onblocks/BlobStoreTest.cpp + implementations/onblocks/datanodestore/DataLeafNodeTest.cpp + implementations/onblocks/datanodestore/DataInnerNodeTest.cpp + implementations/onblocks/datanodestore/DataNodeViewTest.cpp + implementations/onblocks/datanodestore/DataNodeStoreTest.cpp + implementations/onblocks/datatreestore/testutils/DataTreeTest.cpp + implementations/onblocks/datatreestore/impl/GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest.cpp + implementations/onblocks/datatreestore/impl/GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest.cpp + implementations/onblocks/datatreestore/DataTreeTest_ResizeByTraversing.cpp + implementations/onblocks/datatreestore/DataTreeTest_NumStoredBytes.cpp + implementations/onblocks/datatreestore/DataTreeTest_ResizeNumBytes.cpp + implementations/onblocks/datatreestore/DataTreeStoreTest.cpp + implementations/onblocks/datatreestore/DataTreeTest_TraverseLeaves.cpp + implementations/onblocks/BlobSizeTest.cpp + implementations/onblocks/BlobReadWriteTest.cpp + implementations/onblocks/BigBlobsTest.cpp + +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} googletest blobstore) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/blobstore/implementations/onblocks/BigBlobsTest.cpp b/test/blobstore/implementations/onblocks/BigBlobsTest.cpp new file mode 100644 index 00000000..2a04b364 --- /dev/null +++ b/test/blobstore/implementations/onblocks/BigBlobsTest.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include +#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h" +#include "blobstore/implementations/onblocks/BlobOnBlocks.h" + +using namespace blobstore; +using namespace blobstore::onblocks; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using cpputils::DataFixture; +using cpputils::Data; +using blockstore::inmemory::InMemoryBlockStore; +using blockstore::compressing::CompressingBlockStore; +using blockstore::compressing::RunLengthEncoding; + +// Test cases, ensuring that big blobs (>4G) work (i.e. testing that we don't use any 32bit variables for blob size, etc.) +class BigBlobsTest : public ::testing::Test { +public: + static constexpr size_t BLOCKSIZE = 32 * 1024; + static constexpr uint64_t SMALL_BLOB_SIZE = UINT64_C(1024)*1024*1024*3.9; // 3.9 GB (<4GB) + static constexpr uint64_t LARGE_BLOB_SIZE = UINT64_C(1024)*1024*1024*4.1; // 4.1 GB (>4GB) + + static constexpr uint64_t max_uint_32 = std::numeric_limits::max(); + static_assert(SMALL_BLOB_SIZE < max_uint_32, "LARGE_BLOB_SIZE should need 64bit or the test case is mute"); + static_assert(LARGE_BLOB_SIZE > max_uint_32, "LARGE_BLOB_SIZE should need 64bit or the test case is mute"); + + unique_ref blobStore = make_unique_ref(make_unique_ref>(make_unique_ref()), BLOCKSIZE); + unique_ref blob = blobStore->create(); +}; + +constexpr size_t BigBlobsTest::BLOCKSIZE; +constexpr uint64_t BigBlobsTest::SMALL_BLOB_SIZE; +constexpr uint64_t BigBlobsTest::LARGE_BLOB_SIZE; + +TEST_F(BigBlobsTest, Resize) { + //These operations are in one test case and not in many small ones, because it takes quite long to create a >4GB blob. + + //Resize to >4GB + blob->resize(LARGE_BLOB_SIZE); + EXPECT_EQ(LARGE_BLOB_SIZE, blob->size()); + + //Grow while >4GB + blob->resize(LARGE_BLOB_SIZE + 1024); + EXPECT_EQ(LARGE_BLOB_SIZE + 1024, blob->size()); + + //Shrink while >4GB + blob->resize(LARGE_BLOB_SIZE); + EXPECT_EQ(LARGE_BLOB_SIZE, blob->size()); + + //Shrink to <4GB + blob->resize(SMALL_BLOB_SIZE); + EXPECT_EQ(SMALL_BLOB_SIZE, blob->size()); + + //Grow to >4GB + blob->resize(LARGE_BLOB_SIZE); + EXPECT_EQ(LARGE_BLOB_SIZE, blob->size()); + + //Flush >4GB blob + blob->flush(); + + //Destruct >4GB blob + auto key = blob->key(); + cpputils::destruct(std::move(blob)); + + //Load >4GB blob + blob = blobStore->load(key).value(); + + //Remove >4GB blob + blobStore->remove(std::move(blob)); +} + +TEST_F(BigBlobsTest, GrowByWriting_Crossing4GBBorder) { + Data fixture = DataFixture::generate(2*(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE)); + blob->write(fixture.data(), SMALL_BLOB_SIZE, fixture.size()); + + EXPECT_EQ(LARGE_BLOB_SIZE+(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE), blob->size()); + + Data loaded(fixture.size()); + blob->read(loaded.data(), SMALL_BLOB_SIZE, loaded.size()); + EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size())); +} + +TEST_F(BigBlobsTest, GrowByWriting_Outside4GBBorder_StartingSizeZero) { + Data fixture = DataFixture::generate(1024); + blob->write(fixture.data(), LARGE_BLOB_SIZE, fixture.size()); + + EXPECT_EQ(LARGE_BLOB_SIZE+1024, blob->size()); + + Data loaded(fixture.size()); + blob->read(loaded.data(), LARGE_BLOB_SIZE, loaded.size()); + EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size())); +} + +TEST_F(BigBlobsTest, GrowByWriting_Outside4GBBorder_StartingSizeOutside4GBBorder) { + blob->resize(LARGE_BLOB_SIZE); + Data fixture = DataFixture::generate(1024); + blob->write(fixture.data(), LARGE_BLOB_SIZE+1024, fixture.size()); + + EXPECT_EQ(LARGE_BLOB_SIZE+2048, blob->size()); + + Data loaded(fixture.size()); + blob->read(loaded.data(), LARGE_BLOB_SIZE+1024, loaded.size()); + EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size())); +} + +TEST_F(BigBlobsTest, ReadWriteAfterGrown_Crossing4GBBorder) { + blob->resize(LARGE_BLOB_SIZE+(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE)+1024); + Data fixture = DataFixture::generate(2*(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE)); + blob->write(fixture.data(), SMALL_BLOB_SIZE, fixture.size()); + + EXPECT_EQ(LARGE_BLOB_SIZE+(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE)+1024, blob->size()); + + Data loaded(fixture.size()); + blob->read(loaded.data(), SMALL_BLOB_SIZE, loaded.size()); + EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size())); +} + +TEST_F(BigBlobsTest, ReadWriteAfterGrown_Outside4GBBorder) { + blob->resize(LARGE_BLOB_SIZE+2048); + Data fixture = DataFixture::generate(1024); + blob->write(fixture.data(), LARGE_BLOB_SIZE, fixture.size()); + + EXPECT_EQ(LARGE_BLOB_SIZE+2048, blob->size()); + + Data loaded(fixture.size()); + blob->read(loaded.data(), LARGE_BLOB_SIZE, loaded.size()); + EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size())); +} + +//TODO Test Blob::readAll (only on 64bit systems) diff --git a/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp b/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp new file mode 100644 index 00000000..e6459acb --- /dev/null +++ b/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp @@ -0,0 +1,166 @@ +#include "testutils/BlobStoreTest.h" +#include +#include +#include "blobstore/implementations/onblocks/datanodestore/DataNodeView.h" + +using cpputils::unique_ref; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace blobstore; +using blobstore::onblocks::datanodestore::DataNodeLayout; +using blockstore::Key; +using cpputils::Data; +using cpputils::DataFixture; + +class BlobReadWriteTest: public BlobStoreTest { +public: + static constexpr uint32_t LARGE_SIZE = 10 * 1024 * 1024; + static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES); + + BlobReadWriteTest() + :randomData(DataFixture::generate(LARGE_SIZE)), + blob(blobStore->create()) { + } + + Data readBlob(const Blob &blob) { + Data data(blob.size()); + blob.read(data.data(), 0, data.size()); + return data; + } + + template + void EXPECT_DATA_READS_AS(const DataClass &expected, const Blob &actual, uint64_t offset, uint64_t size) { + Data read(size); + actual.read(read.data(), offset, size); + EXPECT_EQ(0, std::memcmp(expected.data(), read.data(), size)); + } + + Data randomData; + unique_ref blob; +}; +constexpr uint32_t BlobReadWriteTest::LARGE_SIZE; +constexpr DataNodeLayout BlobReadWriteTest::LAYOUT; + +TEST_F(BlobReadWriteTest, WritingImmediatelyFlushes_SmallSize) { + blob->resize(5); + blob->write(randomData.data(), 0, 5); + auto loaded = loadBlob(blob->key()); + EXPECT_DATA_READS_AS(randomData, *loaded, 0, 5); +} + +TEST_F(BlobReadWriteTest, WritingImmediatelyFlushes_LargeSize) { + blob->resize(LARGE_SIZE); + blob->write(randomData.data(), 0, LARGE_SIZE); + auto loaded = loadBlob(blob->key()); + EXPECT_DATA_READS_AS(randomData, *loaded, 0, LARGE_SIZE); +} + +// Regression test for a strange bug we had +TEST_F(BlobReadWriteTest, WritingCloseTo16ByteLimitDoesntDestroySize) { + blob->resize(1); + blob->write(randomData.data(), 32776, 4); + EXPECT_EQ(32780u, blob->size()); +} + +struct DataRange { + size_t blobsize; + off_t offset; + size_t count; +}; +class BlobReadWriteDataTest: public BlobReadWriteTest, public WithParamInterface { +public: + Data foregroundData; + Data backgroundData; + + BlobReadWriteDataTest() + : foregroundData(DataFixture::generate(GetParam().count, 0)), + backgroundData(DataFixture::generate(GetParam().blobsize, 1)) { + } + + template + void EXPECT_DATA_READS_AS_OUTSIDE_OF(const DataClass &expected, const Blob &blob, off_t start, size_t count) { + Data begin(start); + Data end(GetParam().blobsize - count - start); + + std::memcpy(begin.data(), expected.data(), start); + std::memcpy(end.data(), (uint8_t*)expected.data()+start+count, end.size()); + + EXPECT_DATA_READS_AS(begin, blob, 0, start); + EXPECT_DATA_READS_AS(end, blob, start + count, end.size()); + } + + void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const Blob &blob, off_t start, size_t count) { + Data ZEROES(GetParam().blobsize); + ZEROES.FillWithZeroes(); + EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, blob, start, count); + } +}; +INSTANTIATE_TEST_CASE_P(BlobReadWriteDataTest, BlobReadWriteDataTest, Values( + //Blob with only one leaf + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf(), 0, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()}, // full size leaf, access beginning to end + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf(), 100, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-200}, // full size leaf, access middle to middle + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf(), 0, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100}, // full size leaf, access beginning to middle + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf(), 100, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100}, // full size leaf, access middle to end + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100, 0, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100}, // non-full size leaf, access beginning to end + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100, 100, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-300}, // non-full size leaf, access middle to middle + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100, 0, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-200}, // non-full size leaf, access beginning to middle + DataRange{BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-100, 100, BlobReadWriteDataTest::LAYOUT.maxBytesPerLeaf()-200}, // non-full size leaf, access middle to end + //Larger blob + DataRange{BlobReadWriteDataTest::LARGE_SIZE, 0, BlobReadWriteDataTest::LARGE_SIZE}, // access beginning to end + DataRange{BlobReadWriteDataTest::LARGE_SIZE, 100, BlobReadWriteDataTest::LARGE_SIZE-200}, // access middle first leaf to middle last leaf + DataRange{BlobReadWriteDataTest::LARGE_SIZE, 0, BlobReadWriteDataTest::LARGE_SIZE-100}, // access beginning to middle last leaf + DataRange{BlobReadWriteDataTest::LARGE_SIZE, 100, BlobReadWriteDataTest::LARGE_SIZE-100}, // access middle first leaf to end + DataRange{BlobReadWriteDataTest::LARGE_SIZE, BlobReadWriteDataTest::LARGE_SIZE*1/3, BlobReadWriteDataTest::LARGE_SIZE*1/3}, // access middle to middle + DataRange{BlobReadWriteDataTest::LARGE_SIZE, 0, BlobReadWriteDataTest::LARGE_SIZE*2/3}, // access beginning to middle + DataRange{BlobReadWriteDataTest::LARGE_SIZE, BlobReadWriteDataTest::LARGE_SIZE*1/3, BlobReadWriteDataTest::LARGE_SIZE*2/3} // access middle to end +)); + +TEST_P(BlobReadWriteDataTest, WritingDoesntChangeSize) { + blob->resize(GetParam().blobsize); + blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + EXPECT_EQ(GetParam().blobsize, blob->size()); +} + +TEST_P(BlobReadWriteDataTest, WriteAndReadImmediately) { + blob->resize(GetParam().blobsize); + blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + + EXPECT_DATA_READS_AS(this->foregroundData, *blob, GetParam().offset, GetParam().count); + EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*blob, GetParam().offset, GetParam().count); +} + +TEST_P(BlobReadWriteDataTest, WriteAndReadAfterLoading) { + blob->resize(GetParam().blobsize); + blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + auto loaded = loadBlob(blob->key()); + + EXPECT_DATA_READS_AS(this->foregroundData, *loaded, GetParam().offset, GetParam().count); + EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*loaded, GetParam().offset, GetParam().count); +} + +TEST_P(BlobReadWriteDataTest, OverwriteAndRead) { + blob->resize(GetParam().blobsize); + blob->write(this->backgroundData.data(), 0, GetParam().blobsize); + blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + EXPECT_DATA_READS_AS(this->foregroundData, *blob, GetParam().offset, GetParam().count); + EXPECT_DATA_READS_AS_OUTSIDE_OF(this->backgroundData, *blob, GetParam().offset, GetParam().count); +} + +TEST_P(BlobReadWriteDataTest, WriteWholeAndReadPart) { + blob->resize(GetParam().blobsize); + blob->write(this->backgroundData.data(), 0, GetParam().blobsize); + Data read(GetParam().count); + blob->read(read.data(), GetParam().offset, GetParam().count); + EXPECT_EQ(0, std::memcmp(read.data(), (uint8_t*)this->backgroundData.data()+GetParam().offset, GetParam().count)); +} + +TEST_P(BlobReadWriteDataTest, WritePartAndReadWhole) { + blob->resize(GetParam().blobsize); + blob->write(this->backgroundData.data(), 0, GetParam().blobsize); + blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + Data read = readBlob(*blob); + EXPECT_EQ(0, std::memcmp(read.data(), this->backgroundData.data(), GetParam().offset)); + EXPECT_EQ(0, std::memcmp((uint8_t*)read.data()+GetParam().offset, this->foregroundData.data(), GetParam().count)); + EXPECT_EQ(0, std::memcmp((uint8_t*)read.data()+GetParam().offset+GetParam().count, (uint8_t*)this->backgroundData.data()+GetParam().offset+GetParam().count, GetParam().blobsize-GetParam().count-GetParam().offset)); +} diff --git a/test/blobstore/implementations/onblocks/BlobSizeTest.cpp b/test/blobstore/implementations/onblocks/BlobSizeTest.cpp new file mode 100644 index 00000000..a334afd5 --- /dev/null +++ b/test/blobstore/implementations/onblocks/BlobSizeTest.cpp @@ -0,0 +1,171 @@ +#include "testutils/BlobStoreTest.h" +#include +#include + +using namespace blobstore; +using blockstore::Key; +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::unique_ref; + +class BlobSizeTest: public BlobStoreTest { +public: + BlobSizeTest(): blob(blobStore->create()) {} + + static constexpr uint32_t MEDIUM_SIZE = 5 * 1024 * 1024; + static constexpr uint32_t LARGE_SIZE = 10 * 1024 * 1024; + + unique_ref blob; +}; +constexpr uint32_t BlobSizeTest::MEDIUM_SIZE; +constexpr uint32_t BlobSizeTest::LARGE_SIZE; + +TEST_F(BlobSizeTest, CreatedBlobIsEmpty) { + EXPECT_EQ(0u, blob->size()); +} + +TEST_F(BlobSizeTest, Growing_1Byte) { + blob->resize(1); + EXPECT_EQ(1u, blob->size()); +} + +TEST_F(BlobSizeTest, Growing_Large) { + blob->resize(LARGE_SIZE); + EXPECT_EQ(LARGE_SIZE, blob->size()); +} + +TEST_F(BlobSizeTest, Shrinking_Empty) { + blob->resize(LARGE_SIZE); + blob->resize(0); + EXPECT_EQ(0u, blob->size()); +} + +TEST_F(BlobSizeTest, Shrinking_1Byte) { + blob->resize(LARGE_SIZE); + blob->resize(1); + EXPECT_EQ(1u, blob->size()); +} + +TEST_F(BlobSizeTest, ResizingToItself_Empty) { + blob->resize(0); + EXPECT_EQ(0u, blob->size()); +} + +TEST_F(BlobSizeTest, ResizingToItself_1Byte) { + blob->resize(1); + blob->resize(1); + EXPECT_EQ(1u, blob->size()); +} + +TEST_F(BlobSizeTest, ResizingToItself_Large) { + blob->resize(LARGE_SIZE); + blob->resize(LARGE_SIZE); + EXPECT_EQ(LARGE_SIZE, blob->size()); +} + +TEST_F(BlobSizeTest, EmptyBlobStaysEmptyWhenLoading) { + Key key = blob->key(); + reset(std::move(blob)); + auto loaded = loadBlob(key); + EXPECT_EQ(0u, loaded->size()); +} + +TEST_F(BlobSizeTest, BlobSizeStaysIntactWhenLoading) { + blob->resize(LARGE_SIZE); + Key key = blob->key(); + reset(std::move(blob)); + auto loaded = loadBlob(key); + EXPECT_EQ(LARGE_SIZE, loaded->size()); +} + +TEST_F(BlobSizeTest, WritingAtEndOfBlobGrowsBlob_Empty) { + int value; + blob->write(&value, 0, 4); + EXPECT_EQ(4u, blob->size()); +} + +TEST_F(BlobSizeTest, WritingAfterEndOfBlobGrowsBlob_Empty) { + int value; + blob->write(&value, 2, 4); + EXPECT_EQ(6u, blob->size()); +} + +TEST_F(BlobSizeTest, WritingOverEndOfBlobGrowsBlob_NonEmpty) { + blob->resize(1); + int value; + blob->write(&value, 0, 4); + EXPECT_EQ(4u, blob->size()); +} + +TEST_F(BlobSizeTest, WritingAtEndOfBlobGrowsBlob_NonEmpty) { + blob->resize(1); + int value; + blob->write(&value, 1, 4); + EXPECT_EQ(5u, blob->size()); +} + +TEST_F(BlobSizeTest, WritingAfterEndOfBlobGrowsBlob_NonEmpty) { + blob->resize(1); + int value; + blob->write(&value, 2, 4); + EXPECT_EQ(6u, blob->size()); +} + +TEST_F(BlobSizeTest, ChangingSizeImmediatelyFlushes) { + blob->resize(LARGE_SIZE); + auto loaded = loadBlob(blob->key()); + EXPECT_EQ(LARGE_SIZE, loaded->size()); +} + +class BlobSizeDataTest: public BlobSizeTest { +public: + BlobSizeDataTest() + :ZEROES(LARGE_SIZE), + randomData(DataFixture::generate(LARGE_SIZE)) { + ZEROES.FillWithZeroes(); + } + + Data readBlob(const Blob &blob) { + Data data(blob.size()); + blob.read(data.data(), 0, data.size()); + return data; + } + + Data ZEROES; + Data randomData; +}; + +TEST_F(BlobSizeDataTest, BlobIsZeroedOutAfterGrowing) { + blob->resize(LARGE_SIZE); + EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), ZEROES.data(), LARGE_SIZE)); +} + +TEST_F(BlobSizeDataTest, BlobIsZeroedOutAfterGrowingAndLoading) { + blob->resize(LARGE_SIZE); + auto loaded = loadBlob(blob->key()); + EXPECT_EQ(0, std::memcmp(readBlob(*loaded).data(), ZEROES.data(), LARGE_SIZE)); +} + +TEST_F(BlobSizeDataTest, DataStaysIntactWhenGrowing) { + blob->resize(MEDIUM_SIZE); + blob->write(randomData.data(), 0, MEDIUM_SIZE); + blob->resize(LARGE_SIZE); + EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), randomData.data(), MEDIUM_SIZE)); + EXPECT_EQ(0, std::memcmp((uint8_t*)readBlob(*blob).data() + MEDIUM_SIZE, ZEROES.data(), LARGE_SIZE-MEDIUM_SIZE)); +} + +TEST_F(BlobSizeDataTest, DataStaysIntactWhenShrinking) { + blob->resize(LARGE_SIZE); + blob->write(randomData.data(), 0, LARGE_SIZE); + blob->resize(MEDIUM_SIZE); + EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), randomData.data(), MEDIUM_SIZE)); +} + +TEST_F(BlobSizeDataTest, ChangedAreaIsZeroedOutWhenShrinkingAndRegrowing) { + blob->resize(LARGE_SIZE); + blob->write(randomData.data(), 0, LARGE_SIZE); + blob->resize(MEDIUM_SIZE); + blob->resize(LARGE_SIZE); + EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), randomData.data(), MEDIUM_SIZE)); + EXPECT_EQ(0, std::memcmp((uint8_t*)readBlob(*blob).data() + MEDIUM_SIZE, ZEROES.data(), LARGE_SIZE-MEDIUM_SIZE)); +} diff --git a/test/blobstore/implementations/onblocks/BlobStoreTest.cpp b/test/blobstore/implementations/onblocks/BlobStoreTest.cpp new file mode 100644 index 00000000..870ffe10 --- /dev/null +++ b/test/blobstore/implementations/onblocks/BlobStoreTest.cpp @@ -0,0 +1,39 @@ +#include "testutils/BlobStoreTest.h" +#include + +using blockstore::Key; +using cpputils::unique_ref; +using blobstore::Blob; +using boost::none; + +TEST_F(BlobStoreTest, LoadNonexistingKeyOnEmptyBlobstore) { + const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972"); + EXPECT_EQ(none, blobStore->load(key)); +} + +TEST_F(BlobStoreTest, LoadNonexistingKeyOnNonEmptyBlobstore) { + blobStore->create(); + const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972"); + EXPECT_EQ(none, blobStore->load(key)); +} + +TEST_F(BlobStoreTest, TwoCreatedBlobsHaveDifferentKeys) { + auto blob1 = blobStore->create(); + auto blob2 = blobStore->create(); + EXPECT_NE(blob1->key(), blob2->key()); +} + +TEST_F(BlobStoreTest, BlobIsNotLoadableAfterDeletion_DeleteDirectly) { + auto blob = blobStore->create(); + Key key = blob->key(); + blobStore->remove(std::move(blob)); + EXPECT_FALSE((bool)blobStore->load(key)); +} + +TEST_F(BlobStoreTest, BlobIsNotLoadableAfterDeletion_DeleteAfterLoading) { + auto blob = blobStore->create(); + Key key = blob->key(); + reset(std::move(blob)); + blobStore->remove(loadBlob(key)); + EXPECT_FALSE((bool)blobStore->load(key)); +} diff --git a/test/blobstore/implementations/onblocks/datanodestore/DataInnerNodeTest.cpp b/test/blobstore/implementations/onblocks/datanodestore/DataInnerNodeTest.cpp new file mode 100644 index 00000000..ce413be3 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datanodestore/DataInnerNodeTest.cpp @@ -0,0 +1,241 @@ +#include + +#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h" + +#include +#include + +#include +#include + +using ::testing::Test; + +using cpputils::dynamic_pointer_move; + +using blockstore::Key; +using blockstore::testfake::FakeBlockStore; +using blockstore::BlockStore; +using cpputils::Data; +using namespace blobstore; +using namespace blobstore::onblocks; +using namespace blobstore::onblocks::datanodestore; + +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +class DataInnerNodeTest: public Test { +public: + static constexpr uint32_t BLOCKSIZE_BYTES = 1024; + + DataInnerNodeTest() : + _blockStore(make_unique_ref()), + blockStore(_blockStore.get()), + nodeStore(make_unique_ref(std::move(_blockStore), BLOCKSIZE_BYTES)), + ZEROES(nodeStore->layout().maxBytesPerLeaf()), + leaf(nodeStore->createNewLeafNode()), + node(nodeStore->createNewInnerNode(*leaf)) { + + ZEROES.FillWithZeroes(); + } + + unique_ref LoadInnerNode(const Key &key) { + auto node = nodeStore->load(key).value(); + return dynamic_pointer_move(node).value(); + } + + Key CreateNewInnerNodeReturnKey(const DataNode &firstChild) { + return nodeStore->createNewInnerNode(firstChild)->key(); + } + + unique_ref CreateNewInnerNode() { + auto new_leaf = nodeStore->createNewLeafNode(); + return nodeStore->createNewInnerNode(*new_leaf); + } + + unique_ref CreateAndLoadNewInnerNode(const DataNode &firstChild) { + auto key = CreateNewInnerNodeReturnKey(firstChild); + return LoadInnerNode(key); + } + + unique_ref CreateNewInnerNode(const DataNode &firstChild, const DataNode &secondChild) { + auto node = nodeStore->createNewInnerNode(firstChild); + node->addChild(secondChild); + return node; + } + + Key CreateNewInnerNodeReturnKey(const DataNode &firstChild, const DataNode &secondChild) { + return CreateNewInnerNode(firstChild, secondChild)->key(); + } + + unique_ref CreateAndLoadNewInnerNode(const DataNode &firstChild, const DataNode &secondChild) { + auto key = CreateNewInnerNodeReturnKey(firstChild, secondChild); + return LoadInnerNode(key); + } + + Key AddALeafTo(DataInnerNode *node) { + auto leaf2 = nodeStore->createNewLeafNode(); + node->addChild(*leaf2); + return leaf2->key(); + } + + Key CreateNodeWithDataConvertItToInnerNodeAndReturnKey() { + auto node = CreateNewInnerNode(); + AddALeafTo(node.get()); + AddALeafTo(node.get()); + auto child = nodeStore->createNewLeafNode(); + unique_ref converted = DataNode::convertToNewInnerNode(std::move(node), *child); + return converted->key(); + } + + unique_ref CopyInnerNode(const DataInnerNode &node) { + auto copied = nodeStore->createNewNodeAsCopyFrom(node); + return dynamic_pointer_move(copied).value(); + } + + Key InitializeInnerNodeAddLeafReturnKey() { + auto node = DataInnerNode::InitializeNewNode(blockStore->create(Data(BLOCKSIZE_BYTES)), *leaf); + AddALeafTo(node.get()); + return node->key(); + } + + unique_ref _blockStore; + BlockStore *blockStore; + unique_ref nodeStore; + Data ZEROES; + unique_ref leaf; + unique_ref node; + +private: + + DISALLOW_COPY_AND_ASSIGN(DataInnerNodeTest); +}; + +constexpr uint32_t DataInnerNodeTest::BLOCKSIZE_BYTES; + +TEST_F(DataInnerNodeTest, CorrectKeyReturnedAfterInitialization) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + Key key = block->key(); + auto node = DataInnerNode::InitializeNewNode(std::move(block), *leaf); + EXPECT_EQ(key, node->key()); +} + +TEST_F(DataInnerNodeTest, CorrectKeyReturnedAfterLoading) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + Key key = block->key(); + DataInnerNode::InitializeNewNode(std::move(block), *leaf); + + auto loaded = nodeStore->load(key).value(); + EXPECT_EQ(key, loaded->key()); +} + +TEST_F(DataInnerNodeTest, InitializesCorrectly) { + auto node = DataInnerNode::InitializeNewNode(blockStore->create(Data(BLOCKSIZE_BYTES)), *leaf); + + EXPECT_EQ(1u, node->numChildren()); + EXPECT_EQ(leaf->key(), node->getChild(0)->key()); +} + +TEST_F(DataInnerNodeTest, ReinitializesCorrectly) { + auto key = InitializeInnerNodeAddLeafReturnKey(); + auto node = DataInnerNode::InitializeNewNode(blockStore->load(key).value(), *leaf); + + EXPECT_EQ(1u, node->numChildren()); + EXPECT_EQ(leaf->key(), node->getChild(0)->key()); +} + +TEST_F(DataInnerNodeTest, IsCorrectlyInitializedAfterLoading) { + auto loaded = CreateAndLoadNewInnerNode(*leaf); + + EXPECT_EQ(1u, loaded->numChildren()); + EXPECT_EQ(leaf->key(), loaded->getChild(0)->key()); +} + +TEST_F(DataInnerNodeTest, AddingASecondLeaf) { + Key leaf2_key = AddALeafTo(node.get()); + + EXPECT_EQ(2u, node->numChildren()); + EXPECT_EQ(leaf->key(), node->getChild(0)->key()); + EXPECT_EQ(leaf2_key, node->getChild(1)->key()); +} + +TEST_F(DataInnerNodeTest, AddingASecondLeafAndReload) { + auto leaf2 = nodeStore->createNewLeafNode(); + auto loaded = CreateAndLoadNewInnerNode(*leaf, *leaf2); + + EXPECT_EQ(2u, loaded->numChildren()); + EXPECT_EQ(leaf->key(), loaded->getChild(0)->key()); + EXPECT_EQ(leaf2->key(), loaded->getChild(1)->key()); +} + +TEST_F(DataInnerNodeTest, BuildingAThreeLevelTree) { + auto node2 = CreateNewInnerNode(); + auto parent = CreateNewInnerNode(*node, *node2); + + EXPECT_EQ(2u, parent->numChildren()); + EXPECT_EQ(node->key(), parent->getChild(0)->key()); + EXPECT_EQ(node2->key(), parent->getChild(1)->key()); +} + +TEST_F(DataInnerNodeTest, BuildingAThreeLevelTreeAndReload) { + auto node2 = CreateNewInnerNode(); + auto parent = CreateAndLoadNewInnerNode(*node, *node2); + + EXPECT_EQ(2u, parent->numChildren()); + EXPECT_EQ(node->key(), parent->getChild(0)->key()); + EXPECT_EQ(node2->key(), parent->getChild(1)->key()); +} + +TEST_F(DataInnerNodeTest, ConvertToInternalNode) { + auto child = nodeStore->createNewLeafNode(); + Key node_key = node->key(); + unique_ref converted = DataNode::convertToNewInnerNode(std::move(node), *child); + + EXPECT_EQ(1u, converted->numChildren()); + EXPECT_EQ(child->key(), converted->getChild(0)->key()); + EXPECT_EQ(node_key, converted->key()); +} + +TEST_F(DataInnerNodeTest, ConvertToInternalNodeZeroesOutChildrenRegion) { + Key key = CreateNodeWithDataConvertItToInnerNodeAndReturnKey(); + + auto block = blockStore->load(key).value(); + EXPECT_EQ(0, std::memcmp(ZEROES.data(), (uint8_t*)block->data()+DataNodeLayout::HEADERSIZE_BYTES+sizeof(DataInnerNode::ChildEntry), nodeStore->layout().maxBytesPerLeaf()-sizeof(DataInnerNode::ChildEntry))); +} + +TEST_F(DataInnerNodeTest, CopyingCreatesNewNode) { + auto copied = CopyInnerNode(*node); + EXPECT_NE(node->key(), copied->key()); +} + +TEST_F(DataInnerNodeTest, CopyInnerNodeWithOneChild) { + auto copied = CopyInnerNode(*node); + + EXPECT_EQ(node->numChildren(), copied->numChildren()); + EXPECT_EQ(node->getChild(0)->key(), copied->getChild(0)->key()); +} + +TEST_F(DataInnerNodeTest, CopyInnerNodeWithTwoChildren) { + AddALeafTo(node.get()); + auto copied = CopyInnerNode(*node); + + EXPECT_EQ(node->numChildren(), copied->numChildren()); + EXPECT_EQ(node->getChild(0)->key(), copied->getChild(0)->key()); + EXPECT_EQ(node->getChild(1)->key(), copied->getChild(1)->key()); +} + +TEST_F(DataInnerNodeTest, LastChildWhenOneChild) { + EXPECT_EQ(leaf->key(), node->LastChild()->key()); +} + +TEST_F(DataInnerNodeTest, LastChildWhenTwoChildren) { + Key key = AddALeafTo(node.get()); + EXPECT_EQ(key, node->LastChild()->key()); +} + +TEST_F(DataInnerNodeTest, LastChildWhenThreeChildren) { + AddALeafTo(node.get()); + Key key = AddALeafTo(node.get()); + EXPECT_EQ(key, node->LastChild()->key()); +} diff --git a/test/blobstore/implementations/onblocks/datanodestore/DataLeafNodeTest.cpp b/test/blobstore/implementations/onblocks/datanodestore/DataLeafNodeTest.cpp new file mode 100644 index 00000000..a53df1a9 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datanodestore/DataLeafNodeTest.cpp @@ -0,0 +1,357 @@ +#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h" +#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h" +#include + +#include + +#include +#include +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Combine; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::string; +using cpputils::DataFixture; + +//TODO Split into multiple files + +using cpputils::dynamic_pointer_move; + +using blockstore::BlockStore; +using cpputils::Data; +using blockstore::Key; +using blockstore::testfake::FakeBlockStore; +using namespace blobstore; +using namespace blobstore::onblocks; +using namespace blobstore::onblocks::datanodestore; + +#define EXPECT_IS_PTR_TYPE(Type, ptr) EXPECT_NE(nullptr, dynamic_cast(ptr)) << "Given pointer cannot be cast to the given type" + +class DataLeafNodeTest: public Test { +public: + + static constexpr uint32_t BLOCKSIZE_BYTES = 1024; + static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES); + + DataLeafNodeTest(): + _blockStore(make_unique_ref()), + blockStore(_blockStore.get()), + nodeStore(make_unique_ref(std::move(_blockStore), BLOCKSIZE_BYTES)), + ZEROES(nodeStore->layout().maxBytesPerLeaf()), + randomData(nodeStore->layout().maxBytesPerLeaf()), + leaf(nodeStore->createNewLeafNode()) { + + ZEROES.FillWithZeroes(); + + Data dataFixture(DataFixture::generate(nodeStore->layout().maxBytesPerLeaf())); + + std::memcpy(randomData.data(), dataFixture.data(), randomData.size()); + } + + Data loadData(const DataLeafNode &leaf) { + Data data(leaf.numBytes()); + leaf.read(data.data(), 0, leaf.numBytes()); + return data; + } + + Key WriteDataToNewLeafBlockAndReturnKey() { + auto newleaf = nodeStore->createNewLeafNode(); + newleaf->resize(randomData.size()); + newleaf->write(randomData.data(), 0, randomData.size()); + return newleaf->key(); + } + + void FillLeafBlockWithData() { + FillLeafBlockWithData(leaf.get()); + } + + void FillLeafBlockWithData(DataLeafNode *leaf_to_fill) { + leaf_to_fill->resize(randomData.size()); + leaf_to_fill->write(randomData.data(), 0, randomData.size()); + } + + unique_ref LoadLeafNode(const Key &key) { + auto leaf = nodeStore->load(key).value(); + return dynamic_pointer_move(leaf).value(); + } + + void ResizeLeaf(const Key &key, size_t size) { + auto leaf = LoadLeafNode(key); + EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get()); + leaf->resize(size); + } + + Key CreateLeafWithDataConvertItToInnerNodeAndReturnKey() { + auto leaf = nodeStore->createNewLeafNode(); + FillLeafBlockWithData(leaf.get()); + auto child = nodeStore->createNewLeafNode(); + unique_ref converted = DataNode::convertToNewInnerNode(std::move(leaf), *child); + return converted->key(); + } + + unique_ref CopyLeafNode(const DataLeafNode &node) { + auto copied = nodeStore->createNewNodeAsCopyFrom(node); + return dynamic_pointer_move(copied).value(); + } + + Key InitializeLeafGrowAndReturnKey() { + auto leaf = DataLeafNode::InitializeNewNode(blockStore->create(Data(BLOCKSIZE_BYTES))); + leaf->resize(5); + return leaf->key(); + } + + unique_ref _blockStore; + BlockStore *blockStore; + unique_ref nodeStore; + Data ZEROES; + Data randomData; + unique_ref leaf; + +private: + DISALLOW_COPY_AND_ASSIGN(DataLeafNodeTest); +}; + +constexpr uint32_t DataLeafNodeTest::BLOCKSIZE_BYTES; +constexpr DataNodeLayout DataLeafNodeTest::LAYOUT; + +TEST_F(DataLeafNodeTest, CorrectKeyReturnedAfterInitialization) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + Key key = block->key(); + auto node = DataLeafNode::InitializeNewNode(std::move(block)); + EXPECT_EQ(key, node->key()); +} + +TEST_F(DataLeafNodeTest, CorrectKeyReturnedAfterLoading) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + Key key = block->key(); + DataLeafNode::InitializeNewNode(std::move(block)); + + auto loaded = nodeStore->load(key).value(); + EXPECT_EQ(key, loaded->key()); +} + +TEST_F(DataLeafNodeTest, InitializesCorrectly) { + auto leaf = DataLeafNode::InitializeNewNode(blockStore->create(Data(BLOCKSIZE_BYTES))); + EXPECT_EQ(0u, leaf->numBytes()); +} + +TEST_F(DataLeafNodeTest, ReinitializesCorrectly) { + auto key = InitializeLeafGrowAndReturnKey(); + auto leaf = DataLeafNode::InitializeNewNode(blockStore->load(key).value()); + EXPECT_EQ(0u, leaf->numBytes()); +} + +TEST_F(DataLeafNodeTest, ReadWrittenDataAfterReloadingBlock) { + Key key = WriteDataToNewLeafBlockAndReturnKey(); + + auto loaded = LoadLeafNode(key); + + EXPECT_EQ(randomData.size(), loaded->numBytes()); + EXPECT_EQ(randomData, loadData(*loaded)); +} + +TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero) { + EXPECT_EQ(0u, leaf->numBytes()); +} + +TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero_AfterLoading) { + Key key = nodeStore->createNewLeafNode()->key(); + auto leaf = LoadLeafNode(key); + + EXPECT_EQ(0u, leaf->numBytes()); +} + +class DataLeafNodeSizeTest: public DataLeafNodeTest, public WithParamInterface { +public: + Key CreateLeafResizeItAndReturnKey() { + auto leaf = nodeStore->createNewLeafNode(); + leaf->resize(GetParam()); + return leaf->key(); + } +}; +INSTANTIATE_TEST_CASE_P(DataLeafNodeSizeTest, DataLeafNodeSizeTest, Values(0, 1, 5, 16, 32, 512, DataNodeLayout(DataLeafNodeTest::BLOCKSIZE_BYTES).maxBytesPerLeaf())); + +TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeImmediately) { + leaf->resize(GetParam()); + EXPECT_EQ(GetParam(), leaf->numBytes()); +} + +TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) { + Key key = CreateLeafResizeItAndReturnKey(); + + auto leaf = LoadLeafNode(key); + EXPECT_EQ(GetParam(), leaf->numBytes()); +} + +TEST_F(DataLeafNodeTest, SpaceIsZeroFilledWhenGrowing) { + leaf->resize(randomData.size()); + EXPECT_EQ(0, std::memcmp(ZEROES.data(), loadData(*leaf).data(), randomData.size())); +} + +TEST_F(DataLeafNodeTest, SpaceGetsZeroFilledWhenShrinkingAndRegrowing) { + FillLeafBlockWithData(); + // resize it smaller and then back to original size + uint32_t smaller_size = randomData.size() - 100; + leaf->resize(smaller_size); + leaf->resize(randomData.size()); + + //Check that the space was filled with zeroes + EXPECT_EQ(0, std::memcmp(ZEROES.data(), ((uint8_t*)loadData(*leaf).data())+smaller_size, 100)); +} + +TEST_F(DataLeafNodeTest, DataGetsZeroFilledWhenShrinking) { + Key key = WriteDataToNewLeafBlockAndReturnKey(); + uint32_t smaller_size = randomData.size() - 100; + { + //At first, we expect there to be random data in the underlying data block + auto block = blockStore->load(key).value(); + EXPECT_EQ(0, std::memcmp((char*)randomData.data()+smaller_size, (uint8_t*)block->data()+DataNodeLayout::HEADERSIZE_BYTES+smaller_size, 100)); + } + + //After shrinking, we expect there to be zeroes in the underlying data block + ResizeLeaf(key, smaller_size); + { + auto block = blockStore->load(key).value(); + EXPECT_EQ(0, std::memcmp(ZEROES.data(), (uint8_t*)block->data()+DataNodeLayout::HEADERSIZE_BYTES+smaller_size, 100)); + } +} + +TEST_F(DataLeafNodeTest, ShrinkingDoesntDestroyValidDataRegion) { + FillLeafBlockWithData(); + uint32_t smaller_size = randomData.size() - 100; + leaf->resize(smaller_size); + + //Check that the remaining data region is unchanged + EXPECT_EQ(0, std::memcmp(randomData.data(), loadData(*leaf).data(), smaller_size)); +} + +TEST_F(DataLeafNodeTest, ConvertToInternalNode) { + auto child = nodeStore->createNewLeafNode(); + Key leaf_key = leaf->key(); + unique_ref converted = DataNode::convertToNewInnerNode(std::move(leaf), *child); + + EXPECT_EQ(1u, converted->numChildren()); + EXPECT_EQ(child->key(), converted->getChild(0)->key()); + EXPECT_EQ(leaf_key, converted->key()); +} + +TEST_F(DataLeafNodeTest, ConvertToInternalNodeZeroesOutChildrenRegion) { + Key key = CreateLeafWithDataConvertItToInnerNodeAndReturnKey(); + + auto block = blockStore->load(key).value(); + EXPECT_EQ(0, std::memcmp(ZEROES.data(), (uint8_t*)block->data()+DataNodeLayout::HEADERSIZE_BYTES+sizeof(DataInnerNode::ChildEntry), nodeStore->layout().maxBytesPerLeaf()-sizeof(DataInnerNode::ChildEntry))); +} + +TEST_F(DataLeafNodeTest, CopyingCreatesANewLeaf) { + auto copied = CopyLeafNode(*leaf); + EXPECT_NE(leaf->key(), copied->key()); +} + +TEST_F(DataLeafNodeTest, CopyEmptyLeaf) { + auto copied = CopyLeafNode(*leaf); + EXPECT_EQ(leaf->numBytes(), copied->numBytes()); +} + +TEST_F(DataLeafNodeTest, CopyDataLeaf) { + FillLeafBlockWithData(); + auto copied = CopyLeafNode(*leaf); + + EXPECT_EQ(leaf->numBytes(), copied->numBytes()); + EXPECT_EQ(0, std::memcmp(loadData(*leaf).data(), loadData(*copied).data(), leaf->numBytes())); + + //Test that they have different data regions (changing the original one doesn't change the copy) + char data = '\0'; + leaf->write(&data, 0, 1); + EXPECT_EQ(data, *(char*)loadData(*leaf).data()); + EXPECT_NE(data, *(char*)loadData(*copied).data()); +} + + +struct DataRange { + size_t leafsize; + off_t offset; + size_t count; +}; + +class DataLeafNodeDataTest: public DataLeafNodeTest, public WithParamInterface { +public: + Data foregroundData; + Data backgroundData; + + DataLeafNodeDataTest(): + foregroundData(DataFixture::generate(GetParam().count, 0)), + backgroundData(DataFixture::generate(GetParam().leafsize, 1)) { + } + + Key CreateLeafWriteToItAndReturnKey(const Data &to_write) { + auto newleaf = nodeStore->createNewLeafNode(); + + newleaf->resize(GetParam().leafsize); + newleaf->write(to_write.data(), GetParam().offset, GetParam().count); + return newleaf->key(); + } + + void EXPECT_DATA_READS_AS(const Data &expected, const DataLeafNode &leaf, off_t offset, size_t count) { + Data read(count); + leaf.read(read.data(), offset, count); + EXPECT_EQ(expected, read); + } + + void EXPECT_DATA_READS_AS_OUTSIDE_OF(const Data &expected, const DataLeafNode &leaf, off_t start, size_t count) { + Data begin(start); + Data end(GetParam().leafsize - count - start); + + std::memcpy(begin.data(), expected.data(), start); + std::memcpy(end.data(), (uint8_t*)expected.data()+start+count, end.size()); + + EXPECT_DATA_READS_AS(begin, leaf, 0, start); + EXPECT_DATA_READS_AS(end, leaf, start + count, end.size()); + } + + void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const DataLeafNode &leaf, off_t start, size_t count) { + Data ZEROES(GetParam().leafsize); + ZEROES.FillWithZeroes(); + EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, leaf, start, count); + } +}; +INSTANTIATE_TEST_CASE_P(DataLeafNodeDataTest, DataLeafNodeDataTest, Values( + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()}, // full size leaf, access beginning to end + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-200}, // full size leaf, access middle to middle + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100}, // full size leaf, access beginning to middle + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100}, // full size leaf, access middle to end + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100}, // non-full size leaf, access beginning to end + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-300}, // non-full size leaf, access middle to middle + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-200}, // non-full size leaf, access beginning to middle + DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-200} // non-full size leaf, access middle to end +)); + +TEST_P(DataLeafNodeDataTest, WriteAndReadImmediately) { + leaf->resize(GetParam().leafsize); + leaf->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + + EXPECT_DATA_READS_AS(this->foregroundData, *leaf, GetParam().offset, GetParam().count); + EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*leaf, GetParam().offset, GetParam().count); +} + +TEST_P(DataLeafNodeDataTest, WriteAndReadAfterLoading) { + Key key = CreateLeafWriteToItAndReturnKey(this->foregroundData); + + auto loaded_leaf = LoadLeafNode(key); + EXPECT_DATA_READS_AS(this->foregroundData, *loaded_leaf, GetParam().offset, GetParam().count); + EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*loaded_leaf, GetParam().offset, GetParam().count); +} + +TEST_P(DataLeafNodeDataTest, OverwriteAndRead) { + leaf->resize(GetParam().leafsize); + leaf->write(this->backgroundData.data(), 0, GetParam().leafsize); + leaf->write(this->foregroundData.data(), GetParam().offset, GetParam().count); + EXPECT_DATA_READS_AS(this->foregroundData, *leaf, GetParam().offset, GetParam().count); + EXPECT_DATA_READS_AS_OUTSIDE_OF(this->backgroundData, *leaf, GetParam().offset, GetParam().count); +} + diff --git a/test/blobstore/implementations/onblocks/datanodestore/DataNodeStoreTest.cpp b/test/blobstore/implementations/onblocks/datanodestore/DataNodeStoreTest.cpp new file mode 100644 index 00000000..05ee7f59 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datanodestore/DataNodeStoreTest.cpp @@ -0,0 +1,137 @@ +#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h" +#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h" +#include + +#include +#include +#include + +using ::testing::Test; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::string; +using boost::none; + +using blockstore::BlockStore; +using blockstore::testfake::FakeBlockStore; +using blockstore::Key; +using cpputils::Data; +using namespace blobstore; +using namespace blobstore::onblocks; +using namespace blobstore::onblocks::datanodestore; + +class DataNodeStoreTest: public Test { +public: + static constexpr uint32_t BLOCKSIZE_BYTES = 1024; + + unique_ref _blockStore = make_unique_ref(); + BlockStore *blockStore = _blockStore.get(); + unique_ref nodeStore = make_unique_ref(std::move(_blockStore), BLOCKSIZE_BYTES); +}; + +constexpr uint32_t DataNodeStoreTest::BLOCKSIZE_BYTES; + +#define EXPECT_IS_PTR_TYPE(Type, ptr) EXPECT_NE(nullptr, dynamic_cast(ptr)) << "Given pointer cannot be cast to the given type" + +TEST_F(DataNodeStoreTest, CreateLeafNodeCreatesLeafNode) { + auto node = nodeStore->createNewLeafNode(); + EXPECT_IS_PTR_TYPE(DataLeafNode, node.get()); +} + +TEST_F(DataNodeStoreTest, CreateInnerNodeCreatesInnerNode) { + auto leaf = nodeStore->createNewLeafNode(); + + auto node = nodeStore->createNewInnerNode(*leaf); + EXPECT_IS_PTR_TYPE(DataInnerNode, node.get()); +} + +TEST_F(DataNodeStoreTest, LeafNodeIsRecognizedAfterStoreAndLoad) { + Key key = nodeStore->createNewLeafNode()->key(); + + auto loaded_node = nodeStore->load(key).value(); + + EXPECT_IS_PTR_TYPE(DataLeafNode, loaded_node.get()); +} + +TEST_F(DataNodeStoreTest, InnerNodeWithDepth1IsRecognizedAfterStoreAndLoad) { + auto leaf = nodeStore->createNewLeafNode(); + Key key = nodeStore->createNewInnerNode(*leaf)->key(); + + auto loaded_node = nodeStore->load(key).value(); + + EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get()); +} + +TEST_F(DataNodeStoreTest, InnerNodeWithDepth2IsRecognizedAfterStoreAndLoad) { + auto leaf = nodeStore->createNewLeafNode(); + auto inner = nodeStore->createNewInnerNode(*leaf); + Key key = nodeStore->createNewInnerNode(*inner)->key(); + + auto loaded_node = nodeStore->load(key).value(); + + EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get()); +} + +TEST_F(DataNodeStoreTest, DataNodeCrashesOnLoadIfDepthIsTooHigh) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + Key key = block->key(); + { + DataNodeView view(std::move(block)); + view.setDepth(DataNodeStore::MAX_DEPTH + 1); + } + + EXPECT_ANY_THROW( + nodeStore->load(key) + ); +} + +TEST_F(DataNodeStoreTest, CreatedInnerNodeIsInitialized) { + auto leaf = nodeStore->createNewLeafNode(); + auto node = nodeStore->createNewInnerNode(*leaf); + EXPECT_EQ(1u, node->numChildren()); + EXPECT_EQ(leaf->key(), node->getChild(0)->key()); +} + +TEST_F(DataNodeStoreTest, CreatedLeafNodeIsInitialized) { + auto leaf = nodeStore->createNewLeafNode(); + EXPECT_EQ(0u, leaf->numBytes()); +} + +TEST_F(DataNodeStoreTest, NodeIsNotLoadableAfterDeleting) { + auto nodekey = nodeStore->createNewLeafNode()->key(); + auto node = nodeStore->load(nodekey); + EXPECT_NE(none, node); + nodeStore->remove(std::move(*node)); + EXPECT_EQ(none, nodeStore->load(nodekey)); +} + +TEST_F(DataNodeStoreTest, NumNodesIsCorrectOnEmptyNodestore) { + EXPECT_EQ(0u, nodeStore->numNodes()); +} + +TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterAddingOneLeafNode) { + nodeStore->createNewLeafNode(); + EXPECT_EQ(1u, nodeStore->numNodes()); +} + +TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterRemovingTheLastNode) { + auto leaf = nodeStore->createNewLeafNode(); + nodeStore->remove(std::move(leaf)); + EXPECT_EQ(0u, nodeStore->numNodes()); +} + +TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterAddingTwoNodes) { + auto leaf = nodeStore->createNewLeafNode(); + auto node = nodeStore->createNewInnerNode(*leaf); + EXPECT_EQ(2u, nodeStore->numNodes()); +} + +TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterRemovingANode) { + auto leaf = nodeStore->createNewLeafNode(); + auto node = nodeStore->createNewInnerNode(*leaf); + nodeStore->remove(std::move(node)); + EXPECT_EQ(1u, nodeStore->numNodes()); +} diff --git a/test/blobstore/implementations/onblocks/datanodestore/DataNodeViewTest.cpp b/test/blobstore/implementations/onblocks/datanodestore/DataNodeViewTest.cpp new file mode 100644 index 00000000..3f31b31e --- /dev/null +++ b/test/blobstore/implementations/onblocks/datanodestore/DataNodeViewTest.cpp @@ -0,0 +1,150 @@ +#include "blobstore/implementations/onblocks/datanodestore/DataNodeView.h" +#include + +#include +#include +#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h" +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; +using std::string; + +using blockstore::BlockStore; +using blockstore::testfake::FakeBlockStore; +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using namespace blobstore; +using namespace blobstore::onblocks; +using namespace blobstore::onblocks::datanodestore; + +class DataNodeViewTest: public Test { +public: + static constexpr uint32_t BLOCKSIZE_BYTES = 1024; + static constexpr uint32_t DATASIZE_BYTES = DataNodeLayout(DataNodeViewTest::BLOCKSIZE_BYTES).datasizeBytes(); + + unique_ref blockStore = make_unique_ref(); +}; + +class DataNodeViewDepthTest: public DataNodeViewTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(DataNodeViewDepthTest, DataNodeViewDepthTest, Values(0, 1, 3, 10, 100)); + +TEST_P(DataNodeViewDepthTest, DepthIsStored) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + auto key = block->key(); + { + DataNodeView view(std::move(block)); + view.setDepth(GetParam()); + } + DataNodeView view(blockStore->load(key).value()); + EXPECT_EQ(GetParam(), view.Depth()); +} + +class DataNodeViewSizeTest: public DataNodeViewTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(DataNodeViewSizeTest, DataNodeViewSizeTest, Values(0, 50, 64, 1024, 1024*1024*1024)); + +TEST_P(DataNodeViewSizeTest, SizeIsStored) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + auto key = block->key(); + { + DataNodeView view(std::move(block)); + view.setSize(GetParam()); + } + DataNodeView view(blockStore->load(key).value()); + EXPECT_EQ(GetParam(), view.Size()); +} + +TEST_F(DataNodeViewTest, DataIsStored) { + Data randomData = DataFixture::generate(DATASIZE_BYTES); + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + auto key = block->key(); + { + DataNodeView view(std::move(block)); + view.write(randomData.data(), 0, randomData.size()); + } + DataNodeView view(blockStore->load(key).value()); + EXPECT_EQ(0, std::memcmp(view.data(), randomData.data(), randomData.size())); +} + +TEST_F(DataNodeViewTest, HeaderAndBodyDontOverlap) { + Data randomData = DataFixture::generate(DATASIZE_BYTES); + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + auto key = block->key(); + { + DataNodeView view(std::move(block)); + view.setDepth(3); + view.setSize(1000000000u); + view.write(randomData.data(), 0, DATASIZE_BYTES); + } + DataNodeView view(blockStore->load(key).value()); + EXPECT_EQ(3, view.Depth()); + EXPECT_EQ(1000000000u, view.Size()); + EXPECT_EQ(0, std::memcmp(view.data(), randomData.data(), DATASIZE_BYTES)); +} + +TEST_F(DataNodeViewTest, DataBeginWorksWithOneByteEntries) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + uint8_t *blockBegin = (uint8_t*)block->data(); + DataNodeView view(std::move(block)); + + EXPECT_EQ(blockBegin+DataNodeLayout::HEADERSIZE_BYTES, view.DataBegin()); +} + +TEST_F(DataNodeViewTest, DataBeginWorksWithEightByteEntries) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + uint8_t *blockBegin = (uint8_t*)block->data(); + DataNodeView view(std::move(block)); + + EXPECT_EQ(blockBegin+DataNodeLayout::HEADERSIZE_BYTES, (uint8_t*)view.DataBegin()); +} + +TEST_F(DataNodeViewTest, DataEndWorksWithOneByteEntries) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + uint8_t *blockBegin = (uint8_t*)block->data(); + DataNodeView view(std::move(block)); + + EXPECT_EQ(blockBegin+BLOCKSIZE_BYTES, view.DataEnd()); +} + +TEST_F(DataNodeViewTest, DataEndWorksWithEightByteEntries) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + uint8_t *blockBegin = (uint8_t*)block->data(); + DataNodeView view(std::move(block)); + + EXPECT_EQ(blockBegin+BLOCKSIZE_BYTES, (uint8_t*)view.DataEnd()); +} + +struct SizedDataEntry { + uint8_t data[6]; +}; +BOOST_STATIC_ASSERT_MSG(DataNodeViewTest::DATASIZE_BYTES % sizeof(SizedDataEntry) != 0, + "This test case only makes sense, if the data entries don't fill up the whole space. " + "There should be some space left at the end that is not used, because it isn't enough space for a full entry. " + "If this static assertion fails, please use a different size for SizedDataEntry."); + +TEST_F(DataNodeViewTest, DataBeginWorksWithStructEntries) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + uint8_t *blockBegin = (uint8_t*)block->data(); + DataNodeView view(std::move(block)); + + EXPECT_EQ(blockBegin+DataNodeLayout::HEADERSIZE_BYTES, (uint8_t*)view.DataBegin()); +} + +TEST_F(DataNodeViewTest, DataEndWorksWithStructByteEntries) { + auto block = blockStore->create(Data(BLOCKSIZE_BYTES)); + uint8_t *blockBegin = (uint8_t*)block->data(); + DataNodeView view(std::move(block)); + + unsigned int numFittingEntries = DATASIZE_BYTES / sizeof(SizedDataEntry); + + uint8_t *dataEnd = (uint8_t*)view.DataEnd(); + EXPECT_EQ(blockBegin+DataNodeLayout::HEADERSIZE_BYTES + numFittingEntries * sizeof(SizedDataEntry), dataEnd); + EXPECT_LT(dataEnd, blockBegin + BLOCKSIZE_BYTES); +} + +//TODO Test that header fields (and data) are also stored over reloads diff --git a/test/blobstore/implementations/onblocks/datatreestore/DataTreeStoreTest.cpp b/test/blobstore/implementations/onblocks/datatreestore/DataTreeStoreTest.cpp new file mode 100644 index 00000000..01da3611 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/DataTreeStoreTest.cpp @@ -0,0 +1,54 @@ +#include "testutils/DataTreeTest.h" + +#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h" +#include "blobstore/implementations/onblocks/datatreestore/DataTreeStore.h" +#include +#include + +using blockstore::testfake::FakeBlockStore; +using blockstore::Key; +using blobstore::onblocks::datanodestore::DataNodeStore; +using boost::none; + +using namespace blobstore::onblocks::datatreestore; + +class DataTreeStoreTest: public DataTreeTest { +}; + +TEST_F(DataTreeStoreTest, CorrectKeyReturned) { + Key key = treeStore.createNewTree()->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(key, tree->key()); +} + +TEST_F(DataTreeStoreTest, CreatedTreeIsLoadable) { + auto key = treeStore.createNewTree()->key(); + auto loaded = treeStore.load(key); + EXPECT_NE(none, loaded); +} + +TEST_F(DataTreeStoreTest, NewTreeIsLeafOnly) { + auto tree = treeStore.createNewTree(); + + EXPECT_IS_LEAF_NODE(tree->key()); +} + +TEST_F(DataTreeStoreTest, TreeIsNotLoadableAfterRemove) { + Key key = treeStore.createNewTree()->key(); + auto tree = treeStore.load(key); + EXPECT_NE(none, tree); + treeStore.remove(std::move(*tree)); + EXPECT_EQ(none, treeStore.load(key)); +} + +TEST_F(DataTreeStoreTest, RemovingTreeRemovesAllNodesOfTheTree) { + auto key = CreateThreeLevelMinData()->key(); + auto tree1 = treeStore.load(key).value(); + auto tree2_key = treeStore.createNewTree()->key(); + + treeStore.remove(std::move(tree1)); + + //Check that the only remaining node is tree2 + EXPECT_EQ(1u, nodeStore->numNodes()); + EXPECT_NE(none, treeStore.load(tree2_key)); +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_NumStoredBytes.cpp b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_NumStoredBytes.cpp new file mode 100644 index 00000000..17c12a6d --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_NumStoredBytes.cpp @@ -0,0 +1,75 @@ +#include "testutils/DataTreeTest.h" +#include + +using ::testing::_; +using ::testing::WithParamInterface; +using ::testing::Values; + +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataNodeLayout; +using blobstore::onblocks::datatreestore::DataTree; +using blockstore::Key; + +class DataTreeTest_NumStoredBytes: public DataTreeTest { +public: +}; + +TEST_F(DataTreeTest_NumStoredBytes, CreatedTreeIsEmpty) { + auto tree = treeStore.createNewTree(); + EXPECT_EQ(0u, tree->numStoredBytes()); +} + +class DataTreeTest_NumStoredBytes_P: public DataTreeTest_NumStoredBytes, public WithParamInterface {}; +INSTANTIATE_TEST_CASE_P(EmptyLastLeaf, DataTreeTest_NumStoredBytes_P, Values(0u)); +INSTANTIATE_TEST_CASE_P(HalfFullLastLeaf, DataTreeTest_NumStoredBytes_P, Values(5u, 10u)); +INSTANTIATE_TEST_CASE_P(FullLastLeaf, DataTreeTest_NumStoredBytes_P, Values((uint32_t)DataNodeLayout(DataTreeTest_NumStoredBytes::BLOCKSIZE_BYTES).maxBytesPerLeaf())); + +TEST_P(DataTreeTest_NumStoredBytes_P, SingleLeaf) { + Key key = CreateLeafWithSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, TwoLeafTree) { + Key key = CreateTwoLeafWithSecondLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, FullTwolevelTree) { + Key key = CreateFullTwoLevelWithLastLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*(nodeStore->layout().maxChildrenPerInnerNode()-1) + GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, ThreeLevelTreeWithOneChild) { + Key key = CreateThreeLevelWithOneChildAndLastLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, ThreeLevelTreeWithTwoChildren) { + Key key = CreateThreeLevelWithTwoChildrenAndLastLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, ThreeLevelTreeWithThreeChildren) { + Key key = CreateThreeLevelWithThreeChildrenAndLastLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(2*nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, FullThreeLevelTree) { + Key key = CreateFullThreeLevelWithLastLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode()*(nodeStore->layout().maxChildrenPerInnerNode()-1) + nodeStore->layout().maxBytesPerLeaf()*(nodeStore->layout().maxChildrenPerInnerNode()-1) + GetParam(), tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_NumStoredBytes_P, FourLevelMinDataTree) { + Key key = CreateFourLevelMinDataWithLastLeafSize(GetParam())->key(); + auto tree = treeStore.load(key).value(); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode() + GetParam(), tree->numStoredBytes()); +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_ResizeByTraversing.cpp b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_ResizeByTraversing.cpp new file mode 100644 index 00000000..9301d0e5 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_ResizeByTraversing.cpp @@ -0,0 +1,201 @@ +#include "testutils/DataTreeTest.h" +#include "testutils/TwoLevelDataFixture.h" +#include "blobstore/implementations/onblocks/utils/Math.h" +#include + +#include + +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Combine; +using std::tuple; +using std::get; +using std::function; +using std::mem_fn; +using cpputils::dynamic_pointer_move; + +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataNodeLayout; +using blobstore::onblocks::datatreestore::DataTree; +using blobstore::onblocks::utils::ceilDivision; +using blockstore::Key; +using cpputils::Data; +using boost::none; + +using cpputils::unique_ref; + +class DataTreeTest_ResizeByTraversing: public DataTreeTest { +public: + static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES); + + unique_ref CreateTree(unique_ref root) { + Key key = root->key(); + cpputils::destruct(std::move(root)); + return treeStore.load(key).value(); + } + + unique_ref CreateLeafTreeWithSize(uint32_t size) { + return CreateTree(CreateLeafWithSize(size)); + } + + unique_ref CreateTwoLeafTreeWithSecondLeafSize(uint32_t size) { + return CreateTree(CreateTwoLeafWithSecondLeafSize(size)); + } + + unique_ref CreateFullTwoLevelTreeWithLastLeafSize(uint32_t size) { + return CreateTree(CreateFullTwoLevelWithLastLeafSize(size)); + } + + unique_ref CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize(uint32_t size) { + return CreateTree(CreateThreeLevelWithTwoChildrenAndLastLeafSize(size)); + } + + unique_ref CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(uint32_t size) { + return CreateTree(CreateThreeLevelWithThreeChildrenAndLastLeafSize(size)); + } + + unique_ref CreateFullThreeLevelTreeWithLastLeafSize(uint32_t size) { + return CreateTree(CreateFullThreeLevelWithLastLeafSize(size)); + } + + unique_ref CreateFourLevelMinDataTreeWithLastLeafSize(uint32_t size) { + return CreateTree(CreateFourLevelMinDataWithLastLeafSize(size)); + } + + void EXPECT_IS_LEFTMAXDATA_TREE(const Key &key) { + auto root = nodeStore->load(key).value(); + DataInnerNode *inner = dynamic_cast(root.get()); + if (inner != nullptr) { + for (uint32_t i = 0; i < inner->numChildren()-1; ++i) { + EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key()); + } + EXPECT_IS_LEFTMAXDATA_TREE(inner->LastChild()->key()); + } + } + + void EXPECT_IS_MAXDATA_TREE(const Key &key) { + auto root = nodeStore->load(key).value(); + DataInnerNode *inner = dynamic_cast(root.get()); + if (inner != nullptr) { + for (uint32_t i = 0; i < inner->numChildren(); ++i) { + EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key()); + } + } else { + DataLeafNode *leaf = dynamic_cast(root.get()); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf->numBytes()); + } + } +}; +constexpr DataNodeLayout DataTreeTest_ResizeByTraversing::LAYOUT; + +class DataTreeTest_ResizeByTraversing_P: public DataTreeTest_ResizeByTraversing, public WithParamInterface(DataTreeTest_ResizeByTraversing*, uint32_t)>, uint32_t, uint32_t>> { +public: + DataTreeTest_ResizeByTraversing_P() + : oldLastLeafSize(get<1>(GetParam())), + tree(get<0>(GetParam())(this, oldLastLeafSize)), + numberOfLeavesToAdd(get<2>(GetParam())), + newNumberOfLeaves(tree->numLeaves()+numberOfLeavesToAdd), + ZEROES(LAYOUT.maxBytesPerLeaf()) + { + ZEROES.FillWithZeroes(); + } + + void GrowTree(const Key &key, uint32_t numLeavesToAdd) { + auto tree = treeStore.load(key); + GrowTree(tree.get().get(), numLeavesToAdd); + } + + void GrowTree(DataTree *tree, uint32_t numLeavesToAdd) { + uint32_t oldNumLeaves = tree->numLeaves(); + uint32_t newNumLeaves = oldNumLeaves + numLeavesToAdd; + //TODO Test cases where beginIndex is inside of the existing leaves + tree->traverseLeaves(newNumLeaves-1, newNumLeaves, [] (DataLeafNode*,uint32_t){}); + tree->flush(); + } + + unique_ref LastLeaf(const Key &key) { + auto root = nodeStore->load(key).value(); + auto leaf = dynamic_pointer_move(root); + if (leaf != none) { + return std::move(*leaf); + } + auto inner = dynamic_pointer_move(root).value(); + return LastLeaf(inner->LastChild()->key()); + } + + uint32_t oldLastLeafSize; + unique_ref tree; + uint32_t numberOfLeavesToAdd; + uint32_t newNumberOfLeaves; + Data ZEROES; +}; +INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeByTraversing_P, DataTreeTest_ResizeByTraversing_P, + Combine( + //Tree we're starting with + Values(DataTreeTest_ResizeByTraversing*, uint32_t)>>( + mem_fn(&DataTreeTest_ResizeByTraversing::CreateLeafTreeWithSize), + mem_fn(&DataTreeTest_ResizeByTraversing::CreateTwoLeafTreeWithSecondLeafSize), + mem_fn(&DataTreeTest_ResizeByTraversing::CreateFullTwoLevelTreeWithLastLeafSize), + mem_fn(&DataTreeTest_ResizeByTraversing::CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize), + mem_fn(&DataTreeTest_ResizeByTraversing::CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize), + mem_fn(&DataTreeTest_ResizeByTraversing::CreateFullThreeLevelTreeWithLastLeafSize), + mem_fn(&DataTreeTest_ResizeByTraversing::CreateFourLevelMinDataTreeWithLastLeafSize) + ), + //Last leaf size of the start tree + Values( + 0u, + 1u, + 10u, + DataTreeTest_ResizeByTraversing::LAYOUT.maxBytesPerLeaf() + ), + //Number of leaves we're adding + Values( + 1u, + 2u, + DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full two level tree + 2* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with two children + 3* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with three children + DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full three level tree + DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() + 1 //Four level mindata tree + ) + ) +); + +TEST_P(DataTreeTest_ResizeByTraversing_P, StructureIsValid) { + GrowTree(tree.get(), numberOfLeavesToAdd); + EXPECT_IS_LEFTMAXDATA_TREE(tree->key()); +} + +TEST_P(DataTreeTest_ResizeByTraversing_P, NumBytesIsCorrect) { + GrowTree(tree.get(), numberOfLeavesToAdd); + // tree->numLeaves() only goes down the right border nodes and expects the tree to be a left max data tree. + // This is what the StructureIsValid test case is for. + EXPECT_EQ(newNumberOfLeaves, tree->numLeaves()); +} + +TEST_P(DataTreeTest_ResizeByTraversing_P, DepthFlagsAreCorrect) { + GrowTree(tree.get(), numberOfLeavesToAdd); + uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode())); + CHECK_DEPTH(depth, tree->key()); +} + +TEST_P(DataTreeTest_ResizeByTraversing_P, KeyDoesntChange) { + Key key = tree->key(); + tree->flush(); + GrowTree(tree.get(), numberOfLeavesToAdd); + EXPECT_EQ(key, tree->key()); +} + +TEST_P(DataTreeTest_ResizeByTraversing_P, DataStaysIntact) { + uint32_t oldNumberOfLeaves = std::max(UINT64_C(1), ceilDivision(tree->numStoredBytes(), (uint64_t)nodeStore->layout().maxBytesPerLeaf())); + TwoLevelDataFixture data(nodeStore, TwoLevelDataFixture::SizePolicy::Unchanged); + Key key = tree->key(); + cpputils::destruct(std::move(tree)); + data.FillInto(nodeStore->load(key).get().get()); + + GrowTree(key, newNumberOfLeaves); + + data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), oldNumberOfLeaves, oldLastLeafSize); +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_ResizeNumBytes.cpp b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_ResizeNumBytes.cpp new file mode 100644 index 00000000..b5c6a4e4 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_ResizeNumBytes.cpp @@ -0,0 +1,226 @@ +#include "testutils/DataTreeTest.h" +#include "testutils/TwoLevelDataFixture.h" +#include "blobstore/implementations/onblocks/utils/Math.h" +#include + +#include + +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Combine; +using std::tuple; +using std::get; +using std::function; +using std::mem_fn; +using cpputils::dynamic_pointer_move; + +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataNodeLayout; +using blobstore::onblocks::datatreestore::DataTree; +using blobstore::onblocks::utils::ceilDivision; +using blockstore::Key; +using cpputils::Data; +using boost::none; + +using cpputils::unique_ref; + +class DataTreeTest_ResizeNumBytes: public DataTreeTest { +public: + static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES); + + unique_ref CreateTree(unique_ref root) { + Key key = root->key(); + cpputils::destruct(std::move(root)); + return treeStore.load(key).value(); + } + + unique_ref CreateLeafTreeWithSize(uint32_t size) { + return CreateTree(CreateLeafWithSize(size)); + } + + unique_ref CreateTwoLeafTreeWithSecondLeafSize(uint32_t size) { + return CreateTree(CreateTwoLeafWithSecondLeafSize(size)); + } + + unique_ref CreateFullTwoLevelTreeWithLastLeafSize(uint32_t size) { + return CreateTree(CreateFullTwoLevelWithLastLeafSize(size)); + } + + unique_ref CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize(uint32_t size) { + return CreateTree(CreateThreeLevelWithTwoChildrenAndLastLeafSize(size)); + } + + unique_ref CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(uint32_t size) { + return CreateTree(CreateThreeLevelWithThreeChildrenAndLastLeafSize(size)); + } + + unique_ref CreateFullThreeLevelTreeWithLastLeafSize(uint32_t size) { + return CreateTree(CreateFullThreeLevelWithLastLeafSize(size)); + } + + unique_ref CreateFourLevelMinDataTreeWithLastLeafSize(uint32_t size) { + return CreateTree(CreateFourLevelMinDataWithLastLeafSize(size)); + } + + void EXPECT_IS_LEFTMAXDATA_TREE(const Key &key) { + auto root = nodeStore->load(key).value(); + DataInnerNode *inner = dynamic_cast(root.get()); + if (inner != nullptr) { + for (uint32_t i = 0; i < inner->numChildren()-1; ++i) { + EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key()); + } + EXPECT_IS_LEFTMAXDATA_TREE(inner->LastChild()->key()); + } + } + + void EXPECT_IS_MAXDATA_TREE(const Key &key) { + auto root = nodeStore->load(key).value(); + DataInnerNode *inner = dynamic_cast(root.get()); + if (inner != nullptr) { + for (uint32_t i = 0; i < inner->numChildren(); ++i) { + EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key()); + } + } else { + DataLeafNode *leaf = dynamic_cast(root.get()); + EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf->numBytes()); + } + } +}; +constexpr DataNodeLayout DataTreeTest_ResizeNumBytes::LAYOUT; + +class DataTreeTest_ResizeNumBytes_P: public DataTreeTest_ResizeNumBytes, public WithParamInterface(DataTreeTest_ResizeNumBytes*, uint32_t)>, uint32_t, uint32_t, uint32_t>> { +public: + DataTreeTest_ResizeNumBytes_P() + : oldLastLeafSize(get<1>(GetParam())), + tree(get<0>(GetParam())(this, oldLastLeafSize)), + newNumberOfLeaves(get<2>(GetParam())), + newLastLeafSize(get<3>(GetParam())), + newSize((newNumberOfLeaves-1) * LAYOUT.maxBytesPerLeaf() + newLastLeafSize), + ZEROES(LAYOUT.maxBytesPerLeaf()) + { + ZEROES.FillWithZeroes(); + } + + void ResizeTree(const Key &key, uint64_t size) { + treeStore.load(key).get()->resizeNumBytes(size); + } + + unique_ref LastLeaf(const Key &key) { + auto root = nodeStore->load(key).value(); + auto leaf = dynamic_pointer_move(root); + if (leaf != none) { + return std::move(*leaf); + } + auto inner = dynamic_pointer_move(root).value(); + return LastLeaf(inner->LastChild()->key()); + } + + uint32_t oldLastLeafSize; + unique_ref tree; + uint32_t newNumberOfLeaves; + uint32_t newLastLeafSize; + uint64_t newSize; + Data ZEROES; +}; +INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeNumBytes_P, DataTreeTest_ResizeNumBytes_P, + Combine( + //Tree we're starting with + Values(DataTreeTest_ResizeNumBytes*, uint32_t)>>( + mem_fn(&DataTreeTest_ResizeNumBytes::CreateLeafTreeWithSize), + mem_fn(&DataTreeTest_ResizeNumBytes::CreateTwoLeafTreeWithSecondLeafSize), + mem_fn(&DataTreeTest_ResizeNumBytes::CreateFullTwoLevelTreeWithLastLeafSize), + mem_fn(&DataTreeTest_ResizeNumBytes::CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize), + mem_fn(&DataTreeTest_ResizeNumBytes::CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize), + mem_fn(&DataTreeTest_ResizeNumBytes::CreateFullThreeLevelTreeWithLastLeafSize), + mem_fn(&DataTreeTest_ResizeNumBytes::CreateFourLevelMinDataTreeWithLastLeafSize) + ), + //Last leaf size of the start tree + Values( + 0u, + 1u, + 10u, + DataTreeTest_ResizeNumBytes::LAYOUT.maxBytesPerLeaf() + ), + //Number of leaves we're resizing to + Values( + 1u, + 2u, + DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Full two level tree + 2* DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with two children + 3* DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with three children + DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Full three level tree + DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode() + 1 //Four level mindata tree + ), + //Last leaf size of the resized tree + Values( + 1u, + 10u, + DataTreeTest_ResizeNumBytes::LAYOUT.maxBytesPerLeaf() + ) + ) +); + +TEST_P(DataTreeTest_ResizeNumBytes_P, StructureIsValid) { + tree->resizeNumBytes(newSize); + tree->flush(); + EXPECT_IS_LEFTMAXDATA_TREE(tree->key()); +} + +TEST_P(DataTreeTest_ResizeNumBytes_P, NumBytesIsCorrect) { + tree->resizeNumBytes(newSize); + tree->flush(); + // tree->numStoredBytes() only goes down the right border nodes and expects the tree to be a left max data tree. + // This is what the StructureIsValid test case is for. + EXPECT_EQ(newSize, tree->numStoredBytes()); +} + +TEST_P(DataTreeTest_ResizeNumBytes_P, DepthFlagsAreCorrect) { + tree->resizeNumBytes(newSize); + tree->flush(); + uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode())); + CHECK_DEPTH(depth, tree->key()); +} + +TEST_P(DataTreeTest_ResizeNumBytes_P, KeyDoesntChange) { + Key key = tree->key(); + tree->flush(); + tree->resizeNumBytes(newSize); + EXPECT_EQ(key, tree->key()); +} + +TEST_P(DataTreeTest_ResizeNumBytes_P, DataStaysIntact) { + uint32_t oldNumberOfLeaves = std::max(UINT64_C(1), ceilDivision(tree->numStoredBytes(), (uint64_t)nodeStore->layout().maxBytesPerLeaf())); + TwoLevelDataFixture data(nodeStore, TwoLevelDataFixture::SizePolicy::Unchanged); + Key key = tree->key(); + cpputils::destruct(std::move(tree)); + data.FillInto(nodeStore->load(key).get().get()); + + ResizeTree(key, newSize); + + if (oldNumberOfLeaves < newNumberOfLeaves || (oldNumberOfLeaves == newNumberOfLeaves && oldLastLeafSize < newLastLeafSize)) { + data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), oldNumberOfLeaves, oldLastLeafSize); + } else { + data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), newNumberOfLeaves, newLastLeafSize); + } +} + +//Resize to zero is not caught in the parametrized test above, in the following, we test it separately. + +TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_NumBytesIsCorrect) { + auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u); + tree->resizeNumBytes(0); + Key key = tree->key(); + cpputils::destruct(std::move(tree)); + auto leaf = LoadLeafNode(key); + EXPECT_EQ(0u, leaf->numBytes()); +} + +TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_KeyDoesntChange) { + auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u); + Key key = tree->key(); + tree->resizeNumBytes(0); + tree->flush(); + EXPECT_EQ(key, tree->key()); +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_TraverseLeaves.cpp b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_TraverseLeaves.cpp new file mode 100644 index 00000000..fdc1a109 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/DataTreeTest_TraverseLeaves.cpp @@ -0,0 +1,386 @@ +#include "testutils/DataTreeTest.h" +#include + +using ::testing::_; + +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datatreestore::DataTree; +using blockstore::Key; + +using cpputils::unique_ref; + +class TraversorMock { +public: + MOCK_METHOD2(called, void(DataLeafNode*, uint32_t)); +}; + +MATCHER_P(KeyEq, expected, "node key equals") { + return arg->key() == expected; +} + +class DataTreeTest_TraverseLeaves: public DataTreeTest { +public: + DataTreeTest_TraverseLeaves() :traversor() {} + + unique_ref CreateThreeLevel() { + return CreateInner({ + CreateFullTwoLevel(), + CreateFullTwoLevel(), + CreateFullTwoLevel(), + CreateFullTwoLevel(), + CreateFullTwoLevel(), + CreateInner({CreateLeaf(), CreateLeaf(), CreateLeaf()})}); + } + + unique_ref CreateFourLevel() { + return CreateInner({ + CreateFullThreeLevel(), + CreateFullThreeLevel(), + CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf()})}) + }); + } + + void EXPECT_TRAVERSE_LEAF(const Key &key, uint32_t leafIndex) { + EXPECT_CALL(traversor, called(KeyEq(key), leafIndex)).Times(1); + } + + void EXPECT_TRAVERSE_ALL_CHILDREN_OF(const DataInnerNode &node, uint32_t firstLeafIndex) { + for (unsigned int i = 0; i < node.numChildren(); ++i) { + EXPECT_TRAVERSE_LEAF(node.getChild(i)->key(), firstLeafIndex+i); + } + } + + void EXPECT_DONT_TRAVERSE_ANY_LEAVES() { + EXPECT_CALL(traversor, called(_, _)).Times(0); + } + + void TraverseLeaves(DataNode *root, uint32_t beginIndex, uint32_t endIndex) { + root->flush(); + auto tree = treeStore.load(root->key()).value(); + tree->traverseLeaves(beginIndex, endIndex, [this] (DataLeafNode *leaf, uint32_t nodeIndex) { + traversor.called(leaf, nodeIndex); + }); + } + + TraversorMock traversor; +}; + +TEST_F(DataTreeTest_TraverseLeaves, TraverseSingleLeafTree) { + auto root = CreateLeaf(); + EXPECT_TRAVERSE_LEAF(root->key(), 0); + + TraverseLeaves(root.get(), 0, 1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInSingleLeafTree1) { + auto root = CreateLeaf(); + EXPECT_DONT_TRAVERSE_ANY_LEAVES(); + + TraverseLeaves(root.get(), 0, 0); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInSingleLeafTree2) { + auto root = CreateLeaf(); + EXPECT_DONT_TRAVERSE_ANY_LEAVES(); + + TraverseLeaves(root.get(), 1, 1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstLeafOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + EXPECT_TRAVERSE_LEAF(root->getChild(0)->key(), 0); + + TraverseLeaves(root.get(), 0, 1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddleLeafOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + EXPECT_TRAVERSE_LEAF(root->getChild(5)->key(), 5); + + TraverseLeaves(root.get(), 5, 6); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseLastLeafOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + EXPECT_TRAVERSE_LEAF(root->getChild(nodeStore->layout().maxChildrenPerInnerNode()-1)->key(), nodeStore->layout().maxChildrenPerInnerNode()-1); + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode()-1, nodeStore->layout().maxChildrenPerInnerNode()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInFullTwolevelTree1) { + auto root = CreateFullTwoLevel(); + EXPECT_DONT_TRAVERSE_ANY_LEAVES(); + + TraverseLeaves(root.get(), 0, 0); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInFullTwolevelTree2) { + auto root = CreateFullTwoLevel(); + EXPECT_DONT_TRAVERSE_ANY_LEAVES(); + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode(), nodeStore->layout().maxChildrenPerInnerNode()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstLeafOfThreeLevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(0)->key())->getChild(0)->key(), 0); + + TraverseLeaves(root.get(), 0, 1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddleLeafOfThreeLevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(0)->key())->getChild(5)->key(), 5); + + TraverseLeaves(root.get(), 5, 6); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseLastLeafOfThreeLevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(1)->key())->getChild(0)->key(), nodeStore->layout().maxChildrenPerInnerNode()); + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode(), nodeStore->layout().maxChildrenPerInnerNode()+1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseAllLeavesOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*root, 0); + + TraverseLeaves(root.get(), 0, nodeStore->layout().maxChildrenPerInnerNode()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseAllLeavesOfThreelevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(0)->key()), 0); + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(1)->key())->getChild(0)->key(), nodeStore->layout().maxChildrenPerInnerNode()); + + TraverseLeaves(root.get(), 0, nodeStore->layout().maxChildrenPerInnerNode()+1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstChildOfThreelevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(0)->key()), 0); + + TraverseLeaves(root.get(), 0, nodeStore->layout().maxChildrenPerInnerNode()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstPartOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + for (unsigned int i = 0; i < 5; ++i) { + EXPECT_TRAVERSE_LEAF(root->getChild(i)->key(), i); + } + + TraverseLeaves(root.get(), 0, 5); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseInnerPartOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + for (unsigned int i = 5; i < 10; ++i) { + EXPECT_TRAVERSE_LEAF(root->getChild(i)->key(), i); + } + + TraverseLeaves(root.get(), 5, 10); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseLastPartOfFullTwolevelTree) { + auto root = CreateFullTwoLevel(); + for (unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + EXPECT_TRAVERSE_LEAF(root->getChild(i)->key(), i); + } + + TraverseLeaves(root.get(), 5, nodeStore->layout().maxChildrenPerInnerNode()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstPartOfThreelevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + auto node = LoadInnerNode(root->getChild(0)->key()); + for (unsigned int i = 0; i < 5; ++i) { + EXPECT_TRAVERSE_LEAF(node->getChild(i)->key(), i); + } + + TraverseLeaves(root.get(), 0, 5); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseInnerPartOfThreelevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + auto node = LoadInnerNode(root->getChild(0)->key()); + for (unsigned int i = 5; i < 10; ++i) { + EXPECT_TRAVERSE_LEAF(node->getChild(i)->key(), i); + } + + TraverseLeaves(root.get(), 5, 10); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseLastPartOfThreelevelMinDataTree) { + auto root = CreateThreeLevelMinData(); + auto node = LoadInnerNode(root->getChild(0)->key()); + for (unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + EXPECT_TRAVERSE_LEAF(node->getChild(i)->key(), i); + } + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(1)->key())->getChild(0)->key(), nodeStore->layout().maxChildrenPerInnerNode()); + + TraverseLeaves(root.get(), 5, nodeStore->layout().maxChildrenPerInnerNode()+1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstLeafOfThreelevelTree) { + auto root = CreateThreeLevel(); + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(0)->key())->getChild(0)->key(), 0); + + TraverseLeaves(root.get(), 0, 1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseLastLeafOfThreelevelTree) { + auto root = CreateThreeLevel(); + uint32_t numLeaves = nodeStore->layout().maxChildrenPerInnerNode() * 5 + 3; + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->LastChild()->key())->LastChild()->key(), numLeaves-1); + + TraverseLeaves(root.get(), numLeaves-1, numLeaves); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddleLeafOfThreelevelTree) { + auto root = CreateThreeLevel(); + uint32_t wantedLeafIndex = nodeStore->layout().maxChildrenPerInnerNode() * 2 + 5; + EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->getChild(2)->key())->getChild(5)->key(), wantedLeafIndex); + + TraverseLeaves(root.get(), wantedLeafIndex, wantedLeafIndex+1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstPartOfThreelevelTree) { + auto root = CreateThreeLevel(); + //Traverse all leaves in the first two children of the root + for(unsigned int i = 0; i < 2; ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse some of the leaves in the third child of the root + auto child = LoadInnerNode(root->getChild(2)->key()); + for(unsigned int i = 0; i < 5; ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), 2 * nodeStore->layout().maxChildrenPerInnerNode() + i); + } + + TraverseLeaves(root.get(), 0, 2 * nodeStore->layout().maxChildrenPerInnerNode() + 5); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddlePartOfThreelevelTree_OnlyFullChildren) { + auto root = CreateThreeLevel(); + //Traverse some of the leaves in the second child of the root + auto child = LoadInnerNode(root->getChild(1)->key()); + for(unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), nodeStore->layout().maxChildrenPerInnerNode() + i); + } + //Traverse all leaves in the third and fourth child of the root + for(unsigned int i = 2; i < 4; ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse some of the leaves in the fifth child of the root + child = LoadInnerNode(root->getChild(4)->key()); + for(unsigned int i = 0; i < 5; ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), 4 * nodeStore->layout().maxChildrenPerInnerNode() + i); + } + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode() + 5, 4 * nodeStore->layout().maxChildrenPerInnerNode() + 5); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddlePartOfThreelevelTree_AlsoLastNonfullChild) { + auto root = CreateThreeLevel(); + //Traverse some of the leaves in the second child of the root + auto child = LoadInnerNode(root->getChild(1)->key()); + for(unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), nodeStore->layout().maxChildrenPerInnerNode() + i); + } + //Traverse all leaves in the third, fourth and fifth child of the root + for(unsigned int i = 2; i < 5; ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse some of the leaves in the sixth child of the root + child = LoadInnerNode(root->getChild(5)->key()); + for(unsigned int i = 0; i < 2; ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), 5 * nodeStore->layout().maxChildrenPerInnerNode() + i); + } + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode() + 5, 5 * nodeStore->layout().maxChildrenPerInnerNode() + 2); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseLastPartOfThreelevelTree) { + auto root = CreateThreeLevel(); + //Traverse some of the leaves in the second child of the root + auto child = LoadInnerNode(root->getChild(1)->key()); + for(unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), nodeStore->layout().maxChildrenPerInnerNode() + i); + } + //Traverse all leaves in the third, fourth and fifth child of the root + for(unsigned int i = 2; i < 5; ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse all of the leaves in the sixth child of the root + child = LoadInnerNode(root->getChild(5)->key()); + for(unsigned int i = 0; i < child->numChildren(); ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), 5 * nodeStore->layout().maxChildrenPerInnerNode() + i); + } + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode() + 5, 5 * nodeStore->layout().maxChildrenPerInnerNode() + child->numChildren()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseAllLeavesOfThreelevelTree) { + auto root = CreateThreeLevel(); + //Traverse all leaves in the third, fourth and fifth child of the root + for(unsigned int i = 0; i < 5; ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse all of the leaves in the sixth child of the root + auto child = LoadInnerNode(root->getChild(5)->key()); + for(unsigned int i = 0; i < child->numChildren(); ++i) { + EXPECT_TRAVERSE_LEAF(child->getChild(i)->key(), 5 * nodeStore->layout().maxChildrenPerInnerNode() + i); + } + + TraverseLeaves(root.get(), 0, 5 * nodeStore->layout().maxChildrenPerInnerNode() + child->numChildren()); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseAllLeavesOfFourLevelTree) { + auto root = CreateFourLevel(); + //Traverse all leaves of the full threelevel tree in the first child + auto firstChild = LoadInnerNode(root->getChild(0)->key()); + for(unsigned int i = 0; i < firstChild->numChildren(); ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(firstChild->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse all leaves of the full threelevel tree in the second child + auto secondChild = LoadInnerNode(root->getChild(1)->key()); + for(unsigned int i = 0; i < secondChild->numChildren(); ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(secondChild->getChild(i)->key()), (nodeStore->layout().maxChildrenPerInnerNode() + i) * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse all leaves of the non-full threelevel tree in the third child + auto thirdChild = LoadInnerNode(root->getChild(2)->key()); + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(thirdChild->getChild(0)->key()), 2 * nodeStore->layout().maxChildrenPerInnerNode() * nodeStore->layout().maxChildrenPerInnerNode()); + EXPECT_TRAVERSE_LEAF(LoadInnerNode(thirdChild->getChild(1)->key())->getChild(0)->key(), 2 * nodeStore->layout().maxChildrenPerInnerNode() * nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode()); + + TraverseLeaves(root.get(), 0, 2*nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode() + 1); +} + +TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddlePartOfFourLevelTree) { + auto root = CreateFourLevel(); + //Traverse some leaves of the full threelevel tree in the first child + auto firstChild = LoadInnerNode(root->getChild(0)->key()); + auto secondChildOfFirstChild = LoadInnerNode(firstChild->getChild(1)->key()); + for(unsigned int i = 5; i < secondChildOfFirstChild->numChildren(); ++i) { + EXPECT_TRAVERSE_LEAF(secondChildOfFirstChild->getChild(i)->key(), nodeStore->layout().maxChildrenPerInnerNode()+i); + } + for(unsigned int i = 2; i < firstChild->numChildren(); ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(firstChild->getChild(i)->key()), i * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse all leaves of the full threelevel tree in the second child + auto secondChild = LoadInnerNode(root->getChild(1)->key()); + for(unsigned int i = 0; i < secondChild->numChildren(); ++i) { + EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(secondChild->getChild(i)->key()), (nodeStore->layout().maxChildrenPerInnerNode() + i) * nodeStore->layout().maxChildrenPerInnerNode()); + } + //Traverse some leaves of the non-full threelevel tree in the third child + auto thirdChild = LoadInnerNode(root->getChild(2)->key()); + auto firstChildOfThirdChild = LoadInnerNode(thirdChild->getChild(0)->key()); + for(unsigned int i = 0; i < firstChildOfThirdChild->numChildren()-1; ++i) { + EXPECT_TRAVERSE_LEAF(firstChildOfThirdChild->getChild(i)->key(), 2 * nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode()+i); + } + + TraverseLeaves(root.get(), nodeStore->layout().maxChildrenPerInnerNode()+5, 2*nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode() -1); +} + +//TODO Refactor the test cases that are too long diff --git a/test/blobstore/implementations/onblocks/datatreestore/impl/GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest.cpp b/test/blobstore/implementations/onblocks/datatreestore/impl/GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest.cpp new file mode 100644 index 00000000..11445157 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/impl/GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest.cpp @@ -0,0 +1,93 @@ +#include + +#include "../testutils/DataTreeTest.h" +#include "blobstore/implementations/onblocks/datatreestore/DataTree.h" +#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h" +#include +#include "blobstore/implementations/onblocks/datatreestore/impl/algorithms.h" + +using ::testing::Test; +using std::pair; +using std::make_pair; + +using blobstore::onblocks::datanodestore::DataNodeStore; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blockstore::testfake::FakeBlockStore; +using blockstore::Key; +using namespace blobstore::onblocks::datatreestore::algorithms; + +class GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest: public DataTreeTest { +public: + struct TestData { + Key rootNode; + Key expectedResult; + }; + + void check(const TestData &testData) { + auto root = nodeStore->load(testData.rootNode).value(); + auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, root.get()); + EXPECT_EQ(testData.expectedResult, result->key()); + } + + TestData CreateTwoRightBorderNodes() { + auto node = CreateInner({CreateLeaf()}); + return TestData{node->key(), node->key()}; + } + + TestData CreateThreeRightBorderNodes() { + auto node = CreateInner({CreateLeaf()}); + auto root = CreateInner({node.get()}); + return TestData{root->key(), node->key()}; + } + + TestData CreateThreeRightBorderNodes_LastFull() { + auto root = CreateInner({CreateFullTwoLevel()}); + return TestData{root->key(), root->key()}; + } + + TestData CreateLargerTree() { + auto node = CreateInner({CreateLeaf(), CreateLeaf()}); + auto root = CreateInner({CreateFullTwoLevel().get(), node.get()}); + return TestData{root->key(), node->key()}; + } +}; + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, Leaf) { + auto leaf = nodeStore->createNewLeafNode(); + auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, leaf.get()); + EXPECT_EQ(nullptr, result.get()); +} + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, TwoRightBorderNodes) { + auto testData = CreateTwoRightBorderNodes(); + check(testData); +} + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, ThreeRightBorderNodes) { + auto testData = CreateThreeRightBorderNodes(); + check(testData); +} + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, ThreeRightBorderNodes_LastFull) { + auto testData = CreateThreeRightBorderNodes_LastFull(); + check(testData); +} + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, LargerTree) { + auto testData = CreateLargerTree(); + check(testData); +} + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, FullTwoLevelTree) { + auto root = CreateFullTwoLevel(); + auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, root.get()); + EXPECT_EQ(nullptr, result.get()); +} + +TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, FullThreeLevelTree) { + auto root = CreateFullThreeLevel(); + auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, root.get()); + EXPECT_EQ(nullptr, result.get()); +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/impl/GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest.cpp b/test/blobstore/implementations/onblocks/datatreestore/impl/GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest.cpp new file mode 100644 index 00000000..1c2261a3 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/impl/GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest.cpp @@ -0,0 +1,126 @@ +#include + +#include "../testutils/DataTreeTest.h" +#include "blobstore/implementations/onblocks/datatreestore/DataTree.h" +#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h" +#include +#include "blobstore/implementations/onblocks/datatreestore/impl/algorithms.h" + +using ::testing::Test; +using std::pair; +using std::make_pair; + +using blobstore::onblocks::datanodestore::DataNodeStore; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blockstore::testfake::FakeBlockStore; +using blockstore::Key; +using namespace blobstore::onblocks::datatreestore::algorithms; + +class GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest: public DataTreeTest { +public: + struct TestData { + Key rootNode; + Key expectedResult; + }; + + void check(const TestData &testData) { + auto root = nodeStore->load(testData.rootNode).value(); + auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, root.get()); + EXPECT_EQ(testData.expectedResult, result->key()); + } + + Key CreateLeafOnlyTree() { + return CreateLeaf()->key(); + } + + Key CreateTwoRightBorderNodes() { + return CreateInner({CreateLeaf()})->key(); + } + + Key CreateThreeRightBorderNodes() { + return CreateInner({CreateInner({CreateLeaf()})})->key(); + } + + TestData CreateThreeRightBorderNodes_LastFull() { + auto node = CreateFullTwoLevel(); + auto root = CreateInner({node.get()}); + return TestData{root->key(), node->key()}; + } + + TestData CreateLargerTree() { + auto node = CreateInner({CreateLeaf(), CreateLeaf()}); + auto root = CreateInner({CreateFullTwoLevel().get(), node.get()}); + return TestData{root->key(), node->key()}; + } + + TestData CreateThreeLevelTreeWithRightBorderSingleNodeChain() { + auto root = CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf()})}); + return TestData{root->key(), root->key()}; + } + + TestData CreateThreeLevelTree() { + auto node = CreateInner({CreateLeaf(), CreateLeaf()}); + auto root = CreateInner({CreateFullTwoLevel().get(), node.get()}); + return TestData{root->key(), node->key()}; + } + + TestData CreateFullTwoLevelTree() { + auto node = CreateFullTwoLevel(); + return TestData{node->key(), node->key()}; + } + + TestData CreateFullThreeLevelTree() { + auto root = CreateFullThreeLevel(); + return TestData{root->key(), root->LastChild()->key()}; + } +}; + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, Leaf) { + auto leaf = nodeStore->load(CreateLeafOnlyTree()).value(); + auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, leaf.get()); + EXPECT_EQ(nullptr, result.get()); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, TwoRightBorderNodes) { + auto node = nodeStore->load(CreateTwoRightBorderNodes()).value(); + auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, node.get()); + EXPECT_EQ(nullptr, result.get()); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeRightBorderNodes) { + auto node = nodeStore->load(CreateThreeRightBorderNodes()).value(); + auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, node.get()); + EXPECT_EQ(nullptr, result.get()); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeRightBorderNodes_LastFull) { + auto testData = CreateThreeRightBorderNodes_LastFull(); + check(testData); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, LargerTree) { + auto testData = CreateLargerTree(); + check(testData); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, FullTwoLevelTree) { + auto testData = CreateFullTwoLevelTree(); + check(testData); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, FullThreeLevelTree) { + auto testData = CreateFullThreeLevelTree(); + check(testData); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeLevelTreeWithRightBorderSingleNodeChain) { + auto testData = CreateThreeLevelTreeWithRightBorderSingleNodeChain(); + check(testData); +} + +TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeLevelTree) { + auto testData = CreateThreeLevelTree(); + check(testData); +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/testutils/DataTreeTest.cpp b/test/blobstore/implementations/onblocks/datatreestore/testutils/DataTreeTest.cpp new file mode 100644 index 00000000..1d749182 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/testutils/DataTreeTest.cpp @@ -0,0 +1,235 @@ +#include "DataTreeTest.h" + +#include +#include +#include + +using blobstore::onblocks::datanodestore::DataNodeStore; +using blobstore::onblocks::datanodestore::DataNode; +using blobstore::onblocks::datanodestore::DataInnerNode; +using blobstore::onblocks::datanodestore::DataLeafNode; +using blobstore::onblocks::datatreestore::DataTree; +using blockstore::testfake::FakeBlockStore; +using blockstore::Key; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::initializer_list; +using std::vector; +using boost::none; +using cpputils::dynamic_pointer_move; + +constexpr uint32_t DataTreeTest::BLOCKSIZE_BYTES; + +DataTreeTest::DataTreeTest() + :_nodeStore(make_unique_ref(make_unique_ref(), BLOCKSIZE_BYTES)), + nodeStore(_nodeStore.get()), + treeStore(std::move(_nodeStore)) { +} + +unique_ref DataTreeTest::CreateLeaf() { + return nodeStore->createNewLeafNode(); +} + +unique_ref DataTreeTest::CreateInner(initializer_list> children) { + vector childrenVector(children.size()); + std::transform(children.begin(), children.end(), childrenVector.begin(), [](const unique_ref &ptr) {return ptr.get();}); + return CreateInner(childrenVector); +} + +unique_ref DataTreeTest::CreateInner(initializer_list children) { + return CreateInner(vector(children)); +} + +unique_ref DataTreeTest::CreateInner(vector children) { + ASSERT(children.size() >= 1, "An inner node must have at least one child"); + auto node = nodeStore->createNewInnerNode(**children.begin()); + for(auto child = children.begin()+1; child != children.end(); ++child) { + node->addChild(**child); + } + return node; +} + +unique_ref DataTreeTest::CreateLeafOnlyTree() { + auto key = CreateLeaf()->key(); + return treeStore.load(key).value(); +} + +void DataTreeTest::FillNode(DataInnerNode *node) { + for(unsigned int i=node->numChildren(); i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + node->addChild(*CreateLeaf()); + } +} + +void DataTreeTest::FillNodeTwoLevel(DataInnerNode *node) { + for(unsigned int i=node->numChildren(); i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) { + node->addChild(*CreateFullTwoLevel()); + } +} + +unique_ref DataTreeTest::CreateFullTwoLevel() { + auto root = CreateInner({CreateLeaf().get()}); + FillNode(root.get()); + return root; +} + +unique_ref DataTreeTest::CreateThreeLevelMinData() { + return CreateInner({ + CreateFullTwoLevel(), + CreateInner({CreateLeaf()}) + }); +} + +unique_ref DataTreeTest::CreateFourLevelMinData() { + return CreateInner({ + CreateFullThreeLevel(), + CreateInner({CreateInner({CreateLeaf()})}) + }); +} + +unique_ref DataTreeTest::CreateFullThreeLevel() { + auto root = CreateInner({CreateFullTwoLevel().get()}); + FillNodeTwoLevel(root.get()); + return root; +} + +unique_ref DataTreeTest::LoadInnerNode(const Key &key) { + auto node = nodeStore->load(key).value(); + auto casted = dynamic_pointer_move(node); + EXPECT_NE(none, casted) << "Is not an inner node"; + return std::move(*casted); +} + +unique_ref DataTreeTest::LoadLeafNode(const Key &key) { + auto node = nodeStore->load(key).value(); + auto casted = dynamic_pointer_move(node); + EXPECT_NE(none, casted) << "Is not a leaf node"; + return std::move(*casted); +} + +unique_ref DataTreeTest::CreateTwoLeaf() { + return CreateInner({CreateLeaf().get(), CreateLeaf().get()}); +} + +unique_ref DataTreeTest::CreateTwoLeafTree() { + auto key = CreateTwoLeaf()->key(); + return treeStore.load(key).value(); +} + +unique_ref DataTreeTest::CreateLeafWithSize(uint32_t size) { + auto leaf = CreateLeaf(); + leaf->resize(size); + return leaf; +} + +unique_ref DataTreeTest::CreateTwoLeafWithSecondLeafSize(uint32_t size) { + return CreateInner({ + CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()), + CreateLeafWithSize(size) + }); +} + +unique_ref DataTreeTest::CreateFullTwoLevelWithLastLeafSize(uint32_t size) { + auto root = CreateFullTwoLevel(); + for (uint32_t i = 0; i < root->numChildren()-1; ++i) { + LoadLeafNode(root->getChild(i)->key())->resize(nodeStore->layout().maxBytesPerLeaf()); + } + LoadLeafNode(root->LastChild()->key())->resize(size); + return root; +} + +unique_ref DataTreeTest::CreateThreeLevelWithOneChildAndLastLeafSize(uint32_t size) { + return CreateInner({ + CreateInner({ + CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()), + CreateLeafWithSize(size) + }) + }); +} + +unique_ref DataTreeTest::CreateThreeLevelWithTwoChildrenAndLastLeafSize(uint32_t size) { + return CreateInner({ + CreateFullTwoLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()), + CreateInner({ + CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()), + CreateLeafWithSize(size) + }) + }); +} + +unique_ref DataTreeTest::CreateThreeLevelWithThreeChildrenAndLastLeafSize(uint32_t size) { + return CreateInner({ + CreateFullTwoLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()), + CreateFullTwoLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()), + CreateInner({ + CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()), + CreateLeafWithSize(size) + }) + }); +} + +unique_ref DataTreeTest::CreateFullThreeLevelWithLastLeafSize(uint32_t size) { + auto root = CreateFullThreeLevel(); + for (uint32_t i = 0; i < root->numChildren(); ++i) { + auto node = LoadInnerNode(root->getChild(i)->key()); + for (uint32_t j = 0; j < node->numChildren(); ++j) { + LoadLeafNode(node->getChild(j)->key())->resize(nodeStore->layout().maxBytesPerLeaf()); + } + } + LoadLeafNode(LoadInnerNode(root->LastChild()->key())->LastChild()->key())->resize(size); + return root; +} + +unique_ref DataTreeTest::CreateFourLevelMinDataWithLastLeafSize(uint32_t size) { + return CreateInner({ + CreateFullThreeLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()), + CreateInner({CreateInner({CreateLeafWithSize(size)})}) + }); +} + +void DataTreeTest::EXPECT_IS_LEAF_NODE(const Key &key) { + auto node = LoadLeafNode(key); + EXPECT_NE(nullptr, node.get()); +} + +void DataTreeTest::EXPECT_IS_INNER_NODE(const Key &key) { + auto node = LoadInnerNode(key); + EXPECT_NE(nullptr, node.get()); +} + +void DataTreeTest::EXPECT_IS_TWONODE_CHAIN(const Key &key) { + auto node = LoadInnerNode(key); + EXPECT_EQ(1u, node->numChildren()); + EXPECT_IS_LEAF_NODE(node->getChild(0)->key()); +} + +void DataTreeTest::EXPECT_IS_FULL_TWOLEVEL_TREE(const Key &key) { + auto node = LoadInnerNode(key); + EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), node->numChildren()); + for (unsigned int i = 0; i < node->numChildren(); ++i) { + EXPECT_IS_LEAF_NODE(node->getChild(i)->key()); + } +} + +void DataTreeTest::EXPECT_IS_FULL_THREELEVEL_TREE(const Key &key) { + auto root = LoadInnerNode(key); + EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), root->numChildren()); + for (unsigned int i = 0; i < root->numChildren(); ++i) { + auto node = LoadInnerNode(root->getChild(i)->key()); + EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), node->numChildren()); + for (unsigned int j = 0; j < node->numChildren(); ++j) { + EXPECT_IS_LEAF_NODE(node->getChild(j)->key()); + } + } +} + +void DataTreeTest::CHECK_DEPTH(int depth, const Key &key) { + if (depth == 0) { + EXPECT_IS_LEAF_NODE(key); + } else { + auto node = LoadInnerNode(key); + EXPECT_EQ(depth, node->depth()); + for (uint32_t i = 0; i < node->numChildren(); ++i) { + CHECK_DEPTH(depth-1, node->getChild(i)->key()); + } + } +} diff --git a/test/blobstore/implementations/onblocks/datatreestore/testutils/DataTreeTest.h b/test/blobstore/implementations/onblocks/datatreestore/testutils/DataTreeTest.h new file mode 100644 index 00000000..0118c101 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/testutils/DataTreeTest.h @@ -0,0 +1,64 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_DATATREETEST_H_ +#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_DATATREETEST_H_ + +#include + +#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h" +#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h" +#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h" +#include "blobstore/implementations/onblocks/datatreestore/DataTree.h" +#include "blobstore/implementations/onblocks/datatreestore/DataTreeStore.h" + +class DataTreeTest: public ::testing::Test { +public: + DataTreeTest(); + + static constexpr uint32_t BLOCKSIZE_BYTES = 256; + + cpputils::unique_ref CreateLeaf(); + cpputils::unique_ref CreateInner(std::vector children); + cpputils::unique_ref CreateInner(std::initializer_list children); + cpputils::unique_ref CreateInner(std::initializer_list> children); + + cpputils::unique_ref CreateLeafOnlyTree(); + cpputils::unique_ref CreateTwoLeaf(); + cpputils::unique_ref CreateTwoLeafTree(); + void FillNode(blobstore::onblocks::datanodestore::DataInnerNode *node); + void FillNodeTwoLevel(blobstore::onblocks::datanodestore::DataInnerNode *node); + cpputils::unique_ref CreateFullTwoLevel(); + cpputils::unique_ref CreateFullThreeLevel(); + + cpputils::unique_ref CreateThreeLevelMinData(); + cpputils::unique_ref CreateFourLevelMinData(); + + cpputils::unique_ref LoadInnerNode(const blockstore::Key &key); + cpputils::unique_ref LoadLeafNode(const blockstore::Key &key); + + cpputils::unique_ref CreateLeafWithSize(uint32_t size); + cpputils::unique_ref CreateTwoLeafWithSecondLeafSize(uint32_t size); + cpputils::unique_ref CreateFullTwoLevelWithLastLeafSize(uint32_t size); + cpputils::unique_ref CreateThreeLevelWithOneChildAndLastLeafSize(uint32_t size); + cpputils::unique_ref CreateThreeLevelWithTwoChildrenAndLastLeafSize(uint32_t size); + cpputils::unique_ref CreateThreeLevelWithThreeChildrenAndLastLeafSize(uint32_t size); + cpputils::unique_ref CreateFullThreeLevelWithLastLeafSize(uint32_t size); + cpputils::unique_ref CreateFourLevelMinDataWithLastLeafSize(uint32_t size); + + cpputils::unique_ref _nodeStore; + blobstore::onblocks::datanodestore::DataNodeStore *nodeStore; + blobstore::onblocks::datatreestore::DataTreeStore treeStore; + + void EXPECT_IS_LEAF_NODE(const blockstore::Key &key); + void EXPECT_IS_INNER_NODE(const blockstore::Key &key); + void EXPECT_IS_TWONODE_CHAIN(const blockstore::Key &key); + void EXPECT_IS_FULL_TWOLEVEL_TREE(const blockstore::Key &key); + void EXPECT_IS_FULL_THREELEVEL_TREE(const blockstore::Key &key); + + void CHECK_DEPTH(int depth, const blockstore::Key &key); + +private: + DISALLOW_COPY_AND_ASSIGN(DataTreeTest); +}; + + +#endif diff --git a/test/blobstore/implementations/onblocks/datatreestore/testutils/LeafDataFixture.h b/test/blobstore/implementations/onblocks/datatreestore/testutils/LeafDataFixture.h new file mode 100644 index 00000000..4b00e533 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/testutils/LeafDataFixture.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_LEAFDATAFIXTURE_H_ +#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_LEAFDATAFIXTURE_H_ + +#include + +#include + +// A data fixture containing data for a leaf. +// The class can fill this data into a given leaf +// and check, whether the data stored in a given leaf is correct. +class LeafDataFixture { +public: + LeafDataFixture(int size, int iv = 0): _data(cpputils::DataFixture::generate(size, iv)) {} + + void FillInto(blobstore::onblocks::datanodestore::DataLeafNode *leaf) const { + leaf->resize(_data.size()); + leaf->write(_data.data(), 0, _data.size()); + } + + void EXPECT_DATA_CORRECT(const blobstore::onblocks::datanodestore::DataLeafNode &leaf, int onlyCheckNumBytes = -1) const { + if (onlyCheckNumBytes == -1) { + EXPECT_EQ(_data.size(), leaf.numBytes()); + EXPECT_EQ(0, std::memcmp(_data.data(), loadData(leaf).data(), _data.size())); + } else { + EXPECT_LE(onlyCheckNumBytes, (int)leaf.numBytes()); + EXPECT_EQ(0, std::memcmp(_data.data(), loadData(leaf).data(), onlyCheckNumBytes)); + } + } + +private: + static cpputils::Data loadData(const blobstore::onblocks::datanodestore::DataLeafNode &leaf) { + cpputils::Data data(leaf.numBytes()); + leaf.read(data.data(), 0, leaf.numBytes()); + return data; + } + cpputils::Data _data; +}; + +#endif diff --git a/test/blobstore/implementations/onblocks/datatreestore/testutils/TwoLevelDataFixture.h b/test/blobstore/implementations/onblocks/datatreestore/testutils/TwoLevelDataFixture.h new file mode 100644 index 00000000..c79599e0 --- /dev/null +++ b/test/blobstore/implementations/onblocks/datatreestore/testutils/TwoLevelDataFixture.h @@ -0,0 +1,86 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_ +#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_ + +#include +#include +#include "LeafDataFixture.h" +#include + +//TODO Rename, since we now allow any number of levels +// A data fixture containing data for a two-level tree (one inner node with leaf children). +// The class can fill this data into the leaf children of a given inner node +// and given an inner node can check, whether the data stored is correct. +class TwoLevelDataFixture { +public: + enum class SizePolicy { + Random, + Full, + Unchanged + }; + TwoLevelDataFixture(blobstore::onblocks::datanodestore::DataNodeStore *dataNodeStore, SizePolicy sizePolicy, int iv=0): _dataNodeStore(dataNodeStore), _iv(iv), _sizePolicy(sizePolicy) {} + + void FillInto(blobstore::onblocks::datanodestore::DataNode *node) { + // _iv-1 means there is no endLeafIndex - we fill all leaves. + ForEachLeaf(node, _iv, _iv-1, [this] (blobstore::onblocks::datanodestore::DataLeafNode *leaf, int leafIndex) { + LeafDataFixture(size(leafIndex, leaf), leafIndex).FillInto(leaf); + }); + } + + void EXPECT_DATA_CORRECT(blobstore::onblocks::datanodestore::DataNode *node, int maxCheckedLeaves = 0, int lastLeafMaxCheckedBytes = -1) { + ForEachLeaf(node, _iv, _iv+maxCheckedLeaves, [this, maxCheckedLeaves, lastLeafMaxCheckedBytes] (blobstore::onblocks::datanodestore::DataLeafNode *leaf, int leafIndex) { + if (leafIndex == _iv+maxCheckedLeaves-1) { + // It is the last leaf + LeafDataFixture(size(leafIndex, leaf), leafIndex).EXPECT_DATA_CORRECT(*leaf, lastLeafMaxCheckedBytes); + } else { + LeafDataFixture(size(leafIndex, leaf), leafIndex).EXPECT_DATA_CORRECT(*leaf); + } + }); + } + +private: + int ForEachLeaf(blobstore::onblocks::datanodestore::DataNode *node, int firstLeafIndex, int endLeafIndex, std::function action) { + if (firstLeafIndex == endLeafIndex) { + return firstLeafIndex; + } + auto leaf = dynamic_cast(node); + if (leaf != nullptr) { + action(leaf, firstLeafIndex); + return firstLeafIndex + 1; + } else { + auto inner = dynamic_cast(node); + int leafIndex = firstLeafIndex; + for (uint32_t i = 0; i < inner->numChildren(); ++i) { + auto child = _dataNodeStore->load(inner->getChild(i)->key()).value(); + leafIndex = ForEachLeaf(child.get(), leafIndex, endLeafIndex, action); + } + return leafIndex; + } + } + + blobstore::onblocks::datanodestore::DataNodeStore *_dataNodeStore; + int _iv; + SizePolicy _sizePolicy; + + int size(int childIndex, blobstore::onblocks::datanodestore::DataLeafNode *leaf) { + switch (_sizePolicy) { + case SizePolicy::Full: + return _dataNodeStore->layout().maxBytesPerLeaf(); + case SizePolicy::Random: + return mod(_dataNodeStore->layout().maxBytesPerLeaf() - childIndex, _dataNodeStore->layout().maxBytesPerLeaf()); + case SizePolicy::Unchanged: + return leaf->numBytes(); + default: + ASSERT(false, "Unknown size policy"); + } + } + + int mod(int value, int mod) { + while(value < 0) { + value += mod; + } + return value; + } +}; + +#endif diff --git a/test/blobstore/implementations/onblocks/testutils/BlobStoreTest.cpp b/test/blobstore/implementations/onblocks/testutils/BlobStoreTest.cpp new file mode 100644 index 00000000..abf443ec --- /dev/null +++ b/test/blobstore/implementations/onblocks/testutils/BlobStoreTest.cpp @@ -0,0 +1,14 @@ +#include "BlobStoreTest.h" + +#include +#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h" + +using blobstore::onblocks::BlobStoreOnBlocks; +using blockstore::testfake::FakeBlockStore; +using cpputils::make_unique_ref; + +constexpr uint32_t BlobStoreTest::BLOCKSIZE_BYTES; + +BlobStoreTest::BlobStoreTest() + : blobStore(make_unique_ref(make_unique_ref(), BLOCKSIZE_BYTES)) { +} diff --git a/test/blobstore/implementations/onblocks/testutils/BlobStoreTest.h b/test/blobstore/implementations/onblocks/testutils/BlobStoreTest.h new file mode 100644 index 00000000..3cf277df --- /dev/null +++ b/test/blobstore/implementations/onblocks/testutils/BlobStoreTest.h @@ -0,0 +1,29 @@ +#pragma once +#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_TESTUTILS_BLOBSTORETEST_H_ +#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_TESTUTILS_BLOBSTORETEST_H_ + +#include + +#include "blobstore/interface/BlobStore.h" + +class BlobStoreTest: public ::testing::Test { +public: + BlobStoreTest(); + + static constexpr uint32_t BLOCKSIZE_BYTES = 4096; + + cpputils::unique_ref blobStore; + + cpputils::unique_ref loadBlob(const blockstore::Key &key) { + auto loaded = blobStore->load(key); + EXPECT_TRUE((bool)loaded); + return std::move(*loaded); + } + + void reset(cpputils::unique_ref ref) { + UNUSED(ref); + //ref is moved into here and then destructed + } +}; + +#endif diff --git a/test/blobstore/implementations/onblocks/utils/CeilDivisionTest.cpp b/test/blobstore/implementations/onblocks/utils/CeilDivisionTest.cpp new file mode 100644 index 00000000..af863111 --- /dev/null +++ b/test/blobstore/implementations/onblocks/utils/CeilDivisionTest.cpp @@ -0,0 +1,88 @@ +#include +#include "blobstore/implementations/onblocks/utils/Math.h" + +#include + +using namespace blobstore::onblocks::utils; +using ::testing::Test; +using std::numeric_limits; + +class CeilDivisionTest: public Test {}; + +TEST_F(CeilDivisionTest, Divide0_4) { + EXPECT_EQ(0, ceilDivision(0, 4)); +} + +TEST_F(CeilDivisionTest, Divide1_4) { + EXPECT_EQ(1, ceilDivision(1, 4)); +} + +TEST_F(CeilDivisionTest, Divide2_4) { + EXPECT_EQ(1, ceilDivision(2, 4)); +} + +TEST_F(CeilDivisionTest, Divide3_4) { + EXPECT_EQ(1, ceilDivision(3, 4)); +} + +TEST_F(CeilDivisionTest, Divide4_4) { + EXPECT_EQ(1, ceilDivision(4, 4)); +} + +TEST_F(CeilDivisionTest, Divide5_4) { + EXPECT_EQ(2, ceilDivision(5, 4)); +} + +TEST_F(CeilDivisionTest, Divide6_4) { + EXPECT_EQ(2, ceilDivision(6, 4)); +} + +TEST_F(CeilDivisionTest, Divide7_4) { + EXPECT_EQ(2, ceilDivision(7, 4)); +} + +TEST_F(CeilDivisionTest, Divide8_4) { + EXPECT_EQ(2, ceilDivision(8, 4)); +} + +TEST_F(CeilDivisionTest, Divide9_4) { + EXPECT_EQ(3, ceilDivision(9, 4)); +} + +TEST_F(CeilDivisionTest, Divide0_1) { + EXPECT_EQ(0, ceilDivision(0, 1)); +} + +TEST_F(CeilDivisionTest, Divide5_1) { + EXPECT_EQ(5, ceilDivision(5, 1)); +} + +TEST_F(CeilDivisionTest, Divide7_2) { + EXPECT_EQ(4, ceilDivision(7, 2)); +} + +TEST_F(CeilDivisionTest, Divide8_2) { + EXPECT_EQ(4, ceilDivision(8, 2)); +} + +TEST_F(CeilDivisionTest, Divide9_2) { + EXPECT_EQ(5, ceilDivision(9, 2)); +} + +TEST_F(CeilDivisionTest, Divide1_1) { + EXPECT_EQ(1, ceilDivision(1, 1)); +} + +TEST_F(CeilDivisionTest, Divide5_5) { + EXPECT_EQ(1, ceilDivision(5, 5)); +} + +TEST_F(CeilDivisionTest, DivideLargeByItself) { + EXPECT_EQ(1, ceilDivision(183495303, 183495303)); +} + +TEST_F(CeilDivisionTest, 64bit) { + uint64_t base = UINT64_C(1024)*1024*1024*1024; + EXPECT_GT(base, std::numeric_limits::max()); + EXPECT_EQ(base/1024, ceilDivision(base, (uint64_t)1024)); +} diff --git a/test/blobstore/implementations/onblocks/utils/CeilLogTest.cpp b/test/blobstore/implementations/onblocks/utils/CeilLogTest.cpp new file mode 100644 index 00000000..064f1b7c --- /dev/null +++ b/test/blobstore/implementations/onblocks/utils/CeilLogTest.cpp @@ -0,0 +1,35 @@ +#include +#include "blobstore/implementations/onblocks/utils/Math.h" + +#include + +using namespace blobstore::onblocks::utils; +using ::testing::Test; +using std::numeric_limits; + +class CeilLogTest: public Test {}; + +TEST_F(CeilLogTest, Log3_1) { + EXPECT_EQ(0, ceilLog(3, 1)); +} + +TEST_F(CeilLogTest, Log3_2) { + EXPECT_EQ(1, ceilLog(3, 2)); +} + +TEST_F(CeilLogTest, Log3_3) { + EXPECT_EQ(1, ceilLog(3, 3)); +} + +TEST_F(CeilLogTest, Log3_4) { + EXPECT_EQ(2, ceilLog(3, 4)); +} + +TEST_F(CeilLogTest, 64bit) { + uint64_t value = UINT64_C(1024)*1024*1024*1024; + EXPECT_GT(value, std::numeric_limits::max()); + EXPECT_EQ(4u, ceilLog((uint64_t)1024, value)); +} + + +//TODO ... diff --git a/test/blobstore/implementations/onblocks/utils/IntPowTest.cpp b/test/blobstore/implementations/onblocks/utils/IntPowTest.cpp new file mode 100644 index 00000000..f7533b69 --- /dev/null +++ b/test/blobstore/implementations/onblocks/utils/IntPowTest.cpp @@ -0,0 +1,77 @@ +#include +#include "blobstore/implementations/onblocks/utils/Math.h" + +using namespace blobstore::onblocks::utils; +using ::testing::Test; + +class IntPowTest: public Test {}; + +TEST_F(IntPowTest, ExponentAndBaseAreZero) { + EXPECT_EQ(1, intPow(0, 0)); +} + +TEST_F(IntPowTest, ExponentIsZero1) { + EXPECT_EQ(1, intPow(1, 0)); +} + +TEST_F(IntPowTest, ExponentIsZero2) { + EXPECT_EQ(1, intPow(1000, 0)); +} + +TEST_F(IntPowTest, BaseIsZero1) { + EXPECT_EQ(0, intPow(0, 1)); +} + +TEST_F(IntPowTest, BaseIsZero2) { + EXPECT_EQ(0, intPow(0, 1000)); +} + +TEST_F(IntPowTest, ExponentIsOne1) { + EXPECT_EQ(0, intPow(0, 1)); +} + +TEST_F(IntPowTest, ExponentIsOne2) { + EXPECT_EQ(2, intPow(2, 1)); +} + +TEST_F(IntPowTest, ExponentIsOne3) { + EXPECT_EQ(1000, intPow(1000, 1)); +} + +TEST_F(IntPowTest, BaseIsTwo1) { + EXPECT_EQ(1024, intPow(2, 10)); +} + +TEST_F(IntPowTest, BaseIsTwo2) { + EXPECT_EQ(1024*1024, intPow(2, 20)); +} + +TEST_F(IntPowTest, BaseIsTwo3) { + EXPECT_EQ(1024*1024*1024, intPow(2, 30)); +} + +TEST_F(IntPowTest, BaseIsTen1) { + EXPECT_EQ(100, intPow(10, 2)); +} + +TEST_F(IntPowTest, BaseIsTen2) { + EXPECT_EQ(1000000, intPow(10, 6)); +} + +TEST_F(IntPowTest, ArbitraryNumbers1) { + EXPECT_EQ(4096, intPow(4, 6)); +} + +TEST_F(IntPowTest, ArbitraryNumbers2) { + EXPECT_EQ(1296, intPow(6, 4)); +} + +TEST_F(IntPowTest, ArbitraryNumbers3) { + EXPECT_EQ(282475249, intPow(7, 10)); +} + +TEST_F(IntPowTest, 64bit) { + uint64_t value = UINT64_C(1024)*1024*1024*1024; + EXPECT_GT(value, std::numeric_limits::max()); + EXPECT_EQ(value*value*value, intPow(value, (uint64_t)3)); +} \ No newline at end of file diff --git a/test/blobstore/implementations/onblocks/utils/MaxZeroSubtractionTest.cpp b/test/blobstore/implementations/onblocks/utils/MaxZeroSubtractionTest.cpp new file mode 100644 index 00000000..6a3b6fbd --- /dev/null +++ b/test/blobstore/implementations/onblocks/utils/MaxZeroSubtractionTest.cpp @@ -0,0 +1,90 @@ +#include +#include "blobstore/implementations/onblocks/utils/Math.h" + +#include + +using namespace blobstore::onblocks::utils; +using ::testing::Test; +using std::numeric_limits; + +class MaxZeroSubtractionTest: public Test {}; + +TEST_F(MaxZeroSubtractionTest, SubtractToZero1) { + EXPECT_EQ(0, maxZeroSubtraction(0, 0)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractToZero2) { + EXPECT_EQ(0, maxZeroSubtraction(5, 5)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractToZero3) { + EXPECT_EQ(0, maxZeroSubtraction(184930, 184930)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractToZero4) { + EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits::max()-1, numeric_limits::max()-1)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractToZero5) { + EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits::max(), numeric_limits::max())); +} + +TEST_F(MaxZeroSubtractionTest, SubtractPositive1) { + EXPECT_EQ(1, maxZeroSubtraction(5, 4)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractPositive2) { + EXPECT_EQ(181081, maxZeroSubtraction(184930, 3849)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractPositive3) { + EXPECT_EQ(numeric_limits::max()-1, maxZeroSubtraction(numeric_limits::max(), UINT32_C(1))); +} + +TEST_F(MaxZeroSubtractionTest, SubtractPositive4) { + EXPECT_EQ(5u, maxZeroSubtraction(numeric_limits::max(), numeric_limits::max()-5)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractNegative1) { + EXPECT_EQ(0, maxZeroSubtraction(4, 5)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractNegative2) { + EXPECT_EQ(0, maxZeroSubtraction(3849, 184930)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractNegative3) { + EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits::max()-1, numeric_limits::max())); +} + +TEST_F(MaxZeroSubtractionTest, SubtractNegative4) { + EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits::max()-5, numeric_limits::max())); +} + +TEST_F(MaxZeroSubtractionTest, SubtractNegative5) { + EXPECT_EQ(0u, maxZeroSubtraction(UINT32_C(5), numeric_limits::max())); +} + +TEST_F(MaxZeroSubtractionTest, SubtractFromZero1) { + EXPECT_EQ(0, maxZeroSubtraction(0, 1)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractFromZero2) { + EXPECT_EQ(0, maxZeroSubtraction(0, 184930)); +} + +TEST_F(MaxZeroSubtractionTest, SubtractFromZero3) { + EXPECT_EQ(0u, maxZeroSubtraction(UINT32_C(0), numeric_limits::max())); +} + +TEST_F(MaxZeroSubtractionTest, 64bit_valid) { + uint64_t value = UINT64_C(1024)*1024*1024*1024; + EXPECT_GT(value, std::numeric_limits::max()); + EXPECT_EQ(value*1024-value, maxZeroSubtraction(value*1024, value)); +} + +TEST_F(MaxZeroSubtractionTest, 64bit_zero) { + uint64_t value = UINT64_C(1024)*1024*1024*1024; + EXPECT_GT(value, std::numeric_limits::max()); + EXPECT_EQ(0u, maxZeroSubtraction(value, value*1024)); +} diff --git a/test/blockstore/CMakeLists.txt b/test/blockstore/CMakeLists.txt new file mode 100644 index 00000000..db55fdd2 --- /dev/null +++ b/test/blockstore/CMakeLists.txt @@ -0,0 +1,41 @@ +project (blockstore-test) + +set(SOURCES + utils/BlockStoreUtilsTest.cpp + interface/helpers/BlockStoreWithRandomKeysTest.cpp + interface/BlockStoreTest.cpp + interface/BlockTest.cpp + implementations/testfake/TestFakeBlockStoreTest.cpp + implementations/inmemory/InMemoryBlockStoreTest.cpp + implementations/parallelaccess/ParallelAccessBlockStoreTest.cpp + implementations/compressing/CompressingBlockStoreTest.cpp + implementations/compressing/compressors/testutils/CompressorTest.cpp + implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp + implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp + implementations/ondisk/OnDiskBlockStoreTest.cpp + implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp + implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp + implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp + implementations/caching/CachingBlockStoreTest.cpp + implementations/caching/cache/QueueMapTest_Values.cpp + implementations/caching/cache/testutils/MinimalKeyType.cpp + implementations/caching/cache/testutils/CopyableMovableValueType.cpp + implementations/caching/cache/testutils/MinimalValueType.cpp + implementations/caching/cache/testutils/QueueMapTest.cpp + implementations/caching/cache/testutils/CacheTest.cpp + implementations/caching/cache/QueueMapTest_Size.cpp + implementations/caching/cache/CacheTest_MoveConstructor.cpp + implementations/caching/cache/CacheTest_PushAndPop.cpp + implementations/caching/cache/QueueMapTest_MoveConstructor.cpp + implementations/caching/cache/QueueMapTest_MemoryLeak.cpp + implementations/caching/cache/CacheTest_RaceCondition.cpp + implementations/caching/cache/PeriodicTaskTest.cpp + implementations/caching/cache/QueueMapTest_Peek.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} googletest blockstore) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/blockstore/implementations/caching/CachingBlockStoreTest.cpp b/test/blockstore/implementations/caching/CachingBlockStoreTest.cpp new file mode 100644 index 00000000..c57e1684 --- /dev/null +++ b/test/blockstore/implementations/caching/CachingBlockStoreTest.cpp @@ -0,0 +1,23 @@ +#include "blockstore/implementations/caching/CachingBlockStore.h" +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include +#include + + +using blockstore::BlockStore; +using blockstore::caching::CachingBlockStore; +using blockstore::testfake::FakeBlockStore; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +class CachingBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref(make_unique_ref()); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(Caching, BlockStoreTest, CachingBlockStoreTestFixture); + +//TODO Add specific tests for the blockstore diff --git a/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp b/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp new file mode 100644 index 00000000..e69a278b --- /dev/null +++ b/test/blockstore/implementations/caching/cache/CacheTest_MoveConstructor.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "blockstore/implementations/caching/cache/Cache.h" +#include "testutils/MinimalKeyType.h" +#include "testutils/CopyableMovableValueType.h" + +using namespace blockstore::caching; + +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using ::testing::Test; + +//Test that Cache uses a move constructor for Value if possible +class CacheTest_MoveConstructor: public Test { +public: + CacheTest_MoveConstructor(): cache(make_unique_ref>()) { + CopyableMovableValueType::numCopyConstructorCalled = 0; + } + unique_ref> cache; +}; + +TEST_F(CacheTest_MoveConstructor, MoveIntoCache) { + cache->push(MinimalKeyType::create(0), CopyableMovableValueType(2)); + CopyableMovableValueType val = cache->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(CacheTest_MoveConstructor, CopyIntoCache) { + CopyableMovableValueType value(2); + cache->push(MinimalKeyType::create(0), value); + CopyableMovableValueType val = cache->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled); +} diff --git a/test/blockstore/implementations/caching/cache/CacheTest_PushAndPop.cpp b/test/blockstore/implementations/caching/cache/CacheTest_PushAndPop.cpp new file mode 100644 index 00000000..88f6462e --- /dev/null +++ b/test/blockstore/implementations/caching/cache/CacheTest_PushAndPop.cpp @@ -0,0 +1,134 @@ +#include "testutils/CacheTest.h" + +#include "blockstore/implementations/caching/cache/Cache.h" +#include "testutils/MinimalKeyType.h" +#include "testutils/MinimalValueType.h" +#include + +using ::testing::Test; + +using namespace blockstore::caching; + +class CacheTest_PushAndPop: public CacheTest {}; + +TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_EmptyCache) { + EXPECT_EQ(boost::none, pop(10)); +} + +TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_NonEmptyCache) { + push(9, 10); + EXPECT_EQ(boost::none, pop(10)); +} + +TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_FullCache) { + //Add a lot of even numbered keys + for (unsigned int i = 0; i < MAX_ENTRIES; ++i) { + push(2*i, 2*i); + } + //Request an odd numbered key + EXPECT_EQ(boost::none, pop(9)); +} + +TEST_F(CacheTest_PushAndPop, OneEntry) { + push(10, 20); + EXPECT_EQ(20, pop(10).value()); +} + +TEST_F(CacheTest_PushAndPop, MultipleEntries) { + push(10, 20); + push(20, 30); + push(30, 40); + EXPECT_EQ(30, pop(20).value()); + EXPECT_EQ(20, pop(10).value()); + EXPECT_EQ(40, pop(30).value()); +} + +TEST_F(CacheTest_PushAndPop, FullCache) { + for(unsigned int i = 0; i < MAX_ENTRIES; ++i) { + push(i, 2*i); + } + for(unsigned int i = 0; i < MAX_ENTRIES; ++i) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, FullCache_PushNonOrdered_PopOrdered) { + for(unsigned int i = 1; i < MAX_ENTRIES; i += 2) { + push(i, 2*i); + } + for(unsigned int i = 0; i < MAX_ENTRIES; i += 2) { + push(i, 2*i); + } + for(unsigned int i = 0; i < MAX_ENTRIES; ++i) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, FullCache_PushOrdered_PopNonOrdered) { + for(unsigned int i = 0; i < MAX_ENTRIES; ++i) { + push(i, 2*i); + } + for(unsigned int i = 1; i < MAX_ENTRIES; i += 2) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } + for(unsigned int i = 0; i < MAX_ENTRIES; i += 2) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } +} + +int roundDownToEven(int number) { + if (number % 2 == 0) { + return number; + } else { + return number - 1; + } +} + +int roundDownToOdd(int number) { + if (number % 2 != 0) { + return number; + } else { + return number - 1; + } +} + +TEST_F(CacheTest_PushAndPop, FullCache_PushNonOrdered_PopNonOrdered) { + for(int i = roundDownToEven(MAX_ENTRIES - 1); i >= 0; i -= 2) { + push(i, 2*i); + } + for(unsigned int i = 1; i < MAX_ENTRIES; i += 2) { + push(i, 2*i); + } + for(int i = roundDownToOdd(MAX_ENTRIES-1); i >= 0; i -= 2) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } + for(unsigned int i = 0; i < MAX_ENTRIES; i += 2) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, MoreThanFullCache) { + for(unsigned int i = 0; i < MAX_ENTRIES + 2; ++i) { + push(i, 2*i); + } + //Check that the oldest two elements got deleted automatically + EXPECT_EQ(boost::none, pop(0)); + EXPECT_EQ(boost::none, pop(1)); + //Check the other elements are still there + for(unsigned int i = 2; i < MAX_ENTRIES + 2; ++i) { + EXPECT_EQ((signed int)(2*i), pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, AfterTimeout) { + constexpr double TIMEOUT1_SEC = Cache::MAX_LIFETIME_SEC * 3/4; + constexpr double TIMEOUT2_SEC = Cache::PURGE_LIFETIME_SEC * 3/4; + static_assert(TIMEOUT1_SEC + TIMEOUT2_SEC > Cache::MAX_LIFETIME_SEC, "Ensure that our chosen timeouts push the first entry out of the cache"); + + push(10, 20); + boost::this_thread::sleep_for(boost::chrono::milliseconds(static_cast(1000 * TIMEOUT1_SEC))); + push(20, 30); + boost::this_thread::sleep_for(boost::chrono::milliseconds(static_cast(1000 * TIMEOUT2_SEC))); + EXPECT_EQ(boost::none, pop(10)); + EXPECT_EQ(30, pop(20).value()); +} diff --git a/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp b/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp new file mode 100644 index 00000000..51531e58 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/CacheTest_RaceCondition.cpp @@ -0,0 +1,109 @@ +#include "testutils/CacheTest.h" +#include +#include +#include +#include +#include + +using namespace blockstore::caching; +using std::chrono::seconds; +using std::string; +using cpputils::ConditionBarrier; +using std::unique_ptr; +using std::make_unique; +using std::future; + +// Regression tests for a race condition. +// An element could be in the process of being thrown out of the cache and while the destructor is running, another +// thread calls pop() for the element and gets none returned. But since the destructor isn't finished yet, the data from +// the cache element also isn't completely written back yet and an application loading it runs into a race condition. + +class ObjectWithLongDestructor { +public: + ObjectWithLongDestructor(ConditionBarrier *onDestructorStarted, bool *destructorFinished) + : _onDestructorStarted(onDestructorStarted), _destructorFinished(destructorFinished) {} + ~ObjectWithLongDestructor() { + _onDestructorStarted->release(); + std::this_thread::sleep_for(seconds(1)); + *_destructorFinished = true; + } +private: + ConditionBarrier *_onDestructorStarted; + bool *_destructorFinished; + + DISALLOW_COPY_AND_ASSIGN(ObjectWithLongDestructor); +}; + +class CacheTest_RaceCondition: public ::testing::Test { +public: + CacheTest_RaceCondition(): cache(), destructorStarted(), destructorFinished(false) {} + + static constexpr unsigned int MAX_ENTRIES = 100; + + Cache, MAX_ENTRIES> cache; + ConditionBarrier destructorStarted; + bool destructorFinished; + + int pushObjectWithLongDestructor() { + cache.push(2, make_unique(&destructorStarted, &destructorFinished)); + return 2; + } + + int pushDummyObject() { + cache.push(3, nullptr); + return 3; + } + + future causeCacheOverflowInOtherThread() { + //Add maximum+1 element in another thread (this causes the cache to flush the first element in another thread) + return std::async(std::launch::async, [this] { + for(unsigned int i = 0; i < MAX_ENTRIES+1; ++i) { + cache.push(MAX_ENTRIES+i, nullptr); + } + }); + } + + void EXPECT_POP_BLOCKS_UNTIL_DESTRUCTOR_FINISHED(int key) { + EXPECT_FALSE(destructorFinished); + cache.pop(key); + EXPECT_TRUE(destructorFinished); + } + + void EXPECT_POP_DOESNT_BLOCK_UNTIL_DESTRUCTOR_FINISHED(int key) { + EXPECT_FALSE(destructorFinished); + cache.pop(key); + EXPECT_FALSE(destructorFinished); + } +}; + +TEST_F(CacheTest_RaceCondition, PopBlocksWhileRequestedElementIsThrownOut_ByAge) { + auto id = pushObjectWithLongDestructor(); + + destructorStarted.wait(); + EXPECT_POP_BLOCKS_UNTIL_DESTRUCTOR_FINISHED(id); +} + +TEST_F(CacheTest_RaceCondition, PopDoesntBlockWhileOtherElementIsThrownOut_ByAge) { + pushObjectWithLongDestructor(); + auto id = pushDummyObject(); + + destructorStarted.wait(); + EXPECT_POP_DOESNT_BLOCK_UNTIL_DESTRUCTOR_FINISHED(id); +} + +TEST_F(CacheTest_RaceCondition, PopBlocksWhileRequestedElementIsThrownOut_ByPush) { + auto id = pushObjectWithLongDestructor(); + + auto future = causeCacheOverflowInOtherThread(); + destructorStarted.wait(); + EXPECT_POP_BLOCKS_UNTIL_DESTRUCTOR_FINISHED(id); +} + +TEST_F(CacheTest_RaceCondition, PopDoesntBlockWhileOtherElementIsThrownOut_ByPush) { + pushObjectWithLongDestructor(); + auto id = pushDummyObject(); + + auto future = causeCacheOverflowInOtherThread(); + destructorStarted.wait(); + EXPECT_POP_DOESNT_BLOCK_UNTIL_DESTRUCTOR_FINISHED(id); +} diff --git a/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp b/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp new file mode 100644 index 00000000..46843015 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/PeriodicTaskTest.cpp @@ -0,0 +1,63 @@ +#include + +#include "blockstore/implementations/caching/cache/PeriodicTask.h" + +#include +#include +#include + +using ::testing::Test; +using std::mutex; +using std::unique_lock; +using std::condition_variable; + +using namespace blockstore::caching; + +class AtomicCounter { +public: + AtomicCounter(int count): _mutex(), _cv(), _counter(count) {} + + void decrease() { + unique_lock lock(_mutex); + --_counter; + _cv.notify_all(); + } + + void waitForZero() { + unique_lock lock(_mutex); + _cv.wait(lock, [this] () {return _counter <= 0;}); + } +private: + mutex _mutex; + condition_variable _cv; + int _counter; +}; + +class PeriodicTaskTest: public Test { +}; + +TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) { + PeriodicTask task([](){}, 1); +} + +TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) { + AtomicCounter counter(10); + + PeriodicTask task([&counter](){ + counter.decrease(); + }, 0.001); + + counter.waitForZero(); +} + +TEST_F(PeriodicTaskTest, DoesntCallCallbackAfterDestruction) { + std::atomic callCount(0); + { + PeriodicTask task([&callCount](){ + callCount += 1; + }, 0.001); + } + int callCountDirectlyAfterDestruction = callCount; + boost::this_thread::sleep_for(boost::chrono::seconds(1)); + EXPECT_EQ(callCountDirectlyAfterDestruction, callCount); +} diff --git a/test/blockstore/implementations/caching/cache/QueueMapTest_MemoryLeak.cpp b/test/blockstore/implementations/caching/cache/QueueMapTest_MemoryLeak.cpp new file mode 100644 index 00000000..74012fc0 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/QueueMapTest_MemoryLeak.cpp @@ -0,0 +1,87 @@ +#include "testutils/QueueMapTest.h" + +// Tests that QueueMap calls destructors correctly. +// This is needed, because QueueMap does its own memory management. +class QueueMapTest_MemoryLeak: public QueueMapTest { +public: + void EXPECT_NUM_INSTANCES(int num) { + EXPECT_EQ(num, MinimalKeyType::instances); + EXPECT_EQ(num, MinimalValueType::instances); + } +}; + +TEST_F(QueueMapTest_MemoryLeak, Empty) { + EXPECT_NUM_INSTANCES(0); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOne) { + push(2, 3); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_NUM_INSTANCES(2); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingOldest) { + push(2, 3); + push(3, 4); + pop(); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingLast) { + push(2, 3); + push(3, 4); + pop(3); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOne) { + push(2, 3); + pop(); + EXPECT_NUM_INSTANCES(0); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKey) { + push(2, 3); + pop(2); + EXPECT_NUM_INSTANCES(0); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePushingOne) { + push(2, 3); + pop(); + push(3, 4); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKeyPushingOne) { + push(2, 3); + pop(2); + push(3, 4); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePushingSame) { + push(2, 3); + pop(); + push(2, 3); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKeyPushingSame) { + push(2, 3); + pop(2); + push(2, 3); + EXPECT_NUM_INSTANCES(1); +} diff --git a/test/blockstore/implementations/caching/cache/QueueMapTest_MoveConstructor.cpp b/test/blockstore/implementations/caching/cache/QueueMapTest_MoveConstructor.cpp new file mode 100644 index 00000000..2a0126ec --- /dev/null +++ b/test/blockstore/implementations/caching/cache/QueueMapTest_MoveConstructor.cpp @@ -0,0 +1,50 @@ +#include +#include +#include "blockstore/implementations/caching/cache/QueueMap.h" +#include "testutils/MinimalKeyType.h" +#include "testutils/CopyableMovableValueType.h" + +using namespace blockstore::caching; + +using ::testing::Test; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +//Test that QueueMap uses a move constructor for Value if possible +class QueueMapTest_MoveConstructor: public Test { +public: + QueueMapTest_MoveConstructor(): map(make_unique_ref>()) { + CopyableMovableValueType::numCopyConstructorCalled = 0; + } + unique_ref> map; +}; + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPopping_MoveIntoMap) { + map->push(MinimalKeyType::create(0), CopyableMovableValueType(2)); + CopyableMovableValueType val = map->pop().value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPoppingPerKey_MoveIntoMap) { + map->push(MinimalKeyType::create(0), CopyableMovableValueType(2)); + CopyableMovableValueType val = map->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPopping_CopyIntoMap) { + CopyableMovableValueType value(2); + map->push(MinimalKeyType::create(0), value); + CopyableMovableValueType val = map->pop().value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPoppingPerKey_CopyIntoMap) { + CopyableMovableValueType value(2); + map->push(MinimalKeyType::create(0), value); + CopyableMovableValueType val = map->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled); +} diff --git a/test/blockstore/implementations/caching/cache/QueueMapTest_Peek.cpp b/test/blockstore/implementations/caching/cache/QueueMapTest_Peek.cpp new file mode 100644 index 00000000..ba3c7ead --- /dev/null +++ b/test/blockstore/implementations/caching/cache/QueueMapTest_Peek.cpp @@ -0,0 +1,34 @@ +#include "testutils/QueueMapTest.h" +#include + +class QueueMapPeekTest: public QueueMapTest {}; + +TEST_F(QueueMapPeekTest, PoppingFromEmpty) { + EXPECT_EQ(boost::none, peek()); +} + +TEST_F(QueueMapPeekTest, PushingOne) { + push(3, 2); + EXPECT_EQ(2, peek().value()); +} + +TEST_F(QueueMapPeekTest, PushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_EQ(3, peek().value()); + EXPECT_EQ(3, peek().value()); + EXPECT_EQ(3, pop().value()); + EXPECT_EQ(4, peek().value()); + EXPECT_EQ(4, peek().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(boost::none, peek()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapPeekTest, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, peek().value()); +} diff --git a/test/blockstore/implementations/caching/cache/QueueMapTest_Size.cpp b/test/blockstore/implementations/caching/cache/QueueMapTest_Size.cpp new file mode 100644 index 00000000..e4d72c9d --- /dev/null +++ b/test/blockstore/implementations/caching/cache/QueueMapTest_Size.cpp @@ -0,0 +1,79 @@ +#include "testutils/QueueMapTest.h" + +class QueueMapTest_Size: public QueueMapTest {}; + +TEST_F(QueueMapTest_Size, Empty) { + EXPECT_EQ(0, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOne) { + push(2, 3); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_EQ(2, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingOldest) { + push(2, 3); + push(3, 4); + pop(); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingLast) { + push(2, 3); + push(3, 4); + pop(3); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOne) { + push(2, 3); + pop(); + EXPECT_EQ(0, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKey) { + push(2, 3); + pop(2); + EXPECT_EQ(0, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePushingOne) { + push(2, 3); + pop(); + push(3, 4); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKeyPushingOne) { + push(2, 3); + pop(2); + push(3, 4); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePushingSame) { + push(2, 3); + pop(); + push(2, 3); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKeyPushingSame) { + push(2, 3); + pop(2); + push(2, 3); + EXPECT_EQ(1, size()); +} diff --git a/test/blockstore/implementations/caching/cache/QueueMapTest_Values.cpp b/test/blockstore/implementations/caching/cache/QueueMapTest_Values.cpp new file mode 100644 index 00000000..0532c366 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/QueueMapTest_Values.cpp @@ -0,0 +1,151 @@ +#include "testutils/QueueMapTest.h" +#include + +class QueueMapTest_Values: public QueueMapTest {}; + +TEST_F(QueueMapTest_Values, PoppingFromEmpty) { + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PoppingFromEmptyPerKey) { + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, PoppingNonexistingPerKey) { + push(3, 2); + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, PushingOne) { + push(3, 2); + EXPECT_EQ(2, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_EQ(3, pop().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingTwoAndPoppingLast) { + push(2, 3); + push(3, 4); + pop(3); + EXPECT_EQ(boost::none, pop(3)); + EXPECT_EQ(3, pop(2).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOne) { + push(2, 3); + pop(); + EXPECT_EQ(boost::none, pop()); + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePerKey) { + push(2, 3); + pop(2); + EXPECT_EQ(boost::none, pop()); + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePushingOne) { + push(2, 3); + pop(); + push(3, 4); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePerKeyPushingOne) { + push(2, 3); + pop(2); + push(3, 4); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingSomePoppingMiddlePerKey) { + push(1, 2); + push(2, 3); + push(3, 4); + push(4, 5); + push(5, 6); + EXPECT_EQ(3, pop(2).value()); + EXPECT_EQ(5, pop(4).value()); + EXPECT_EQ(2, pop().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(6, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingSomePoppingFirstPerKey) { + push(1, 2); + push(2, 3); + push(3, 4); + push(4, 5); + push(5, 6); + EXPECT_EQ(2, pop(1).value()); + EXPECT_EQ(3, pop(2).value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(5, pop().value()); + EXPECT_EQ(6, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingSomePoppingLastPerKey) { + push(1, 2); + push(2, 3); + push(3, 4); + push(4, 5); + push(5, 6); + EXPECT_EQ(6, pop(5).value()); + EXPECT_EQ(5, pop(4).value()); + EXPECT_EQ(2, pop().value()); + EXPECT_EQ(3, pop().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +//This test forces the underlying datastructure (std::map or std::unordered_map) to grow and reallocate memory. +//So it tests, that QueueMap still works after reallocating memory. +TEST_F(QueueMapTest_Values, ManyValues) { + //Push 1 million entries + for (int i = 0; i < 1000000; ++i) { + push(i, 2*i); + } + //pop every other one by key + for (int i = 0; i < 1000000; i += 2) { + EXPECT_EQ(2*i, pop(i).value()); + } + //pop the rest in queue order + for (int i = 1; i < 1000000; i += 2) { + EXPECT_EQ(2*i, peek().value()); + EXPECT_EQ(2*i, pop().value()); + } + EXPECT_EQ(0, size()); + EXPECT_EQ(boost::none, pop()); + EXPECT_EQ(boost::none, peek()); +} + +TEST_F(QueueMapTest_Values, PushAlreadyExistingValue) { + push(2, 3); + EXPECT_ANY_THROW( + push(2, 4); + ); +} diff --git a/test/blockstore/implementations/caching/cache/testutils/CacheTest.cpp b/test/blockstore/implementations/caching/cache/testutils/CacheTest.cpp new file mode 100644 index 00000000..b93bbccb --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/CacheTest.cpp @@ -0,0 +1,13 @@ +#include "CacheTest.h" + +void CacheTest::push(int key, int value) { + return _cache.push(MinimalKeyType::create(key), MinimalValueType::create(value)); +} + +boost::optional CacheTest::pop(int key) { + boost::optional entry = _cache.pop(MinimalKeyType::create(key)); + if (!entry) { + return boost::none; + } + return entry->value(); +} diff --git a/test/blockstore/implementations/caching/cache/testutils/CacheTest.h b/test/blockstore/implementations/caching/cache/testutils/CacheTest.h new file mode 100644 index 00000000..f4c520b1 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/CacheTest.h @@ -0,0 +1,29 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ + +#include +#include "blockstore/implementations/caching/cache/Cache.h" +#include "MinimalKeyType.h" +#include "MinimalValueType.h" +#include + +// This class is a parent class for tests on QueueMap. +// It offers functions to work with a QueueMap test object which is built using types having only the minimal type requirements. +// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values). +class CacheTest: public ::testing::Test { +public: + CacheTest(): _cache() {} + + void push(int key, int value); + boost::optional pop(int key); + + static constexpr unsigned int MAX_ENTRIES = 100; + + using Cache = blockstore::caching::Cache; + +private: + Cache _cache; +}; + +#endif diff --git a/test/blockstore/implementations/caching/cache/testutils/CopyableMovableValueType.cpp b/test/blockstore/implementations/caching/cache/testutils/CopyableMovableValueType.cpp new file mode 100644 index 00000000..cf23bd88 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/CopyableMovableValueType.cpp @@ -0,0 +1,3 @@ +#include "CopyableMovableValueType.h" + +int CopyableMovableValueType::numCopyConstructorCalled = 0; diff --git a/test/blockstore/implementations/caching/cache/testutils/CopyableMovableValueType.h b/test/blockstore/implementations/caching/cache/testutils/CopyableMovableValueType.h new file mode 100644 index 00000000..e69c1de6 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/CopyableMovableValueType.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_COPYABLEMOVABLEVALUETYPE_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_COPYABLEMOVABLEVALUETYPE_H_ + +class CopyableMovableValueType { +public: + static int numCopyConstructorCalled; + CopyableMovableValueType(int value): _value(value) {} + CopyableMovableValueType(const CopyableMovableValueType &rhs): CopyableMovableValueType(rhs._value) { + ++numCopyConstructorCalled; + } + CopyableMovableValueType &operator=(const CopyableMovableValueType &rhs) { + _value = rhs._value; + ++numCopyConstructorCalled; + return *this; + } + CopyableMovableValueType(CopyableMovableValueType &&rhs): CopyableMovableValueType(rhs._value) { + //Don't increase numCopyConstructorCalled + } + CopyableMovableValueType &operator=(CopyableMovableValueType &&rhs) { + //Don't increase numCopyConstructorCalled + _value = rhs._value; + return *this; + } + int value() const { + return _value; + } +private: + int _value; +}; + + +#endif diff --git a/test/blockstore/implementations/caching/cache/testutils/MinimalKeyType.cpp b/test/blockstore/implementations/caching/cache/testutils/MinimalKeyType.cpp new file mode 100644 index 00000000..f6b327ff --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/MinimalKeyType.cpp @@ -0,0 +1,3 @@ +#include "MinimalKeyType.h" + +int MinimalKeyType::instances = 0; diff --git a/test/blockstore/implementations/caching/cache/testutils/MinimalKeyType.h b/test/blockstore/implementations/caching/cache/testutils/MinimalKeyType.h new file mode 100644 index 00000000..14dfb5be --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/MinimalKeyType.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALKEYTYPE_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALKEYTYPE_H_ + +#include + +// This is a not-default-constructible Key type +class MinimalKeyType { +public: + static int instances; + + static MinimalKeyType create(int value) { + return MinimalKeyType(value); + } + + MinimalKeyType(const MinimalKeyType &rhs): MinimalKeyType(rhs.value()) { + } + + ~MinimalKeyType() { + --instances; + } + + int value() const { + return _value; + } + +private: + MinimalKeyType(int value): _value(value) { + ++instances; + } + + int _value; +}; + +namespace std { +template <> struct hash { + size_t operator()(const MinimalKeyType &obj) const { + return obj.value(); + } +}; +} + +inline bool operator==(const MinimalKeyType &lhs, const MinimalKeyType &rhs) { + return lhs.value() == rhs.value(); +} + +#endif diff --git a/test/blockstore/implementations/caching/cache/testutils/MinimalValueType.cpp b/test/blockstore/implementations/caching/cache/testutils/MinimalValueType.cpp new file mode 100644 index 00000000..f5cbdbae --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/MinimalValueType.cpp @@ -0,0 +1,3 @@ +#include "MinimalValueType.h" + +int MinimalValueType::instances = 0; diff --git a/test/blockstore/implementations/caching/cache/testutils/MinimalValueType.h b/test/blockstore/implementations/caching/cache/testutils/MinimalValueType.h new file mode 100644 index 00000000..72d4aef2 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/MinimalValueType.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALVALUETYPE_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALVALUETYPE_H_ + +#include +#include +#include + +// This is a not-default-constructible non-copyable but moveable Value type +class MinimalValueType { +public: + static int instances; + + static MinimalValueType create(int value) { + return MinimalValueType(value); + } + + MinimalValueType(MinimalValueType &&rhs): MinimalValueType(rhs.value()) { + rhs._isMoved = true; + } + + MinimalValueType &operator=(MinimalValueType &&rhs) { + _value = rhs.value(); + _isMoved = false; + rhs._isMoved = true; + return *this; + } + + ~MinimalValueType() { + ASSERT(!_isDestructed, "Object was already destructed before"); + --instances; + _isDestructed = true; + } + + int value() const { + ASSERT(!_isMoved && !_isDestructed, "Object is invalid"); + return _value; + } + +private: + MinimalValueType(int value): _value(value), _isMoved(false), _isDestructed(false) { + ++instances; + } + + int _value; + bool _isMoved; + bool _isDestructed; + + DISALLOW_COPY_AND_ASSIGN(MinimalValueType); +}; + +#endif diff --git a/test/blockstore/implementations/caching/cache/testutils/QueueMapTest.cpp b/test/blockstore/implementations/caching/cache/testutils/QueueMapTest.cpp new file mode 100644 index 00000000..76fae6fe --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/QueueMapTest.cpp @@ -0,0 +1,44 @@ +#include "QueueMapTest.h" + +QueueMapTest::QueueMapTest(): _map(cpputils::make_unique_ref>()) { + MinimalKeyType::instances = 0; + MinimalValueType::instances = 0; +} + +QueueMapTest::~QueueMapTest() { + cpputils::destruct(std::move(_map)); + EXPECT_EQ(0, MinimalKeyType::instances); + EXPECT_EQ(0, MinimalValueType::instances); +} + +void QueueMapTest::push(int key, int value) { + _map->push(MinimalKeyType::create(key), MinimalValueType::create(value)); +} + +boost::optional QueueMapTest::pop() { + auto elem = _map->pop(); + if (!elem) { + return boost::none; + } + return elem.value().value(); +} + +boost::optional QueueMapTest::pop(int key) { + auto elem = _map->pop(MinimalKeyType::create(key)); + if (!elem) { + return boost::none; + } + return elem.value().value(); +} + +boost::optional QueueMapTest::peek() { + auto elem = _map->peek(); + if (!elem) { + return boost::none; + } + return elem.value().value(); +} + +int QueueMapTest::size() { + return _map->size(); +} diff --git a/test/blockstore/implementations/caching/cache/testutils/QueueMapTest.h b/test/blockstore/implementations/caching/cache/testutils/QueueMapTest.h new file mode 100644 index 00000000..c4d4cf00 --- /dev/null +++ b/test/blockstore/implementations/caching/cache/testutils/QueueMapTest.h @@ -0,0 +1,31 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ + +#include +#include +#include "blockstore/implementations/caching/cache/QueueMap.h" +#include "MinimalKeyType.h" +#include "MinimalValueType.h" +#include + +// This class is a parent class for tests on QueueMap. +// It offers functions to work with a QueueMap test object which is built using types having only the minimal type requirements. +// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values). +class QueueMapTest: public ::testing::Test { +public: + QueueMapTest(); + ~QueueMapTest(); + + void push(int key, int value); + boost::optional pop(); + boost::optional pop(int key); + boost::optional peek(); + int size(); + +private: + cpputils::unique_ref> _map; +}; + + +#endif diff --git a/test/blockstore/implementations/compressing/CompressingBlockStoreTest.cpp b/test/blockstore/implementations/compressing/CompressingBlockStoreTest.cpp new file mode 100644 index 00000000..15dfd538 --- /dev/null +++ b/test/blockstore/implementations/compressing/CompressingBlockStoreTest.cpp @@ -0,0 +1,28 @@ +#include "blockstore/implementations/compressing/CompressingBlockStore.h" +#include "blockstore/implementations/compressing/compressors/Gzip.h" +#include "blockstore/implementations/compressing/compressors/RunLengthEncoding.h" +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include + +using ::testing::Test; + +using blockstore::BlockStore; +using blockstore::compressing::CompressingBlockStore; +using blockstore::compressing::Gzip; +using blockstore::compressing::RunLengthEncoding; +using blockstore::testfake::FakeBlockStore; + +using cpputils::make_unique_ref; +using cpputils::unique_ref; + +template +class CompressingBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref>(make_unique_ref()); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(Compressing_Gzip, BlockStoreTest, CompressingBlockStoreTestFixture); +INSTANTIATE_TYPED_TEST_CASE_P(Compressing_RunLengthEncoding, BlockStoreTest, CompressingBlockStoreTestFixture); diff --git a/test/blockstore/implementations/compressing/compressors/testutils/CompressorTest.cpp b/test/blockstore/implementations/compressing/compressors/testutils/CompressorTest.cpp new file mode 100644 index 00000000..6916b142 --- /dev/null +++ b/test/blockstore/implementations/compressing/compressors/testutils/CompressorTest.cpp @@ -0,0 +1,92 @@ +#include +#include "blockstore/implementations/compressing/compressors/Gzip.h" +#include "blockstore/implementations/compressing/compressors/RunLengthEncoding.h" +#include + +using namespace blockstore::compressing; +using cpputils::Data; +using cpputils::DataFixture; + +template +class CompressorTest: public ::testing::Test { +public: + void EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(const Data &data) { + Data compressed = Compressor::Compress(data); + Data decompressed = Compressor::Decompress(compressed.data(), compressed.size()); + EXPECT_EQ(data, decompressed); + } +}; + +TYPED_TEST_CASE_P(CompressorTest); + +TYPED_TEST_P(CompressorTest, Empty) { + Data empty(0); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(empty); +} + +TYPED_TEST_P(CompressorTest, ArbitraryData) { + Data fixture = DataFixture::generate(10240); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(fixture); +} + +TYPED_TEST_P(CompressorTest, Zeroes) { + Data zeroes(10240); + zeroes.FillWithZeroes(); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(zeroes); +} + +TYPED_TEST_P(CompressorTest, Runs) { + Data data(4096); + std::memset(data.dataOffset(0), 0xF2, 1024); + std::memset(data.dataOffset(1024), 0x00, 1024); + std::memset(data.dataOffset(2048), 0x01, 2048); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data); +} + +TYPED_TEST_P(CompressorTest, RunsAndArbitrary) { + Data data(4096); + std::memset(data.dataOffset(0), 0xF2, 1024); + std::memcpy(data.dataOffset(1024), DataFixture::generate(1024).data(), 1024); + std::memset(data.dataOffset(2048), 0x01, 2048); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data); +} + +TYPED_TEST_P(CompressorTest, LargeData) { + // this is larger than what fits into 16bit (16bit are for example used as run length indicator in RunLengthEncoding) + Data fixture = DataFixture::generate(200000); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(fixture); +} + +TYPED_TEST_P(CompressorTest, LargeRuns) { + // each run is larger than what fits into 16bit (16bit are for example used as run length indicator in RunLengthEncoding) + constexpr size_t RUN_SIZE = 200000; + Data data(3*RUN_SIZE); + std::memset(data.dataOffset(0), 0xF2, RUN_SIZE); + std::memset(data.dataOffset(RUN_SIZE), 0x00, RUN_SIZE); + std::memset(data.dataOffset(2*RUN_SIZE), 0x01, RUN_SIZE); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data); +} + +TYPED_TEST_P(CompressorTest, LargeRunsAndArbitrary) { + // each run is larger than what fits into 16bit (16bit are for example used as run length indicator in RunLengthEncoding) + constexpr size_t RUN_SIZE = 200000; + Data data(3*RUN_SIZE); + std::memset(data.dataOffset(0), 0xF2, RUN_SIZE); + std::memcpy(data.dataOffset(RUN_SIZE), DataFixture::generate(RUN_SIZE).data(), RUN_SIZE); + std::memset(data.dataOffset(2*RUN_SIZE), 0x01, RUN_SIZE); + this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data); +} + +REGISTER_TYPED_TEST_CASE_P(CompressorTest, + Empty, + ArbitraryData, + Zeroes, + Runs, + RunsAndArbitrary, + LargeData, + LargeRuns, + LargeRunsAndArbitrary +); + +INSTANTIATE_TYPED_TEST_CASE_P(Gzip, CompressorTest, Gzip); +INSTANTIATE_TYPED_TEST_CASE_P(RunLengthEncoding, CompressorTest, RunLengthEncoding); diff --git a/test/blockstore/implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp b/test/blockstore/implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp new file mode 100644 index 00000000..e4c4362b --- /dev/null +++ b/test/blockstore/implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp @@ -0,0 +1,40 @@ +#include +#include +#include "blockstore/implementations/encrypted/EncryptedBlockStore.h" +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +//TODO Move FakeAuthenticatedCipher out of test folder to normal folder. Dependencies should not point into tests of other modules. +#include "../../../cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h" +#include + +using ::testing::Test; + +using blockstore::BlockStore; +using blockstore::encrypted::EncryptedBlockStore; +using blockstore::testfake::FakeBlockStore; +using cpputils::AES256_GCM; +using cpputils::AES256_CFB; +using cpputils::FakeAuthenticatedCipher; + +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::make_unique_ref; +using cpputils::unique_ref; + +template +class EncryptedBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref>(make_unique_ref(), createKeyFixture()); + } + +private: + static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) { + Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed); + return Cipher::EncryptionKey::FromBinary(data.data()); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_FakeCipher, BlockStoreTest, EncryptedBlockStoreTestFixture); +INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_GCM, BlockStoreTest, EncryptedBlockStoreTestFixture); +INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_CFB, BlockStoreTest, EncryptedBlockStoreTestFixture); diff --git a/test/blockstore/implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp b/test/blockstore/implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp new file mode 100644 index 00000000..9f22da0d --- /dev/null +++ b/test/blockstore/implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp @@ -0,0 +1,114 @@ +#include +#include "../../../cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h" +#include "blockstore/implementations/encrypted/EncryptedBlockStore.h" +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include "blockstore/utils/BlockStoreUtils.h" +#include + +using ::testing::Test; + +using cpputils::DataFixture; +using cpputils::Data; +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using cpputils::FakeAuthenticatedCipher; + +using blockstore::testfake::FakeBlockStore; + +using namespace blockstore::encrypted; + +class EncryptedBlockStoreTest: public Test { +public: + static constexpr unsigned int BLOCKSIZE = 1024; + EncryptedBlockStoreTest(): + baseBlockStore(new FakeBlockStore), + blockStore(make_unique_ref>(std::move(cpputils::nullcheck(std::unique_ptr(baseBlockStore)).value()), FakeAuthenticatedCipher::Key1())), + data(DataFixture::generate(BLOCKSIZE)) { + } + FakeBlockStore *baseBlockStore; + unique_ref> blockStore; + Data data; + + blockstore::Key CreateBlockDirectlyWithFixtureAndReturnKey() { + return blockStore->create(data)->key(); + } + + blockstore::Key CreateBlockWriteFixtureToItAndReturnKey() { + auto block = blockStore->create(Data(data.size())); + block->write(data.data(), 0, data.size()); + return block->key(); + } + + void ModifyBaseBlock(const blockstore::Key &key) { + auto block = baseBlockStore->load(key).value(); + uint8_t middle_byte = ((byte*)block->data())[10]; + uint8_t new_middle_byte = middle_byte + 1; + block->write(&new_middle_byte, 10, 1); + } + + blockstore::Key CopyBaseBlock(const blockstore::Key &key) { + auto source = baseBlockStore->load(key).value(); + return blockstore::utils::copyToNewBlock(baseBlockStore, *source)->key(); + } + +private: + DISALLOW_COPY_AND_ASSIGN(EncryptedBlockStoreTest); +}; + +TEST_F(EncryptedBlockStoreTest, LoadingWithSameKeyWorks_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + auto loaded = blockStore->load(key); + EXPECT_NE(boost::none, loaded); + EXPECT_EQ(data.size(), (*loaded)->size()); + EXPECT_EQ(0, std::memcmp(data.data(), (*loaded)->data(), data.size())); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithSameKeyWorks_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + auto loaded = blockStore->load(key); + EXPECT_NE(boost::none, loaded); + EXPECT_EQ(data.size(), (*loaded)->size()); + EXPECT_EQ(0, std::memcmp(data.data(), (*loaded)->data(), data.size())); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentKeyDoesntWork_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + blockStore->__setKey(FakeAuthenticatedCipher::Key2()); + auto loaded = blockStore->load(key); + EXPECT_EQ(boost::none, loaded); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentKeyDoesntWork_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + blockStore->__setKey(FakeAuthenticatedCipher::Key2()); + auto loaded = blockStore->load(key); + EXPECT_EQ(boost::none, loaded); +} + +TEST_F(EncryptedBlockStoreTest, LoadingModifiedBlockFails_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + ModifyBaseBlock(key); + auto loaded = blockStore->load(key); + EXPECT_EQ(boost::none, loaded); +} + +TEST_F(EncryptedBlockStoreTest, LoadingModifiedBlockFails_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + ModifyBaseBlock(key); + auto loaded = blockStore->load(key); + EXPECT_EQ(boost::none, loaded); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentBlockIdFails_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + auto key2 = CopyBaseBlock(key); + auto loaded = blockStore->load(key2); + EXPECT_EQ(boost::none, loaded); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentBlockIdFails_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + auto key2 = CopyBaseBlock(key); + auto loaded = blockStore->load(key2); + EXPECT_EQ(boost::none, loaded); +} diff --git a/test/blockstore/implementations/inmemory/InMemoryBlockStoreTest.cpp b/test/blockstore/implementations/inmemory/InMemoryBlockStoreTest.cpp new file mode 100644 index 00000000..ddac72d8 --- /dev/null +++ b/test/blockstore/implementations/inmemory/InMemoryBlockStoreTest.cpp @@ -0,0 +1,31 @@ +#include "blockstore/implementations/inmemory/InMemoryBlock.h" +#include "blockstore/implementations/inmemory/InMemoryBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include "../../testutils/BlockStoreWithRandomKeysTest.h" +#include +#include + + +using blockstore::BlockStore; +using blockstore::BlockStoreWithRandomKeys; +using blockstore::inmemory::InMemoryBlockStore; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +class InMemoryBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref(); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(InMemory, BlockStoreTest, InMemoryBlockStoreTestFixture); + +class InMemoryBlockStoreWithRandomKeysTestFixture: public BlockStoreWithRandomKeysTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref(); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(InMemory, BlockStoreWithRandomKeysTest, InMemoryBlockStoreWithRandomKeysTestFixture); diff --git a/test/blockstore/implementations/ondisk/OnDiskBlockStoreTest.cpp b/test/blockstore/implementations/ondisk/OnDiskBlockStoreTest.cpp new file mode 100644 index 00000000..f7936eba --- /dev/null +++ b/test/blockstore/implementations/ondisk/OnDiskBlockStoreTest.cpp @@ -0,0 +1,42 @@ +#include "blockstore/implementations/ondisk/OnDiskBlock.h" +#include "blockstore/implementations/ondisk/OnDiskBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include "../../testutils/BlockStoreWithRandomKeysTest.h" +#include + +#include + + +using blockstore::BlockStore; +using blockstore::BlockStoreWithRandomKeys; +using blockstore::ondisk::OnDiskBlockStore; + +using cpputils::TempDir; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +class OnDiskBlockStoreTestFixture: public BlockStoreTestFixture { +public: + OnDiskBlockStoreTestFixture(): tempdir() {} + + unique_ref createBlockStore() override { + return make_unique_ref(tempdir.path()); + } +private: + TempDir tempdir; +}; + +INSTANTIATE_TYPED_TEST_CASE_P(OnDisk, BlockStoreTest, OnDiskBlockStoreTestFixture); + +class OnDiskBlockStoreWithRandomKeysTestFixture: public BlockStoreWithRandomKeysTestFixture { +public: + OnDiskBlockStoreWithRandomKeysTestFixture(): tempdir() {} + + unique_ref createBlockStore() override { + return make_unique_ref(tempdir.path()); + } +private: + TempDir tempdir; +}; + +INSTANTIATE_TYPED_TEST_CASE_P(OnDisk, BlockStoreWithRandomKeysTest, OnDiskBlockStoreWithRandomKeysTestFixture); diff --git a/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp b/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp new file mode 100644 index 00000000..5de0ede1 --- /dev/null +++ b/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp @@ -0,0 +1,83 @@ +#include "blockstore/implementations/ondisk/OnDiskBlock.h" +#include + +#include +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; +using cpputils::Data; +using cpputils::TempFile; +using cpputils::TempDir; +using cpputils::unique_ref; + +using namespace blockstore; +using namespace blockstore::ondisk; + +namespace bf = boost::filesystem; + +class OnDiskBlockCreateTest: public Test { +public: + OnDiskBlockCreateTest() + // Don't create the temp file yet (therefore pass false to the TempFile constructor) + : dir(), + key(Key::FromString("1491BB4932A389EE14BC7090AC772972")), + file(dir.path() / key.ToString(), false) { + } + TempDir dir; + Key key; + TempFile file; +}; + +TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) { + EXPECT_FALSE(bf::exists(file.path())); + + auto block = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0)); + + EXPECT_TRUE(bf::exists(file.path())); + EXPECT_TRUE(bf::is_regular_file(file.path())); +} + +TEST_F(OnDiskBlockCreateTest, CreatingExistingBlockReturnsNull) { + auto block1 = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0)); + auto block2 = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0)); + EXPECT_TRUE((bool)block1); + EXPECT_FALSE((bool)block2); +} + +class OnDiskBlockCreateSizeTest: public OnDiskBlockCreateTest, public WithParamInterface { +public: + unique_ref block; + Data ZEROES; + + OnDiskBlockCreateSizeTest(): + block(OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(Data(GetParam()).FillWithZeroes())).value()), + ZEROES(block->size()) + { + ZEROES.FillWithZeroes(); + } +}; +INSTANTIATE_TEST_CASE_P(OnDiskBlockCreateSizeTest, OnDiskBlockCreateSizeTest, Values(0, 1, 5, 1024, 10*1024*1024)); + +TEST_P(OnDiskBlockCreateSizeTest, OnDiskSizeIsCorrect) { + Data fileContent = Data::LoadFromFile(file.path()).value(); + EXPECT_EQ(GetParam(), fileContent.size()); +} + +TEST_P(OnDiskBlockCreateSizeTest, OnDiskBlockIsZeroedOut) { + Data fileContent = Data::LoadFromFile(file.path()).value(); + EXPECT_EQ(ZEROES, fileContent); +} + +// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface. +// Here, we create it using OnDiskBlock::CreateOnDisk() +TEST_P(OnDiskBlockCreateSizeTest, InMemorySizeIsCorrect) { + EXPECT_EQ(GetParam(), block->size()); +} + +// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface. +// Here, we create it using OnDiskBlock::CreateOnDisk() +TEST_P(OnDiskBlockCreateSizeTest, InMemoryBlockIsZeroedOut) { + EXPECT_EQ(0, std::memcmp(ZEROES.data(), block->data(), block->size())); +} diff --git a/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp b/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp new file mode 100644 index 00000000..6300e034 --- /dev/null +++ b/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp @@ -0,0 +1,115 @@ +#include "blockstore/implementations/ondisk/OnDiskBlock.h" +#include +#include + +#include +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; + +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::TempFile; +using cpputils::TempDir; +using cpputils::unique_ref; + +using namespace blockstore; +using namespace blockstore::ondisk; + +namespace bf = boost::filesystem; + +class OnDiskBlockFlushTest: public Test, public WithParamInterface { +public: + OnDiskBlockFlushTest() + // Don't create the temp file yet (therefore pass false to the TempFile constructor) + : dir(), + key(Key::FromString("1491BB4932A389EE14BC7090AC772972")), + file(dir.path() / key.ToString(), false), + randomData(DataFixture::generate(GetParam())) { + } + TempDir dir; + Key key; + TempFile file; + + Data randomData; + + unique_ref CreateBlockAndLoadItFromDisk() { + { + OnDiskBlock::CreateOnDisk(dir.path(), key, randomData.copy()).value(); + } + return OnDiskBlock::LoadFromDisk(dir.path(), key).value(); + } + + unique_ref CreateBlock() { + return OnDiskBlock::CreateOnDisk(dir.path(), key, randomData.copy()).value(); + } + + void WriteDataToBlock(const unique_ref &block) { + block->write(randomData.data(), 0, randomData.size()); + } + + void EXPECT_BLOCK_DATA_CORRECT(const unique_ref &block) { + EXPECT_EQ(randomData.size(), block->size()); + EXPECT_EQ(0, std::memcmp(randomData.data(), block->data(), randomData.size())); + } + + void EXPECT_STORED_FILE_DATA_CORRECT() { + Data actual = Data::LoadFromFile(file.path()).value(); + EXPECT_EQ(randomData, actual); + } +}; +INSTANTIATE_TEST_CASE_P(OnDiskBlockFlushTest, OnDiskBlockFlushTest, Values((size_t)0, (size_t)1, (size_t)1024, (size_t)4096, (size_t)10*1024*1024)); + +// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface. +// Here, we create it using OnDiskBlock::CreateOnDisk() +TEST_P(OnDiskBlockFlushTest, AfterCreate_FlushingDoesntChangeBlock) { + auto block = CreateBlock(); + WriteDataToBlock(block); + + EXPECT_BLOCK_DATA_CORRECT(block); +} + +// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface. +// Here, we create it using OnDiskBlock::CreateOnDisk() / OnDiskBlock::LoadFromDisk() +TEST_P(OnDiskBlockFlushTest, AfterLoad_FlushingDoesntChangeBlock) { + auto block = CreateBlockAndLoadItFromDisk(); + WriteDataToBlock(block); + + EXPECT_BLOCK_DATA_CORRECT(block); +} + +TEST_P(OnDiskBlockFlushTest, AfterCreate_FlushingWritesCorrectData) { + auto block = CreateBlock(); + WriteDataToBlock(block); + + EXPECT_STORED_FILE_DATA_CORRECT(); +} + +TEST_P(OnDiskBlockFlushTest, AfterLoad_FlushingWritesCorrectData) { + auto block = CreateBlockAndLoadItFromDisk(); + WriteDataToBlock(block); + + EXPECT_STORED_FILE_DATA_CORRECT(); +} + +// This test is also tested by OnDiskBlockStoreTest, but there it can only checks block content by loading it again. +// Here, we check the content on disk. +TEST_P(OnDiskBlockFlushTest, AfterCreate_FlushesWhenDestructed) { + { + auto block = CreateBlock(); + WriteDataToBlock(block); + } + EXPECT_STORED_FILE_DATA_CORRECT(); +} + +// This test is also tested by OnDiskBlockStoreTest, but there it can only checks block content by loading it again. +// Here, we check the content on disk. +TEST_P(OnDiskBlockFlushTest, AfterLoad_FlushesWhenDestructed) { + { + auto block = CreateBlockAndLoadItFromDisk(); + WriteDataToBlock(block); + } + EXPECT_STORED_FILE_DATA_CORRECT(); +} diff --git a/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp b/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp new file mode 100644 index 00000000..a73d9a20 --- /dev/null +++ b/test/blockstore/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp @@ -0,0 +1,80 @@ +#include "blockstore/implementations/ondisk/OnDiskBlock.h" +#include +#include "blockstore/utils/FileDoesntExistException.h" +#include + +#include +#include +#include +#include +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; + +using std::ofstream; +using std::ios; +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::TempFile; +using cpputils::TempDir; +using cpputils::unique_ref; + +using namespace blockstore; +using namespace blockstore::ondisk; + +namespace bf = boost::filesystem; + +class OnDiskBlockLoadTest: public Test, public WithParamInterface { +public: + OnDiskBlockLoadTest(): + dir(), + key(Key::FromString("1491BB4932A389EE14BC7090AC772972")), + file(dir.path() / key.ToString()) { + } + TempDir dir; + Key key; + TempFile file; + + void SetFileSize(size_t size) { + Data data(size); + data.StoreToFile(file.path()); + } + + void StoreData(const Data &data) { + data.StoreToFile(file.path()); + } + + unique_ref LoadBlock() { + return OnDiskBlock::LoadFromDisk(dir.path(), key).value(); + } + + void EXPECT_BLOCK_DATA_EQ(const Data &expected, const OnDiskBlock &actual) { + EXPECT_EQ(expected.size(), actual.size()); + EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), expected.size())); + } +}; +INSTANTIATE_TEST_CASE_P(OnDiskBlockLoadTest, OnDiskBlockLoadTest, Values(0, 1, 5, 1024, 10*1024*1024)); + +TEST_P(OnDiskBlockLoadTest, FileSizeIsCorrect) { + SetFileSize(GetParam()); + + auto block = LoadBlock(); + + EXPECT_EQ(GetParam(), block->size()); +} + +TEST_P(OnDiskBlockLoadTest, LoadedDataIsCorrect) { + Data randomData = DataFixture::generate(GetParam()); + StoreData(randomData); + + auto block = LoadBlock(); + + EXPECT_BLOCK_DATA_EQ(randomData, *block); +} + +TEST_F(OnDiskBlockLoadTest, LoadNotExistingBlock) { + Key key2 = Key::FromString("272EE5517627CFA147A971A8E6E747E0"); + EXPECT_EQ(boost::none, OnDiskBlock::LoadFromDisk(dir.path(), key2)); +} diff --git a/test/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreTest.cpp b/test/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreTest.cpp new file mode 100644 index 00000000..c33c82e7 --- /dev/null +++ b/test/blockstore/implementations/parallelaccess/ParallelAccessBlockStoreTest.cpp @@ -0,0 +1,23 @@ +#include "blockstore/implementations/parallelaccess/ParallelAccessBlockStore.h" +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include + + +using blockstore::BlockStore; +using blockstore::parallelaccess::ParallelAccessBlockStore; +using blockstore::testfake::FakeBlockStore; + +using cpputils::make_unique_ref; +using cpputils::unique_ref; + +class ParallelAccessBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref(make_unique_ref()); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(ParallelAccess, BlockStoreTest, ParallelAccessBlockStoreTestFixture); + +//TODO Add specific tests ensuring that loading the same block twice doesn't load it twice from the underlying blockstore diff --git a/test/blockstore/implementations/testfake/TestFakeBlockStoreTest.cpp b/test/blockstore/implementations/testfake/TestFakeBlockStoreTest.cpp new file mode 100644 index 00000000..09bd86e1 --- /dev/null +++ b/test/blockstore/implementations/testfake/TestFakeBlockStoreTest.cpp @@ -0,0 +1,30 @@ +#include "blockstore/implementations/testfake/FakeBlock.h" +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include "../../testutils/BlockStoreWithRandomKeysTest.h" +#include + + +using blockstore::BlockStore; +using blockstore::BlockStoreWithRandomKeys; +using blockstore::testfake::FakeBlockStore; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +class FakeBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref(); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(TestFake, BlockStoreTest, FakeBlockStoreTestFixture); + +class FakeBlockStoreWithRandomKeysTestFixture: public BlockStoreWithRandomKeysTestFixture { +public: + unique_ref createBlockStore() override { + return make_unique_ref(); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(TestFake, BlockStoreWithRandomKeysTest, FakeBlockStoreWithRandomKeysTestFixture); diff --git a/test/blockstore/interface/BlockStoreTest.cpp b/test/blockstore/interface/BlockStoreTest.cpp new file mode 100644 index 00000000..6b2b29c9 --- /dev/null +++ b/test/blockstore/interface/BlockStoreTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "blockstore/interface/BlockStore.h" diff --git a/test/blockstore/interface/BlockTest.cpp b/test/blockstore/interface/BlockTest.cpp new file mode 100644 index 00000000..a2db4214 --- /dev/null +++ b/test/blockstore/interface/BlockTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "blockstore/interface/Block.h" diff --git a/test/blockstore/interface/helpers/BlockStoreWithRandomKeysTest.cpp b/test/blockstore/interface/helpers/BlockStoreWithRandomKeysTest.cpp new file mode 100644 index 00000000..a3a1acef --- /dev/null +++ b/test/blockstore/interface/helpers/BlockStoreWithRandomKeysTest.cpp @@ -0,0 +1,143 @@ +#include "blockstore/interface/helpers/BlockStoreWithRandomKeys.h" +#include +#include +#include + +using ::testing::Test; +using ::testing::_; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Eq; +using ::testing::ByRef; + +using std::string; +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::unique_ref; +using boost::optional; + +using namespace blockstore; + +class BlockStoreWithRandomKeysMock: public BlockStoreWithRandomKeys { +public: + optional> tryCreate(const Key &key, Data data) { + return cpputils::nullcheck(std::unique_ptr(do_create(key, data))); + } + MOCK_METHOD2(do_create, Block*(const Key &, const Data &data)); + optional> load(const Key &key) { + return cpputils::nullcheck(std::unique_ptr(do_load(key))); + } + MOCK_METHOD1(do_load, Block*(const Key &)); + void remove(unique_ref block) {UNUSED(block);} + MOCK_CONST_METHOD0(numBlocks, uint64_t()); +}; + +class BlockMock: public Block { +public: + BlockMock(): Block(cpputils::Random::PseudoRandom().getFixedSize()) {} + MOCK_CONST_METHOD0(data, const void*()); + MOCK_METHOD3(write, void(const void*, uint64_t, uint64_t)); + MOCK_METHOD0(flush, void()); + MOCK_CONST_METHOD0(size, size_t()); + MOCK_METHOD1(resize, void(size_t)); + MOCK_CONST_METHOD0(key, const Key&()); +}; + +class BlockStoreWithRandomKeysTest: public Test { +public: + BlockStoreWithRandomKeysTest() :blockStoreMock(), blockStore(blockStoreMock), + key(Key::FromString("1491BB4932A389EE14BC7090AC772972")) {} + + BlockStoreWithRandomKeysMock blockStoreMock; + BlockStore &blockStore; + const blockstore::Key key; + + Data createDataWithSize(size_t size) { + Data fixture(DataFixture::generate(size)); + Data data(size); + std::memcpy(data.data(), fixture.data(), size); + return data; + } +}; + +TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough0) { + Data data = createDataWithSize(0); + EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock)); + blockStore.create(data); +} + +TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough1) { + Data data = createDataWithSize(1); + EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock)); + blockStore.create(data); +} + +TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough1024) { + Data data = createDataWithSize(1024); + EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock)); + blockStore.create(data); +} + +TEST_F(BlockStoreWithRandomKeysTest, KeyHasCorrectSize) { + EXPECT_CALL(blockStoreMock, do_create(_, _)).WillOnce(Invoke([](const Key &key, const Data &) { + EXPECT_EQ(Key::STRING_LENGTH, key.ToString().size()); + return new BlockMock; + })); + + blockStore.create(createDataWithSize(1024)); +} + +TEST_F(BlockStoreWithRandomKeysTest, TwoBlocksGetDifferentKeys) { + Key first_key = key; + EXPECT_CALL(blockStoreMock, do_create(_, _)) + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + first_key = key; + return new BlockMock; + })) + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + EXPECT_NE(first_key, key); + return new BlockMock; + })); + + Data data = createDataWithSize(1024); + blockStore.create(data); + blockStore.create(data); +} + +TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExists) { + Key first_key = key; + Data data = createDataWithSize(1024); + EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))) + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + first_key = key; + return nullptr; + })) + //TODO Check that this test case fails when the second do_create call gets different data + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + EXPECT_NE(first_key, key); + return new BlockMock; + })); + + blockStore.create(data); +} + +TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExistsTwoTimes) { + Key first_key = key; + Data data = createDataWithSize(1024); + EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))) + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + first_key = key; + return nullptr; + })) + //TODO Check that this test case fails when the second/third do_create calls get different data + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + first_key = key; + return nullptr; + })) + .WillOnce(Invoke([&first_key](const Key &key, const Data &) { + EXPECT_NE(first_key, key); + return new BlockMock; + })); + + blockStore.create(data); +} diff --git a/test/blockstore/testutils/BlockStoreTest.h b/test/blockstore/testutils/BlockStoreTest.h new file mode 100644 index 00000000..830cb3f2 --- /dev/null +++ b/test/blockstore/testutils/BlockStoreTest.h @@ -0,0 +1,148 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORETEST_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORETEST_H_ + +#include + +#include "blockstore/interface/BlockStore.h" + +class BlockStoreTestFixture { +public: + virtual ~BlockStoreTestFixture() {} + virtual cpputils::unique_ref createBlockStore() = 0; +}; + +template +class BlockStoreTest: public ::testing::Test { +public: + BlockStoreTest() :fixture() {} + + BOOST_STATIC_ASSERT_MSG( + (std::is_base_of::value), + "Given test fixture for instantiating the (type parameterized) BlockStoreTest must inherit from BlockStoreTestFixture" + ); + + ConcreteBlockStoreTestFixture fixture; +}; + +TYPED_TEST_CASE_P(BlockStoreTest); + +TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentKeys) { + auto blockStore = this->fixture.createBlockStore(); + auto block1 = blockStore->create(cpputils::Data(1024)); + auto block2 = blockStore->create(cpputils::Data(1024)); + EXPECT_NE(block1->key(), block2->key()); +} + +TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting) { + auto blockStore = this->fixture.createBlockStore(); + auto blockkey = blockStore->create(cpputils::Data(1024))->key(); + auto block = blockStore->load(blockkey); + EXPECT_NE(boost::none, block); + blockStore->remove(std::move(*block)); + EXPECT_EQ(boost::none, blockStore->load(blockkey)); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectOnEmptyBlockstore) { + auto blockStore = this->fixture.createBlockStore(); + EXPECT_EQ(0u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->create(cpputils::Data(1)); + EXPECT_EQ(1u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) { + auto blockStore = this->fixture.createBlockStore(); + blockStore->create(cpputils::Data(1)); + EXPECT_EQ(1u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->create(cpputils::Data(1)); + blockStore->remove(std::move(block)); + EXPECT_EQ(0u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks) { + auto blockStore = this->fixture.createBlockStore(); + auto block1 = blockStore->create(cpputils::Data(1)); + auto block2 = blockStore->create(cpputils::Data(0)); + EXPECT_EQ(2u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) { + auto blockStore = this->fixture.createBlockStore(); + blockStore->create(cpputils::Data(1)); + auto block2 = blockStore->create(cpputils::Data(0)); + EXPECT_EQ(2u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) { + auto blockStore = this->fixture.createBlockStore(); + auto block1 = blockStore->create(cpputils::Data(1)); + blockStore->create(cpputils::Data(0)); + EXPECT_EQ(2u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) { + auto blockStore = this->fixture.createBlockStore(); + blockStore->create(cpputils::Data(1)); + blockStore->create(cpputils::Data(0)); + EXPECT_EQ(2u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->create(cpputils::Data(1)); + blockStore->create(cpputils::Data(1)); + blockStore->remove(std::move(block)); + EXPECT_EQ(1u, blockStore->numBlocks()); +} + +TYPED_TEST_P(BlockStoreTest, CanRemoveModifiedBlock) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->create(cpputils::Data(5)); + block->write("data", 0, 4); + blockStore->remove(std::move(block)); + EXPECT_EQ(0u, blockStore->numBlocks()); +} + +#include "BlockStoreTest_Size.h" +#include "BlockStoreTest_Data.h" + + +REGISTER_TYPED_TEST_CASE_P(BlockStoreTest, + CreatedBlockHasCorrectSize, + LoadingUnchangedBlockHasCorrectSize, + CreatedBlockData, + LoadingUnchangedBlockData, + LoadedBlockIsCorrect, +// LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing, + AfterCreate_FlushingDoesntChangeBlock, + AfterLoad_FlushingDoesntChangeBlock, + AfterCreate_FlushesWhenDestructed, + AfterLoad_FlushesWhenDestructed, + LoadNonExistingBlock, + TwoCreatedBlocksHaveDifferentKeys, + BlockIsNotLoadableAfterDeleting, + NumBlocksIsCorrectOnEmptyBlockstore, + NumBlocksIsCorrectAfterAddingOneBlock, + NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock, + NumBlocksIsCorrectAfterRemovingTheLastBlock, + NumBlocksIsCorrectAfterAddingTwoBlocks, + NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock, + NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock, + NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks, + NumBlocksIsCorrectAfterRemovingABlock, + WriteAndReadImmediately, + WriteAndReadAfterLoading, + OverwriteAndRead, + CanRemoveModifiedBlock +); + + +#endif diff --git a/test/blockstore/testutils/BlockStoreTest_Data.h b/test/blockstore/testutils/BlockStoreTest_Data.h new file mode 100644 index 00000000..be0e4f1f --- /dev/null +++ b/test/blockstore/testutils/BlockStoreTest_Data.h @@ -0,0 +1,107 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_DATA_H_ +#define MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_DATA_H_ + +// This file is meant to be included by BlockStoreTest.h only + +struct DataRange { + size_t blocksize; + off_t offset; + size_t count; +}; + +class BlockStoreDataParametrizedTest { +public: + BlockStoreDataParametrizedTest(cpputils::unique_ref blockStore_, const DataRange &testData_) + : blockStore(std::move(blockStore_)), + testData(testData_), + foregroundData(cpputils::DataFixture::generate(testData.count, 0)), + backgroundData(cpputils::DataFixture::generate(testData.blocksize, 1)) { + } + + void TestWriteAndReadImmediately() { + auto block = blockStore->create(cpputils::Data(testData.blocksize).FillWithZeroes()); + block->write(foregroundData.data(), testData.offset, testData.count); + + EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count); + EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*block, testData.offset, testData.count); + } + + void TestWriteAndReadAfterLoading() { + blockstore::Key key = CreateBlockWriteToItAndReturnKey(foregroundData); + + auto loaded_block = blockStore->load(key).value(); + EXPECT_DATA_READS_AS(foregroundData, *loaded_block, testData.offset, testData.count); + EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*loaded_block, testData.offset, testData.count); + } + + void TestOverwriteAndRead() { + auto block = blockStore->create(cpputils::Data(testData.blocksize)); + block->write(backgroundData.data(), 0, testData.blocksize); + block->write(foregroundData.data(), testData.offset, testData.count); + EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count); + EXPECT_DATA_READS_AS_OUTSIDE_OF(backgroundData, *block, testData.offset, testData.count); + } + +private: + cpputils::unique_ref blockStore; + DataRange testData; + cpputils::Data foregroundData; + cpputils::Data backgroundData; + + blockstore::Key CreateBlockWriteToItAndReturnKey(const cpputils::Data &to_write) { + auto newblock = blockStore->create(cpputils::Data(testData.blocksize).FillWithZeroes()); + + newblock->write(to_write.data(), testData.offset, testData.count); + return newblock->key(); + } + + void EXPECT_DATA_READS_AS(const cpputils::Data &expected, const blockstore::Block &block, off_t offset, size_t count) { + cpputils::Data read(count); + std::memcpy(read.data(), (uint8_t*)block.data() + offset, count); + EXPECT_EQ(expected, read); + } + + void EXPECT_DATA_READS_AS_OUTSIDE_OF(const cpputils::Data &expected, const blockstore::Block &block, off_t start, size_t count) { + cpputils::Data begin(start); + cpputils::Data end(testData.blocksize - count - start); + + std::memcpy(begin.data(), expected.data(), start); + std::memcpy(end.data(), (uint8_t*)expected.data()+start+count, end.size()); + + EXPECT_DATA_READS_AS(begin, block, 0, start); + EXPECT_DATA_READS_AS(end, block, start + count, end.size()); + } + + void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const blockstore::Block &block, off_t start, size_t count) { + cpputils::Data ZEROES(testData.blocksize); + ZEROES.FillWithZeroes(); + EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, block, start, count); + } +}; + +inline std::vector DATA_RANGES() { + return { + DataRange{1024, 0, 1024}, // full size leaf, access beginning to end + DataRange{1024, 100, 1024 - 200}, // full size leaf, access middle to middle + DataRange{1024, 0, 1024 - 100}, // full size leaf, access beginning to middle + DataRange{1024, 100, 1024 - 100}, // full size leaf, access middle to end + DataRange{1024 - 100, 0, 1024 - 100}, // non-full size leaf, access beginning to end + DataRange{1024 - 100, 100, 1024 - 300}, // non-full size leaf, access middle to middle + DataRange{1024 - 100, 0, 1024 - 200}, // non-full size leaf, access beginning to middle + DataRange{1024 - 100, 100, 1024 - 200} // non-full size leaf, access middle to end + }; +}; +#define TYPED_TEST_P_FOR_ALL_DATA_RANGES(TestName) \ + TYPED_TEST_P(BlockStoreTest, TestName) { \ + for (auto dataRange: DATA_RANGES()) { \ + BlockStoreDataParametrizedTest(this->fixture.createBlockStore(), dataRange) \ + .Test##TestName(); \ + } \ + } + +TYPED_TEST_P_FOR_ALL_DATA_RANGES(WriteAndReadImmediately); +TYPED_TEST_P_FOR_ALL_DATA_RANGES(WriteAndReadAfterLoading); +TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteAndRead); + +#endif diff --git a/test/blockstore/testutils/BlockStoreTest_Size.h b/test/blockstore/testutils/BlockStoreTest_Size.h new file mode 100644 index 00000000..85effa79 --- /dev/null +++ b/test/blockstore/testutils/BlockStoreTest_Size.h @@ -0,0 +1,164 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_SIZE_H_ +#define MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_SIZE_H_ + +// This file is meant to be included by BlockStoreTest.h only + +#include +#include + +class BlockStoreSizeParameterizedTest { +public: + BlockStoreSizeParameterizedTest(cpputils::unique_ref blockStore_, size_t size_): blockStore(std::move(blockStore_)), size(size_) {} + + void TestCreatedBlockHasCorrectSize() { + auto block = CreateBlock(); + EXPECT_EQ(size, block->size()); + } + + void TestLoadingUnchangedBlockHasCorrectSize() { + blockstore::Key key = CreateBlock()->key(); + auto loaded_block = blockStore->load(key).value(); + EXPECT_EQ(size, loaded_block->size()); + } + + void TestCreatedBlockData() { + cpputils::Data data = cpputils::DataFixture::generate(size); + auto block = blockStore->create(data); + EXPECT_EQ(0, std::memcmp(data.data(), block->data(), size)); + } + + void TestLoadingUnchangedBlockData() { + cpputils::Data data = cpputils::DataFixture::generate(size); + blockstore::Key key = blockStore->create(data)->key(); + auto loaded_block = blockStore->load(key).value(); + EXPECT_EQ(0, std::memcmp(data.data(), loaded_block->data(), size)); + } + + void TestLoadedBlockIsCorrect() { + cpputils::Data randomData = cpputils::DataFixture::generate(size); + auto loaded_block = StoreDataToBlockAndLoadIt(randomData); + EXPECT_EQ(size, loaded_block->size()); + EXPECT_EQ(0, std::memcmp(randomData.data(), loaded_block->data(), size)); + } + + void TestLoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing() { + cpputils::Data randomData = cpputils::DataFixture::generate(size); + auto loaded_block = StoreDataToBlockAndLoadItDirectlyAfterFlushing(randomData); + EXPECT_EQ(size, loaded_block->size()); + EXPECT_EQ(0, std::memcmp(randomData.data(), loaded_block->data(), size)); + } + + void TestAfterCreate_FlushingDoesntChangeBlock() { + cpputils::Data randomData = cpputils::DataFixture::generate(size); + auto block = CreateBlock(); + WriteDataToBlock(block.get(), randomData); + block->flush(); + + EXPECT_BLOCK_DATA_CORRECT(*block, randomData); + } + + void TestAfterLoad_FlushingDoesntChangeBlock() { + cpputils::Data randomData = cpputils::DataFixture::generate(size); + auto block = CreateBlockAndLoadIt(); + WriteDataToBlock(block.get(), randomData); + block->flush(); + + EXPECT_BLOCK_DATA_CORRECT(*block, randomData); + } + + void TestAfterCreate_FlushesWhenDestructed() { + cpputils::Data randomData = cpputils::DataFixture::generate(size); + blockstore::Key key = blockstore::Key::Null(); + { + auto block = blockStore->create(cpputils::Data(size)); + key = block->key(); + WriteDataToBlock(block.get(), randomData); + } + auto loaded_block = blockStore->load(key).value(); + EXPECT_BLOCK_DATA_CORRECT(*loaded_block, randomData); + } + + void TestAfterLoad_FlushesWhenDestructed() { + cpputils::Data randomData = cpputils::DataFixture::generate(size); + blockstore::Key key = blockstore::Key::Null(); + { + key = CreateBlock()->key(); + auto block = blockStore->load(key).value(); + WriteDataToBlock(block.get(), randomData); + } + auto loaded_block = blockStore->load(key).value(); + EXPECT_BLOCK_DATA_CORRECT(*loaded_block, randomData); + } + + void TestLoadNonExistingBlock() { + EXPECT_EQ(boost::none, blockStore->load(key)); + } + +private: + const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972"); + cpputils::unique_ref blockStore; + size_t size; + + cpputils::Data ZEROES(size_t size) { + cpputils::Data ZEROES(size); + ZEROES.FillWithZeroes(); + return ZEROES; + } + + cpputils::unique_ref StoreDataToBlockAndLoadIt(const cpputils::Data &data) { + blockstore::Key key = StoreDataToBlockAndGetKey(data); + return blockStore->load(key).value(); + } + + blockstore::Key StoreDataToBlockAndGetKey(const cpputils::Data &data) { + return blockStore->create(data)->key(); + } + + cpputils::unique_ref StoreDataToBlockAndLoadItDirectlyAfterFlushing(const cpputils::Data &data) { + auto block = blockStore->create(data); + block->flush(); + return blockStore->load(block->key()).value(); + } + + cpputils::unique_ref CreateBlockAndLoadIt() { + blockstore::Key key = CreateBlock()->key(); + return blockStore->load(key).value(); + } + + cpputils::unique_ref CreateBlock() { + return blockStore->create(cpputils::Data(size)); + } + + void WriteDataToBlock(blockstore::Block *block, const cpputils::Data &randomData) { + block->write(randomData.data(), 0, randomData.size()); + } + + void EXPECT_BLOCK_DATA_CORRECT(const blockstore::Block &block, const cpputils::Data &randomData) { + EXPECT_EQ(randomData.size(), block.size()); + EXPECT_EQ(0, std::memcmp(randomData.data(), block.data(), randomData.size())); + } +}; + +constexpr std::initializer_list SIZES = {0, 1, 1024, 4096, 10*1024*1024}; +#define TYPED_TEST_P_FOR_ALL_SIZES(TestName) \ + TYPED_TEST_P(BlockStoreTest, TestName) { \ + for (auto size: SIZES) { \ + BlockStoreSizeParameterizedTest(this->fixture.createBlockStore(), size) \ + .Test##TestName(); \ + } \ + } \ + +TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockHasCorrectSize); +TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockHasCorrectSize); +TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockData); +TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockData); +TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrect); +//TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing); +TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushingDoesntChangeBlock); +TYPED_TEST_P_FOR_ALL_SIZES(AfterLoad_FlushingDoesntChangeBlock); +TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushesWhenDestructed); +TYPED_TEST_P_FOR_ALL_SIZES(AfterLoad_FlushesWhenDestructed); +TYPED_TEST_P_FOR_ALL_SIZES(LoadNonExistingBlock); + +#endif diff --git a/test/blockstore/testutils/BlockStoreWithRandomKeysTest.h b/test/blockstore/testutils/BlockStoreWithRandomKeysTest.h new file mode 100644 index 00000000..a394f55d --- /dev/null +++ b/test/blockstore/testutils/BlockStoreWithRandomKeysTest.h @@ -0,0 +1,88 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTOREWITHRANDOMKEYSTEST_H_ +#define MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTOREWITHRANDOMKEYSTEST_H_ + +#include + +#include "blockstore/interface/BlockStore.h" + +class BlockStoreWithRandomKeysTestFixture { +public: + virtual ~BlockStoreWithRandomKeysTestFixture() {} + virtual cpputils::unique_ref createBlockStore() = 0; +}; + +template +class BlockStoreWithRandomKeysTest: public ::testing::Test { +public: + BlockStoreWithRandomKeysTest(): fixture() {} + + BOOST_STATIC_ASSERT_MSG( + (std::is_base_of::value), + "Given test fixture for instantiating the (type parameterized) BlockStoreWithRandomKeysTest must inherit from BlockStoreWithRandomKeysTestFixture" + ); + + blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972"); + + const std::vector SIZES = {0, 1, 1024, 4096, 10*1024*1024}; + + ConcreteBlockStoreWithRandomKeysTestFixture fixture; +}; + +TYPED_TEST_CASE_P(BlockStoreWithRandomKeysTest); + +TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSameSize) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->tryCreate(this->key, cpputils::Data(1024)); + (*block)->flush(); //TODO Ideally, flush shouldn't be necessary here. + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(1024)); + EXPECT_NE(boost::none, block); + EXPECT_EQ(boost::none, block2); +} + +TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndDifferentSize) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->tryCreate(this->key, cpputils::Data(1024)); + (*block)->flush(); //TODO Ideally, flush shouldn't be necessary here. + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(4096)); + EXPECT_NE(boost::none, block); + EXPECT_EQ(boost::none, block2); +} + +TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndFirstNullSize) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->tryCreate(this->key, cpputils::Data(0)); + (*block)->flush(); //TODO Ideally, flush shouldn't be necessary here. + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(1024)); + EXPECT_NE(boost::none, block); + EXPECT_EQ(boost::none, block2); +} + +TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSecondNullSize) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->tryCreate(this->key, cpputils::Data(1024)); + (*block)->flush(); //TODO Ideally, flush shouldn't be necessary here. + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(0)); + EXPECT_NE(boost::none, block); + EXPECT_EQ(boost::none, block2); +} + +TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndBothNullSize) { + auto blockStore = this->fixture.createBlockStore(); + auto block = blockStore->tryCreate(this->key, cpputils::Data(0)); + (*block)->flush(); //TODO Ideally, flush shouldn't be necessary here. + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(0)); + EXPECT_NE(boost::none, block); + EXPECT_EQ(boost::none, block2); +} + +REGISTER_TYPED_TEST_CASE_P(BlockStoreWithRandomKeysTest, + CreateTwoBlocksWithSameKeyAndSameSize, + CreateTwoBlocksWithSameKeyAndDifferentSize, + CreateTwoBlocksWithSameKeyAndFirstNullSize, + CreateTwoBlocksWithSameKeyAndSecondNullSize, + CreateTwoBlocksWithSameKeyAndBothNullSize +); + + +#endif diff --git a/test/blockstore/utils/BlockStoreUtilsTest.cpp b/test/blockstore/utils/BlockStoreUtilsTest.cpp new file mode 100644 index 00000000..dc90a07a --- /dev/null +++ b/test/blockstore/utils/BlockStoreUtilsTest.cpp @@ -0,0 +1,113 @@ +#include "blockstore/implementations/testfake/FakeBlockStore.h" +#include +#include "blockstore/utils/BlockStoreUtils.h" +#include + +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; + +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +using namespace blockstore; +using namespace blockstore::utils; + +using blockstore::testfake::FakeBlockStore; + +class BlockStoreUtilsTest: public Test { +public: + unsigned int SIZE = 1024 * 1024; + BlockStoreUtilsTest(): + ZEROES(SIZE), + dataFixture(DataFixture::generate(SIZE)), + blockStore(make_unique_ref()) { + ZEROES.FillWithZeroes(); + } + + Data ZEROES; + Data dataFixture; + unique_ref blockStore; +}; + +TEST_F(BlockStoreUtilsTest, FillWithZeroes) { + auto block = blockStore->create(Data(SIZE)); + block->write(dataFixture.data(), 0, SIZE); + EXPECT_NE(0, std::memcmp(ZEROES.data(), block->data(), SIZE)); + fillWithZeroes(block.get()); + EXPECT_EQ(0, std::memcmp(ZEROES.data(), block->data(), SIZE)); +} + +class BlockStoreUtilsTest_CopyToNewBlock: public BlockStoreUtilsTest {}; + +TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyEmptyBlock) { + auto block = blockStore->create(Data(0)); + auto block2 = copyToNewBlock(blockStore.get(), *block); + + EXPECT_EQ(0u, block2->size()); +} + +TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) { + auto block = blockStore->create(ZEROES); + auto block2 = copyToNewBlock(blockStore.get(), *block); + + EXPECT_EQ(SIZE, block2->size()); + EXPECT_EQ(0, std::memcmp(ZEROES.data(), block2->data(), SIZE)); +} + +TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) { + auto block = blockStore->create(Data(SIZE)); + block->write(dataFixture.data(), 0, SIZE); + auto block2 = copyToNewBlock(blockStore.get(), *block); + + EXPECT_EQ(SIZE, block2->size()); + EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE)); +} + +TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) { + auto block = blockStore->create(Data(SIZE)); + block->write(dataFixture.data(), 0, SIZE); + auto block2 = copyToNewBlock(blockStore.get(), *block); + + EXPECT_EQ(SIZE, block->size()); + EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE)); +} + +class BlockStoreUtilsTest_CopyToExistingBlock: public BlockStoreUtilsTest {}; + +TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyEmptyBlock) { + auto block = blockStore->create(Data(0)); + auto block2 = blockStore->create(Data(0)); + copyTo(block2.get(), *block); +} + +TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) { + auto block = blockStore->create(ZEROES); + auto block2 = blockStore->create(Data(SIZE)); + block2->write(dataFixture.data(), 0, SIZE); + copyTo(block2.get(), *block); + + EXPECT_EQ(0, std::memcmp(ZEROES.data(), block2->data(), SIZE)); +} + +TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyDataBlock) { + auto block = blockStore->create(Data(SIZE)); + block->write(dataFixture.data(), 0, SIZE); + auto block2 = blockStore->create(Data(SIZE)); + copyTo(block2.get(), *block); + + EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE)); +} + +TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, OriginalBlockUnchanged) { + auto block = blockStore->create(Data(SIZE)); + block->write(dataFixture.data(), 0, SIZE); + auto block2 = blockStore->create(Data(SIZE)); + copyTo(block2.get(), *block); + + EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE)); +} diff --git a/test/cpp-utils/CMakeLists.txt b/test/cpp-utils/CMakeLists.txt new file mode 100644 index 00000000..dc132db6 --- /dev/null +++ b/test/cpp-utils/CMakeLists.txt @@ -0,0 +1,57 @@ +project (cpp-utils-test) + +set(SOURCES + EitherIncludeTest.cpp + crypto/symmetric/CipherTest.cpp + crypto/symmetric/testutils/FakeAuthenticatedCipher.cpp + crypto/kdf/SCryptTest.cpp + crypto/kdf/DerivedKeyTest.cpp + crypto/kdf/DerivedKeyConfigTest.cpp + MacrosIncludeTest.cpp + pointer/unique_ref_test.cpp + pointer/cast_include_test.cpp + pointer/cast_test.cpp + pointer/unique_ref_boost_optional_gtest_workaround_include_test.cpp + pointer/optional_ownership_ptr_include_test.cpp + pointer/optional_ownership_ptr_test.cpp + pointer/unique_ref_include_test.cpp + process/daemonize_include_test.cpp + process/subprocess_include_test.cpp + tempfile/TempFileTest.cpp + tempfile/TempFileIncludeTest.cpp + tempfile/TempDirIncludeTest.cpp + tempfile/TempDirTest.cpp + network/CurlHttpClientTest.cpp + network/FakeHttpClientTest.cpp + io/ConsoleIncludeTest.cpp + io/ConsoleTest_AskYesNo.cpp + io/ConsoleTest_Print.cpp + io/ConsoleTest_Ask.cpp + random/RandomIncludeTest.cpp + lock/LockPoolIncludeTest.cpp + lock/ConditionBarrierIncludeTest.cpp + lock/MutexPoolLockIncludeTest.cpp + data/FixedSizeDataTest.cpp + data/DataFixtureIncludeTest.cpp + data/DataFixtureTest.cpp + data/DataTest.cpp + data/FixedSizeDataIncludeTest.cpp + data/DataIncludeTest.cpp + logging/LoggingLevelTest.cpp + logging/LoggerTest.cpp + logging/LoggingTest.cpp + logging/LoggerIncludeTest.cpp + logging/LoggingIncludeTest.cpp + EitherTest.cpp + assert/assert_release_test.cpp + assert/backtrace_include_test.cpp + assert/assert_include_test.cpp + assert/assert_debug_test.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} googletest cpp-utils) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/cpp-utils/EitherIncludeTest.cpp b/test/cpp-utils/EitherIncludeTest.cpp new file mode 100644 index 00000000..5d3dd7b0 --- /dev/null +++ b/test/cpp-utils/EitherIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/either.h" + +//Test that either can be included without needing additional dependencies diff --git a/test/cpp-utils/EitherTest.cpp b/test/cpp-utils/EitherTest.cpp new file mode 100644 index 00000000..e5555318 --- /dev/null +++ b/test/cpp-utils/EitherTest.cpp @@ -0,0 +1,505 @@ +#include +#include +#include +#include "cpp-utils/either.h" +#include "cpp-utils/macros.h" +#include + +//TODO Go through all test cases and think about whether it makes sense to add the same test case but with primitive types. + +using std::ostringstream; +using std::string; +using std::vector; +using std::pair; +using std::make_pair; +using namespace cpputils; +using ::testing::Test; + +class OnlyMoveable { +public: + OnlyMoveable(int value_): value(value_) {} + OnlyMoveable(OnlyMoveable &&source): value(source.value) {source.value = -1;} + bool operator==(const OnlyMoveable &rhs) const { + return value == rhs.value; + } + int value; +private: + DISALLOW_COPY_AND_ASSIGN(OnlyMoveable); +}; + +template +struct StoreWith1ByteFlag { + T val; + char flag; +}; + +class EitherTest: public Test { +public: + template + void EXPECT_IS_LEFT(const either &val) { + EXPECT_TRUE(val.is_left()); + EXPECT_FALSE(val.is_right()); + } + template + void EXPECT_IS_RIGHT(const either &val) { + EXPECT_FALSE(val.is_left()); + EXPECT_TRUE(val.is_right()); + } + template + void EXPECT_LEFT_IS(const Expected &expected, either &value) { + EXPECT_IS_LEFT(value); + EXPECT_EQ(expected, value.left()); + EXPECT_EQ(expected, value.left_opt().get()); + EXPECT_EQ(boost::none, value.right_opt()); + const either &const_value = value; + EXPECT_EQ(expected, const_value.left()); + EXPECT_EQ(expected, const_value.left_opt().get()); + EXPECT_EQ(boost::none, const_value.right_opt()); + } + template + void EXPECT_RIGHT_IS(const Expected &expected, either &value) { + EXPECT_IS_RIGHT(value); + EXPECT_EQ(expected, value.right()); + EXPECT_EQ(expected, value.right_opt().get()); + EXPECT_EQ(boost::none, value.left_opt()); + const either &const_value = value; + EXPECT_EQ(expected, const_value.right()); + EXPECT_EQ(expected, const_value.right_opt().get()); + EXPECT_EQ(boost::none, const_value.left_opt()); + } +}; + +template +void TestSpaceUsage() { + EXPECT_EQ(std::max(sizeof(StoreWith1ByteFlag), sizeof(StoreWith1ByteFlag)), sizeof(either)); +} + +TEST_F(EitherTest, SpaceUsage) { + TestSpaceUsage(); + TestSpaceUsage(); + TestSpaceUsage(); + TestSpaceUsage(); + TestSpaceUsage>(); +} + +TEST_F(EitherTest, LeftCanBeConstructed) { + either val = 3; + UNUSED(val); +} + +TEST_F(EitherTest, RightCanBeConstructed) { + either val = string("string"); + UNUSED(val); +} + +TEST_F(EitherTest, IsLeft) { + either val = 3; + EXPECT_IS_LEFT(val); +} + +TEST_F(EitherTest, IsRight) { + either val = string("string"); + EXPECT_IS_RIGHT(val); +} + +TEST_F(EitherTest, LeftIsStored) { + either val = 3; + EXPECT_LEFT_IS(3, val); +} + +TEST_F(EitherTest, RightIsStored) { + either val = string("string"); + EXPECT_RIGHT_IS("string", val); +} + +TEST_F(EitherTest, LeftCanBeMoveContructed) { + either val = OnlyMoveable(1); + UNUSED(val); +} + +TEST_F(EitherTest, RightCanBeMoveContructed) { + either val = OnlyMoveable(1); + UNUSED(val); +} + +TEST_F(EitherTest, IsLeftWhenMoveContructed) { + either val = OnlyMoveable(1); + EXPECT_IS_LEFT(val); +} + +TEST_F(EitherTest, IsRightWhenMoveContructed) { + either val = OnlyMoveable(1); + EXPECT_IS_RIGHT(val); +} + +TEST_F(EitherTest, LeftIsStoredWhenMoveContructed) { + either val = OnlyMoveable(2); + EXPECT_LEFT_IS(OnlyMoveable(2), val); +} + +TEST_F(EitherTest, RightIsStoredWhenMoveContructed) { + either val = OnlyMoveable(3); + EXPECT_RIGHT_IS(OnlyMoveable(3), val); +} + +TEST_F(EitherTest, LeftCanBeCopied) { + either val = string("string"); + either val2 = val; + EXPECT_LEFT_IS("string", val2); +} + +TEST_F(EitherTest, CopyingLeftDoesntChangeSource) { + either val = string("string"); + either val2 = val; + EXPECT_LEFT_IS("string", val); +} + +TEST_F(EitherTest, RightCanBeCopied) { + either val = string("string"); + either val2 = val; + EXPECT_RIGHT_IS("string", val2); +} + +TEST_F(EitherTest, CopyingRightDoesntChangeSource) { + either val = string("string"); + either val2 = val; + EXPECT_RIGHT_IS("string", val); +} + +TEST_F(EitherTest, LeftCanBeMoved) { + either val = OnlyMoveable(5); + either val2 = std::move(val); + EXPECT_LEFT_IS(OnlyMoveable(5), val2); +} + +TEST_F(EitherTest, RightCanBeMoved) { + either val = OnlyMoveable(5); + either val2 = std::move(val); + EXPECT_RIGHT_IS(OnlyMoveable(5), val2); +} + +TEST_F(EitherTest, LeftCanBeAssigned) { + either val = string("string"); + either val2 = string("otherstring"); + val2 = val; + EXPECT_LEFT_IS("string", val2); +} + +TEST_F(EitherTest, RightCanBeAssigned) { + either val = string("string"); + either val2 = string("otherstring"); + val2 = val; + EXPECT_RIGHT_IS("string", val2); +} + +TEST_F(EitherTest, LeftCanBeMoveAssigned) { + either val = OnlyMoveable(3); + either val2 = OnlyMoveable(4); + val2 = std::move(val); + EXPECT_LEFT_IS(OnlyMoveable(3), val2); +} + +TEST_F(EitherTest, RightCanBeMoveAssigned) { + either val = OnlyMoveable(3); + either val2 = OnlyMoveable(4); + val2 = std::move(val); + EXPECT_RIGHT_IS(OnlyMoveable(3), val2); +} + +TEST_F(EitherTest, LeftCanBeDirectlyAssigned) { + either val = string("string"); + val = string("otherstring"); + EXPECT_LEFT_IS("otherstring", val); +} + +TEST_F(EitherTest, RightCanBeDirectlyAssigned) { + either val = string("string"); + val = string("otherstring"); + EXPECT_RIGHT_IS("otherstring", val); +} + +TEST_F(EitherTest, LeftCanBeDirectlyMoveAssigned) { + either val = OnlyMoveable(3); + val = OnlyMoveable(5); + EXPECT_LEFT_IS(OnlyMoveable(5), val); +} + +TEST_F(EitherTest, RightCanBeDirectlyMoveAssigned) { + either val = OnlyMoveable(3); + val = OnlyMoveable(5); + EXPECT_RIGHT_IS(OnlyMoveable(5), val); +} + +TEST_F(EitherTest, ModifyLeft) { + either val = string("mystring1"); + val.left() = "mystring2"; + EXPECT_LEFT_IS("mystring2", val); +} + +TEST_F(EitherTest, ModifyRight) { + either val = string("mystring1"); + val.right() = "mystring2"; + EXPECT_RIGHT_IS("mystring2", val); +} + +TEST_F(EitherTest, ModifyLeftOpt) { + either val = string("mystring1"); + val.left_opt().get() = "mystring2"; + EXPECT_LEFT_IS("mystring2", val); +} + +TEST_F(EitherTest, ModifyRightOpt) { + either val = string("mystring1"); + val.right_opt().get() = "mystring2"; + EXPECT_RIGHT_IS("mystring2", val); +} + +TEST_F(EitherTest, LeftEquals) { + either val1 = string("mystring"); + either val2 = string("mystring"); + EXPECT_TRUE(val1 == val2); + EXPECT_TRUE(val2 == val1); + EXPECT_FALSE(val1 != val2); + EXPECT_FALSE(val2 != val1); +} + +TEST_F(EitherTest, LeftNotEquals) { + either val1 = string("mystring"); + either val2 = string("mystring2"); + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_FALSE(val1 == val2); + EXPECT_FALSE(val2 == val1); +} + +TEST_F(EitherTest, RightEquals) { + either val1 = string("mystring"); + either val2 = string("mystring"); + EXPECT_TRUE(val1 == val2); + EXPECT_TRUE(val2 == val1); + EXPECT_FALSE(val1 != val2); + EXPECT_FALSE(val2 != val1); +} + +TEST_F(EitherTest, RightNotEquals) { + either val1 = string("mystring"); + either val2 = string("mystring2"); + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_FALSE(val1 == val2); + EXPECT_FALSE(val2 == val1); +} + +TEST_F(EitherTest, LeftNotEqualsRight) { + either val1 = string("mystring"); + either val2 = 3; + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_FALSE(val1 == val2); + EXPECT_FALSE(val2 == val1); +} + +TEST_F(EitherTest, OutputLeft) { + ostringstream str; + str << either("mystring"); + EXPECT_EQ("Left(mystring)", str.str()); +} + +TEST_F(EitherTest, OutputRight) { + ostringstream str; + str << either("mystring"); + EXPECT_EQ("Right(mystring)", str.str()); +} + +TEST_F(EitherTest, MakeLeft) { + either var = make_left("mystring"); + EXPECT_LEFT_IS("mystring", var); +} + +TEST_F(EitherTest, MakeLeft_OnlyMoveable) { + either var = make_left(4); + EXPECT_LEFT_IS(OnlyMoveable(4), var); +} + +TEST_F(EitherTest, MakeLeft_MultiParam) { + either, int> var = make_left, int>(4, 5); + EXPECT_LEFT_IS(make_pair(4,5), var); +} + +TEST_F(EitherTest, MakeRight) { + either var = make_right("mystring"); + EXPECT_RIGHT_IS("mystring", var); +} + +TEST_F(EitherTest, MakeRight_OnlyMoveable) { + either var = make_right(4); + EXPECT_RIGHT_IS(OnlyMoveable(4), var); +} + +TEST_F(EitherTest, MakeRight_MultiParam) { + either> var = make_right>(4, 5); + EXPECT_RIGHT_IS(make_pair(4,5), var); +} + +TEST_F(EitherTest, LeftCanBeQueriedAsRvalue) { + OnlyMoveable val = make_left(3).left(); + EXPECT_EQ(OnlyMoveable(3), val); +} + +TEST_F(EitherTest, RightCanBeQueriedAsRvalue) { + OnlyMoveable val = make_right(3).right(); + EXPECT_EQ(OnlyMoveable(3), val); +} + +TEST_F(EitherTest, LeftOptCanBeQueriedAsRvalue) { + OnlyMoveable val = make_left(3).left_opt().value(); + EXPECT_EQ(OnlyMoveable(3), val); +} + +TEST_F(EitherTest, RightOptCanBeQueriedAsRvalue) { + OnlyMoveable val = make_right(3).right_opt().value(); + EXPECT_EQ(OnlyMoveable(3), val); +} + +class DestructorCallback { +public: + MOCK_CONST_METHOD0(call, void()); + + void EXPECT_CALLED(int times = 1) { + EXPECT_CALL(*this, call()).Times(times); + } +}; +class ClassWithDestructorCallback { +public: + ClassWithDestructorCallback(const DestructorCallback *destructorCallback) : _destructorCallback(destructorCallback) {} + ClassWithDestructorCallback(const ClassWithDestructorCallback &rhs): _destructorCallback(rhs._destructorCallback) {} + + ~ClassWithDestructorCallback() { + _destructorCallback->call(); + } + +private: + const DestructorCallback *_destructorCallback; + + ClassWithDestructorCallback &operator=(const ClassWithDestructorCallback &rhs) = delete; +}; +class OnlyMoveableClassWithDestructorCallback { +public: + OnlyMoveableClassWithDestructorCallback(const DestructorCallback *destructorCallback) : _destructorCallback(destructorCallback) { } + OnlyMoveableClassWithDestructorCallback(OnlyMoveableClassWithDestructorCallback &&source): _destructorCallback(source._destructorCallback) {} + + ~OnlyMoveableClassWithDestructorCallback() { + _destructorCallback->call(); + } + +private: + DISALLOW_COPY_AND_ASSIGN(OnlyMoveableClassWithDestructorCallback); + const DestructorCallback *_destructorCallback; +}; + +class EitherTest_Destructor: public EitherTest { +}; + +TEST_F(EitherTest_Destructor, LeftDestructorIsCalled) { + DestructorCallback destructorCallback; + destructorCallback.EXPECT_CALLED(2); //Once for the temp object, once when the either class destructs + + ClassWithDestructorCallback temp(&destructorCallback); + either var = temp; +} + +TEST_F(EitherTest_Destructor, RightDestructorIsCalled) { + DestructorCallback destructorCallback; + destructorCallback.EXPECT_CALLED(2); //Once for the temp object, once when the either class destructs + + ClassWithDestructorCallback temp(&destructorCallback); + either var = temp; +} + +TEST_F(EitherTest_Destructor, LeftDestructorIsCalledAfterCopying) { + DestructorCallback destructorCallback; + destructorCallback.EXPECT_CALLED(3); //Once for the temp object, once for var1 and once for var2 + + ClassWithDestructorCallback temp(&destructorCallback); + either var1 = temp; + either var2 = var1; +} + +TEST_F(EitherTest_Destructor, RightDestructorIsCalledAfterCopying) { + DestructorCallback destructorCallback; + destructorCallback.EXPECT_CALLED(3); //Once for the temp object, once for var1 and once for var2 + + ClassWithDestructorCallback temp(&destructorCallback); + either var1 = temp; + either var2 = var1; +} + +TEST_F(EitherTest_Destructor, LeftDestructorIsCalledAfterMoving) { + DestructorCallback destructorCallback; + destructorCallback.EXPECT_CALLED(3); //Once for the temp object, once for var1 and once for var2 + + OnlyMoveableClassWithDestructorCallback temp(&destructorCallback); + either var1 = std::move(temp); + either var2 = std::move(var1); +} + +TEST_F(EitherTest_Destructor, RightDestructorIsCalledAfterMoving) { + DestructorCallback destructorCallback; + destructorCallback.EXPECT_CALLED(3); //Once for the temp object, once for var1 and once for var2 + + OnlyMoveableClassWithDestructorCallback temp(&destructorCallback); + either var1 = std::move(temp); + either var2 = std::move(var1); +} + +TEST_F(EitherTest_Destructor, LeftDestructorIsCalledAfterAssignment) { + DestructorCallback destructorCallback1; + DestructorCallback destructorCallback2; + destructorCallback1.EXPECT_CALLED(2); //Once for the temp1 object, once at the assignment + destructorCallback2.EXPECT_CALLED(3); //Once for the temp2 object, once in destructor of var2, once in destructor of var1 + + ClassWithDestructorCallback temp1(&destructorCallback1); + either var1 = temp1; + ClassWithDestructorCallback temp2(&destructorCallback2); + either var2 = temp2; + var1 = var2; +} + +TEST_F(EitherTest_Destructor, RightDestructorIsCalledAfterAssignment) { + DestructorCallback destructorCallback1; + DestructorCallback destructorCallback2; + destructorCallback1.EXPECT_CALLED(2); //Once for the temp1 object, once at the assignment + destructorCallback2.EXPECT_CALLED(3); //Once for the temp2 object, once in destructor of var2, once in destructor of var1 + + ClassWithDestructorCallback temp1(&destructorCallback1); + either var1 = temp1; + ClassWithDestructorCallback temp2(&destructorCallback2); + either var2 = temp2; + var1 = var2; +} + +TEST_F(EitherTest_Destructor, LeftDestructorIsCalledAfterMoveAssignment) { + DestructorCallback destructorCallback1; + DestructorCallback destructorCallback2; + destructorCallback1.EXPECT_CALLED(2); //Once for the temp1 object, once at the assignment + destructorCallback2.EXPECT_CALLED(3); //Once for the temp2 object, once in destructor of var2, once in destructor of var1 + + OnlyMoveableClassWithDestructorCallback temp1(&destructorCallback1); + either var1 = std::move(temp1); + OnlyMoveableClassWithDestructorCallback temp2(&destructorCallback2); + either var2 = std::move(temp2); + var1 = std::move(var2); +} + +TEST_F(EitherTest_Destructor, RightDestructorIsCalledAfterMoveAssignment) { + DestructorCallback destructorCallback1; + DestructorCallback destructorCallback2; + destructorCallback1.EXPECT_CALLED(2); //Once for the temp1 object, once at the assignment + destructorCallback2.EXPECT_CALLED(3); //Once for the temp2 object, once in destructor of var2, once in destructor of var1 + + OnlyMoveableClassWithDestructorCallback temp1(&destructorCallback1); + either var1 = std::move(temp1); + OnlyMoveableClassWithDestructorCallback temp2(&destructorCallback2); + either var2 = std::move(temp2); + var1 = std::move(var2); +} diff --git a/test/cpp-utils/MacrosIncludeTest.cpp b/test/cpp-utils/MacrosIncludeTest.cpp new file mode 100644 index 00000000..6e16a15d --- /dev/null +++ b/test/cpp-utils/MacrosIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/macros.h" + +// Test that macros.h can be included without needing additional dependencies diff --git a/test/cpp-utils/assert/assert_debug_test.cpp b/test/cpp-utils/assert/assert_debug_test.cpp new file mode 100644 index 00000000..7da2627e --- /dev/null +++ b/test/cpp-utils/assert/assert_debug_test.cpp @@ -0,0 +1,26 @@ +#include +#include + +//Include the ASSERT macro for a debug build +#undef NDEBUG +#include "cpp-utils/assert/assert.h" + +using testing::MatchesRegex; + +TEST(AssertTest_DebugBuild, DoesntDieIfTrue) { + ASSERT(true, "bla"); +} + +TEST(AssertTest_DebugBuild, DiesIfFalse) { + EXPECT_DEATH( + ASSERT(false, "bla"), + "" + ); +} + +TEST(AssertTest_DebugBuild, AssertMessage) { + EXPECT_DEATH( + ASSERT(2==5, "my message"), + "Assertion \\[2==5\\] failed in .*/assert_debug_test.cpp:[0-9]+: my message" + ); +} diff --git a/test/cpp-utils/assert/assert_include_test.cpp b/test/cpp-utils/assert/assert_include_test.cpp new file mode 100644 index 00000000..b7dbf23a --- /dev/null +++ b/test/cpp-utils/assert/assert_include_test.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/assert/assert.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/assert/assert_release_test.cpp b/test/cpp-utils/assert/assert_release_test.cpp new file mode 100644 index 00000000..05ef87f6 --- /dev/null +++ b/test/cpp-utils/assert/assert_release_test.cpp @@ -0,0 +1,30 @@ +#include +#include + +//Include the ASSERT macro for a release build +#define NDEBUG +#include "cpp-utils/assert/assert.h" + +using testing::MatchesRegex; + +TEST(AssertTest_ReleaseBuild, DoesntThrowIfTrue) { + ASSERT(true, "bla"); +} + +TEST(AssertTest_ReleaseBuild, ThrowsIfFalse) { + EXPECT_THROW( + ASSERT(false, "bla"), + cpputils::AssertFailed + ); +} + +TEST(AssertTest_ReleaseBuild, AssertMessage) { + try { + ASSERT(2==5, "my message"); + FAIL(); + } catch (const cpputils::AssertFailed &e) { + EXPECT_THAT(e.what(), MatchesRegex( + "Assertion \\[2==5\\] failed in .*/assert_release_test.cpp:23: my message.*" + )); + } +} \ No newline at end of file diff --git a/test/cpp-utils/assert/backtrace_include_test.cpp b/test/cpp-utils/assert/backtrace_include_test.cpp new file mode 100644 index 00000000..fc2e6226 --- /dev/null +++ b/test/cpp-utils/assert/backtrace_include_test.cpp @@ -0,0 +1,4 @@ +#include "cpp-utils/assert/backtrace.h" + +// Test the header can be included without needing additional dependencies + diff --git a/test/cpp-utils/crypto/kdf/DerivedKeyConfigTest.cpp b/test/cpp-utils/crypto/kdf/DerivedKeyConfigTest.cpp new file mode 100644 index 00000000..a8927500 --- /dev/null +++ b/test/cpp-utils/crypto/kdf/DerivedKeyConfigTest.cpp @@ -0,0 +1,86 @@ +#include +#include "cpp-utils/crypto/kdf/DerivedKeyConfig.h" +#include "cpp-utils/data/DataFixture.h" +#include + +using namespace cpputils; + +class DerivedKeyConfigTest : public ::testing::Test { +public: + DerivedKeyConfig SaveAndLoad(const DerivedKeyConfig &source) { + Serializer serializer(source.serializedSize()); + source.serialize(&serializer); + Data serialized = serializer.finished(); + Deserializer deserializer(&serialized); + return DerivedKeyConfig::deserialize(&deserializer); + } +}; + +TEST_F(DerivedKeyConfigTest, Salt) { + DerivedKeyConfig cfg(DataFixture::generate(32), 0, 0, 0); + EXPECT_EQ(DataFixture::generate(32), cfg.salt()); +} + +TEST_F(DerivedKeyConfigTest, Salt_Move) { + DerivedKeyConfig cfg(DataFixture::generate(32), 0, 0, 0); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(DataFixture::generate(32), moved.salt()); +} + +TEST_F(DerivedKeyConfigTest, Salt_SaveAndLoad) { + DerivedKeyConfig cfg(DataFixture::generate(32), 0, 0, 0); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(DataFixture::generate(32), loaded.salt()); +} + +TEST_F(DerivedKeyConfigTest, N) { + DerivedKeyConfig cfg(Data(0), 1024, 0, 0); + EXPECT_EQ(1024u, cfg.N()); +} + +TEST_F(DerivedKeyConfigTest, N_Move) { + DerivedKeyConfig cfg(Data(0), 1024, 0, 0); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(1024u, moved.N()); +} + +TEST_F(DerivedKeyConfigTest, N_SaveAndLoad) { + DerivedKeyConfig cfg(Data(0), 1024, 0, 0); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(1024u, loaded.N()); +} + +TEST_F(DerivedKeyConfigTest, r) { + DerivedKeyConfig cfg(Data(0), 0, 8, 0); + EXPECT_EQ(8u, cfg.r()); +} + +TEST_F(DerivedKeyConfigTest, r_Move) { + DerivedKeyConfig cfg(Data(0), 0, 8, 0); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(8u, moved.r()); +} + +TEST_F(DerivedKeyConfigTest, r_SaveAndLoad) { + DerivedKeyConfig cfg(Data(0), 0, 8, 0); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(8u, loaded.r()); +} + +TEST_F(DerivedKeyConfigTest, p) { + DerivedKeyConfig cfg(Data(0), 0, 0, 16); + EXPECT_EQ(16u, cfg.p()); +} + +TEST_F(DerivedKeyConfigTest, p_Move) { + DerivedKeyConfig cfg(Data(0), 0, 0, 16); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(16u, moved.p()); +} + + +TEST_F(DerivedKeyConfigTest, p_SaveAndLoad) { + DerivedKeyConfig cfg(Data(0), 0, 0, 16); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(16u, loaded.p()); +} \ No newline at end of file diff --git a/test/cpp-utils/crypto/kdf/DerivedKeyTest.cpp b/test/cpp-utils/crypto/kdf/DerivedKeyTest.cpp new file mode 100644 index 00000000..c04ee187 --- /dev/null +++ b/test/cpp-utils/crypto/kdf/DerivedKeyTest.cpp @@ -0,0 +1,18 @@ +#include +#include "cpp-utils/crypto/kdf/DerivedKey.h" +#include "cpp-utils/data/DataFixture.h" + +using namespace cpputils; + +TEST(DerivedKeyTest, Config) { + DerivedKey<32> key(DerivedKeyConfig(DataFixture::generate(32, 1), 1024, 8, 16), DataFixture::generateFixedSize<32>(2)); + EXPECT_EQ(DataFixture::generate(32, 1), key.config().salt()); + EXPECT_EQ(1024u, key.config().N()); + EXPECT_EQ(8u, key.config().r()); + EXPECT_EQ(16u, key.config().p()); +} + +TEST(DerivedKeyTest, Key) { + DerivedKey<32> key(DerivedKeyConfig(DataFixture::generate(32, 1), 1024, 8, 16), DataFixture::generateFixedSize<32>(2)); + EXPECT_EQ(DataFixture::generateFixedSize<32>(2), key.key()); +} diff --git a/test/cpp-utils/crypto/kdf/SCryptTest.cpp b/test/cpp-utils/crypto/kdf/SCryptTest.cpp new file mode 100644 index 00000000..ae12678e --- /dev/null +++ b/test/cpp-utils/crypto/kdf/SCryptTest.cpp @@ -0,0 +1,50 @@ +#include +#include "cpp-utils/crypto/kdf/Scrypt.h" + +using namespace cpputils; + +TEST(SCryptTest, GeneratedKeyIsReproductible_448) { + auto created = SCrypt().generateKey<56>("mypassword", SCrypt::TestSettings); + auto recreated = SCrypt().generateKeyFromConfig<56>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, GeneratedKeyIsReproductible_256) { + auto created = SCrypt().generateKey<32>("mypassword", SCrypt::TestSettings); + auto recreated = SCrypt().generateKeyFromConfig<32>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, GeneratedKeyIsReproductible_128) { + auto created = SCrypt().generateKey<16>("mypassword", SCrypt::TestSettings); + auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, GeneratedKeyIsReproductible_DefaultSettings) { + auto created = SCrypt().generateKey<16>("mypassword", SCrypt::DefaultSettings); + auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, DifferentPasswordResultsInDifferentKey) { + auto created = SCrypt().generateKey<16>("mypassword", SCrypt::TestSettings); + auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword2", created.config()); + EXPECT_NE(created.key(), recreated); +} + +TEST(SCryptTest, UsesCorrectSettings) { + auto created = SCrypt().generateKey<16>("mypassword", SCrypt::TestSettings); + EXPECT_EQ(SCrypt::TestSettings.SALT_LEN, created.config().salt().size()); + EXPECT_EQ(SCrypt::TestSettings.N, created.config().N()); + EXPECT_EQ(SCrypt::TestSettings.r, created.config().r()); + EXPECT_EQ(SCrypt::TestSettings.p, created.config().p()); +} + +TEST(SCryptTest, UsesCorrectDefaultSettings) { + auto created = SCrypt().generateKey<16>("mypassword", SCrypt::DefaultSettings); + EXPECT_EQ(SCrypt::DefaultSettings.SALT_LEN, created.config().salt().size()); + EXPECT_EQ(SCrypt::DefaultSettings.N, created.config().N()); + EXPECT_EQ(SCrypt::DefaultSettings.r, created.config().r()); + EXPECT_EQ(SCrypt::DefaultSettings.p, created.config().p()); +} diff --git a/test/cpp-utils/crypto/symmetric/CipherTest.cpp b/test/cpp-utils/crypto/symmetric/CipherTest.cpp new file mode 100644 index 00000000..aa3b2e8c --- /dev/null +++ b/test/cpp-utils/crypto/symmetric/CipherTest.cpp @@ -0,0 +1,284 @@ +#include +#include "cpp-utils/crypto/symmetric/Cipher.h" +#include "cpp-utils/crypto/symmetric/ciphers.h" +#include "testutils/FakeAuthenticatedCipher.h" + +#include "cpp-utils/data/DataFixture.h" +#include "cpp-utils/data/Data.h" +#include + +using namespace cpputils; +using std::string; + +template +class CipherTest: public ::testing::Test { +public: + BOOST_CONCEPT_ASSERT((CipherConcept)); + typename Cipher::EncryptionKey encKey = createKeyFixture(); + + static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) { + Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed); + return Cipher::EncryptionKey::FromBinary(data.data()); + } + + void CheckEncryptThenDecryptIsIdentity(const Data &plaintext) { + Data ciphertext = Encrypt(plaintext); + Data decrypted = Decrypt(ciphertext); + EXPECT_EQ(plaintext, decrypted); + } + + void CheckEncryptIsIndeterministic(const Data &plaintext) { + Data ciphertext = Encrypt(plaintext); + Data ciphertext2 = Encrypt(plaintext); + EXPECT_NE(ciphertext, ciphertext2); + } + + void CheckEncryptedSize(const Data &plaintext) { + Data ciphertext = Encrypt(plaintext); + EXPECT_EQ(Cipher::ciphertextSize(plaintext.size()), ciphertext.size()); + } + + void ExpectDoesntDecrypt(const Data &ciphertext) { + auto decrypted = Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey); + EXPECT_FALSE(decrypted); + } + + Data Encrypt(const Data &plaintext) { + return Cipher::encrypt((byte*)plaintext.data(), plaintext.size(), this->encKey); + } + + Data Decrypt(const Data &ciphertext) { + return Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey).value(); + } + + static Data CreateZeroes(unsigned int size) { + return std::move(Data(size).FillWithZeroes()); + } + + static Data CreateData(unsigned int size, unsigned int seed = 0) { + return DataFixture::generate(size, seed); + } +}; + +TYPED_TEST_CASE_P(CipherTest); + +constexpr std::initializer_list SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520}; + +TYPED_TEST_P(CipherTest, Size) { + for (auto size: SIZES) { + EXPECT_EQ(size, TypeParam::ciphertextSize(TypeParam::plaintextSize(size))); + EXPECT_EQ(size, TypeParam::plaintextSize(TypeParam::ciphertextSize(size))); + } +} + +TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Zeroes) { + for (auto size: SIZES) { + Data plaintext = this->CreateZeroes(size); + this->CheckEncryptThenDecryptIsIdentity(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Data) { + for (auto size: SIZES) { + Data plaintext = this->CreateData(size); + this->CheckEncryptThenDecryptIsIdentity(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Zeroes) { + for (auto size: SIZES) { + Data plaintext = this->CreateZeroes(size); + this->CheckEncryptIsIndeterministic(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Data) { + for (auto size: SIZES) { + Data plaintext = this->CreateData(size); + this->CheckEncryptIsIndeterministic(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptedSize) { + for (auto size: SIZES) { + Data plaintext = this->CreateData(size); + this->CheckEncryptedSize(plaintext); + } +} + +TYPED_TEST_P(CipherTest, TryDecryptDataThatIsTooSmall) { + Data tooSmallCiphertext(TypeParam::ciphertextSize(0) - 1); + this->ExpectDoesntDecrypt(tooSmallCiphertext); +} + +TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_0) { + static_assert(TypeParam::ciphertextSize(0) > 0, "If this fails, the test case doesn't make sense."); + Data tooSmallCiphertext(0); + this->ExpectDoesntDecrypt(tooSmallCiphertext); +} + +TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_1) { + static_assert(TypeParam::ciphertextSize(0) > 1, "If this fails, the test case doesn't make sense."); + Data tooSmallCiphertext(1); + this->ExpectDoesntDecrypt(tooSmallCiphertext); +} + +REGISTER_TYPED_TEST_CASE_P(CipherTest, + Size, + EncryptThenDecrypt_Zeroes, + EncryptThenDecrypt_Data, + EncryptIsIndeterministic_Zeroes, + EncryptIsIndeterministic_Data, + EncryptedSize, + TryDecryptDataThatIsTooSmall, + TryDecryptDataThatIsMuchTooSmall_0, + TryDecryptDataThatIsMuchTooSmall_1 +); + +template +class AuthenticatedCipherTest: public CipherTest { +public: + Data zeroes1 = CipherTest::CreateZeroes(1); + Data plaintext1 = CipherTest::CreateData(1); + Data zeroes2 = CipherTest::CreateZeroes(100 * 1024); + Data plaintext2 = CipherTest::CreateData(100 * 1024); +}; + +TYPED_TEST_CASE_P(AuthenticatedCipherTest); + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes_Size1) { + Data ciphertext = this->Encrypt(this->zeroes1); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data_Size1) { + Data ciphertext = this->Encrypt(this->plaintext1); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes) { + Data ciphertext = this->Encrypt(this->zeroes2); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data) { + Data ciphertext = this->Encrypt(this->plaintext2); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Zeroes) { + Data ciphertext = this->Encrypt(this->zeroes2); + ((byte*)ciphertext.data())[ciphertext.size() - 1] = ((byte*)ciphertext.data())[ciphertext.size() - 1] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Data) { + Data ciphertext = this->Encrypt(this->plaintext2); + ((byte*)ciphertext.data())[ciphertext.size() - 1] = ((byte*)ciphertext.data())[ciphertext.size() - 1] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Zeroes) { + Data ciphertext = this->Encrypt(this->zeroes2); + ((byte*)ciphertext.data())[ciphertext.size()/2] = ((byte*)ciphertext.data())[ciphertext.size()/2] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Data) { + Data ciphertext = this->Encrypt(this->plaintext2); + ((byte*)ciphertext.data())[ciphertext.size()/2] = ((byte*)ciphertext.data())[ciphertext.size()/2] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptZeroesData) { + this->ExpectDoesntDecrypt(this->zeroes2); +} + +TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptRandomData) { + this->ExpectDoesntDecrypt(this->plaintext2); +} + +REGISTER_TYPED_TEST_CASE_P(AuthenticatedCipherTest, + ModifyFirstByte_Zeroes_Size1, + ModifyFirstByte_Zeroes, + ModifyFirstByte_Data_Size1, + ModifyFirstByte_Data, + ModifyLastByte_Zeroes, + ModifyLastByte_Data, + ModifyMiddleByte_Zeroes, + ModifyMiddleByte_Data, + TryDecryptZeroesData, + TryDecryptRandomData +); + + +INSTANTIATE_TYPED_TEST_CASE_P(Fake, CipherTest, FakeAuthenticatedCipher); +INSTANTIATE_TYPED_TEST_CASE_P(Fake, AuthenticatedCipherTest, FakeAuthenticatedCipher); + +INSTANTIATE_TYPED_TEST_CASE_P(AES256_CFB, CipherTest, AES256_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, CipherTest, AES256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, AuthenticatedCipherTest, AES256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(AES128_CFB, CipherTest, AES128_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(AES128_GCM, CipherTest, AES128_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(AES128_GCM, AuthenticatedCipherTest, AES128_GCM); + +INSTANTIATE_TYPED_TEST_CASE_P(Twofish256_CFB, CipherTest, Twofish256_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Twofish256_GCM, CipherTest, Twofish256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Twofish256_GCM, AuthenticatedCipherTest, Twofish256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Twofish128_CFB, CipherTest, Twofish128_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Twofish128_GCM, CipherTest, Twofish128_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Twofish128_GCM, AuthenticatedCipherTest, Twofish128_GCM); + +INSTANTIATE_TYPED_TEST_CASE_P(Serpent256_CFB, CipherTest, Serpent256_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Serpent256_GCM, CipherTest, Serpent256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Serpent256_GCM, AuthenticatedCipherTest, Serpent256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Serpent128_CFB, CipherTest, Serpent128_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Serpent128_GCM, CipherTest, Serpent128_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Serpent128_GCM, AuthenticatedCipherTest, Serpent128_GCM); + +INSTANTIATE_TYPED_TEST_CASE_P(Cast256_CFB, CipherTest, Cast256_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Cast256_GCM, CipherTest, Cast256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Cast256_GCM, AuthenticatedCipherTest, Cast256_GCM); + +INSTANTIATE_TYPED_TEST_CASE_P(Mars448_CFB, CipherTest, Mars448_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Mars448_GCM, CipherTest, Mars448_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Mars448_GCM, AuthenticatedCipherTest, Mars448_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Mars256_CFB, CipherTest, Mars256_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Mars256_GCM, CipherTest, Mars256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Mars256_GCM, AuthenticatedCipherTest, Mars256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Mars128_CFB, CipherTest, Mars128_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(Mars128_GCM, CipherTest, Mars128_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(Mars128_GCM, AuthenticatedCipherTest, Mars128_GCM); + + +// Test cipher names +TEST(CipherNameTest, TestCipherNames) { + EXPECT_EQ("aes-256-gcm", string(AES256_GCM::NAME)); + EXPECT_EQ("aes-256-cfb", string(AES256_CFB::NAME)); + EXPECT_EQ("aes-128-gcm", string(AES128_GCM::NAME)); + EXPECT_EQ("aes-128-cfb", string(AES128_CFB::NAME)); + + EXPECT_EQ("twofish-256-gcm", string(Twofish256_GCM::NAME)); + EXPECT_EQ("twofish-256-cfb", string(Twofish256_CFB::NAME)); + EXPECT_EQ("twofish-128-gcm", string(Twofish128_GCM::NAME)); + EXPECT_EQ("twofish-128-cfb", string(Twofish128_CFB::NAME)); + + EXPECT_EQ("serpent-256-gcm", string(Serpent256_GCM::NAME)); + EXPECT_EQ("serpent-256-cfb", string(Serpent256_CFB::NAME)); + EXPECT_EQ("serpent-128-gcm", string(Serpent128_GCM::NAME)); + EXPECT_EQ("serpent-128-cfb", string(Serpent128_CFB::NAME)); + + EXPECT_EQ("cast-256-gcm", string(Cast256_GCM::NAME)); + EXPECT_EQ("cast-256-cfb", string(Cast256_CFB::NAME)); + + EXPECT_EQ("mars-448-gcm", string(Mars448_GCM::NAME)); + EXPECT_EQ("mars-448-cfb", string(Mars448_CFB::NAME)); + EXPECT_EQ("mars-256-gcm", string(Mars256_GCM::NAME)); + EXPECT_EQ("mars-256-cfb", string(Mars256_CFB::NAME)); + EXPECT_EQ("mars-128-gcm", string(Mars128_GCM::NAME)); + EXPECT_EQ("mars-128-cfb", string(Mars128_CFB::NAME)); +} diff --git a/test/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.cpp b/test/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.cpp new file mode 100644 index 00000000..e2a6232a --- /dev/null +++ b/test/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.cpp @@ -0,0 +1,5 @@ +#include "FakeAuthenticatedCipher.h" + +namespace cpputils { + constexpr unsigned int FakeKey::BINARY_LENGTH; +} diff --git a/test/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h b/test/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h new file mode 100644 index 00000000..2aa9b8fc --- /dev/null +++ b/test/cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h @@ -0,0 +1,114 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_TEST_CRYPTO_SYMMETRIC_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_ +#define MESSMER_CPPUTILS_TEST_CRYPTO_SYMMETRIC_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_ + +#include "cpp-utils/crypto/symmetric/Cipher.h" +#include "cpp-utils/data/FixedSizeData.h" +#include "cpp-utils/data/Data.h" +#include "cpp-utils/random/RandomGenerator.h" + +namespace cpputils { + + struct FakeKey { + static FakeKey FromBinary(const void *data) { + return FakeKey{*(uint8_t *) data}; + } + + static constexpr unsigned int BINARY_LENGTH = 1; + + uint8_t value; + }; + + // This is a fake cipher that uses an indeterministic caesar chiffre and a 4-byte parity for a simple authentication mechanism + class FakeAuthenticatedCipher { + public: + BOOST_CONCEPT_ASSERT((CipherConcept)); + + using EncryptionKey = FakeKey; + + static EncryptionKey CreateKey(RandomGenerator &randomGenerator) { + auto data = randomGenerator.getFixedSize<1>(); + return FakeKey{*((uint8_t *) data.data())}; + } + + static EncryptionKey Key1() { + return FakeKey{5}; + } + + static EncryptionKey Key2() { + return FakeKey{63}; + } + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + 5; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - 5; + } + + static Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + Data result(ciphertextSize(plaintextSize)); + + //Add a random IV + uint8_t iv = rand(); + std::memcpy(result.data(), &iv, 1); + + //Use caesar chiffre on plaintext + _caesar((byte *) result.data() + 1, plaintext, plaintextSize, encKey.value + iv); + + //Add parity information + int32_t parity = _parity((byte *) result.data(), plaintextSize + 1); + std::memcpy((byte *) result.data() + plaintextSize + 1, &parity, 4); + + return result; + } + + static boost::optional decrypt(const byte *ciphertext, unsigned int ciphertextSize, + const EncryptionKey &encKey) { + //We need at least 5 bytes (iv + parity) + if (ciphertextSize < 5) { + return boost::none; + } + + //Check parity + int32_t expectedParity = _parity(ciphertext, plaintextSize(ciphertextSize) + 1); + int32_t actualParity = *(int32_t * )(ciphertext + plaintextSize(ciphertextSize) + 1); + if (expectedParity != actualParity) { + return boost::none; + } + + //Decrypt caesar chiffre from ciphertext + int32_t iv = *(int32_t *) ciphertext; + Data result(plaintextSize(ciphertextSize)); + _caesar((byte *) result.data(), ciphertext + 1, plaintextSize(ciphertextSize), -(encKey.value + iv)); + return std::move(result); + } + + static constexpr const char *NAME = "FakeAuthenticatedCipher"; + + private: + static int32_t _parity(const byte *data, unsigned int size) { + int32_t parity = 34343435; // some init value + int32_t *intData = (int32_t *) data; + unsigned int intSize = size / sizeof(int32_t); + for (unsigned int i = 0; i < intSize; ++i) { + parity += intData[i]; + } + unsigned int remainingBytes = size - 4 * intSize; + for (unsigned int i = 0; i < remainingBytes; ++i) { + parity += (data[4 * intSize + i] << (24 - 8 * i)); + } + return parity; + } + + static void _caesar(byte *dst, const byte *src, unsigned int size, uint8_t key) { + for (unsigned int i = 0; i < size; ++i) { + dst[i] = src[i] + key; + } + } + }; + +} + +#endif diff --git a/test/cpp-utils/data/DataFixtureIncludeTest.cpp b/test/cpp-utils/data/DataFixtureIncludeTest.cpp new file mode 100644 index 00000000..e30657ac --- /dev/null +++ b/test/cpp-utils/data/DataFixtureIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/data/DataFixture.h" + +// Test the header can be included without needing additional dependencies \ No newline at end of file diff --git a/test/cpp-utils/data/DataFixtureTest.cpp b/test/cpp-utils/data/DataFixtureTest.cpp new file mode 100644 index 00000000..5029a3ff --- /dev/null +++ b/test/cpp-utils/data/DataFixtureTest.cpp @@ -0,0 +1,76 @@ +#include + +#include "cpp-utils/data/Data.h" +#include "cpp-utils/data/DataFixture.h" + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; + +namespace bf = boost::filesystem; + +using namespace cpputils; + +class DataFixtureTest: public Test { +}; + +TEST_F(DataFixtureTest, CreateEmptyFixture) { + Data data = DataFixture::generate(0); + EXPECT_EQ(0u, data.size()); +} + +TEST_F(DataFixtureTest, CreateOneByteFixture) { + Data data = DataFixture::generate(1); + EXPECT_EQ(1u, data.size()); +} + +TEST_F(DataFixtureTest, CreateLargerFixture) { + Data data = DataFixture::generate(20 * 1024 * 1024); + EXPECT_EQ(20u * 1024u * 1024u, data.size()); +} + +TEST_F(DataFixtureTest, FixturesAreDeterministic_DefaultSeed) { + Data data1 = DataFixture::generate(1024 * 1024); + Data data2 = DataFixture::generate(1024 * 1024); + EXPECT_EQ(data1, data2); +} + +TEST_F(DataFixtureTest, FixturesAreDeterministic_SeedIs5) { + Data data1 = DataFixture::generate(1024 * 1024, 5); + Data data2 = DataFixture::generate(1024 * 1024, 5); + EXPECT_EQ(data1, data2); +} + +TEST_F(DataFixtureTest, DifferentSeedIsDifferentFixture) { + Data data1 = DataFixture::generate(1024 * 1024, 0); + Data data2 = DataFixture::generate(1024 * 1024, 1); + EXPECT_NE(data1, data2); +} + +TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_DefaultSeed_1) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(1); + + EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 1)); +} + +TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_DefaultSeed_2) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(501); //Intentionally not 64bit-aligned, because the generate() function generates 64bit values for performance + + EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 501)); +} + +TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_SeedIs5_1) { + Data data1 = DataFixture::generate(1024, 5); + Data data2 = DataFixture::generate(1, 5); + + EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 1)); +} + +TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_SeedIs5_2) { + Data data1 = DataFixture::generate(1024, 5); + Data data2 = DataFixture::generate(501, 5); //Intentionally not 64bit-aligned, because the generate() function generates 64bit values for performance + + EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 501)); +} diff --git a/test/cpp-utils/data/DataIncludeTest.cpp b/test/cpp-utils/data/DataIncludeTest.cpp new file mode 100644 index 00000000..f9230be7 --- /dev/null +++ b/test/cpp-utils/data/DataIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/data/Data.h" + +// Test the header can be included without needing additional dependencies \ No newline at end of file diff --git a/test/cpp-utils/data/DataTest.cpp b/test/cpp-utils/data/DataTest.cpp new file mode 100644 index 00000000..6f9df070 --- /dev/null +++ b/test/cpp-utils/data/DataTest.cpp @@ -0,0 +1,208 @@ +#include "cpp-utils/data/DataFixture.h" +#include "cpp-utils/data/Data.h" +#include + +#include "cpp-utils/tempfile/TempFile.h" + +#include + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; + +using cpputils::TempFile; + +using std::ifstream; +using std::ofstream; + +namespace bf = boost::filesystem; + +using namespace cpputils; + +class DataTest: public Test { +public: + bool DataIsZeroes(const Data &data) { + for (size_t i = 0; i != data.size(); ++ i) { + if (((char*)data.data())[i] != 0) { + return false; + } + } + return true; + } +}; + +class DataTestWithSizeParam: public DataTest, public WithParamInterface { +public: + Data randomData; + + DataTestWithSizeParam(): randomData(DataFixture::generate(GetParam())) {} + + static void StoreData(const Data &data, const bf::path &filepath) { + ofstream file(filepath.c_str(), std::ios::binary | std::ios::trunc); + file.write((char*)data.data(), data.size()); + } + + static void EXPECT_STORED_FILE_DATA_CORRECT(const Data &data, const bf::path &filepath) { + EXPECT_EQ(data.size(), bf::file_size(filepath)); + + ifstream file(filepath.c_str(), std::ios::binary); + char *read_data = new char[data.size()]; + file.read(read_data, data.size()); + + EXPECT_EQ(0, std::memcmp(data.data(), read_data, data.size())); + delete[] read_data; + } +}; + +INSTANTIATE_TEST_CASE_P(DataTestWithSizeParam, DataTestWithSizeParam, Values(0, 1, 2, 1024, 4096, 10*1024*1024)); + +TEST_P(DataTestWithSizeParam, ZeroInitializedDataIsDifferentToRandomData) { + if (GetParam() != 0) { + Data data(GetParam()); + data.FillWithZeroes(); + EXPECT_NE(randomData, data); + } +} + +// Working on a large data area without a crash is a good indicator that we +// are actually working on memory that was validly allocated for us. +TEST_P(DataTestWithSizeParam, WriteAndCheck) { + Data data = randomData.copy(); + EXPECT_EQ(randomData, data); +} + +TEST_P(DataTestWithSizeParam, Size) { + Data data(GetParam()); + EXPECT_EQ(GetParam(), data.size()); +} + +TEST_P(DataTestWithSizeParam, CheckStoredFile) { + TempFile file; + randomData.StoreToFile(file.path()); + + EXPECT_STORED_FILE_DATA_CORRECT(randomData, file.path()); +} + +TEST_P(DataTestWithSizeParam, CheckLoadedData) { + TempFile file; + StoreData(randomData, file.path()); + + Data data = Data::LoadFromFile(file.path()).value(); + + EXPECT_EQ(randomData, data); +} + +TEST_P(DataTestWithSizeParam, StoreDoesntChangeData) { + Data data = randomData.copy(); + + TempFile file; + data.StoreToFile(file.path()); + + EXPECT_EQ(randomData, data); +} + +TEST_P(DataTestWithSizeParam, StoreAndLoad) { + TempFile file; + randomData.StoreToFile(file.path()); + Data loaded_data = Data::LoadFromFile(file.path()).value(); + + EXPECT_EQ(randomData, loaded_data); +} + +TEST_P(DataTestWithSizeParam, Copy) { + Data copy = randomData.copy(); + EXPECT_EQ(randomData, copy); +} + +TEST_F(DataTest, ChangingCopyDoesntChangeOriginal) { + Data original = DataFixture::generate(1024); + Data copy = original.copy(); + ((uint8_t*)copy.data())[0] = ((uint8_t*)copy.data())[0] + 1; + EXPECT_EQ(DataFixture::generate(1024), original); + EXPECT_NE(copy, original); +} + +TEST_F(DataTest, InitializeWithZeroes) { + Data data(10*1024); + data.FillWithZeroes(); + EXPECT_TRUE(DataIsZeroes(data)); +} + +TEST_F(DataTest, FillModifiedDataWithZeroes) { + Data data = DataFixture::generate(10*1024); + EXPECT_FALSE(DataIsZeroes(data)); + + data.FillWithZeroes(); + EXPECT_TRUE(DataIsZeroes(data)); +} + +TEST_F(DataTest, MoveConstructor) { + Data original = DataFixture::generate(1024); + Data copy(std::move(original)); + EXPECT_EQ(DataFixture::generate(1024), copy); + EXPECT_EQ(nullptr, original.data()); + EXPECT_EQ(0u, original.size()); +} + +TEST_F(DataTest, MoveAssignment) { + Data original = DataFixture::generate(1024); + Data copy(0); + copy = std::move(original); + EXPECT_EQ(DataFixture::generate(1024), copy); + EXPECT_EQ(nullptr, original.data()); + EXPECT_EQ(0u, original.size()); +} + +TEST_F(DataTest, Equality) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(1024); + EXPECT_TRUE(data1 == data2); + EXPECT_FALSE(data1 != data2); +} + +TEST_F(DataTest, Inequality_DifferentSize) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(1023); + EXPECT_FALSE(data1 == data2); + EXPECT_TRUE(data1 != data2); +} + +TEST_F(DataTest, Inequality_DifferentFirstByte) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(1024); + ((uint8_t*)data2.data())[0] = ((uint8_t*)data2.data())[0] + 1; + EXPECT_FALSE(data1 == data2); + EXPECT_TRUE(data1 != data2); +} + +TEST_F(DataTest, Inequality_DifferentMiddleByte) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(1024); + ((uint8_t*)data2.data())[500] = ((uint8_t*)data2.data())[500] + 1; + EXPECT_FALSE(data1 == data2); + EXPECT_TRUE(data1 != data2); +} + +TEST_F(DataTest, Inequality_DifferentLastByte) { + Data data1 = DataFixture::generate(1024); + Data data2 = DataFixture::generate(1024); + ((uint8_t*)data2.data())[1023] = ((uint8_t*)data2.data())[1023] + 1; + EXPECT_FALSE(data1 == data2); + EXPECT_TRUE(data1 != data2); +} + +#ifdef __x86_64__ +TEST_F(DataTest, LargesizeSize) { + //Needs 64bit for representation. This value isn't in the size param list, because the list is also used for read/write checks. + uint64_t size = 4.5L*1024*1024*1024; + Data data(size); + EXPECT_EQ(size, data.size()); +} +#else +#warning This is not a 64bit architecture. Large size data tests are disabled. +#endif + +TEST_F(DataTest, LoadingNonexistingFile) { + TempFile file(false); // Pass false to constructor, so the tempfile is not created + EXPECT_FALSE(Data::LoadFromFile(file.path())); +} diff --git a/test/cpp-utils/data/FixedSizeDataIncludeTest.cpp b/test/cpp-utils/data/FixedSizeDataIncludeTest.cpp new file mode 100644 index 00000000..817e1c6f --- /dev/null +++ b/test/cpp-utils/data/FixedSizeDataIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/data/FixedSizeData.h" + +// Test the header can be included without needing additional dependencies \ No newline at end of file diff --git a/test/cpp-utils/data/FixedSizeDataTest.cpp b/test/cpp-utils/data/FixedSizeDataTest.cpp new file mode 100644 index 00000000..dbbb554f --- /dev/null +++ b/test/cpp-utils/data/FixedSizeDataTest.cpp @@ -0,0 +1,189 @@ +#include "cpp-utils/data/DataFixture.h" +#include "cpp-utils/data/FixedSizeData.h" +#include "cpp-utils/data/Data.h" +#include + + +using ::testing::Test; +using ::testing::WithParamInterface; +using ::testing::Values; + +using std::string; + +using namespace cpputils; + +class FixedSizeDataTest: public Test { +public: + static constexpr size_t SIZE = 16; + + const string DATA1_AS_STRING = "1491BB4932A389EE14BC7090AC772972"; + const string DATA2_AS_STRING = "272EE5517627CFA147A971A8E6E747E0"; + + const Data DATA3_AS_BINARY; + const Data DATA4_AS_BINARY; + + FixedSizeDataTest() : DATA3_AS_BINARY(DataFixture::generate(SIZE, 1)), DATA4_AS_BINARY(DataFixture::generate(SIZE, 2)) {} + + template + void EXPECT_DATA_EQ(const Data &expected, const FixedSizeData &actual) { + EXPECT_EQ(expected.size(), SIZE); + EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), SIZE)); + } +}; + +constexpr size_t FixedSizeDataTest::SIZE; + +TEST_F(FixedSizeDataTest, EqualsTrue) { + FixedSizeData DATA1_1 = FixedSizeData::FromString(DATA1_AS_STRING); + FixedSizeData DATA1_2 = FixedSizeData::FromString(DATA1_AS_STRING); + + EXPECT_TRUE(DATA1_1 == DATA1_2); + EXPECT_TRUE(DATA1_2 == DATA1_1); +} + +TEST_F(FixedSizeDataTest, EqualsFalse) { + FixedSizeData DATA1_1 = FixedSizeData::FromString(DATA1_AS_STRING); + FixedSizeData DATA2_1 = FixedSizeData::FromString(DATA2_AS_STRING); + + EXPECT_FALSE(DATA1_1 == DATA2_1); + EXPECT_FALSE(DATA2_1 == DATA1_1); +} + +TEST_F(FixedSizeDataTest, NotEqualsFalse) { + FixedSizeData DATA1_1 = FixedSizeData::FromString(DATA1_AS_STRING); + FixedSizeData DATA1_2 = FixedSizeData::FromString(DATA1_AS_STRING); + + EXPECT_FALSE(DATA1_1 != DATA1_2); + EXPECT_FALSE(DATA1_2 != DATA1_1); +} + +TEST_F(FixedSizeDataTest, NotEqualsTrue) { + FixedSizeData DATA1_1 = FixedSizeData::FromString(DATA1_AS_STRING); + FixedSizeData DATA2_1 = FixedSizeData::FromString(DATA2_AS_STRING); + + EXPECT_TRUE(DATA1_1 != DATA2_1); + EXPECT_TRUE(DATA2_1 != DATA1_1); +} + +class FixedSizeDataTestWithStringParam: public FixedSizeDataTest, public WithParamInterface {}; +INSTANTIATE_TEST_CASE_P(FixedSizeDataTestWithStringParam, FixedSizeDataTestWithStringParam, Values("2898B4B8A13CA63CBE0F0278CCE465DB", "6FFEBAD90C0DAA2B79628F0627CE9841")); + +TEST_P(FixedSizeDataTestWithStringParam, FromAndToString) { + FixedSizeData data = FixedSizeData::FromString(GetParam()); + EXPECT_EQ(GetParam(), data.ToString()); +} + +TEST_P(FixedSizeDataTestWithStringParam, ToAndFromString) { + FixedSizeData data = FixedSizeData::FromString(GetParam()); + FixedSizeData data2 = FixedSizeData::FromString(data.ToString()); + EXPECT_EQ(data, data2); +} + +class FixedSizeDataTestWithBinaryParam: public FixedSizeDataTest, public WithParamInterface { +public: + static const Data VALUE1; + static const Data VALUE2; +}; +const Data FixedSizeDataTestWithBinaryParam::VALUE1(DataFixture::generate(SIZE, 3)); +const Data FixedSizeDataTestWithBinaryParam::VALUE2(DataFixture::generate(SIZE, 4)); +INSTANTIATE_TEST_CASE_P(FixedSizeDataTestWithBinaryParam, FixedSizeDataTestWithBinaryParam, Values(&FixedSizeDataTestWithBinaryParam::VALUE1, &FixedSizeDataTestWithBinaryParam::VALUE2)); + +TEST_P(FixedSizeDataTestWithBinaryParam, FromBinary) { + FixedSizeData data = FixedSizeData::FromBinary((uint8_t*)GetParam()->data()); + EXPECT_DATA_EQ(*GetParam(), data); +} + +TEST_P(FixedSizeDataTestWithBinaryParam, FromAndToBinary) { + FixedSizeData data = FixedSizeData::FromBinary((uint8_t*)GetParam()->data()); + Data output(FixedSizeData::BINARY_LENGTH); + data.ToBinary(output.data()); + EXPECT_EQ(*GetParam(), output); +} + +TEST_P(FixedSizeDataTestWithBinaryParam, ToAndFromBinary) { + FixedSizeData data = FixedSizeData::FromBinary((uint8_t*)GetParam()->data()); + Data stored(FixedSizeData::BINARY_LENGTH); + data.ToBinary(stored.data()); + FixedSizeData loaded = FixedSizeData::FromBinary(stored.data()); + EXPECT_EQ(data, loaded); +} + +class FixedSizeDataTestWithParam: public FixedSizeDataTest, public WithParamInterface> {}; +INSTANTIATE_TEST_CASE_P(FixedSizeDataTestWithParam, FixedSizeDataTestWithParam, Values(FixedSizeData::FromString("2898B4B8A13CA63CBE0F0278CCE465DB"), FixedSizeData::FromString("6FFEBAD90C0DAA2B79628F0627CE9841"))); + +TEST_P(FixedSizeDataTestWithParam, CopyConstructor) { + FixedSizeData copy(GetParam()); + EXPECT_EQ(GetParam(), copy); +} + +TEST_P(FixedSizeDataTestWithParam, Take_Half) { + FixedSizeData source(GetParam()); + FixedSizeData taken = source.take(); + EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), SIZE/2)); +} + +TEST_P(FixedSizeDataTestWithParam, Drop_Half) { + FixedSizeData source(GetParam()); + FixedSizeData taken = source.drop(); + EXPECT_EQ(0, std::memcmp(source.data() + SIZE/2, taken.data(), SIZE/2)); +} + +TEST_P(FixedSizeDataTestWithParam, Take_One) { + FixedSizeData source(GetParam()); + FixedSizeData<1> taken = source.take<1>(); + EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), 1)); +} + +TEST_P(FixedSizeDataTestWithParam, Drop_One) { + FixedSizeData source(GetParam()); + FixedSizeData taken = source.drop<1>(); + EXPECT_EQ(0, std::memcmp(source.data() + 1, taken.data(), SIZE-1)); +} + +TEST_P(FixedSizeDataTestWithParam, Take_Nothing) { + FixedSizeData source(GetParam()); + FixedSizeData<0> taken = source.take<0>(); +} + +TEST_P(FixedSizeDataTestWithParam, Drop_Nothing) { + FixedSizeData source(GetParam()); + FixedSizeData taken = source.drop<0>(); + EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), SIZE)); +} + +TEST_P(FixedSizeDataTestWithParam, Take_All) { + FixedSizeData source(GetParam()); + FixedSizeData taken = source.take(); + EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), SIZE)); +} + +TEST_P(FixedSizeDataTestWithParam, Drop_All) { + FixedSizeData source(GetParam()); + FixedSizeData<0> taken = source.drop(); +} + +TEST_F(FixedSizeDataTest, CopyConstructorDoesntChangeSource) { + FixedSizeData data1 = FixedSizeData::FromString(DATA1_AS_STRING); + FixedSizeData data2(data1); + EXPECT_EQ(DATA1_AS_STRING, data1.ToString()); +} + +TEST_P(FixedSizeDataTestWithParam, IsEqualAfterAssignment1) { + FixedSizeData data2 = FixedSizeData::FromString(DATA2_AS_STRING); + EXPECT_NE(GetParam(), data2); + data2 = GetParam(); + EXPECT_EQ(GetParam(), data2); +} + +TEST_F(FixedSizeDataTest, AssignmentDoesntChangeSource) { + FixedSizeData data1 = FixedSizeData::FromString(DATA1_AS_STRING); + FixedSizeData data2 = FixedSizeData::FromString(DATA2_AS_STRING); + data2 = data1; + EXPECT_EQ(DATA1_AS_STRING, data1.ToString()); +} + +// This tests that a FixedSizeData object is very lightweight +// (it is meant to be kept on stack and passed around) +TEST_F(FixedSizeDataTest, IsLightweightObject) { + EXPECT_EQ(FixedSizeData::BINARY_LENGTH, sizeof(FixedSizeData)); +} diff --git a/test/cpp-utils/io/ConsoleIncludeTest.cpp b/test/cpp-utils/io/ConsoleIncludeTest.cpp new file mode 100644 index 00000000..7f24a430 --- /dev/null +++ b/test/cpp-utils/io/ConsoleIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/io/Console.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/io/ConsoleTest.h b/test/cpp-utils/io/ConsoleTest.h new file mode 100644 index 00000000..303b0b09 --- /dev/null +++ b/test/cpp-utils/io/ConsoleTest.h @@ -0,0 +1,78 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_TEST_IO_CONSOLETEST_H +#define MESSMER_CPPUTILS_TEST_IO_CONSOLETEST_H + +#include + +#include "cpp-utils/io/Console.h" + +#include +#include +#include "cpp-utils/io/pipestream.h" + +class ConsoleThread { +public: + ConsoleThread(std::ostream &ostr, std::istream &istr): _console(ostr, istr) {} + std::future ask(const std::string &question, const std::vector &options) { + return std::async(std::launch::async, [this, question, options]() { + return _console.ask(question, options); + }); + } + std::future askYesNo(const std::string &question) { + return std::async(std::launch::async, [this, question]() { + return _console.askYesNo(question); + }); + } + void print(const std::string &output) { + _console.print(output); + } +private: + cpputils::IOStreamConsole _console; +}; + +class ConsoleTest: public ::testing::Test { +public: + ConsoleTest(): _inputStr(), _outputStr(), _input(&_inputStr), _output(&_outputStr), _console(_output, _input) {} + + void EXPECT_OUTPUT_LINES(std::initializer_list lines) { + for (const std::string &line : lines) { + EXPECT_OUTPUT_LINE(line); + } + } + + void EXPECT_OUTPUT_LINE(const std::string &expected, char delimiter = '\n', const std::string &expected_after_delimiter = "") { + std::string actual; + std::getline(_output, actual, delimiter); + EXPECT_EQ(expected, actual); + for (char expected_char : expected_after_delimiter) { + char actual_char; + _output.get(actual_char); + EXPECT_EQ(expected_char, actual_char); + } + } + + void sendInputLine(const std::string &line) { + _input << line << "\n" << std::flush; + } + + std::future ask(const std::string &question, const std::vector &options) { + return _console.ask(question, options); + } + + std::future askYesNo(const std::string &question) { + return _console.askYesNo(question); + } + + void print(const std::string &output) { + _console.print(output); + } + +private: + cpputils::pipestream _inputStr; + cpputils::pipestream _outputStr; + std::iostream _input; + std::iostream _output; + ConsoleThread _console; +}; + +#endif diff --git a/test/cpp-utils/io/ConsoleTest_Ask.cpp b/test/cpp-utils/io/ConsoleTest_Ask.cpp new file mode 100644 index 00000000..400680ad --- /dev/null +++ b/test/cpp-utils/io/ConsoleTest_Ask.cpp @@ -0,0 +1,188 @@ +#include "ConsoleTest.h" + +using std::stringstream; +using std::string; +using std::vector; +using std::istream; +using std::ostream; +using std::future; +using std::initializer_list; + +class ConsoleTest_Ask: public ConsoleTest {}; + +TEST_F(ConsoleTest_Ask, CrashesWithoutOptions) { + EXPECT_THROW( + (ask("My Question?", {}).get()), + std::invalid_argument + ); +} + +TEST_F(ConsoleTest_Ask, OneOption) { + auto chosen = ask("My Question?", {"First Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-1]", ':', " "); + sendInputLine("1"); + EXPECT_EQ(0u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, TwoOptions_ChooseFirst) { + auto chosen = ask("My Question?", {"First Option", "Second Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option", + " [2] Second Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("1"); + EXPECT_EQ(0u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, TwoOptions_ChooseSecond) { + auto chosen = ask("My Question?", {"First Option", "Second Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option", + " [2] Second Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("2"); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseFirst) { + auto chosen = ask("My Other Question?", {"1st Option", "2nd Option", "3rd Option"}); + EXPECT_OUTPUT_LINES({ + "My Other Question?", + " [1] 1st Option", + " [2] 2nd Option", + " [3] 3rd Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " "); + sendInputLine("1"); + EXPECT_EQ(0u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseSecond) { + auto chosen = ask("My Question?", {"1st Option", "2nd Option", "3rd Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] 1st Option", + " [2] 2nd Option", + " [3] 3rd Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " "); + sendInputLine("2"); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseThird) { + auto chosen = ask("My Question?", {"1st Option", "2nd Option", "3rd Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] 1st Option", + " [2] 2nd Option", + " [3] 3rd Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " "); + sendInputLine("3"); + EXPECT_EQ(2u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, InputWithLeadingSpaces) { + auto chosen = ask("My Question?", {"First Option", "Second Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option", + " [2] Second Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine(" 2"); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, InputWithFollowingSpaces) { + auto chosen = ask("My Question?", {"First Option", "Second Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option", + " [2] Second Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("2 "); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, InputWithLeadingAndFollowingSpaces) { + auto chosen = ask("My Question?", {"First Option", "Second Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option", + " [2] Second Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine(" 2 "); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, InputEmptyLine) { + auto chosen = ask("My Question?", {"First Option", "Second Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] First Option", + " [2] Second Option" + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine(""); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine(" "); // empty line with space + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("2"); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, InputWrongNumbers) { + auto chosen = ask("My Question?", {"1st Option", "2nd Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] 1st Option", + " [2] 2nd Option", + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("0"); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("-1"); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("3"); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("1.5"); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("1,5"); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("2"); + EXPECT_EQ(1u, chosen.get()); +} + +TEST_F(ConsoleTest_Ask, InputNonNumbers) { + auto chosen = ask("My Question?", {"1st Option", "2nd Option"}); + EXPECT_OUTPUT_LINES({ + "My Question?", + " [1] 1st Option", + " [2] 2nd Option", + }); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("abc"); + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("3a"); // Wrong number and string attached + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("1a"); // Right number but string attached + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("a3"); // Wrong number and string attached + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("a1"); // Right number but string attached + EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " "); + sendInputLine("2"); + EXPECT_EQ(1u, chosen.get()); +} \ No newline at end of file diff --git a/test/cpp-utils/io/ConsoleTest_AskYesNo.cpp b/test/cpp-utils/io/ConsoleTest_AskYesNo.cpp new file mode 100644 index 00000000..b1da6a55 --- /dev/null +++ b/test/cpp-utils/io/ConsoleTest_AskYesNo.cpp @@ -0,0 +1,108 @@ +#include "ConsoleTest.h" + +using std::string; + +class ConsoleTest_AskYesNo: public ConsoleTest { +public: + void EXPECT_TRUE_ON_INPUT(const string &input) { + EXPECT_RESULT_ON_INPUT(true, input); + } + + void EXPECT_FALSE_ON_INPUT(const string &input) { + EXPECT_RESULT_ON_INPUT(false, input); + } + + void EXPECT_RESULT_ON_INPUT(const bool expected, const string &input) { + auto chosen = askYesNo("Are you sure blablub?"); + EXPECT_OUTPUT_LINES({"Are you sure blablub?"}); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine(input); + EXPECT_EQ(expected, chosen.get()); + } +}; + +TEST_F(ConsoleTest_AskYesNo, Input_Yes) { + EXPECT_TRUE_ON_INPUT("Yes"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_yes) { + EXPECT_TRUE_ON_INPUT("yes"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_Y) { + EXPECT_TRUE_ON_INPUT("Y"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_y) { + EXPECT_TRUE_ON_INPUT("y"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_No) { + EXPECT_FALSE_ON_INPUT("No"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_no) { + EXPECT_FALSE_ON_INPUT("no"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_N) { + EXPECT_FALSE_ON_INPUT("N"); +} + +TEST_F(ConsoleTest_AskYesNo, Input_n) { + EXPECT_FALSE_ON_INPUT("n"); +} + +TEST_F(ConsoleTest_AskYesNo, InputWithLeadingSpaces) { + EXPECT_TRUE_ON_INPUT(" y"); +} + +TEST_F(ConsoleTest_AskYesNo, InputWithFollowingSpaces) { + EXPECT_TRUE_ON_INPUT("y "); +} + +TEST_F(ConsoleTest_AskYesNo, InputWithLeadingAndFollowingSpaces) { + EXPECT_TRUE_ON_INPUT(" y "); +} + +TEST_F(ConsoleTest_AskYesNo, InputEmptyLine) { + auto chosen = askYesNo("My Question?"); + EXPECT_OUTPUT_LINES({"My Question?"}); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine(""); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine(" "); // empty line with space + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("y"); + EXPECT_EQ(true, chosen.get()); +} + +TEST_F(ConsoleTest_AskYesNo, WrongInput) { + auto chosen = askYesNo("My Question?"); + EXPECT_OUTPUT_LINES({"My Question?"}); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("0"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("1"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("bla"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("Y_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("y_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("N_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("n_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("Yes_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("yes_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("No_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("no_andsomethingelse"); + EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " "); + sendInputLine("y"); + EXPECT_EQ(true, chosen.get()); +} diff --git a/test/cpp-utils/io/ConsoleTest_Print.cpp b/test/cpp-utils/io/ConsoleTest_Print.cpp new file mode 100644 index 00000000..e38585ee --- /dev/null +++ b/test/cpp-utils/io/ConsoleTest_Print.cpp @@ -0,0 +1,6 @@ +#include "ConsoleTest.h" + +TEST_F(ConsoleTest, Print) { + print("Bla Blub"); + EXPECT_OUTPUT_LINE("Bla Blu", 'b'); // 'b' is the delimiter for reading +} diff --git a/test/cpp-utils/lock/ConditionBarrierIncludeTest.cpp b/test/cpp-utils/lock/ConditionBarrierIncludeTest.cpp new file mode 100644 index 00000000..eb8eb018 --- /dev/null +++ b/test/cpp-utils/lock/ConditionBarrierIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/lock/ConditionBarrier.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/lock/LockPoolIncludeTest.cpp b/test/cpp-utils/lock/LockPoolIncludeTest.cpp new file mode 100644 index 00000000..2b333e36 --- /dev/null +++ b/test/cpp-utils/lock/LockPoolIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/lock/LockPool.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/lock/MutexPoolLockIncludeTest.cpp b/test/cpp-utils/lock/MutexPoolLockIncludeTest.cpp new file mode 100644 index 00000000..6fe2c90d --- /dev/null +++ b/test/cpp-utils/lock/MutexPoolLockIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/lock/MutexPoolLock.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/logging/LoggerIncludeTest.cpp b/test/cpp-utils/logging/LoggerIncludeTest.cpp new file mode 100644 index 00000000..0f2fc1e5 --- /dev/null +++ b/test/cpp-utils/logging/LoggerIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/logging/Logger.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/logging/LoggerTest.cpp b/test/cpp-utils/logging/LoggerTest.cpp new file mode 100644 index 00000000..a909ee49 --- /dev/null +++ b/test/cpp-utils/logging/LoggerTest.cpp @@ -0,0 +1,20 @@ +#include "testutils/LoggingTest.h" + +/* + * Contains test cases for the Logger class + */ + +using namespace cpputils::logging; +using std::string; +using testing::MatchesRegex; + +class LoggerTest: public LoggingTest {}; + +TEST_F(LoggerTest, IsSingleton) { + ASSERT_EQ(&logger(), &logger()); +} + +TEST_F(LoggerTest, SetLogger) { + logger().setLogger(spdlog::stderr_logger_mt("MyTestLog1")); + EXPECT_EQ("MyTestLog1", logger()->name()); +} diff --git a/test/cpp-utils/logging/LoggingIncludeTest.cpp b/test/cpp-utils/logging/LoggingIncludeTest.cpp new file mode 100644 index 00000000..0cdeee02 --- /dev/null +++ b/test/cpp-utils/logging/LoggingIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/logging/logging.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/logging/LoggingLevelTest.cpp b/test/cpp-utils/logging/LoggingLevelTest.cpp new file mode 100644 index 00000000..d5da126d --- /dev/null +++ b/test/cpp-utils/logging/LoggingLevelTest.cpp @@ -0,0 +1,128 @@ +#include "testutils/LoggingTest.h" + +using namespace cpputils::logging; +using std::string; +using testing::MatchesRegex; + +class LoggingLevelTest: public LoggingTest { +public: + void EXPECT_DEBUG_LOG_ENABLED() { + LOG(DEBUG) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); + } + + void EXPECT_DEBUG_LOG_DISABLED() { + LOG(DEBUG) << "My log message"; + EXPECT_EQ("", mockLogger.capturedLog()); + } + + void EXPECT_INFO_LOG_ENABLED() { + LOG(INFO) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + } + + void EXPECT_INFO_LOG_DISABLED() { + LOG(INFO) << "My log message"; + EXPECT_EQ("", mockLogger.capturedLog()); + } + + void EXPECT_WARNING_LOG_ENABLED() { + LOG(WARN) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); + } + + void EXPECT_WARNING_LOG_DISABLED() { + LOG(WARN) << "My log message"; + EXPECT_EQ("", mockLogger.capturedLog()); + } + + void EXPECT_ERROR_LOG_ENABLED() { + LOG(ERROR) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); + } + + void EXPECT_ERROR_LOG_DISABLED() { + LOG(ERROR) << "My log message"; + EXPECT_EQ("", mockLogger.capturedLog()); + } +}; + +TEST_F(LoggingLevelTest, DefaultLevelIsInfo) { + setLogger(mockLogger.get()); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_ENABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, DEBUG_SetBeforeSettingLogger) { + setLevel(DEBUG); + setLogger(mockLogger.get()); + EXPECT_DEBUG_LOG_ENABLED(); + EXPECT_INFO_LOG_ENABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, DEBUG_SetAfterSettingLogger) { + setLogger(mockLogger.get()); + setLevel(DEBUG); + EXPECT_DEBUG_LOG_ENABLED(); + EXPECT_INFO_LOG_ENABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, INFO_SetBeforeSettingLogger) { + setLevel(INFO); + setLogger(mockLogger.get()); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_ENABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, INFO_SetAfterSettingLogger) { + setLogger(mockLogger.get()); + setLevel(INFO); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_ENABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, WARNING_SetBeforeSettingLogger) { + setLevel(WARN); + setLogger(mockLogger.get()); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_DISABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, WARNING_SetAfterSettingLogger) { + setLogger(mockLogger.get()); + setLevel(WARN); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_DISABLED(); + EXPECT_WARNING_LOG_ENABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, ERROR_SetBeforeSettingLogger) { + setLevel(ERROR); + setLogger(mockLogger.get()); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_DISABLED(); + EXPECT_WARNING_LOG_DISABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} + +TEST_F(LoggingLevelTest, ERROR_SetAfterSettingLogger) { + setLogger(mockLogger.get()); + setLevel(ERROR); + EXPECT_DEBUG_LOG_DISABLED(); + EXPECT_INFO_LOG_DISABLED(); + EXPECT_WARNING_LOG_DISABLED(); + EXPECT_ERROR_LOG_ENABLED(); +} diff --git a/test/cpp-utils/logging/LoggingTest.cpp b/test/cpp-utils/logging/LoggingTest.cpp new file mode 100644 index 00000000..6e26280e --- /dev/null +++ b/test/cpp-utils/logging/LoggingTest.cpp @@ -0,0 +1,80 @@ +#include "testutils/LoggingTest.h" + +/* + * Contains test cases for the following logging interface: + * LOG(INFO) << "My log message" + */ + +using namespace cpputils::logging; +using std::string; +using testing::MatchesRegex; + +TEST_F(LoggingTest, DefaultLoggerIsStderr) { + string output = captureStderr([]{ + LOG(INFO) << "My log message"; + }); + EXPECT_THAT(output, MatchesRegex(".*\\[Log\\].*\\[info\\].*My log message.*")); +} + +TEST_F(LoggingTest, SetLogger_NewLoggerIsUsed) { + setLogger(spdlog::stderr_logger_mt("MyTestLog2")); + string output = captureStderr([]{ + LOG(INFO) << "My log message"; + }); + EXPECT_THAT(output, MatchesRegex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*")); +} + +TEST_F(LoggingTest, SetNonStderrLogger_LogsToNewLogger) { + setLogger(mockLogger.get()); + logger()->info() << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); +} + +TEST_F(LoggingTest, SetNonStderrLogger_DoesNotLogToStderr) { + setLogger(mockLogger.get()); + string output = captureStderr([] { + logger()->info() << "My log message"; + }); + EXPECT_EQ("", output); +} + +TEST_F(LoggingTest, InfoLog) { + setLogger(mockLogger.get()); + LOG(INFO) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); +} + +TEST_F(LoggingTest, WarningLog) { + setLogger(mockLogger.get()); + LOG(WARN) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); +} + +TEST_F(LoggingTest, DebugLog) { + setLevel(DEBUG); + setLogger(mockLogger.get()); + LOG(DEBUG) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); +} + +TEST_F(LoggingTest, ErrorLog) { + setLogger(mockLogger.get()); + LOG(ERROR) << "My log message"; + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); +} + +void logAndExit(const string &message) { + LOG(INFO) << message; + exit(1); +} + +// fork() only forks the main thread. This test ensures that logging doesn't depend on threads that suddenly aren't +// there anymore after a fork(). +TEST_F(LoggingTest, LoggingAlsoWorksAfterFork) { + setLogger(spdlog::stderr_logger_mt("StderrLogger")); + EXPECT_EXIT( + logAndExit("My log message"), + ::testing::ExitedWithCode(1), + "My log message" + ); +} diff --git a/test/cpp-utils/logging/testutils/LoggingTest.h b/test/cpp-utils/logging/testutils/LoggingTest.h new file mode 100644 index 00000000..42d0a593 --- /dev/null +++ b/test/cpp-utils/logging/testutils/LoggingTest.h @@ -0,0 +1,51 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_TEST_LOGGING_TESTUTILS_LOGGINGTEST_H +#define MESSMER_CPPUTILS_TEST_LOGGING_TESTUTILS_LOGGINGTEST_H + +#include +#include +#include "cpp-utils/logging/logging.h" + +class MockLogger final { +public: + MockLogger(): + _capturedLogData(), + _sink(std::make_shared>(_capturedLogData, true)), + _logger(spdlog::create("MockLogger", {_sink})) { + } + + ~MockLogger() { + spdlog::drop("MockLogger"); + }; + + std::shared_ptr get() { + return _logger; + } + + std::string capturedLog() const { + return _capturedLogData.str(); + } +private: + std::ostringstream _capturedLogData; + std::shared_ptr> _sink; + std::shared_ptr _logger; +}; + +class LoggingTest: public ::testing::Test { +public: + LoggingTest(): mockLogger() {} + + std::string captureStderr(std::function func) { + testing::internal::CaptureStderr(); + func(); + return testing::internal::GetCapturedStderr(); + } + + ~LoggingTest() { + cpputils::logging::reset(); + } + + MockLogger mockLogger; +}; + +#endif diff --git a/test/cpp-utils/network/CurlHttpClientTest.cpp b/test/cpp-utils/network/CurlHttpClientTest.cpp new file mode 100644 index 00000000..cccac537 --- /dev/null +++ b/test/cpp-utils/network/CurlHttpClientTest.cpp @@ -0,0 +1,32 @@ +#include +#include +#include "cpp-utils/network/CurlHttpClient.h" +#include "cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h" + +using std::string; +using boost::none; +using testing::MatchesRegex; + +using namespace cpputils; + +TEST(CurlHttpClientTest, InvalidProtocol) { + EXPECT_EQ(none, CurlHttpClient().get("invalid://example.com")); +} + +TEST(CurlHttpClientTest, InvalidTld) { + EXPECT_EQ(none, CurlHttpClient().get("http://example.invalidtld")); +} + +TEST(CurlHttpClientTest, InvalidDomain) { + EXPECT_EQ(none, CurlHttpClient().get("http://this_is_a_not_existing_domain.com")); +} + +TEST(CurlHttpClientTest, ValidHttp) { + string content = CurlHttpClient().get("http://example.com").value(); + EXPECT_THAT(content, MatchesRegex(".*Example Domain.*")); +} + +TEST(CurlHttpClientTest, ValidHttps) { + string content = CurlHttpClient().get("https://example.com").value(); + EXPECT_THAT(content, MatchesRegex(".*Example Domain.*")); +} diff --git a/test/cpp-utils/network/FakeHttpClientTest.cpp b/test/cpp-utils/network/FakeHttpClientTest.cpp new file mode 100644 index 00000000..be052838 --- /dev/null +++ b/test/cpp-utils/network/FakeHttpClientTest.cpp @@ -0,0 +1,39 @@ +#include +#include "cpp-utils/network/FakeHttpClient.h" +#include "cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h" + +using boost::none; + +using namespace cpputils; + +TEST(FakeHttpClientTest, Empty) { + EXPECT_EQ(none, FakeHttpClient().get("http://example.com")); +} + +TEST(FakeHttpClientTest, Nonexisting) { + FakeHttpClient client; + client.addWebsite("http://existing.com", "content"); + EXPECT_EQ(none, client.get("http://notexisting.com")); +} + +TEST(FakeHttpClientTest, Existing) { + FakeHttpClient client; + client.addWebsite("http://existing.com", "content"); + EXPECT_EQ("content", client.get("http://existing.com").value()); +} + +TEST(FakeHttpClientTest, TwoExisting) { + FakeHttpClient client; + client.addWebsite("http://firstexisting.com", "first_content"); + client.addWebsite("http://secondexisting.com", "second_content"); + EXPECT_EQ("first_content", client.get("http://firstexisting.com").value()); + EXPECT_EQ("second_content", client.get("http://secondexisting.com").value()); + EXPECT_EQ(none, client.get("http://notexisting.com")); +} + +TEST(FakeHttpClientTest, Overwriting) { + FakeHttpClient client; + client.addWebsite("http://existing.com", "content"); + client.addWebsite("http://existing.com", "new_content"); + EXPECT_EQ("new_content", client.get("http://existing.com").value()); +} diff --git a/test/cpp-utils/pointer/cast_include_test.cpp b/test/cpp-utils/pointer/cast_include_test.cpp new file mode 100644 index 00000000..362558b5 --- /dev/null +++ b/test/cpp-utils/pointer/cast_include_test.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/pointer/cast.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/pointer/cast_test.cpp b/test/cpp-utils/pointer/cast_test.cpp new file mode 100644 index 00000000..ec2eeb9a --- /dev/null +++ b/test/cpp-utils/pointer/cast_test.cpp @@ -0,0 +1,189 @@ +#include +#include +#include "cpp-utils/pointer/cast.h" +#include "cpp-utils/pointer/unique_ref.h" +#include "cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h" + +//TODO There is a lot of duplication here, because each test case is there twice - once for unique_ptr, once for unique_ref. Remove redundancy by using generic test cases. +//TODO Then also move the unique_ref related test cases there - cast_test.cpp should only contain the unique_ptr related ones. + +using namespace cpputils; +using std::unique_ptr; +using std::make_unique; +using boost::optional; +using boost::none; + +class DestructorCallback { +public: + MOCK_CONST_METHOD0(call, void()); +}; + +class Parent { +public: + virtual ~Parent() { } +}; + +class Child : public Parent { +public: + Child(const DestructorCallback *childDestructorCallback) : _destructorCallback(childDestructorCallback) { } + Child(): Child(nullptr) {} + + ~Child() { + if (_destructorCallback != nullptr) { + _destructorCallback->call(); + } + } + +private: + const DestructorCallback *_destructorCallback; + DISALLOW_COPY_AND_ASSIGN(Child); +}; + +class Child2 : public Parent {}; + + +TEST(UniquePtr_DynamicPointerMoveTest, NullPtrParentToChildCast) { + unique_ptr source(nullptr); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(nullptr, source.get()); + EXPECT_EQ(nullptr, casted.get()); +} + +TEST(UniquePtr_DynamicPointerMoveTest, NullPtrChildToParentCast) { + unique_ptr source(nullptr); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(nullptr, source.get()); + EXPECT_EQ(nullptr, casted.get()); +} + +TEST(UniquePtr_DynamicPointerMoveTest, NullPtrSelfCast) { + unique_ptr source(nullptr); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(nullptr, source.get()); + EXPECT_EQ(nullptr, casted.get()); +} + +TEST(UniqueRef_DynamicPointerMoveTest, ValidParentToChildCast) { + Child *obj = new Child(); + unique_ref source(nullcheck(unique_ptr(obj)).value()); + unique_ref casted = dynamic_pointer_move(source).value(); + EXPECT_EQ(nullptr, source.get()); // source lost ownership + EXPECT_EQ(obj, casted.get()); +} + +TEST(UniquePtr_DynamicPointerMoveTest, ValidParentToChildCast) { + Child *obj = new Child(); + unique_ptr source(obj); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(nullptr, source.get()); // source lost ownership + EXPECT_EQ(obj, casted.get()); +} + +TEST(UniqueRef_DynamicPointerMoveTest, InvalidParentToChildCast1) { + Parent *obj = new Parent(); + unique_ref source(nullcheck(unique_ptr(obj)).value()); + optional> casted = dynamic_pointer_move(source); + EXPECT_EQ(obj, source.get()); // source still has ownership + EXPECT_EQ(none, casted); +} + +TEST(UniquePtr_DynamicPointerMoveTest, InvalidParentToChildCast1) { + Parent *obj = new Parent(); + unique_ptr source(obj); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(obj, source.get()); // source still has ownership + EXPECT_EQ(nullptr, casted.get()); +} + +TEST(UniqueRef_DynamicPointerMoveTest, InvalidParentToChildCast2) { + Child2 *obj = new Child2(); + unique_ref source(nullcheck(unique_ptr(obj)).value()); + optional> casted = dynamic_pointer_move(source); + EXPECT_EQ(obj, source.get()); // source still has ownership + EXPECT_EQ(none, casted); +} + +TEST(UniquePtr_DynamicPointerMoveTest, InvalidParentToChildCast2) { + Child2 *obj = new Child2(); + unique_ptr source(obj); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(obj, source.get()); // source still has ownership + EXPECT_EQ(nullptr, casted.get()); +} + +TEST(UniqueRef_DynamicPointerMoveTest, ChildToParentCast) { + Child *obj = new Child(); + unique_ref source(nullcheck(unique_ptr(obj)).value()); + unique_ref casted = dynamic_pointer_move(source).value(); + EXPECT_EQ(nullptr, source.get()); // source lost ownership + EXPECT_EQ(obj, casted.get()); +} + +TEST(UniquePtr_DynamicPointerMoveTest, ChildToParentCast) { + Child *obj = new Child(); + unique_ptr source(obj); + unique_ptr casted = dynamic_pointer_move(source); + EXPECT_EQ(nullptr, source.get()); // source lost ownership + EXPECT_EQ(obj, casted.get()); +} + + +class UniqueRef_DynamicPointerMoveDestructorTest: public ::testing::Test { +public: + UniqueRef_DynamicPointerMoveDestructorTest(): childDestructorCallback() {} + + DestructorCallback childDestructorCallback; + unique_ref createChild() { + return make_unique_ref(&childDestructorCallback); + } + void EXPECT_CHILD_DESTRUCTOR_CALLED() { + EXPECT_CALL(childDestructorCallback, call()).Times(1); + } +}; + +class UniquePtr_DynamicPointerMoveDestructorTest: public ::testing::Test { +public: + UniquePtr_DynamicPointerMoveDestructorTest(): childDestructorCallback() {} + + DestructorCallback childDestructorCallback; + unique_ptr createChild() { + return make_unique(&childDestructorCallback); + } + void EXPECT_CHILD_DESTRUCTOR_CALLED() { + EXPECT_CALL(childDestructorCallback, call()).Times(1); + } +}; + +TEST_F(UniqueRef_DynamicPointerMoveDestructorTest, ChildInParentPtr) { + unique_ref parent = createChild(); + EXPECT_CHILD_DESTRUCTOR_CALLED(); +} + +TEST_F(UniquePtr_DynamicPointerMoveDestructorTest, ChildInParentPtr) { + unique_ptr parent = createChild(); + EXPECT_CHILD_DESTRUCTOR_CALLED(); +} + +TEST_F(UniqueRef_DynamicPointerMoveDestructorTest, ChildToParentCast) { + unique_ref child = createChild(); + unique_ref parent = dynamic_pointer_move(child).value(); + EXPECT_CHILD_DESTRUCTOR_CALLED(); +} + +TEST_F(UniquePtr_DynamicPointerMoveDestructorTest, ChildToParentCast) { + unique_ptr child = createChild(); + unique_ptr parent = dynamic_pointer_move(child); + EXPECT_CHILD_DESTRUCTOR_CALLED(); +} + +TEST_F(UniqueRef_DynamicPointerMoveDestructorTest, ParentToChildCast) { + unique_ref parent = createChild(); + unique_ref child = dynamic_pointer_move(parent).value(); + EXPECT_CHILD_DESTRUCTOR_CALLED(); +} + +TEST_F(UniquePtr_DynamicPointerMoveDestructorTest, ParentToChildCast) { + unique_ptr parent = createChild(); + unique_ptr child = dynamic_pointer_move(parent); + EXPECT_CHILD_DESTRUCTOR_CALLED(); +} diff --git a/test/cpp-utils/pointer/optional_ownership_ptr_include_test.cpp b/test/cpp-utils/pointer/optional_ownership_ptr_include_test.cpp new file mode 100644 index 00000000..3c28e29d --- /dev/null +++ b/test/cpp-utils/pointer/optional_ownership_ptr_include_test.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/pointer/optional_ownership_ptr.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/pointer/optional_ownership_ptr_test.cpp b/test/cpp-utils/pointer/optional_ownership_ptr_test.cpp new file mode 100644 index 00000000..f8e513b2 --- /dev/null +++ b/test/cpp-utils/pointer/optional_ownership_ptr_test.cpp @@ -0,0 +1,125 @@ +#include +#include "cpp-utils/pointer/optional_ownership_ptr.h" +#include "cpp-utils/macros.h" + +using std::unique_ptr; +using std::function; +using ::testing::Test; + +using namespace cpputils; + +class TestObject { +public: + TestObject(function destructorListener) : _destructorListener(destructorListener) {} + virtual ~TestObject() { + _destructorListener(); + } + +private: + function _destructorListener; +}; + +class TestObjectHolder { +public: + TestObjectHolder() + : _isDestructed(false), + _testObject(new TestObject([this]() {_isDestructed = true;})) { + } + + ~TestObjectHolder() { + if (!_isDestructed) { + delete _testObject; + _isDestructed = true; + } + } + + TestObject *get() { + return _testObject; + } + + bool isDestructed() { + return _isDestructed; + } +private: + bool _isDestructed; + TestObject *_testObject; + DISALLOW_COPY_AND_ASSIGN(TestObjectHolder); +}; + +class OptionalOwnershipPointerTest: public Test { +public: + OptionalOwnershipPointerTest(): obj(), obj2() {} + + TestObjectHolder obj; + TestObjectHolder obj2; +}; + +TEST_F(OptionalOwnershipPointerTest, TestIsInitializedCorrectly) { + EXPECT_FALSE(obj.isDestructed()); +} + +TEST_F(OptionalOwnershipPointerTest, DestructsWhenItHasOwnership_UniquePtr) { + { + optional_ownership_ptr ptr = WithOwnership(unique_ptr(obj.get())); + EXPECT_FALSE(obj.isDestructed()); + UNUSED(ptr); + } + EXPECT_TRUE(obj.isDestructed()); +} + +TEST_F(OptionalOwnershipPointerTest, DestructsWhenItHasOwnership_UniqueRef) { + { + optional_ownership_ptr ptr = WithOwnership(cpputils::nullcheck(unique_ptr(obj.get())).value()); + EXPECT_FALSE(obj.isDestructed()); + UNUSED(ptr); + } + EXPECT_TRUE(obj.isDestructed()); +} + + +TEST_F(OptionalOwnershipPointerTest, DestructsWhenItHasOwnershipAfterAssignment) { + { + optional_ownership_ptr ptr = WithoutOwnership(obj.get()); + ptr = WithOwnership(unique_ptr(obj2.get())); + } + EXPECT_FALSE(obj.isDestructed()); + EXPECT_TRUE(obj2.isDestructed()); +} + +TEST_F(OptionalOwnershipPointerTest, DoesntDestructWhenItDoesntHaveOwnership) { + { + optional_ownership_ptr ptr = WithoutOwnership(obj.get()); + UNUSED(ptr); + } + EXPECT_FALSE(obj.isDestructed()); +} + +TEST_F(OptionalOwnershipPointerTest, DoesntDestructWhenItDoesntHaveOwnershipAfterAssignment) { + { + optional_ownership_ptr ptr = WithOwnership(unique_ptr(obj.get())); + ptr = WithoutOwnership(obj2.get()); + EXPECT_TRUE(obj.isDestructed()); + } + EXPECT_FALSE(obj2.isDestructed()); +} + +TEST_F(OptionalOwnershipPointerTest, DestructsOnReassignmentWithNull) { + optional_ownership_ptr ptr = WithOwnership(unique_ptr(obj.get())); + ptr = null(); + EXPECT_TRUE(obj.isDestructed()); +} + +TEST_F(OptionalOwnershipPointerTest, DoesntCrashWhenDestructingNullptr1) { + optional_ownership_ptr ptr = null(); + UNUSED(ptr); +} + +TEST_F(OptionalOwnershipPointerTest, DoesntCrashWhenDestructingNullptrWithoutOwnership) { + optional_ownership_ptr ptr = WithoutOwnership((TestObject*)nullptr); + UNUSED(ptr); +} + +TEST_F(OptionalOwnershipPointerTest, DoesntCrashWhenDestructingNullptrWithOwnership) { + optional_ownership_ptr ptr = WithOwnership(unique_ptr(nullptr)); + UNUSED(ptr); +} diff --git a/test/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround_include_test.cpp b/test/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround_include_test.cpp new file mode 100644 index 00000000..a8612a88 --- /dev/null +++ b/test/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround_include_test.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/pointer/unique_ref_include_test.cpp b/test/cpp-utils/pointer/unique_ref_include_test.cpp new file mode 100644 index 00000000..65342d39 --- /dev/null +++ b/test/cpp-utils/pointer/unique_ref_include_test.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/pointer/unique_ref.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/pointer/unique_ref_test.cpp b/test/cpp-utils/pointer/unique_ref_test.cpp new file mode 100644 index 00000000..53eb066e --- /dev/null +++ b/test/cpp-utils/pointer/unique_ref_test.cpp @@ -0,0 +1,422 @@ +#include +#include "cpp-utils/pointer/unique_ref.h" +#include +#include +#include +#include +#include + +using namespace cpputils; + +//TODO Test unique_ref destructor +//TODO Test cpputils::destruct() + +class SomeClass0Parameters {}; +class SomeClass1Parameter { +public: + SomeClass1Parameter(int param_): param(param_) {} + int param; +}; +class SomeClass2Parameters { +public: + SomeClass2Parameters(int param1_, int param2_): param1(param1_), param2(param2_) {} + int param1; + int param2; +}; +using SomeClass = SomeClass0Parameters; + +TEST(MakeUniqueRefTest, Primitive) { + unique_ref var = make_unique_ref(3); + EXPECT_EQ(3, *var); +} + +TEST(MakeUniqueRefTest, ClassWith0Parameters) { + unique_ref var = make_unique_ref(); + //Check that the type is correct + EXPECT_EQ(var.get(), dynamic_cast(var.get())); +} + +TEST(MakeUniqueRefTest, ClassWith1Parameter) { + unique_ref var = make_unique_ref(5); + EXPECT_EQ(5, var->param); +} + +TEST(MakeUniqueRefTest, ClassWith2Parameters) { + unique_ref var = make_unique_ref(7,2); + EXPECT_EQ(7, var->param1); + EXPECT_EQ(2, var->param2); +} + +TEST(MakeUniqueRefTest, TypeIsAutoDeductible) { + auto var1 = make_unique_ref(3); + auto var2 = make_unique_ref(); + auto var3 = make_unique_ref(2); + auto var4 = make_unique_ref(2, 3); +} + +TEST(NullcheckTest, PrimitiveNullptr) { + boost::optional> var = nullcheck(std::unique_ptr(nullptr)); + EXPECT_FALSE((bool)var); +} + +TEST(NullcheckTest, ObjectNullptr) { + boost::optional> var = nullcheck(std::unique_ptr(nullptr)); + EXPECT_FALSE((bool)var); +} + +TEST(NullcheckTest, Primitive) { + boost::optional> var = nullcheck(std::make_unique(3)); + EXPECT_TRUE((bool)var); + EXPECT_EQ(3, **var); +} + +TEST(NullcheckTest, ClassWith0Parameters) { + boost::optional> var = nullcheck(std::make_unique()); + EXPECT_TRUE((bool)var); + //Check that the type is correct + EXPECT_EQ(var->get(), dynamic_cast(var->get())); +} + +TEST(NullcheckTest, ClassWith1Parameter) { + boost::optional> var = nullcheck(std::make_unique(5)); + EXPECT_TRUE((bool)var); + EXPECT_EQ(5, (*var)->param); +} + +TEST(NullcheckTest, ClassWith2Parameters) { + boost::optional> var = nullcheck(std::make_unique(7,2)); + EXPECT_TRUE((bool)var); + EXPECT_EQ(7, (*var)->param1); + EXPECT_EQ(2, (*var)->param2); +} + +TEST(NullcheckTest, OptionIsResolvable_Primitive) { + boost::optional> var = nullcheck(std::make_unique(3)); + unique_ref resolved = std::move(var).value(); +} + +TEST(NullcheckTest, OptionIsResolvable_Object) { + boost::optional> var = nullcheck(std::make_unique()); + unique_ref resolved = std::move(var).value(); +} + +TEST(NullcheckTest, OptionIsAutoResolvable_Primitive) { + auto var = nullcheck(std::make_unique(3)); + auto resolved = std::move(var).value(); +} + +TEST(NullcheckTest, OptionIsAutoResolvable_Object) { + auto var = nullcheck(std::make_unique()); + auto resolved = std::move(var).value(); +} + +class UniqueRefTest: public ::testing::Test { +public: + template void makeInvalid(unique_ref ref) { + UNUSED(ref); + //ref is moved in here and then destructed + } +}; + +TEST_F(UniqueRefTest, Get_Primitive) { + unique_ref obj = make_unique_ref(3); + EXPECT_EQ(3, *obj.get()); +} + +TEST_F(UniqueRefTest, Get_Object) { + unique_ref obj = make_unique_ref(5); + EXPECT_EQ(5, obj.get()->param); +} + +TEST_F(UniqueRefTest, Deref_Primitive) { + unique_ref obj = make_unique_ref(3); + EXPECT_EQ(3, *obj); +} + +TEST_F(UniqueRefTest, Deref_Object) { + unique_ref obj = make_unique_ref(5); + EXPECT_EQ(5, (*obj).param); +} + +TEST_F(UniqueRefTest, DerefArrow) { + unique_ref obj = make_unique_ref(3); + EXPECT_EQ(3, obj->param); +} + +TEST_F(UniqueRefTest, Assignment) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + obj2 = std::move(obj1); + EXPECT_EQ(obj1ptr, obj2.get()); + EXPECT_EQ(nullptr, obj1.get()); +} + +TEST_F(UniqueRefTest, MoveConstructor) { + unique_ref obj1 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + unique_ref obj2 = std::move(obj1); + EXPECT_EQ(obj1ptr, obj2.get()); + EXPECT_EQ(nullptr, obj1.get()); +} + +TEST_F(UniqueRefTest, Swap) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + SomeClass *obj2ptr = obj2.get(); + std::swap(obj1, obj2); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapFromInvalid) { + unique_ref obj1 = make_unique_ref(); + makeInvalid(std::move(obj1)); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj2ptr = obj2.get(); + std::swap(obj1, obj2); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(nullptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapWithInvalid) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + makeInvalid(std::move(obj2)); + SomeClass *obj1ptr = obj1.get(); + std::swap(obj1, obj2); + EXPECT_EQ(nullptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapInvalidWithInvalid) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + makeInvalid(std::move(obj1)); + makeInvalid(std::move(obj2)); + std::swap(obj1, obj2); + EXPECT_EQ(nullptr, obj1.get()); + EXPECT_EQ(nullptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapFromRValue) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + SomeClass *obj2ptr = obj2.get(); + std::swap(std::move(obj1), obj2); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, SwapWithRValue) { + unique_ref obj1 = make_unique_ref(); + unique_ref obj2 = make_unique_ref(); + SomeClass *obj1ptr = obj1.get(); + SomeClass *obj2ptr = obj2.get(); + std::swap(obj1, std::move(obj2)); + EXPECT_EQ(obj2ptr, obj1.get()); + EXPECT_EQ(obj1ptr, obj2.get()); +} + +TEST_F(UniqueRefTest, CanBePutInContainer_Primitive) { + std::vector> vec; + vec.push_back(make_unique_ref(3)); + EXPECT_EQ(3, *vec[0]); +} + +TEST_F(UniqueRefTest, CanBePutInContainer_Object) { + std::vector> vec; + vec.push_back(make_unique_ref(5)); + EXPECT_EQ(5, vec[0]->param); +} + +TEST_F(UniqueRefTest, CanBePutInContainer_Nullcheck) { + std::vector> vec; + vec.push_back(*nullcheck(std::make_unique(3))); + EXPECT_EQ(3, *vec[0]); +} + +TEST_F(UniqueRefTest, CanBePutInSet_Primitive) { + std::set> set; + set.insert(make_unique_ref(3)); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInSet_Object) { + std::set> set; + set.insert(make_unique_ref(5)); + EXPECT_EQ(5, (*set.begin())->param); +} + +TEST_F(UniqueRefTest, CanBePutInSet_Nullcheck) { + std::set> set; + set.insert(*nullcheck(std::make_unique(3))); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedSet_Primitive) { + std::unordered_set> set; + set.insert(make_unique_ref(3)); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedSet_Object) { + std::unordered_set> set; + set.insert(make_unique_ref(5)); + EXPECT_EQ(5, (*set.begin())->param); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedSet_Nullcheck) { + std::unordered_set> set; + set.insert(*nullcheck(std::make_unique(3))); + EXPECT_EQ(3, **set.begin()); +} + +TEST_F(UniqueRefTest, CanBePutInMap_Primitive) { + std::map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(3), make_unique_ref(5))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, CanBePutInMap_Object) { + std::map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(5), make_unique_ref(3))); + EXPECT_EQ(5, map.begin()->first->param); + EXPECT_EQ(3, map.begin()->second->param); +} + +TEST_F(UniqueRefTest, CanBePutInMap_Nullcheck) { + std::map, unique_ref> map; + map.insert(std::make_pair(*nullcheck(std::make_unique(3)), *nullcheck(std::make_unique(5)))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedMap_Primitive) { + std::unordered_map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(3), make_unique_ref(5))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedMap_Object) { + std::unordered_map, unique_ref> map; + map.insert(std::make_pair(make_unique_ref(5), make_unique_ref(3))); + EXPECT_EQ(5, map.begin()->first->param); + EXPECT_EQ(3, map.begin()->second->param); +} + +TEST_F(UniqueRefTest, CanBePutInUnorderedMap_Nullcheck) { + std::unordered_map, unique_ref> map; + map.insert(std::make_pair(*nullcheck(std::make_unique(3)), *nullcheck(std::make_unique(5)))); + EXPECT_EQ(3, *map.begin()->first); + EXPECT_EQ(5, *map.begin()->second); +} + +TEST_F(UniqueRefTest, Equality_Nullptr) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(4); + makeInvalid(std::move(var1)); + makeInvalid(std::move(var2)); + EXPECT_TRUE(var1 == var2); + EXPECT_FALSE(var1 != var2); +} + +TEST_F(UniqueRefTest, Nonequality) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + EXPECT_TRUE(var1 != var2); + EXPECT_FALSE(var1 == var2); +} + +TEST_F(UniqueRefTest, Nonequality_NullptrLeft) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + EXPECT_TRUE(var1 != var2); + EXPECT_FALSE(var1 == var2); +} + +TEST_F(UniqueRefTest, Nonequality_NullptrRight) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var2)); + EXPECT_TRUE(var1 != var2); + EXPECT_FALSE(var1 == var2); +} + +TEST_F(UniqueRefTest, HashIsDifferent) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + EXPECT_NE(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, HashIsDifferent_NullptrLeft) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + EXPECT_NE(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, HashIsDifferent_NullptrRight) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var2)); + EXPECT_NE(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, HashIsSame_BothNullptr) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + makeInvalid(std::move(var2)); + EXPECT_EQ(std::hash>()(var1), std::hash>()(var2)); +} + +TEST_F(UniqueRefTest, OneIsLess) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + EXPECT_TRUE(std::less>()(var1, var2) != std::less>()(var2, var1)); +} + +TEST_F(UniqueRefTest, NullptrIsLess1) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + EXPECT_TRUE(std::less>()(var1, var2)); +} + +TEST_F(UniqueRefTest, NullptrIsLess2) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var2)); + EXPECT_TRUE(std::less>()(var2, var1)); +} + +TEST_F(UniqueRefTest, NullptrIsNotLessThanNullptr) { + unique_ref var1 = make_unique_ref(3); + unique_ref var2 = make_unique_ref(3); + makeInvalid(std::move(var1)); + makeInvalid(std::move(var2)); + EXPECT_FALSE(std::less>()(var1, var2)); +} + +class OnlyMoveable { +public: + OnlyMoveable(int value_): value(value_) {} + OnlyMoveable(OnlyMoveable &&source): value(source.value) {source.value = -1;} + bool operator==(const OnlyMoveable &rhs) const { + return value == rhs.value; + } + int value; +private: + DISALLOW_COPY_AND_ASSIGN(OnlyMoveable); +}; + +TEST_F(UniqueRefTest, AllowsDerefOnRvalue) { + OnlyMoveable val = *make_unique_ref(5); + EXPECT_EQ(OnlyMoveable(5), val); +} \ No newline at end of file diff --git a/test/cpp-utils/process/daemonize_include_test.cpp b/test/cpp-utils/process/daemonize_include_test.cpp new file mode 100644 index 00000000..f851c992 --- /dev/null +++ b/test/cpp-utils/process/daemonize_include_test.cpp @@ -0,0 +1,4 @@ +#include "cpp-utils/process/daemonize.h" + +// Test the header can be included without needing additional dependencies + diff --git a/test/cpp-utils/process/subprocess_include_test.cpp b/test/cpp-utils/process/subprocess_include_test.cpp new file mode 100644 index 00000000..186d3ce2 --- /dev/null +++ b/test/cpp-utils/process/subprocess_include_test.cpp @@ -0,0 +1,4 @@ +#include "cpp-utils/process/subprocess.h" + +// Test the header can be included without needing additional dependencies + diff --git a/test/cpp-utils/random/RandomIncludeTest.cpp b/test/cpp-utils/random/RandomIncludeTest.cpp new file mode 100644 index 00000000..6a2e159c --- /dev/null +++ b/test/cpp-utils/random/RandomIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/random/Random.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/tempfile/TempDirIncludeTest.cpp b/test/cpp-utils/tempfile/TempDirIncludeTest.cpp new file mode 100644 index 00000000..43262566 --- /dev/null +++ b/test/cpp-utils/tempfile/TempDirIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/tempfile/TempDir.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/tempfile/TempDirTest.cpp b/test/cpp-utils/tempfile/TempDirTest.cpp new file mode 100644 index 00000000..39cecde8 --- /dev/null +++ b/test/cpp-utils/tempfile/TempDirTest.cpp @@ -0,0 +1,58 @@ +#include + +#include "cpp-utils/tempfile/TempDir.h" + +#include + +using ::testing::Test; +using std::ofstream; + +using namespace cpputils; + +namespace bf = boost::filesystem; + +class TempDirTest: public Test { +public: + void EXPECT_ENTRY_COUNT(int expected, const bf::path &path) { + int actual = CountEntries(path); + EXPECT_EQ(expected, actual); + } + + int CountEntries(const bf::path &path) { + int count = 0; + for (bf::directory_iterator iter(path); iter != bf::directory_iterator(); ++iter) { + ++count; + } + return count; + } + + void CreateFile(const bf::path &path) { + ofstream file(path.c_str()); + } +}; + +TEST_F(TempDirTest, DirIsCreated) { + TempDir dir; + EXPECT_TRUE(bf::exists(dir.path())); + EXPECT_TRUE(bf::is_directory(dir.path())); +} + +TEST_F(TempDirTest, DirIsCreatedEmpty) { + TempDir dir; + EXPECT_ENTRY_COUNT(0, dir.path()); +} + +TEST_F(TempDirTest, DirIsWriteable) { + TempDir dir; + CreateFile(dir.path() / "myfile"); + EXPECT_ENTRY_COUNT(1, dir.path()); +} + +TEST_F(TempDirTest, DirIsDeletedAfterUse) { + bf::path dirpath; + { + TempDir dir; + dirpath = dir.path(); + } + EXPECT_FALSE(bf::exists(dirpath)); +} diff --git a/test/cpp-utils/tempfile/TempFileIncludeTest.cpp b/test/cpp-utils/tempfile/TempFileIncludeTest.cpp new file mode 100644 index 00000000..63ded6b2 --- /dev/null +++ b/test/cpp-utils/tempfile/TempFileIncludeTest.cpp @@ -0,0 +1,3 @@ +#include "cpp-utils/tempfile/TempFile.h" + +// Test the header can be included without needing additional dependencies diff --git a/test/cpp-utils/tempfile/TempFileTest.cpp b/test/cpp-utils/tempfile/TempFileTest.cpp new file mode 100644 index 00000000..7fad6206 --- /dev/null +++ b/test/cpp-utils/tempfile/TempFileTest.cpp @@ -0,0 +1,117 @@ +#include + +#include "cpp-utils/tempfile/TempFile.h" +#include "cpp-utils/tempfile/TempDir.h" + +#include + +using ::testing::Test; +using std::ifstream; +using std::ofstream; + +using namespace cpputils; + +namespace bf = boost::filesystem; + +class TempFileTest: public Test { +public: + TempFileTest(): tempdir(), filepath_sample(tempdir.path() / "myfile") {} + + TempDir tempdir; + bf::path filepath_sample; + + void CreateFile(const bf::path &path) { + ofstream file(path.c_str()); + } +}; + +TEST_F(TempFileTest, FileIsCreated) { + TempFile file; + EXPECT_TRUE(bf::exists(file.path())); + EXPECT_TRUE(bf::is_regular_file(file.path())); +} + +TEST_F(TempFileTest, FileIsReadable) { + TempFile file; + ifstream opened(file.path().c_str()); + EXPECT_TRUE(opened.good()); +} + +TEST_F(TempFileTest, FileIsCreatedEmpty) { + TempFile file; + ifstream opened(file.path().c_str()); + opened.get(); + EXPECT_TRUE(opened.eof()); +} + +TEST_F(TempFileTest, FileIsWriteable) { + TempFile file; + ofstream opened(file.path().c_str()); + EXPECT_TRUE(opened.good()); +} + +TEST_F(TempFileTest, FileIsDeletedAfterUse) { + bf::path filepath; + { + TempFile file; + filepath = file.path(); + } + EXPECT_FALSE(bf::exists(filepath)); +} + +TEST_F(TempFileTest, DontCreateFileSpecified_FileIsNotCreated) { + TempFile file(false); + EXPECT_FALSE(bf::exists(file.path())); +} + +TEST_F(TempFileTest, DontCreateFileSpecified_FileIsCreatable) { + TempFile file(false); + CreateFile(file.path()); + EXPECT_TRUE(bf::exists(file.path())); +} + +TEST_F(TempFileTest, DontCreateFileSpecified_FileIsDeletedAfterUse) { + bf::path filepath; + { + TempFile file(false); + CreateFile(file.path()); + filepath = file.path(); + } + EXPECT_FALSE(bf::exists(filepath)); +} + +TEST_F(TempFileTest, PathGiven_FileIsCreatedAtGivenPath) { + TempFile file(filepath_sample); + EXPECT_EQ(filepath_sample, file.path()); +} + +TEST_F(TempFileTest, PathGiven_FileIsCreatedAndAccessible) { + TempFile file(filepath_sample); + EXPECT_TRUE(bf::exists(filepath_sample)); +} + +TEST_F(TempFileTest, PathGiven_FileIsDeletedAfterUse) { + { + TempFile file(filepath_sample); + } + EXPECT_FALSE(bf::exists(filepath_sample)); +} + +TEST_F(TempFileTest, PathGiven_DontCreateFileSpecified_FileIsNotCreated) { + TempFile file(filepath_sample, false); + EXPECT_FALSE(bf::exists(filepath_sample)); +} + +TEST_F(TempFileTest, PathGiven_DontCreateFileSpecified_FileIsCreatable) { + TempFile file(filepath_sample, false); + CreateFile(filepath_sample); + EXPECT_TRUE(bf::exists(filepath_sample)); +} + +TEST_F(TempFileTest, PathGiven_DontCreateFileSpecified_FileIsDeletedAfterUse) { + { + TempFile file(filepath_sample, false); + CreateFile(filepath_sample); + } + EXPECT_FALSE(bf::exists(filepath_sample)); +} diff --git a/test/cryfs/CMakeLists.txt b/test/cryfs/CMakeLists.txt new file mode 100644 index 00000000..8ba9d344 --- /dev/null +++ b/test/cryfs/CMakeLists.txt @@ -0,0 +1,35 @@ +project (cryfs-test) + +set(SOURCES + cli/CallAfterTimeoutTest.cpp + cli/testutils/CliTest.cpp + cli/CliTest_Setup.cpp + cli/CliTest_WrongEnvironment.cpp + cli/program_options/UtilsTest.cpp + cli/program_options/ProgramOptionsTest.cpp + cli/program_options/ParserTest.cpp + cli/CliTest_ShowingHelp.cpp + cli/VersionCheckerTest.cpp + config/crypto/CryConfigEncryptorFactoryTest.cpp + config/crypto/outer/OuterConfigTest.cpp + config/crypto/outer/OuterEncryptorTest.cpp + config/crypto/inner/ConcreteInnerEncryptorTest.cpp + config/crypto/inner/InnerConfigTest.cpp + config/crypto/CryConfigEncryptorTest.cpp + config/CompatibilityTest.cpp + config/CryConfigCreatorTest.cpp + config/CryConfigFileTest.cpp + config/CryConfigTest.cpp + config/CryCipherTest.cpp + config/CryConfigLoaderTest.cpp + config/CryConfigConsoleTest.cpp + filesystem/CryFsTest.cpp + filesystem/FileSystemTest.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} googletest cryfs_lib) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/cli/CallAfterTimeoutTest.cpp b/test/cryfs/cli/CallAfterTimeoutTest.cpp similarity index 93% rename from test/cli/CallAfterTimeoutTest.cpp rename to test/cryfs/cli/CallAfterTimeoutTest.cpp index 1fa564e0..35333e65 100644 --- a/test/cli/CallAfterTimeoutTest.cpp +++ b/test/cryfs/cli/CallAfterTimeoutTest.cpp @@ -1,6 +1,6 @@ -#include -#include -#include "../../src/cli/CallAfterTimeout.h" +#include +#include +#include using cpputils::unique_ref; using cpputils::make_unique_ref; diff --git a/test/cli/CliTest_Setup.cpp b/test/cryfs/cli/CliTest_Setup.cpp similarity index 100% rename from test/cli/CliTest_Setup.cpp rename to test/cryfs/cli/CliTest_Setup.cpp diff --git a/test/cli/CliTest_ShowingHelp.cpp b/test/cryfs/cli/CliTest_ShowingHelp.cpp similarity index 100% rename from test/cli/CliTest_ShowingHelp.cpp rename to test/cryfs/cli/CliTest_ShowingHelp.cpp diff --git a/test/cli/CliTest_WrongEnvironment.cpp b/test/cryfs/cli/CliTest_WrongEnvironment.cpp similarity index 100% rename from test/cli/CliTest_WrongEnvironment.cpp rename to test/cryfs/cli/CliTest_WrongEnvironment.cpp diff --git a/test/cli/VersionCheckerTest.cpp b/test/cryfs/cli/VersionCheckerTest.cpp similarity index 96% rename from test/cli/VersionCheckerTest.cpp rename to test/cryfs/cli/VersionCheckerTest.cpp index fad42836..6f8c14d2 100644 --- a/test/cli/VersionCheckerTest.cpp +++ b/test/cryfs/cli/VersionCheckerTest.cpp @@ -1,7 +1,7 @@ -#include -#include "../../src/cli/VersionChecker.h" -#include -#include +#include +#include +#include +#include using std::shared_ptr; using std::make_shared; diff --git a/test/cli/program_options/ParserTest.cpp b/test/cryfs/cli/program_options/ParserTest.cpp similarity index 98% rename from test/cli/program_options/ParserTest.cpp rename to test/cryfs/cli/program_options/ParserTest.cpp index d3277271..60854e51 100644 --- a/test/cli/program_options/ParserTest.cpp +++ b/test/cryfs/cli/program_options/ParserTest.cpp @@ -1,6 +1,6 @@ #include "testutils/ProgramOptionsTestBase.h" -#include "../../../src/cli/program_options/Parser.h" -#include "../../../src/config/CryCipher.h" +#include +#include using namespace cryfs; using namespace cryfs::program_options; diff --git a/test/cli/program_options/ProgramOptionsTest.cpp b/test/cryfs/cli/program_options/ProgramOptionsTest.cpp similarity index 96% rename from test/cli/program_options/ProgramOptionsTest.cpp rename to test/cryfs/cli/program_options/ProgramOptionsTest.cpp index fa616f5a..4036930b 100644 --- a/test/cli/program_options/ProgramOptionsTest.cpp +++ b/test/cryfs/cli/program_options/ProgramOptionsTest.cpp @@ -1,6 +1,6 @@ #include "testutils/ProgramOptionsTestBase.h" -#include "../../../src/cli/program_options/ProgramOptions.h" -#include +#include +#include using namespace cryfs::program_options; using std::vector; diff --git a/test/cli/program_options/UtilsTest.cpp b/test/cryfs/cli/program_options/UtilsTest.cpp similarity index 99% rename from test/cli/program_options/UtilsTest.cpp rename to test/cryfs/cli/program_options/UtilsTest.cpp index cd55d9ec..811e92d0 100644 --- a/test/cli/program_options/UtilsTest.cpp +++ b/test/cryfs/cli/program_options/UtilsTest.cpp @@ -1,5 +1,5 @@ #include "testutils/ProgramOptionsTestBase.h" -#include "../../../src/cli/program_options/utils.h" +#include using namespace cryfs::program_options; using std::pair; diff --git a/test/cli/program_options/testutils/ProgramOptionsTestBase.h b/test/cryfs/cli/program_options/testutils/ProgramOptionsTestBase.h similarity index 96% rename from test/cli/program_options/testutils/ProgramOptionsTestBase.h rename to test/cryfs/cli/program_options/testutils/ProgramOptionsTestBase.h index d4d220c4..342a6b1f 100644 --- a/test/cli/program_options/testutils/ProgramOptionsTestBase.h +++ b/test/cryfs/cli/program_options/testutils/ProgramOptionsTestBase.h @@ -2,7 +2,7 @@ #ifndef MESSMER_CRYFS_TEST_PROGRAMOPTIONS_PROGRAMOPTIONSTEST_H #define MESSMER_CRYFS_TEST_PROGRAMOPTIONS_PROGRAMOPTIONSTEST_H -#include +#include class ProgramOptionsTestBase: public ::testing::Test { public: diff --git a/test/cli/testutils/CliTest.cpp b/test/cryfs/cli/testutils/CliTest.cpp similarity index 100% rename from test/cli/testutils/CliTest.cpp rename to test/cryfs/cli/testutils/CliTest.cpp diff --git a/test/cli/testutils/CliTest.h b/test/cryfs/cli/testutils/CliTest.h similarity index 87% rename from test/cli/testutils/CliTest.h rename to test/cryfs/cli/testutils/CliTest.h index b3ad6259..af6a29cd 100644 --- a/test/cli/testutils/CliTest.h +++ b/test/cryfs/cli/testutils/CliTest.h @@ -2,15 +2,15 @@ #ifndef MESSMER_CRYFS_TEST_CLI_TESTUTILS_CLITEST_H #define MESSMER_CRYFS_TEST_CLI_TESTUTILS_CLITEST_H -#include -#include -#include -#include -#include "../../../src/cli/Cli.h" -#include "../../../src/cli/VersionChecker.h" -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "../../testutils/MockConsole.h" class CliTest : public ::testing::Test { diff --git a/test/config/CompatibilityTest.cpp b/test/cryfs/config/CompatibilityTest.cpp similarity index 96% rename from test/config/CompatibilityTest.cpp rename to test/cryfs/config/CompatibilityTest.cpp index d8cf6212..ebaeaed6 100644 --- a/test/config/CompatibilityTest.cpp +++ b/test/cryfs/config/CompatibilityTest.cpp @@ -1,11 +1,11 @@ -#include +#include #include #include -#include -#include -#include -#include -#include "../../src/config/CryConfigFile.h" +#include +#include +#include +#include +#include using std::vector; using cpputils::Data; diff --git a/test/config/CryCipherTest.cpp b/test/cryfs/config/CryCipherTest.cpp similarity index 92% rename from test/config/CryCipherTest.cpp rename to test/cryfs/config/CryCipherTest.cpp index 3c56c626..b2231348 100644 --- a/test/config/CryCipherTest.cpp +++ b/test/cryfs/config/CryCipherTest.cpp @@ -1,12 +1,12 @@ -#include -#include -#include "../../src/config/CryCipher.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace cryfs; using namespace blockstore::encrypted; diff --git a/test/config/CryConfigConsoleTest.cpp b/test/cryfs/config/CryConfigConsoleTest.cpp similarity index 92% rename from test/config/CryConfigConsoleTest.cpp rename to test/cryfs/config/CryConfigConsoleTest.cpp index 713a8fe1..b0f693ba 100644 --- a/test/config/CryConfigConsoleTest.cpp +++ b/test/cryfs/config/CryConfigConsoleTest.cpp @@ -1,8 +1,8 @@ -#include -#include -#include "../../src/config/CryConfigConsole.h" -#include "../../src/config/CryCipher.h" -#include +#include +#include +#include +#include +#include #include "../testutils/MockConsole.h" using namespace cryfs; diff --git a/test/config/CryConfigCreatorTest.cpp b/test/cryfs/config/CryConfigCreatorTest.cpp similarity index 93% rename from test/config/CryConfigCreatorTest.cpp rename to test/cryfs/config/CryConfigCreatorTest.cpp index 861acb90..d21ecf6c 100644 --- a/test/config/CryConfigCreatorTest.cpp +++ b/test/cryfs/config/CryConfigCreatorTest.cpp @@ -1,8 +1,8 @@ -#include -#include -#include "../../src/config/CryConfigCreator.h" -#include "../../src/config/CryCipher.h" -#include +#include +#include +#include +#include +#include #include "../testutils/MockConsole.h" using namespace cryfs; diff --git a/test/config/CryConfigFileTest.cpp b/test/cryfs/config/CryConfigFileTest.cpp similarity index 97% rename from test/config/CryConfigFileTest.cpp rename to test/cryfs/config/CryConfigFileTest.cpp index 1fd2a305..dfc53e99 100644 --- a/test/config/CryConfigFileTest.cpp +++ b/test/cryfs/config/CryConfigFileTest.cpp @@ -1,7 +1,7 @@ -#include +#include -#include "../../src/config/CryConfigFile.h" -#include +#include +#include #include using namespace cryfs; diff --git a/test/config/CryConfigLoaderTest.cpp b/test/cryfs/config/CryConfigLoaderTest.cpp similarity index 94% rename from test/config/CryConfigLoaderTest.cpp rename to test/cryfs/config/CryConfigLoaderTest.cpp index b7f1a52f..dffa00aa 100644 --- a/test/config/CryConfigLoaderTest.cpp +++ b/test/cryfs/config/CryConfigLoaderTest.cpp @@ -1,9 +1,9 @@ -#include -#include "../../src/config/CryConfigLoader.h" +#include +#include #include "../testutils/MockConsole.h" -#include -#include -#include +#include +#include +#include #include using cpputils::unique_ref; diff --git a/test/config/CryConfigTest.cpp b/test/cryfs/config/CryConfigTest.cpp similarity index 96% rename from test/config/CryConfigTest.cpp rename to test/cryfs/config/CryConfigTest.cpp index 23ec6ee7..89977ee7 100644 --- a/test/config/CryConfigTest.cpp +++ b/test/cryfs/config/CryConfigTest.cpp @@ -1,6 +1,6 @@ -#include +#include -#include "../../src/config/CryConfig.h" +#include using namespace cryfs; using cpputils::Data; diff --git a/test/config/crypto/CryConfigEncryptorFactoryTest.cpp b/test/cryfs/config/crypto/CryConfigEncryptorFactoryTest.cpp similarity index 89% rename from test/config/crypto/CryConfigEncryptorFactoryTest.cpp rename to test/cryfs/config/crypto/CryConfigEncryptorFactoryTest.cpp index e52a939a..12558d7a 100644 --- a/test/config/crypto/CryConfigEncryptorFactoryTest.cpp +++ b/test/cryfs/config/crypto/CryConfigEncryptorFactoryTest.cpp @@ -1,8 +1,8 @@ -#include -#include "../../../src/config/crypto/CryConfigEncryptorFactory.h" -#include -#include -#include +#include +#include +#include +#include +#include using cpputils::SCrypt; using cpputils::AES256_GCM; diff --git a/test/config/crypto/CryConfigEncryptorTest.cpp b/test/cryfs/config/crypto/CryConfigEncryptorTest.cpp similarity index 96% rename from test/config/crypto/CryConfigEncryptorTest.cpp rename to test/cryfs/config/crypto/CryConfigEncryptorTest.cpp index aca1958b..f857c663 100644 --- a/test/config/crypto/CryConfigEncryptorTest.cpp +++ b/test/cryfs/config/crypto/CryConfigEncryptorTest.cpp @@ -1,8 +1,8 @@ -#include -#include -#include +#include +#include +#include #include -#include "../../../src/config/crypto/CryConfigEncryptor.h" +#include using std::ostream; using cpputils::unique_ref; diff --git a/test/config/crypto/inner/ConcreteInnerEncryptorTest.cpp b/test/cryfs/config/crypto/inner/ConcreteInnerEncryptorTest.cpp similarity index 93% rename from test/config/crypto/inner/ConcreteInnerEncryptorTest.cpp rename to test/cryfs/config/crypto/inner/ConcreteInnerEncryptorTest.cpp index 3a29468e..43688099 100644 --- a/test/config/crypto/inner/ConcreteInnerEncryptorTest.cpp +++ b/test/cryfs/config/crypto/inner/ConcreteInnerEncryptorTest.cpp @@ -1,7 +1,7 @@ -#include -#include "../../../../src/config/crypto/inner/ConcreteInnerEncryptor.h" -#include -#include +#include +#include +#include +#include #include using std::ostream; diff --git a/test/config/crypto/inner/InnerConfigTest.cpp b/test/cryfs/config/crypto/inner/InnerConfigTest.cpp similarity index 92% rename from test/config/crypto/inner/InnerConfigTest.cpp rename to test/cryfs/config/crypto/inner/InnerConfigTest.cpp index 60cee701..e780a642 100644 --- a/test/config/crypto/inner/InnerConfigTest.cpp +++ b/test/cryfs/config/crypto/inner/InnerConfigTest.cpp @@ -1,7 +1,7 @@ -#include -#include +#include +#include #include -#include "../../../../src/config/crypto/inner/InnerConfig.h" +#include using cpputils::Data; using cpputils::DataFixture; diff --git a/test/config/crypto/outer/OuterConfigTest.cpp b/test/cryfs/config/crypto/outer/OuterConfigTest.cpp similarity index 93% rename from test/config/crypto/outer/OuterConfigTest.cpp rename to test/cryfs/config/crypto/outer/OuterConfigTest.cpp index aa04965e..fd6b658f 100644 --- a/test/config/crypto/outer/OuterConfigTest.cpp +++ b/test/cryfs/config/crypto/outer/OuterConfigTest.cpp @@ -1,7 +1,7 @@ -#include -#include +#include +#include #include -#include "../../../../src/config/crypto/outer/OuterConfig.h" +#include using cpputils::Data; using cpputils::DataFixture; diff --git a/test/config/crypto/outer/OuterEncryptorTest.cpp b/test/cryfs/config/crypto/outer/OuterEncryptorTest.cpp similarity index 92% rename from test/config/crypto/outer/OuterEncryptorTest.cpp rename to test/cryfs/config/crypto/outer/OuterEncryptorTest.cpp index f74eaaba..d01af67e 100644 --- a/test/config/crypto/outer/OuterEncryptorTest.cpp +++ b/test/cryfs/config/crypto/outer/OuterEncryptorTest.cpp @@ -1,7 +1,7 @@ -#include -#include "../../../../src/config/crypto/outer/OuterEncryptor.h" -#include -#include +#include +#include +#include +#include #include using std::ostream; diff --git a/test/filesystem/CryFsTest.cpp b/test/cryfs/filesystem/CryFsTest.cpp similarity index 79% rename from test/filesystem/CryFsTest.cpp rename to test/cryfs/filesystem/CryFsTest.cpp index 177b5e06..23903d69 100644 --- a/test/filesystem/CryFsTest.cpp +++ b/test/cryfs/filesystem/CryFsTest.cpp @@ -1,14 +1,14 @@ -#include -#include -#include -#include -#include -#include "../../src/filesystem/CryDevice.h" -#include "../../src/filesystem/CryDir.h" -#include "../../src/filesystem/CryFile.h" -#include "../../src/filesystem/CryOpenFile.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "../testutils/MockConsole.h" -#include "../../src/config/CryConfigLoader.h" +#include //TODO (whole project) Make constructors explicit when implicit construction not needed diff --git a/test/filesystem/FileSystemTest.cpp b/test/cryfs/filesystem/FileSystemTest.cpp similarity index 79% rename from test/filesystem/FileSystemTest.cpp rename to test/cryfs/filesystem/FileSystemTest.cpp index 43fd4dfd..cf35b22d 100644 --- a/test/filesystem/FileSystemTest.cpp +++ b/test/cryfs/filesystem/FileSystemTest.cpp @@ -1,9 +1,9 @@ -#include -#include -#include +#include +#include +#include -#include "../../src/filesystem/CryDevice.h" -#include "../../src/config/CryConfigLoader.h" +#include +#include #include "../testutils/MockConsole.h" using cpputils::unique_ref; diff --git a/test/testutils/MockConsole.h b/test/cryfs/testutils/MockConsole.h similarity index 93% rename from test/testutils/MockConsole.h rename to test/cryfs/testutils/MockConsole.h index d5322fb6..563a0859 100644 --- a/test/testutils/MockConsole.h +++ b/test/cryfs/testutils/MockConsole.h @@ -2,8 +2,8 @@ #ifndef MESSMER_CRYFS_TEST_TESTUTILS_MOCKCONSOLE_H #define MESSMER_CRYFS_TEST_TESTUTILS_MOCKCONSOLE_H -#include -#include +#include +#include class MockConsole: public cpputils::Console { public: diff --git a/test/fspp/CMakeLists.txt b/test/fspp/CMakeLists.txt new file mode 100644 index 00000000..3df535d7 --- /dev/null +++ b/test/fspp/CMakeLists.txt @@ -0,0 +1,110 @@ +project (fspp-test) + +set(SOURCES + testutils/FuseTest.cpp + testutils/FuseThread.cpp + testutils/InMemoryFile.cpp + impl/FuseOpenFileListTest.cpp + impl/IdListTest.cpp + fuse/lstat/FuseLstatReturnUidTest.cpp + fuse/lstat/testutils/FuseLstatTest.cpp + fuse/lstat/FuseLstatReturnCtimeTest.cpp + fuse/lstat/FuseLstatReturnGidTest.cpp + fuse/lstat/FuseLstatPathParameterTest.cpp + fuse/lstat/FuseLstatReturnNlinkTest.cpp + fuse/lstat/FuseLstatReturnModeTest.cpp + fuse/lstat/FuseLstatReturnAtimeTest.cpp + fuse/lstat/FuseLstatErrorTest.cpp + fuse/lstat/FuseLstatReturnMtimeTest.cpp + fuse/lstat/FuseLstatReturnSizeTest.cpp + fuse/read/FuseReadFileDescriptorTest.cpp + fuse/read/testutils/FuseReadTest.cpp + fuse/read/FuseReadOverflowTest.cpp + fuse/read/FuseReadErrorTest.cpp + fuse/read/FuseReadReturnedDataTest.cpp + fuse/flush/testutils/FuseFlushTest.cpp + fuse/flush/FuseFlushErrorTest.cpp + fuse/flush/FuseFlushFileDescriptorTest.cpp + fuse/rename/testutils/FuseRenameTest.cpp + fuse/rename/FuseRenameErrorTest.cpp + fuse/rename/FuseRenameFilenameTest.cpp + fuse/utimens/testutils/FuseUtimensTest.cpp + fuse/utimens/FuseUtimensErrorTest.cpp + fuse/utimens/FuseUtimensFilenameTest.cpp + fuse/utimens/FuseUtimensTimeParameterTest.cpp + fuse/unlink/testutils/FuseUnlinkTest.cpp + fuse/unlink/FuseUnlinkErrorTest.cpp + fuse/unlink/FuseUnlinkFilenameTest.cpp + fuse/ftruncate/testutils/FuseFTruncateTest.cpp + fuse/ftruncate/FuseFTruncateFileDescriptorTest.cpp + fuse/ftruncate/FuseFTruncateSizeTest.cpp + fuse/ftruncate/FuseFTruncateErrorTest.cpp + fuse/fstat/testutils/FuseFstatTest.cpp + fuse/fstat/FuseFstatParameterTest.cpp + fuse/fstat/FuseFstatErrorTest.cpp + fuse/truncate/FuseTruncateSizeTest.cpp + fuse/truncate/testutils/FuseTruncateTest.cpp + fuse/truncate/FuseTruncateErrorTest.cpp + fuse/truncate/FuseTruncateFilenameTest.cpp + fuse/statfs/FuseStatfsReturnFilesTest.cpp + fuse/statfs/FuseStatfsReturnFfreeTest.cpp + fuse/statfs/FuseStatfsReturnNamemaxTest.cpp + fuse/statfs/testutils/FuseStatfsTest.cpp + fuse/statfs/FuseStatfsReturnBsizeTest.cpp + fuse/statfs/FuseStatfsErrorTest.cpp + fuse/statfs/FuseStatfsReturnBfreeTest.cpp + fuse/statfs/FuseStatfsPathParameterTest.cpp + fuse/statfs/FuseStatfsReturnBlocksTest.cpp + fuse/statfs/FuseStatfsReturnBavailTest.cpp + fuse/closeFile/FuseCloseTest.cpp + fuse/fsync/testutils/FuseFsyncTest.cpp + fuse/fsync/FuseFsyncFileDescriptorTest.cpp + fuse/fsync/FuseFsyncErrorTest.cpp + fuse/openFile/testutils/FuseOpenTest.cpp + fuse/openFile/FuseOpenFilenameTest.cpp + fuse/openFile/FuseOpenFlagsTest.cpp + fuse/openFile/FuseOpenFileDescriptorTest.cpp + fuse/openFile/FuseOpenErrorTest.cpp + fuse/access/FuseAccessFilenameTest.cpp + fuse/access/testutils/FuseAccessTest.cpp + fuse/access/FuseAccessModeTest.cpp + fuse/access/FuseAccessErrorTest.cpp + fuse/BasicFuseTest.cpp + fuse/rmdir/testutils/FuseRmdirTest.cpp + fuse/rmdir/FuseRmdirErrorTest.cpp + fuse/rmdir/FuseRmdirDirnameTest.cpp + fuse/fdatasync/testutils/FuseFdatasyncTest.cpp + fuse/fdatasync/FuseFdatasyncErrorTest.cpp + fuse/fdatasync/FuseFdatasyncFileDescriptorTest.cpp + fuse/mkdir/testutils/FuseMkdirTest.cpp + fuse/mkdir/FuseMkdirErrorTest.cpp + fuse/mkdir/FuseMkdirModeTest.cpp + fuse/mkdir/FuseMkdirDirnameTest.cpp + fuse/write/FuseWriteErrorTest.cpp + fuse/write/testutils/FuseWriteTest.cpp + fuse/write/FuseWriteOverflowTest.cpp + fuse/write/FuseWriteFileDescriptorTest.cpp + fuse/write/FuseWriteDataTest.cpp + fuse/readDir/testutils/FuseReadDirTest.cpp + fuse/readDir/FuseReadDirDirnameTest.cpp + fuse/readDir/FuseReadDirErrorTest.cpp + fuse/readDir/FuseReadDirReturnTest.cpp + fuse/createAndOpenFile/FuseCreateAndOpenFilenameTest.cpp + fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.cpp + fuse/createAndOpenFile/FuseCreateAndOpenFlagsTest.cpp + fuse/createAndOpenFile/FuseCreateAndOpenFileDescriptorTest.cpp + fuse/createAndOpenFile/FuseCreateAndOpenErrorTest.cpp + fuse/FilesystemTest.cpp + fs_interface/NodeTest.cpp + fs_interface/FileTest.cpp + fs_interface/DirTest.cpp + fs_interface/DeviceTest.cpp + fs_interface/OpenFileTest.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} googletest fspp) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/fspp/fs_interface/DeviceTest.cpp b/test/fspp/fs_interface/DeviceTest.cpp new file mode 100644 index 00000000..dea9204e --- /dev/null +++ b/test/fspp/fs_interface/DeviceTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "fspp/fs_interface/Device.h" diff --git a/test/fspp/fs_interface/DirTest.cpp b/test/fspp/fs_interface/DirTest.cpp new file mode 100644 index 00000000..91a92c24 --- /dev/null +++ b/test/fspp/fs_interface/DirTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "fspp/fs_interface/Dir.h" diff --git a/test/fspp/fs_interface/FileTest.cpp b/test/fspp/fs_interface/FileTest.cpp new file mode 100644 index 00000000..fd4831bb --- /dev/null +++ b/test/fspp/fs_interface/FileTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "fspp/fs_interface/File.h" diff --git a/test/fspp/fs_interface/NodeTest.cpp b/test/fspp/fs_interface/NodeTest.cpp new file mode 100644 index 00000000..9b1a03d2 --- /dev/null +++ b/test/fspp/fs_interface/NodeTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "fspp/fs_interface/Node.h" diff --git a/test/fspp/fs_interface/OpenFileTest.cpp b/test/fspp/fs_interface/OpenFileTest.cpp new file mode 100644 index 00000000..20db933d --- /dev/null +++ b/test/fspp/fs_interface/OpenFileTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "fspp/fs_interface/OpenFile.h" diff --git a/test/fspp/fuse/BasicFuseTest.cpp b/test/fspp/fuse/BasicFuseTest.cpp new file mode 100644 index 00000000..042b28f0 --- /dev/null +++ b/test/fspp/fuse/BasicFuseTest.cpp @@ -0,0 +1,14 @@ +#include "../testutils/FuseTest.h" + +using namespace fspp::fuse; +using namespace fspp::fuse; + +using ::testing::_; +using ::testing::StrEq; + +typedef FuseTest BasicFuseTest; + +//This test case simply checks whether a filesystem can be setup and teardown without crashing. +TEST_F(BasicFuseTest, setupAndTearDown) { + auto fs = TestFS(); +} diff --git a/test/fspp/fuse/FilesystemTest.cpp b/test/fspp/fuse/FilesystemTest.cpp new file mode 100644 index 00000000..d78cb35d --- /dev/null +++ b/test/fspp/fuse/FilesystemTest.cpp @@ -0,0 +1,4 @@ +/* + * Tests that the header can be included without needing additional header includes as dependencies. + */ +#include "fspp/fuse/Filesystem.h" diff --git a/test/fspp/fuse/access/FuseAccessErrorTest.cpp b/test/fspp/fuse/access/FuseAccessErrorTest.cpp new file mode 100644 index 00000000..8c07bb69 --- /dev/null +++ b/test/fspp/fuse/access/FuseAccessErrorTest.cpp @@ -0,0 +1,24 @@ +#include "testutils/FuseAccessTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseAccessErrorTest: public FuseAccessTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseAccessErrorTest, FuseAccessErrorTest, Values(EACCES, ELOOP, ENAMETOOLONG, ENOENT, ENOTDIR, EROFS, EFAULT, EINVAL, EIO, ENOMEM, ETXTBSY)); + +TEST_P(FuseAccessErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, access(StrEq(FILENAME), _)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = AccessFileReturnError(FILENAME, 0); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/access/FuseAccessFilenameTest.cpp b/test/fspp/fuse/access/FuseAccessFilenameTest.cpp new file mode 100644 index 00000000..751edf1e --- /dev/null +++ b/test/fspp/fuse/access/FuseAccessFilenameTest.cpp @@ -0,0 +1,35 @@ +#include "testutils/FuseAccessTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseAccessFilenameTest: public FuseAccessTest { +}; + +TEST_F(FuseAccessFilenameTest, AccessFile) { + ReturnIsFileOnLstat("/myfile"); + EXPECT_CALL(fsimpl, access(StrEq("/myfile"), _)) + .Times(1).WillOnce(Return()); + + AccessFile("/myfile", 0); +} + +TEST_F(FuseAccessFilenameTest, AccessFileNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/myfile"); + EXPECT_CALL(fsimpl, access(StrEq("/mydir/myfile"), _)) + .Times(1).WillOnce(Return()); + + AccessFile("/mydir/myfile", 0); +} + +TEST_F(FuseAccessFilenameTest, AccessFileNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, access(StrEq("/mydir/mydir2/myfile"), _)) + .Times(1).WillOnce(Return()); + + AccessFile("/mydir/mydir2/myfile", 0); +} diff --git a/test/fspp/fuse/access/FuseAccessModeTest.cpp b/test/fspp/fuse/access/FuseAccessModeTest.cpp new file mode 100644 index 00000000..b29031db --- /dev/null +++ b/test/fspp/fuse/access/FuseAccessModeTest.cpp @@ -0,0 +1,20 @@ +#include "testutils/FuseAccessTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseAccessModeTest: public FuseAccessTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseAccessModeTest, FuseAccessModeTest, Values(0, F_OK, R_OK, W_OK, X_OK, R_OK|W_OK, W_OK|X_OK, R_OK|X_OK, R_OK|W_OK|X_OK)); + + +TEST_P(FuseAccessModeTest, AccessFile) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, access(StrEq(FILENAME), GetParam())) + .Times(1).WillOnce(Return()); + + AccessFile(FILENAME, GetParam()); +} diff --git a/test/fspp/fuse/access/testutils/FuseAccessTest.cpp b/test/fspp/fuse/access/testutils/FuseAccessTest.cpp new file mode 100644 index 00000000..c1206ce2 --- /dev/null +++ b/test/fspp/fuse/access/testutils/FuseAccessTest.cpp @@ -0,0 +1,18 @@ +#include "FuseAccessTest.h" + +void FuseAccessTest::AccessFile(const char *filename, int mode) { + int error = AccessFileReturnError(filename, mode); + EXPECT_EQ(0, error); +} + +int FuseAccessTest::AccessFileReturnError(const char *filename, int mode) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / filename; + int retval = ::access(realpath.c_str(), mode); + if (retval == 0) { + return 0; + } else { + return errno; + } +} diff --git a/test/fspp/fuse/access/testutils/FuseAccessTest.h b/test/fspp/fuse/access/testutils/FuseAccessTest.h new file mode 100644 index 00000000..4b430f10 --- /dev/null +++ b/test/fspp/fuse/access/testutils/FuseAccessTest.h @@ -0,0 +1,15 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_ACCESS_TESTUTILS_FUSEACCESSTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_ACCESS_TESTUTILS_FUSEACCESSTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseAccessTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + void AccessFile(const char *filename, int mode); + int AccessFileReturnError(const char *filename, int mode); +}; + +#endif diff --git a/test/fspp/fuse/closeFile/FuseCloseTest.cpp b/test/fspp/fuse/closeFile/FuseCloseTest.cpp new file mode 100644 index 00000000..acfc2121 --- /dev/null +++ b/test/fspp/fuse/closeFile/FuseCloseTest.cpp @@ -0,0 +1,99 @@ +#include "../../testutils/FuseTest.h" +#include + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Eq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::InSequence; +using ::testing::AtLeast; + +using std::string; +using std::mutex; +using std::unique_lock; +using std::condition_variable; +using std::chrono::duration; +using std::chrono::seconds; + +// The fuse behaviour is: For each open(), there will be exactly one call to release(). +// Directly before this call to release(), flush() will be called. After flush() returns, +// the ::close() syscall (in the process using the filesystem) returns. So the fuse release() call is +// called asynchronously afterwards. Errors have to be returned in the implementation of flush(). + +// Citing FUSE spec: +// 1) Flush is called on each close() of a file descriptor. +// 2) Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at all. +// I can't get these sentences together. For the test cases here, I go with the first one and assume that +// flush() will ALWAYS be called on a file close. + +class Barrier { +public: + Barrier(): m(), cv(), finished(false) {} + + template + void WaitAtMost(const duration &atMost) { + unique_lock lock(m); + if (!finished) { + cv.wait_for(lock, atMost, [this] () {return finished;}); + } + } + + void Release() { + unique_lock lock(m); + finished = true; + cv.notify_all(); + } +private: + mutex m; + condition_variable cv; + bool finished; +}; + +class FuseCloseTest: public FuseTest, public WithParamInterface { +public: + const string FILENAME = "/myfile"; + + void OpenAndCloseFile(const string &filename) { + auto fs = TestFS(); + int fd = OpenFile(fs.get(), filename); + CloseFile(fd); + } + + int OpenFile(const TempTestFS *fs, const string &filename) { + auto real_path = fs->mountDir() / filename; + int fd = ::open(real_path.c_str(), O_RDONLY); + EXPECT_GE(fd, 0) << "Opening file failed"; + return fd; + } + + void CloseFile(int fd) { + int retval = ::close(fd); + EXPECT_EQ(0, retval); + } +}; +INSTANTIATE_TEST_CASE_P(FuseCloseTest, FuseCloseTest, Values(0, 1, 2, 100, 1024*1024*1024)); + +//TODO Figure out what's wrong and enable this test +//Disabled, because it is flaky. libfuse seems to not send the release() event sometimes. +/*TEST_P(FuseCloseTest, CloseFile) { + Barrier barrier; + + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam())); + { + //InSequence fileCloseSequence; + EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1); + EXPECT_CALL(fsimpl, closeFile(Eq(GetParam()))).Times(1).WillOnce(Invoke([&barrier] (int) { + // Release the waiting lock at the end of this test case, because the fuse release() came in now. + barrier.Release(); + })); + } + + OpenAndCloseFile(FILENAME); + + // Wait, until fuse release() was called, so we can check for the function call expectation. + barrier.WaitAtMost(seconds(10)); +}*/ diff --git a/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenErrorTest.cpp b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenErrorTest.cpp new file mode 100644 index 00000000..d3cda188 --- /dev/null +++ b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenErrorTest.cpp @@ -0,0 +1,34 @@ +#include "testutils/FuseCreateAndOpenTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; +using ::testing::Throw; +using ::testing::StrEq; +using ::testing::_; + +using namespace fspp::fuse; + +class FuseCreateAndOpenErrorTest: public FuseCreateAndOpenTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenErrorTest, FuseCreateAndOpenErrorTest, Values(EACCES, EDQUOT, EEXIST, EFAULT, EFBIG, EINTR, EOVERFLOW, EINVAL, EISDIR, ELOOP, EMFILE, ENAMETOOLONG, ENFILE, ENODEV, ENOENT, ENOMEM, ENOSPC, ENOTDIR, ENXIO, EOPNOTSUPP, EPERM, EROFS, ETXTBSY, EWOULDBLOCK, EBADF, ENOTDIR)); + +TEST_F(FuseCreateAndOpenErrorTest, ReturnNoError) { + ReturnDoesntExistOnLstat(FILENAME); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Return(1)); + //For the syscall to succeed, we also need to give an fstat implementation. + ReturnIsFileOnFstat(1); + + int error = CreateAndOpenFileReturnError(FILENAME, O_RDONLY); + EXPECT_EQ(0, error); +} + +TEST_P(FuseCreateAndOpenErrorTest, ReturnError) { + ReturnDoesntExistOnLstat(FILENAME); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = CreateAndOpenFileReturnError(FILENAME, O_RDONLY); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFileDescriptorTest.cpp b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFileDescriptorTest.cpp new file mode 100644 index 00000000..22a71a8c --- /dev/null +++ b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFileDescriptorTest.cpp @@ -0,0 +1,42 @@ +#include "testutils/FuseCreateAndOpenTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; + +class FuseCreateAndOpenFileDescriptorTest: public FuseCreateAndOpenTest, public WithParamInterface { +public: + void CreateAndOpenAndReadFile(const char *filename) { + auto fs = TestFS(); + + int fd = CreateAndOpenFile(fs.get(), filename); + ReadFile(fd); + } + +private: + int CreateAndOpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH); + EXPECT_GE(fd, 0) << "Creating file failed"; + return fd; + } + void ReadFile(int fd) { + uint8_t buf; + int retval = ::read(fd, &buf, 1); + EXPECT_EQ(1, retval) << "Reading file failed"; + } +}; +INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenFileDescriptorTest, FuseCreateAndOpenFileDescriptorTest, Values(0, 2, 5, 1000, 1024*1024*1024)); + +TEST_P(FuseCreateAndOpenFileDescriptorTest, TestReturnedFileDescriptor) { + ReturnDoesntExistOnLstat(FILENAME); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)) + .Times(1).WillOnce(Return(GetParam())); + EXPECT_CALL(fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(1)); + //For the syscall to succeed, we also need to give an fstat implementation. + ReturnIsFileOnFstatWithSize(GetParam(), 1); + + CreateAndOpenAndReadFile(FILENAME); +} diff --git a/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFilenameTest.cpp b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFilenameTest.cpp new file mode 100644 index 00000000..a9e3a900 --- /dev/null +++ b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFilenameTest.cpp @@ -0,0 +1,42 @@ +#include "testutils/FuseCreateAndOpenTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseCreateAndOpenFilenameTest: public FuseCreateAndOpenTest { +public: +}; + +TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFile) { + ReturnDoesntExistOnLstat("/myfile"); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/myfile"), _, _, _)) + .Times(1).WillOnce(Return(0)); + //For the syscall to succeed, we also need to give an fstat implementation. + ReturnIsFileOnFstat(0); + + CreateAndOpenFile("/myfile", O_RDONLY); +} + +TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFileNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnDoesntExistOnLstat("/mydir/myfile"); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/mydir/myfile"), _, _, _)) + .Times(1).WillOnce(Return(0)); + //For the syscall to succeed, we also need to give an fstat implementation. + ReturnIsFileOnFstat(0); + + CreateAndOpenFile("/mydir/myfile", O_RDONLY); +} + +TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFileNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnDoesntExistOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/mydir/mydir2/myfile"), _, _, _)) + .Times(1).WillOnce(Return(0)); + //For the syscall to succeed, we also need to give an fstat implementation. + ReturnIsFileOnFstat(0); + + CreateAndOpenFile("/mydir/mydir2/myfile", O_RDONLY); +} diff --git a/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFlagsTest.cpp b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFlagsTest.cpp new file mode 100644 index 00000000..400f2f4e --- /dev/null +++ b/test/fspp/fuse/createAndOpenFile/FuseCreateAndOpenFlagsTest.cpp @@ -0,0 +1,22 @@ +#include "testutils/FuseCreateAndOpenTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; + +class FuseCreateAndOpenFlagsTest: public FuseCreateAndOpenTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenFlagsTest, FuseCreateAndOpenFlagsTest, Values(O_RDWR, O_RDONLY, O_WRONLY)); + +//TODO Disabled because it doesn't seem to work. Fuse doesn't seem to pass flags to create(). Why? +/*TEST_P(FuseCreateAndOpenFlagsTest, testFlags) { + ReturnDoesntExistOnLstat(FILENAME); + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), OpenFlagsEq(GetParam()), _, _)) + .Times(1).WillOnce(Return(0)); + //For the syscall to succeed, we also need to give an fstat implementation. + ReturnIsFileOnFstat(0); + + CreateAndOpenFile(FILENAME, GetParam()); +}*/ diff --git a/test/fspp/fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.cpp b/test/fspp/fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.cpp new file mode 100644 index 00000000..6b851088 --- /dev/null +++ b/test/fspp/fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.cpp @@ -0,0 +1,30 @@ +#include "FuseCreateAndOpenTest.h" + +using ::testing::_; + +int FuseCreateAndOpenTest::CreateAndOpenFile(const char *filename, int flags) { + int fd = CreateAndOpenFileAllowError(filename, flags); + EXPECT_GE(fd, 0); + return fd; +} + +int FuseCreateAndOpenTest::CreateAndOpenFileReturnError(const char *filename, int flags) { + int fd = CreateAndOpenFileAllowError(filename, flags); + if (fd >= 0) { + return 0; + } else { + return -fd; + } +} + +int FuseCreateAndOpenTest::CreateAndOpenFileAllowError(const char *filename, int flags) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), flags | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd >= 0) { + return fd; + } else { + return -errno; + } +} diff --git a/test/fspp/fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.h b/test/fspp/fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.h new file mode 100644 index 00000000..c19b2f4c --- /dev/null +++ b/test/fspp/fuse/createAndOpenFile/testutils/FuseCreateAndOpenTest.h @@ -0,0 +1,21 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_CREATEANDOPENFILE_TESTUTILS_FUSECREATEANDOPENTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_CREATEANDOPENFILE_TESTUTILS_FUSECREATEANDOPENTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseCreateAndOpenTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + int CreateAndOpenFile(const char *FILENAME, int flags); + int CreateAndOpenFileReturnError(const char *FILENAME, int flags); +private: + int CreateAndOpenFileAllowError(const char *FILENAME, int flags); +}; + +MATCHER_P(OpenFlagsEq, expectedFlags, "") { + return expectedFlags == (O_ACCMODE & arg); +} + +#endif diff --git a/test/fspp/fuse/fdatasync/FuseFdatasyncErrorTest.cpp b/test/fspp/fuse/fdatasync/FuseFdatasyncErrorTest.cpp new file mode 100644 index 00000000..4136345b --- /dev/null +++ b/test/fspp/fuse/fdatasync/FuseFdatasyncErrorTest.cpp @@ -0,0 +1,25 @@ +#include "testutils/FuseFdatasyncTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseFdatasyncErrorTest: public FuseFdatasyncTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFdatasyncErrorTest, FuseFdatasyncErrorTest, Values(EBADF, EIO, EROFS, EINVAL)); + +TEST_P(FuseFdatasyncErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, fdatasync(0)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = FdatasyncFileReturnError(FILENAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/fdatasync/FuseFdatasyncFileDescriptorTest.cpp b/test/fspp/fuse/fdatasync/FuseFdatasyncFileDescriptorTest.cpp new file mode 100644 index 00000000..fb5c99c8 --- /dev/null +++ b/test/fspp/fuse/fdatasync/FuseFdatasyncFileDescriptorTest.cpp @@ -0,0 +1,26 @@ +#include "testutils/FuseFdatasyncTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; + +using namespace fspp::fuse; + +class FuseFdatasyncFileDescriptorTest: public FuseFdatasyncTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFdatasyncFileDescriptorTest, FuseFdatasyncFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024)); + + +TEST_P(FuseFdatasyncFileDescriptorTest, FileDescriptorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, GetParam()); + EXPECT_CALL(fsimpl, fdatasync(Eq(GetParam()))) + .Times(1).WillOnce(Return()); + + FdatasyncFile(FILENAME); +} diff --git a/test/fspp/fuse/fdatasync/testutils/FuseFdatasyncTest.cpp b/test/fspp/fuse/fdatasync/testutils/FuseFdatasyncTest.cpp new file mode 100644 index 00000000..8ca0f583 --- /dev/null +++ b/test/fspp/fuse/fdatasync/testutils/FuseFdatasyncTest.cpp @@ -0,0 +1,31 @@ +#include "FuseFdatasyncTest.h" +#include + +void FuseFdatasyncTest::FdatasyncFile(const char *filename) { + int error = FdatasyncFileReturnError(filename); + EXPECT_EQ(0, error); +} + +int FuseFdatasyncTest::FdatasyncFileReturnError(const char *filename) { + auto fs = TestFS(); + + int fd = OpenFile(fs.get(), filename); +#ifdef F_FULLFSYNC + // This is MacOSX, which doesn't know fdatasync + int retval = fcntl(fd, F_FULLFSYNC); +#else + int retval = ::fdatasync(fd); +#endif + if (retval != -1) { + return 0; + } else { + return errno; + } +} + +int FuseFdatasyncTest::OpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_RDWR); + EXPECT_GE(fd, 0) << "Error opening file"; + return fd; +} diff --git a/test/fspp/fuse/fdatasync/testutils/FuseFdatasyncTest.h b/test/fspp/fuse/fdatasync/testutils/FuseFdatasyncTest.h new file mode 100644 index 00000000..8c73cce3 --- /dev/null +++ b/test/fspp/fuse/fdatasync/testutils/FuseFdatasyncTest.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_FDATASYNC_TESTUTILS_FUSEFDATASYNCTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_FDATASYNC_TESTUTILS_FUSEFDATASYNCTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseFdatasyncTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + void FdatasyncFile(const char *filename); + int FdatasyncFileReturnError(const char *filename); + +private: + int OpenFile(const TempTestFS *fs, const char *filename); +}; + +#endif diff --git a/test/fspp/fuse/flush/FuseFlushErrorTest.cpp b/test/fspp/fuse/flush/FuseFlushErrorTest.cpp new file mode 100644 index 00000000..54be24c6 --- /dev/null +++ b/test/fspp/fuse/flush/FuseFlushErrorTest.cpp @@ -0,0 +1,32 @@ +#include "testutils/FuseFlushTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::WithParamInterface; +using ::testing::StrEq; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Throw; +using ::testing::AtLeast; +using ::testing::Values; +using ::testing::_; + +using fspp::fuse::FuseErrnoException; + +class FuseFlushErrorTest: public FuseFlushTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFlushErrorTest, FuseFlushErrorTest, Values(EBADF, EINTR, EIO)); + +TEST_P(FuseFlushErrorTest, ReturnErrorFromFlush) { + ReturnIsFileOnLstat(FILENAME); + + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam())); + EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + auto fs = TestFS(); + int fd = OpenFile(fs.get(), FILENAME); + + int close_result = ::close(fd); + EXPECT_EQ(GetParam(), errno); + EXPECT_EQ(-1, close_result); +} diff --git a/test/fspp/fuse/flush/FuseFlushFileDescriptorTest.cpp b/test/fspp/fuse/flush/FuseFlushFileDescriptorTest.cpp new file mode 100644 index 00000000..11e14bcb --- /dev/null +++ b/test/fspp/fuse/flush/FuseFlushFileDescriptorTest.cpp @@ -0,0 +1,35 @@ +#include "testutils/FuseFlushTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Eq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; +using ::testing::AtLeast; + +using std::string; + +// The fuse behaviour is: For each open(), there will be exactly one call to release(). +// Directly before this call to release(), flush() will be called. After flush() returns, +// the ::close() syscall (in the process using the filesystem) returns. So the fuse release() call is +// called asynchronously afterwards. Errors have to be returned in the implementation of flush(). + +// Citing FUSE spec: +// 1) Flush is called on each close() of a file descriptor. +// 2) Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at all. +// I can't get these sentences together. For the test cases here, I go with the first one and assume that +// flush() will ALWAYS be called on a file close. + +class FuseFlushFileDescriptorTest: public FuseFlushTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFlushFileDescriptorTest, FuseFlushFileDescriptorTest, Values(0, 1, 2, 100, 1024*1024*1024)); + +TEST_P(FuseFlushFileDescriptorTest, FlushOnCloseFile) { + ReturnIsFileOnLstat(FILENAME); + + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam())); + EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1); + + OpenAndCloseFile(FILENAME); +} diff --git a/test/fspp/fuse/flush/testutils/FuseFlushTest.cpp b/test/fspp/fuse/flush/testutils/FuseFlushTest.cpp new file mode 100644 index 00000000..1000ca56 --- /dev/null +++ b/test/fspp/fuse/flush/testutils/FuseFlushTest.cpp @@ -0,0 +1,19 @@ +#include "FuseFlushTest.h" + +void FuseFlushTest::OpenAndCloseFile(const std::string &filename) { + auto fs = TestFS(); + int fd = OpenFile(fs.get(), filename); + CloseFile(fd); +} + +int FuseFlushTest::OpenFile(const TempTestFS *fs, const std::string &filename) { + auto real_path = fs->mountDir() / filename; + int fd = ::open(real_path.c_str(), O_RDONLY); + EXPECT_GE(fd, 0) << "Opening file failed"; + return fd; +} + +void FuseFlushTest::CloseFile(int fd) { + int retval = ::close(fd); + EXPECT_EQ(0, retval); +} diff --git a/test/fspp/fuse/flush/testutils/FuseFlushTest.h b/test/fspp/fuse/flush/testutils/FuseFlushTest.h new file mode 100644 index 00000000..d90ff8fb --- /dev/null +++ b/test/fspp/fuse/flush/testutils/FuseFlushTest.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_FLUSH_TESTUTILS_FUSEFLUSHTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_FLUSH_TESTUTILS_FUSEFLUSHTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseFlushTest: public FuseTest { +public: + const std::string FILENAME = "/myfile"; + + void OpenAndCloseFile(const std::string &filename); + int OpenFile(const TempTestFS *fs, const std::string &filename); + void CloseFile(int fd); +}; + + +#endif diff --git a/test/fspp/fuse/fstat/FuseFstatErrorTest.cpp b/test/fspp/fuse/fstat/FuseFstatErrorTest.cpp new file mode 100644 index 00000000..f9765832 --- /dev/null +++ b/test/fspp/fuse/fstat/FuseFstatErrorTest.cpp @@ -0,0 +1,39 @@ +#include "testutils/FuseFstatTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Throw; + +using namespace fspp::fuse; + +// Cite from FUSE documentation on the fgetattr function: +// "Currently this is only called after the create() method if that is implemented (see above). +// Later it may be called for invocations of fstat() too." +// So we need to issue a create to get our fstat called. + +class FuseFstatErrorTest: public FuseFstatTest, public WithParamInterface { +public: + int CreateFileAllowErrors(const TempTestFS *fs, const std::string &filename) { + auto real_path = fs->mountDir() / filename; + return ::open(real_path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + } +}; +INSTANTIATE_TEST_CASE_P(FuseFstatErrorTest, FuseFstatErrorTest, Values(EACCES, EBADF, EFAULT, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW)); + +TEST_P(FuseFstatErrorTest, ReturnedErrorCodeIsCorrect) { + ReturnDoesntExistOnLstat(FILENAME); + OnCreateAndOpenReturnFileDescriptor(FILENAME, 0); + + EXPECT_CALL(fsimpl, fstat(Eq(0), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + auto fs = TestFS(); + + int error = CreateFileReturnError(fs.get(), FILENAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/fstat/FuseFstatParameterTest.cpp b/test/fspp/fuse/fstat/FuseFstatParameterTest.cpp new file mode 100644 index 00000000..e0fd043f --- /dev/null +++ b/test/fspp/fuse/fstat/FuseFstatParameterTest.cpp @@ -0,0 +1,37 @@ +#include "testutils/FuseFstatTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Throw; + +using namespace fspp::fuse; + +// Cite from FUSE documentation on the fgetattr function: +// "Currently this is only called after the create() method if that is implemented (see above). +// Later it may be called for invocations of fstat() too." +// So we need to issue a create to get our fstat called. + +class FuseFstatParameterTest: public FuseFstatTest, public WithParamInterface { +public: + void CallFstat(const char *filename) { + auto fs = TestFS(); + CreateFile(fs.get(), filename); + } +}; +INSTANTIATE_TEST_CASE_P(FuseFstatParameterTest, FuseFstatParameterTest, Values(0,1,10,1000,1024*1024*1024)); + + +TEST_P(FuseFstatParameterTest, FileDescriptorIsCorrect) { + ReturnDoesntExistOnLstat(FILENAME); + OnCreateAndOpenReturnFileDescriptor(FILENAME, GetParam()); + + EXPECT_CALL(fsimpl, fstat(Eq(GetParam()), _)).Times(1).WillOnce(ReturnIsFileFstat); + + CallFstat(FILENAME); +} diff --git a/test/fspp/fuse/fstat/README b/test/fspp/fuse/fstat/README new file mode 100644 index 00000000..79d496a2 --- /dev/null +++ b/test/fspp/fuse/fstat/README @@ -0,0 +1,6 @@ +Cite from FUSE documentation on the fgetattr function: + Currently this is only called after the create() method if that is implemented (see above). + Later it may be called for invocations of fstat() too. +So we need to issue a create to get our fstat called. +Since fstat is currently only called after create, we can't call it directly. +So we can't test the returned values. \ No newline at end of file diff --git a/test/fspp/fuse/fstat/testutils/FuseFstatTest.cpp b/test/fspp/fuse/fstat/testutils/FuseFstatTest.cpp new file mode 100644 index 00000000..0b3cfa27 --- /dev/null +++ b/test/fspp/fuse/fstat/testutils/FuseFstatTest.cpp @@ -0,0 +1,34 @@ +#include "FuseFstatTest.h" + +using ::testing::StrEq; +using ::testing::_; +using ::testing::Return; + +int FuseFstatTest::CreateFile(const TempTestFS *fs, const std::string &filename) { + int fd = CreateFileAllowErrors(fs, filename); + EXPECT_GE(fd, 0) << "Opening file failed"; + return fd; +} + +int FuseFstatTest::CreateFileReturnError(const TempTestFS *fs, const std::string &filename) { + int fd = CreateFileAllowErrors(fs, filename); + if (fd >= 0) { + return 0; + } else { + return -fd; + } +} + +int FuseFstatTest::CreateFileAllowErrors(const TempTestFS *fs, const std::string &filename) { + auto real_path = fs->mountDir() / filename; + int fd = ::open(real_path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd >= 0) { + return fd; + } else { + return -errno; + } +} + +void FuseFstatTest::OnCreateAndOpenReturnFileDescriptor(const char *filename, int descriptor) { + EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(filename), _, _, _)).Times(1).WillOnce(Return(descriptor)); +} diff --git a/test/fspp/fuse/fstat/testutils/FuseFstatTest.h b/test/fspp/fuse/fstat/testutils/FuseFstatTest.h new file mode 100644 index 00000000..761a82a1 --- /dev/null +++ b/test/fspp/fuse/fstat/testutils/FuseFstatTest.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_FSTAT_TESTUTILS_FUSEFSTATTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_FSTAT_TESTUTILS_FUSEFSTATTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseFstatTest: public FuseTest { +public: + int CreateFile(const TempTestFS *fs, const std::string &filename); + int CreateFileReturnError(const TempTestFS *fs, const std::string &filename); + void OnCreateAndOpenReturnFileDescriptor(const char *filename, int descriptor); +private: + int CreateFileAllowErrors(const TempTestFS *fs, const std::string &filename); +}; + + +#endif diff --git a/test/fspp/fuse/fsync/FuseFsyncErrorTest.cpp b/test/fspp/fuse/fsync/FuseFsyncErrorTest.cpp new file mode 100644 index 00000000..81a7dbec --- /dev/null +++ b/test/fspp/fuse/fsync/FuseFsyncErrorTest.cpp @@ -0,0 +1,25 @@ +#include "testutils/FuseFsyncTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseFsyncErrorTest: public FuseFsyncTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFsyncErrorTest, FuseFsyncErrorTest, Values(EBADF, EIO, EROFS, EINVAL)); + +TEST_P(FuseFsyncErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, fsync(0)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = FsyncFileReturnError(FILENAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/fsync/FuseFsyncFileDescriptorTest.cpp b/test/fspp/fuse/fsync/FuseFsyncFileDescriptorTest.cpp new file mode 100644 index 00000000..d6413305 --- /dev/null +++ b/test/fspp/fuse/fsync/FuseFsyncFileDescriptorTest.cpp @@ -0,0 +1,26 @@ +#include "testutils/FuseFsyncTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; + +using namespace fspp::fuse; + +class FuseFsyncFileDescriptorTest: public FuseFsyncTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFsyncFileDescriptorTest, FuseFsyncFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024)); + + +TEST_P(FuseFsyncFileDescriptorTest, FileDescriptorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, GetParam()); + EXPECT_CALL(fsimpl, fsync(Eq(GetParam()))) + .Times(1).WillOnce(Return()); + + FsyncFile(FILENAME); +} diff --git a/test/fspp/fuse/fsync/testutils/FuseFsyncTest.cpp b/test/fspp/fuse/fsync/testutils/FuseFsyncTest.cpp new file mode 100644 index 00000000..97de440c --- /dev/null +++ b/test/fspp/fuse/fsync/testutils/FuseFsyncTest.cpp @@ -0,0 +1,25 @@ +#include "FuseFsyncTest.h" + +void FuseFsyncTest::FsyncFile(const char *filename) { + int error = FsyncFileReturnError(filename); + EXPECT_EQ(0, error); +} + +int FuseFsyncTest::FsyncFileReturnError(const char *filename) { + auto fs = TestFS(); + + int fd = OpenFile(fs.get(), filename); + int retval = ::fsync(fd); + if (retval == 0) { + return 0; + } else { + return errno; + } +} + +int FuseFsyncTest::OpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_RDWR); + EXPECT_GE(fd, 0) << "Error opening file"; + return fd; +} diff --git a/test/fspp/fuse/fsync/testutils/FuseFsyncTest.h b/test/fspp/fuse/fsync/testutils/FuseFsyncTest.h new file mode 100644 index 00000000..1ab04c96 --- /dev/null +++ b/test/fspp/fuse/fsync/testutils/FuseFsyncTest.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_FSYNC_TESTUTILS_FUSEFSYNCTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_FSYNC_TESTUTILS_FUSEFSYNCTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseFsyncTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + void FsyncFile(const char *filename); + int FsyncFileReturnError(const char *filename); + +private: + int OpenFile(const TempTestFS *fs, const char *filename); +}; + +#endif diff --git a/test/fspp/fuse/ftruncate/FuseFTruncateErrorTest.cpp b/test/fspp/fuse/ftruncate/FuseFTruncateErrorTest.cpp new file mode 100644 index 00000000..4aeca3e8 --- /dev/null +++ b/test/fspp/fuse/ftruncate/FuseFTruncateErrorTest.cpp @@ -0,0 +1,27 @@ +#include "testutils/FuseFTruncateTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseFTruncateErrorTest: public FuseFTruncateTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFTruncateErrorTest, FuseFTruncateErrorTest, Values(EACCES, EFAULT, EFBIG, EINTR, EINVAL, EIO, EISDIR, ELOOP, ENAMETOOLONG, ENOENT, ENOTDIR, EPERM, EROFS, ETXTBSY, EBADF)); + +TEST_P(FuseFTruncateErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, ftruncate(0, _)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + //Needed to make ::ftruncate system call return successfully + ReturnIsFileOnFstat(0); + + int error = FTruncateFileReturnError(FILENAME, 0); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/ftruncate/FuseFTruncateFileDescriptorTest.cpp b/test/fspp/fuse/ftruncate/FuseFTruncateFileDescriptorTest.cpp new file mode 100644 index 00000000..2374bc81 --- /dev/null +++ b/test/fspp/fuse/ftruncate/FuseFTruncateFileDescriptorTest.cpp @@ -0,0 +1,29 @@ +#include "testutils/FuseFTruncateTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Throw; + +using namespace fspp::fuse; + +class FuseFTruncateFileDescriptorTest: public FuseFTruncateTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFTruncateFileDescriptorTest, FuseFTruncateFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024)); + + +TEST_P(FuseFTruncateFileDescriptorTest, FileDescriptorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, GetParam()); + EXPECT_CALL(fsimpl, ftruncate(Eq(GetParam()), _)) + .Times(1).WillOnce(Return()); + //Needed to make ::ftruncate system call return successfully + ReturnIsFileOnFstat(GetParam()); + + FTruncateFile(FILENAME, 0); +} diff --git a/test/fspp/fuse/ftruncate/FuseFTruncateSizeTest.cpp b/test/fspp/fuse/ftruncate/FuseFTruncateSizeTest.cpp new file mode 100644 index 00000000..82dc5561 --- /dev/null +++ b/test/fspp/fuse/ftruncate/FuseFTruncateSizeTest.cpp @@ -0,0 +1,24 @@ +#include "testutils/FuseFTruncateTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Eq; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseFTruncateSizeTest: public FuseFTruncateTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseFTruncateSizeTest, FuseFTruncateSizeTest, Values(0, 1, 10, 1024, 1024*1024*1024)); + + +TEST_P(FuseFTruncateSizeTest, FTruncateFile) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, ftruncate(Eq(0), GetParam())) + .Times(1).WillOnce(Return()); + //Needed to make ::ftruncate system call return successfully + ReturnIsFileOnFstat(0); + + FTruncateFile(FILENAME, GetParam()); +} diff --git a/test/fspp/fuse/ftruncate/testutils/FuseFTruncateTest.cpp b/test/fspp/fuse/ftruncate/testutils/FuseFTruncateTest.cpp new file mode 100644 index 00000000..7adcfa2b --- /dev/null +++ b/test/fspp/fuse/ftruncate/testutils/FuseFTruncateTest.cpp @@ -0,0 +1,25 @@ +#include "FuseFTruncateTest.h" + +void FuseFTruncateTest::FTruncateFile(const char *filename, off_t size) { + int error = FTruncateFileReturnError(filename, size); + EXPECT_EQ(0, error); +} + +int FuseFTruncateTest::FTruncateFileReturnError(const char *filename, off_t size) { + auto fs = TestFS(); + + int fd = OpenFile(fs.get(), filename); + int retval = ::ftruncate(fd, size); + if (0 == retval) { + return 0; + } else { + return errno; + } +} + +int FuseFTruncateTest::OpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_RDWR); + EXPECT_GE(fd, 0) << "Error opening file"; + return fd; +} diff --git a/test/fspp/fuse/ftruncate/testutils/FuseFTruncateTest.h b/test/fspp/fuse/ftruncate/testutils/FuseFTruncateTest.h new file mode 100644 index 00000000..d77dff07 --- /dev/null +++ b/test/fspp/fuse/ftruncate/testutils/FuseFTruncateTest.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_FTRUNCATE_TESTUTILS_FUSEFTRUNCATETEST_H_ +#define MESSMER_FSPP_TEST_FUSE_FTRUNCATE_TESTUTILS_FUSEFTRUNCATETEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseFTruncateTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + void FTruncateFile(const char *filename, off_t size); + int FTruncateFileReturnError(const char *filename, off_t size); + +private: + int OpenFile(const TempTestFS *fs, const char *filename); +}; + +#endif diff --git a/test/fspp/fuse/lstat/FuseLstatErrorTest.cpp b/test/fspp/fuse/lstat/FuseLstatErrorTest.cpp new file mode 100644 index 00000000..d3cdca96 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatErrorTest.cpp @@ -0,0 +1,29 @@ +#include "testutils/FuseLstatTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::StrEq; +using ::testing::_; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using fspp::fuse::FuseErrnoException; + +class FuseLstatErrorTest: public FuseLstatTest, public WithParamInterface { +public: +}; +INSTANTIATE_TEST_CASE_P(LstatErrorCodes, FuseLstatErrorTest, Values(EACCES, EBADF, EFAULT, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW, EINVAL, ENOTDIR)); + +TEST_F(FuseLstatErrorTest, ReturnNoError) { + EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).Times(1).WillOnce(ReturnIsFile); + errno = 0; + int error = LstatPathReturnError(FILENAME); + EXPECT_EQ(0, error); +} + +TEST_P(FuseLstatErrorTest, ReturnError) { + EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + int error = LstatPathReturnError(FILENAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/lstat/FuseLstatPathParameterTest.cpp b/test/fspp/fuse/lstat/FuseLstatPathParameterTest.cpp new file mode 100644 index 00000000..deee7967 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatPathParameterTest.cpp @@ -0,0 +1,36 @@ +#include "testutils/FuseLstatTest.h" + +using ::testing::_; +using ::testing::StrEq; + +class FuseLstatPathParameterTest: public FuseLstatTest { +}; + +TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectRoot) { + EXPECT_CALL(fsimpl, lstat(StrEq("/"), _)).Times(1).WillOnce(ReturnIsDir); + LstatPath("/"); +} + +TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectSimpleFile) { + EXPECT_CALL(fsimpl, lstat(StrEq("/myfile"), _)).Times(1).WillOnce(ReturnIsFile); + LstatPath("/myfile"); +} + +TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectSimpleDir) { + EXPECT_CALL(fsimpl, lstat(StrEq("/mydir"), _)).Times(1).WillOnce(ReturnIsDir); + LstatPath("/mydir/"); +} + +TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectNestedFile) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + EXPECT_CALL(fsimpl, lstat(StrEq("/mydir/mydir2/myfile"), _)).Times(1).WillOnce(ReturnIsFile); + LstatPath("/mydir/mydir2/myfile"); +} + +TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectNestedDir) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + EXPECT_CALL(fsimpl, lstat(StrEq("/mydir/mydir2/mydir3"), _)).Times(1).WillOnce(ReturnIsDir); + LstatPath("/mydir/mydir2/mydir3/"); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnAtimeTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnAtimeTest.cpp new file mode 100644 index 00000000..30381332 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnAtimeTest.cpp @@ -0,0 +1,27 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnATimeTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, time_t value) override { + stat->st_atime = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnATimeTest, FuseLstatReturnATimeTest, Values( + 0, + 100, + 1416496809, // current timestamp as of writing the test + 32503680000 // needs a 64bit timestamp +)); + +TEST_P(FuseLstatReturnATimeTest, ReturnedFileAtimeIsCorrect) { + struct ::stat result = CallFileLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_atime); +} + +TEST_P(FuseLstatReturnATimeTest, ReturnedDirAtimeIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_atime); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnCtimeTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnCtimeTest.cpp new file mode 100644 index 00000000..0da49cc5 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnCtimeTest.cpp @@ -0,0 +1,27 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnCtimeTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, time_t value) override { + stat->st_ctime = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnCtimeTest, FuseLstatReturnCtimeTest, Values( + 0, + 100, + 1416496809, // current timestamp as of writing the test + 32503680000 // needs a 64bit timestamp +)); + +TEST_P(FuseLstatReturnCtimeTest, ReturnedFileCtimeIsCorrect) { + struct ::stat result = CallFileLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_ctime); +} + +TEST_P(FuseLstatReturnCtimeTest, ReturnedDirCtimeIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_ctime); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnGidTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnGidTest.cpp new file mode 100644 index 00000000..7e0eda96 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnGidTest.cpp @@ -0,0 +1,25 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnGidTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, gid_t value) override { + stat->st_gid = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnGidTest, FuseLstatReturnGidTest, Values( + 0, + 10 +)); + +TEST_P(FuseLstatReturnGidTest, ReturnedFileGidIsCorrect) { + struct ::stat result = CallFileLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_gid); +} + +TEST_P(FuseLstatReturnGidTest, ReturnedDirGidIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_gid); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnModeTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnModeTest.cpp new file mode 100644 index 00000000..b87951af --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnModeTest.cpp @@ -0,0 +1,24 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnModeTest: public FuseLstatTest, public WithParamInterface { +public: + struct stat CallLstatWithValue(mode_t mode) { + return CallLstatWithImpl([mode] (struct stat *stat) { + stat->st_mode = mode; + }); + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnModeTest, FuseLstatReturnModeTest, Values( + S_IFREG, + S_IFDIR, + S_IFREG | S_IRUSR | S_IWGRP | S_IXOTH, // a file with some access bits set + S_IFDIR | S_IWUSR | S_IXGRP | S_IROTH // a dir with some access bits set +)); + +TEST_P(FuseLstatReturnModeTest, ReturnedModeIsCorrect) { + struct ::stat result = CallLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_mode); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnMtimeTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnMtimeTest.cpp new file mode 100644 index 00000000..b40b1ce9 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnMtimeTest.cpp @@ -0,0 +1,27 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnMtimeTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, time_t value) override { + stat->st_mtime = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnMtimeTest, FuseLstatReturnMtimeTest, Values( + 0, + 100, + 1416496809, // current timestamp as of writing the test + 32503680000 // needs a 64bit timestamp +)); + +TEST_P(FuseLstatReturnMtimeTest, ReturnedFileMtimeIsCorrect) { + struct ::stat result = CallFileLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_mtime); +} + +TEST_P(FuseLstatReturnMtimeTest, ReturnedDirMtimeIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_mtime); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnNlinkTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnNlinkTest.cpp new file mode 100644 index 00000000..45337158 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnNlinkTest.cpp @@ -0,0 +1,28 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnNlinkTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, nlink_t value) override { + stat->st_nlink = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnNlinkTest, FuseLstatReturnNlinkTest, Values( + 1, + 2, + 5, + 100 +)); + +TEST_P(FuseLstatReturnNlinkTest, ReturnedFileNlinkIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_nlink); +} + +TEST_P(FuseLstatReturnNlinkTest, ReturnedDirNlinkIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_nlink); +} + diff --git a/test/fspp/fuse/lstat/FuseLstatReturnSizeTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnSizeTest.cpp new file mode 100644 index 00000000..5b797244 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnSizeTest.cpp @@ -0,0 +1,27 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnSizeTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, off_t value) override { + stat->st_size = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnSizeTest, FuseLstatReturnSizeTest, Values( + 0, + 1, + 4096, + 1024*1024*1024 +)); + +TEST_P(FuseLstatReturnSizeTest, ReturnedFileSizeIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_size); +} + +TEST_P(FuseLstatReturnSizeTest, ReturnedDirSizeIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_size); +} diff --git a/test/fspp/fuse/lstat/FuseLstatReturnUidTest.cpp b/test/fspp/fuse/lstat/FuseLstatReturnUidTest.cpp new file mode 100644 index 00000000..aaa3ca54 --- /dev/null +++ b/test/fspp/fuse/lstat/FuseLstatReturnUidTest.cpp @@ -0,0 +1,25 @@ +#include "testutils/FuseLstatReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseLstatReturnUidTest: public FuseLstatReturnTest, public WithParamInterface { +private: + void set(struct stat *stat, uid_t value) override { + stat->st_uid = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseLstatReturnUidTest, FuseLstatReturnUidTest, Values( + 0, + 10 +)); + +TEST_P(FuseLstatReturnUidTest, ReturnedFileUidIsCorrect) { + struct ::stat result = CallFileLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_uid); +} + +TEST_P(FuseLstatReturnUidTest, ReturnedDirUidIsCorrect) { + struct ::stat result = CallDirLstatWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.st_uid); +} diff --git a/test/fspp/fuse/lstat/testutils/FuseLstatReturnTest.h b/test/fspp/fuse/lstat/testutils/FuseLstatReturnTest.h new file mode 100644 index 00000000..6b271022 --- /dev/null +++ b/test/fspp/fuse/lstat/testutils/FuseLstatReturnTest.h @@ -0,0 +1,43 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATRETURNTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATRETURNTEST_H_ + +#include "FuseLstatTest.h" + +// This class offers test helpers for testing (struct stat) entries. We return them from +// our mock filesystem, set up a temporary filesystem, call lstat syscall on it, and +// then check the return value. +template +class FuseLstatReturnTest: public FuseLstatTest { +public: + // Set the specified (struct stat) entry to the given value, and test whether it is correctly returned from the syscall. + // The CallFile[...] version tests it on a file node of the filesystem, the CallDir[...] version on a dir node. + struct stat CallFileLstatWithValue(Property value); + struct stat CallDirLstatWithValue(Property value); + +private: + std::function SetPropertyImpl(Property value); + + // Override this function to specify, how to set the specified (struct stat) entry on the passed (struct stat *) object. + virtual void set(struct stat *stat, Property value) = 0; +}; + +template +struct stat FuseLstatReturnTest::CallFileLstatWithValue(Property value) { + return CallFileLstatWithImpl(SetPropertyImpl(value)); +} + +template +struct stat FuseLstatReturnTest::CallDirLstatWithValue(Property value) { + return CallDirLstatWithImpl(SetPropertyImpl(value)); +} + +template +std::function FuseLstatReturnTest::SetPropertyImpl(Property value) { + return [this, value] (struct stat *stat) { + set(stat, value); + }; +} + + +#endif diff --git a/test/fspp/fuse/lstat/testutils/FuseLstatTest.cpp b/test/fspp/fuse/lstat/testutils/FuseLstatTest.cpp new file mode 100644 index 00000000..ebfda067 --- /dev/null +++ b/test/fspp/fuse/lstat/testutils/FuseLstatTest.cpp @@ -0,0 +1,59 @@ +#include "FuseLstatTest.h" + +using std::function; +using ::testing::StrEq; +using ::testing::_; +using ::testing::Invoke; + +void FuseLstatTest::LstatPath(const std::string &path) { + struct stat dummy; + LstatPath(path, &dummy); +} + +int FuseLstatTest::LstatPathReturnError(const std::string &path) { + struct stat dummy; + return LstatPathReturnError(path, &dummy); +} + +void FuseLstatTest::LstatPath(const std::string &path, struct stat *result) { + int error = LstatPathReturnError(path, result); + EXPECT_EQ(0, error) << "lstat syscall failed. errno: " << error; +} + +int FuseLstatTest::LstatPathReturnError(const std::string &path, struct stat *result) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / path; + int retval = ::lstat(realpath.c_str(), result); + if (retval == 0) { + return 0; + } else { + return errno; + } +} + +struct stat FuseLstatTest::CallFileLstatWithImpl(function implementation) { + return CallLstatWithModeAndImpl(S_IFREG, implementation); +} + +struct stat FuseLstatTest::CallDirLstatWithImpl(function implementation) { + return CallLstatWithModeAndImpl(S_IFDIR, implementation); +} + +struct stat FuseLstatTest::CallLstatWithImpl(function implementation) { + EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).WillRepeatedly(Invoke([implementation](const char*, struct ::stat *stat) { + implementation(stat); + })); + + struct stat result; + LstatPath(FILENAME, &result); + + return result; +} + +struct stat FuseLstatTest::CallLstatWithModeAndImpl(mode_t mode, function implementation) { + return CallLstatWithImpl([mode, implementation] (struct stat *stat) { + stat->st_mode = mode; + implementation(stat); + }); +} diff --git a/test/fspp/fuse/lstat/testutils/FuseLstatTest.h b/test/fspp/fuse/lstat/testutils/FuseLstatTest.h new file mode 100644 index 00000000..b31d3f14 --- /dev/null +++ b/test/fspp/fuse/lstat/testutils/FuseLstatTest.h @@ -0,0 +1,43 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATTEST_H_ + +#include +#include +#include + +#include "../../../testutils/FuseTest.h" + +// This class offers some utility functions for testing lstat(). +class FuseLstatTest: public FuseTest { +protected: + const char *FILENAME = "/myfile"; + + // Set up a temporary filesystem (using the fsimpl mock in FuseTest as filesystem implementation) + // and call the lstat syscall on the given (filesystem-relative) path. + void LstatPath(const std::string &path); + // Same as LstatPath above, but also return the result of the lstat syscall. + void LstatPath(const std::string &path, struct stat *result); + + // These two functions are the same as LstatPath above, but they don't fail the test when the lstat syscall + // crashes. Instead, they return the value of errno after calling ::lstat. + int LstatPathReturnError(const std::string &path); + int LstatPathReturnError(const std::string &path, struct stat *result); + + // You can specify an implementation, which can modify the (struct stat *) result, + // our fuse mock filesystem implementation will then return this to fuse on an lstat call. + // This functions then set up a temporary filesystem with this mock, call lstat on a filesystem node + // and return the (struct stat) returned from an lstat syscall to this filesystem. + struct stat CallLstatWithImpl(std::function implementation); + + // These two functions are like CallLstatWithImpl, but they also modify the (struct stat).st_mode + // field, so the node accessed is specified to be a file/directory. + struct stat CallFileLstatWithImpl(std::function implementation); + struct stat CallDirLstatWithImpl(std::function implementation); + +private: + + struct stat CallLstatWithModeAndImpl(mode_t mode, std::function implementation); +}; + +#endif diff --git a/test/fspp/fuse/mkdir/FuseMkdirDirnameTest.cpp b/test/fspp/fuse/mkdir/FuseMkdirDirnameTest.cpp new file mode 100644 index 00000000..8dd3887b --- /dev/null +++ b/test/fspp/fuse/mkdir/FuseMkdirDirnameTest.cpp @@ -0,0 +1,43 @@ +#include "testutils/FuseMkdirTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +class FuseMkdirDirnameTest: public FuseMkdirTest { +}; + +TEST_F(FuseMkdirDirnameTest, Mkdir) { + ReturnDoesntExistOnLstat("/mydir"); + EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir"), _, _, _)) + // After mkdir was called, lstat should return that it is a dir. + // This is needed to make the ::mkdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnIsDirOnLstat()); + + Mkdir("/mydir", 0); +} + +TEST_F(FuseMkdirDirnameTest, MkdirNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnDoesntExistOnLstat("/mydir/mysubdir"); + EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir/mysubdir"), _, _, _)) + // After mkdir was called, lstat should return that it is a dir. + // This is needed to make the ::mkdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnIsDirOnLstat()); + + Mkdir("/mydir/mysubdir", 0); +} + +TEST_F(FuseMkdirDirnameTest, MkdirNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnDoesntExistOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir/mydir2/mydir3"), _, _, _)) + // After mkdir was called, lstat should return that it is a dir. + // This is needed to make the ::mkdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnIsDirOnLstat()); + + Mkdir("/mydir/mydir2/mydir3", 0); +} diff --git a/test/fspp/fuse/mkdir/FuseMkdirErrorTest.cpp b/test/fspp/fuse/mkdir/FuseMkdirErrorTest.cpp new file mode 100644 index 00000000..a52f211f --- /dev/null +++ b/test/fspp/fuse/mkdir/FuseMkdirErrorTest.cpp @@ -0,0 +1,33 @@ +#include "testutils/FuseMkdirTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseMkdirErrorTest: public FuseMkdirTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseMkdirErrorTest, FuseMkdirErrorTest, Values(EACCES, EDQUOT, EEXIST, EFAULT, ELOOP, EMLINK, ENAMETOOLONG, ENOENT, ENOMEM, ENOSPC, ENOTDIR, EPERM, EROFS, EBADF)); + +TEST_F(FuseMkdirErrorTest, NoError) { + ReturnDoesntExistOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), _, _, _)) + .Times(1).WillOnce(FromNowOnReturnIsDirOnLstat()); + + int error = MkdirReturnError(DIRNAME, 0); + EXPECT_EQ(0, error); +} + +TEST_P(FuseMkdirErrorTest, ReturnedErrorIsCorrect) { + ReturnDoesntExistOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), _, _, _)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = MkdirReturnError(DIRNAME, 0); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/mkdir/FuseMkdirModeTest.cpp b/test/fspp/fuse/mkdir/FuseMkdirModeTest.cpp new file mode 100644 index 00000000..ec4fa36f --- /dev/null +++ b/test/fspp/fuse/mkdir/FuseMkdirModeTest.cpp @@ -0,0 +1,20 @@ +#include "testutils/FuseMkdirTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseMkdirModeTest: public FuseMkdirTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseMkdirModeTest, FuseMkdirModeTest, Values(0, S_IRUSR, S_IRGRP, S_IXOTH, S_IRUSR|S_IRGRP|S_IROTH|S_IRGRP)); + + +TEST_P(FuseMkdirModeTest, Mkdir) { + ReturnDoesntExistOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), GetParam(), _, _)) + .Times(1).WillOnce(FromNowOnReturnIsDirOnLstat()); + + Mkdir(DIRNAME, GetParam()); +} diff --git a/test/fspp/fuse/mkdir/testutils/FuseMkdirTest.cpp b/test/fspp/fuse/mkdir/testutils/FuseMkdirTest.cpp new file mode 100644 index 00000000..05142ec6 --- /dev/null +++ b/test/fspp/fuse/mkdir/testutils/FuseMkdirTest.cpp @@ -0,0 +1,27 @@ +#include "FuseMkdirTest.h" + +using ::testing::Action; +using ::testing::Invoke; + +void FuseMkdirTest::Mkdir(const char *dirname, mode_t mode) { + int error = MkdirReturnError(dirname, mode); + EXPECT_EQ(0, error); +} + +int FuseMkdirTest::MkdirReturnError(const char *dirname, mode_t mode) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / dirname; + int retval = ::mkdir(realpath.c_str(), mode); + if (retval == 0) { + return 0; + } else { + return errno; + } +} + +Action FuseMkdirTest::FromNowOnReturnIsDirOnLstat() { + return Invoke([this](const char *dirname, mode_t, uid_t, gid_t) { + ReturnIsDirOnLstat(dirname); + }); +} diff --git a/test/fspp/fuse/mkdir/testutils/FuseMkdirTest.h b/test/fspp/fuse/mkdir/testutils/FuseMkdirTest.h new file mode 100644 index 00000000..3cbeb9ea --- /dev/null +++ b/test/fspp/fuse/mkdir/testutils/FuseMkdirTest.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_MKDIR_TESTUTILS_FUSEMKDIRTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_MKDIR_TESTUTILS_FUSEMKDIRTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseMkdirTest: public FuseTest { +public: + const char *DIRNAME = "/mydir"; + + void Mkdir(const char *dirname, mode_t mode); + int MkdirReturnError(const char *dirname, mode_t mode); + + ::testing::Action FromNowOnReturnIsDirOnLstat(); +}; + +#endif diff --git a/test/fspp/fuse/openFile/FuseOpenErrorTest.cpp b/test/fspp/fuse/openFile/FuseOpenErrorTest.cpp new file mode 100644 index 00000000..4ab7653a --- /dev/null +++ b/test/fspp/fuse/openFile/FuseOpenErrorTest.cpp @@ -0,0 +1,31 @@ +#include "testutils/FuseOpenTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; +using ::testing::Throw; +using ::testing::StrEq; +using ::testing::_; + +using namespace fspp::fuse; + +class FuseOpenErrorTest: public FuseOpenTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseOpenErrorTest, FuseOpenErrorTest, Values(EACCES, EDQUOT, EEXIST, EFAULT, EFBIG, EINTR, EOVERFLOW, EINVAL, EISDIR, ELOOP, EMFILE, ENAMETOOLONG, ENFILE, ENODEV, ENOENT, ENOMEM, ENOSPC, ENOTDIR, ENXIO, EOPNOTSUPP, EPERM, EROFS, ETXTBSY, EWOULDBLOCK, EBADF, ENOTDIR)); + +TEST_F(FuseOpenErrorTest, ReturnNoError) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Return(1)); + errno = 0; + int error = OpenFileReturnError(FILENAME, O_RDONLY); + EXPECT_EQ(0, error); +} + +TEST_P(FuseOpenErrorTest, ReturnError) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + int error = OpenFileReturnError(FILENAME, O_RDONLY); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/openFile/FuseOpenFileDescriptorTest.cpp b/test/fspp/fuse/openFile/FuseOpenFileDescriptorTest.cpp new file mode 100644 index 00000000..18212aba --- /dev/null +++ b/test/fspp/fuse/openFile/FuseOpenFileDescriptorTest.cpp @@ -0,0 +1,41 @@ +#include "testutils/FuseOpenTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; + +class FuseOpenFileDescriptorTest: public FuseOpenTest, public WithParamInterface { +public: + void OpenAndReadFile(const char *filename) { + auto fs = TestFS(); + + int fd = OpenFile(fs.get(), filename); + ReadFile(fd); + } + +private: + int OpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_RDONLY); + EXPECT_GE(fd, 0) << "Opening file failed"; + return fd; + } + void ReadFile(int fd) { + uint8_t buf; + int retval = ::read(fd, &buf, 1); + EXPECT_EQ(1, retval) << "Reading file failed"; + } +}; +INSTANTIATE_TEST_CASE_P(FuseOpenFileDescriptorTest, FuseOpenFileDescriptorTest, Values(0, 2, 5, 1000, 1024*1024*1024)); + +TEST_P(FuseOpenFileDescriptorTest, TestReturnedFileDescriptor) { + ReturnIsFileOnLstatWithSize(FILENAME, 1); + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)) + .Times(1).WillOnce(Return(GetParam())); + EXPECT_CALL(fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(1)); + + OpenAndReadFile(FILENAME); +} + diff --git a/test/fspp/fuse/openFile/FuseOpenFilenameTest.cpp b/test/fspp/fuse/openFile/FuseOpenFilenameTest.cpp new file mode 100644 index 00000000..98d7994d --- /dev/null +++ b/test/fspp/fuse/openFile/FuseOpenFilenameTest.cpp @@ -0,0 +1,36 @@ +#include "testutils/FuseOpenTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseOpenFilenameTest: public FuseOpenTest { +public: +}; + +TEST_F(FuseOpenFilenameTest, OpenFile) { + ReturnIsFileOnLstat("/myfile"); + EXPECT_CALL(fsimpl, openFile(StrEq("/myfile"), _)) + .Times(1).WillOnce(Return(0)); + + OpenFile("/myfile", O_RDONLY); +} + +TEST_F(FuseOpenFilenameTest, OpenFileNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/myfile"); + EXPECT_CALL(fsimpl, openFile(StrEq("/mydir/myfile"), _)) + .Times(1).WillOnce(Return(0)); + + OpenFile("/mydir/myfile", O_RDONLY); +} + +TEST_F(FuseOpenFilenameTest, OpenFileNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, openFile(StrEq("/mydir/mydir2/myfile"), _)) + .Times(1).WillOnce(Return(0)); + + OpenFile("/mydir/mydir2/myfile", O_RDONLY); +} diff --git a/test/fspp/fuse/openFile/FuseOpenFlagsTest.cpp b/test/fspp/fuse/openFile/FuseOpenFlagsTest.cpp new file mode 100644 index 00000000..2009cccd --- /dev/null +++ b/test/fspp/fuse/openFile/FuseOpenFlagsTest.cpp @@ -0,0 +1,19 @@ +#include "testutils/FuseOpenTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Return; + +class FuseOpenFlagsTest: public FuseOpenTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseOpenFlagsTest, FuseOpenFlagsTest, Values(O_RDWR, O_RDONLY, O_WRONLY)); + +TEST_P(FuseOpenFlagsTest, testFlags) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), OpenFlagsEq(GetParam()))) + .Times(1).WillOnce(Return(0)); + + OpenFile(FILENAME, GetParam()); +} diff --git a/test/fspp/fuse/openFile/testutils/FuseOpenTest.cpp b/test/fspp/fuse/openFile/testutils/FuseOpenTest.cpp new file mode 100644 index 00000000..6a307b7f --- /dev/null +++ b/test/fspp/fuse/openFile/testutils/FuseOpenTest.cpp @@ -0,0 +1,28 @@ +#include "FuseOpenTest.h" + +int FuseOpenTest::OpenFile(const char *filename, int flags) { + int fd = OpenFileAllowError(filename, flags); + EXPECT_GE(fd, 0); + return fd; +} + +int FuseOpenTest::OpenFileReturnError(const char *filename, int flags) { + int fd = OpenFileAllowError(filename, flags); + if (fd >= 0) { + return 0; + } else { + return -fd; + } +} + +int FuseOpenTest::OpenFileAllowError(const char *filename, int flags) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), flags); + if (fd >= 0) { + return fd; + } else { + return -errno; + } +} diff --git a/test/fspp/fuse/openFile/testutils/FuseOpenTest.h b/test/fspp/fuse/openFile/testutils/FuseOpenTest.h new file mode 100644 index 00000000..edb07ca0 --- /dev/null +++ b/test/fspp/fuse/openFile/testutils/FuseOpenTest.h @@ -0,0 +1,21 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_OPENFILE_TESTUTILS_FUSEOPENTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_OPENFILE_TESTUTILS_FUSEOPENTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseOpenTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + int OpenFile(const char *FILENAME, int flags); + int OpenFileReturnError(const char *FILENAME, int flags); +private: + int OpenFileAllowError(const char *FILENAME, int flags); +}; + +MATCHER_P(OpenFlagsEq, expectedFlags, "") { + return expectedFlags == (O_ACCMODE & arg); +} + +#endif diff --git a/test/fspp/fuse/read/FuseReadErrorTest.cpp b/test/fspp/fuse/read/FuseReadErrorTest.cpp new file mode 100644 index 00000000..ab50fb76 --- /dev/null +++ b/test/fspp/fuse/read/FuseReadErrorTest.cpp @@ -0,0 +1,61 @@ +#include "testutils/FuseReadTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Throw; + +using namespace fspp::fuse; + +class FuseReadErrorTest: public FuseReadTest, public WithParamInterface { +public: + size_t FILESIZE = 64*1024*1024; + size_t READCOUNT = 32*1024*1024; + + void SetUp() override { + //Make the file size big enough that fuse should issue at least two reads + ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE); + OnOpenReturnFileDescriptor(FILENAME, 0); + } +}; +INSTANTIATE_TEST_CASE_P(FuseReadErrorTest, FuseReadErrorTest, Values(EAGAIN, EBADF, EFAULT, EINTR, EINVAL, EIO, EISDIR, EOVERFLOW, ESPIPE, ENXIO)); + + +TEST_P(FuseReadErrorTest, ReturnErrorOnFirstReadCall) { + EXPECT_CALL(fsimpl, read(0, _, _, _)) + .WillRepeatedly(Throw(FuseErrnoException(GetParam()))); + + char *buf = new char[READCOUNT]; + auto retval = ReadFileReturnError(FILENAME, buf, READCOUNT, 0); + EXPECT_EQ(GetParam(), retval.error); + delete[] buf; +} + +TEST_P(FuseReadErrorTest, ReturnErrorOnSecondReadCall) { + // The first read request is from the beginning of the file and works, but the later ones fail. + // We store the number of bytes the first call could successfully read and check later that our + // read syscall returns exactly this number of bytes + size_t successfullyReadBytes = -1; + EXPECT_CALL(fsimpl, read(0, _, _, Eq(0))) + .Times(1) + .WillOnce(Invoke([&successfullyReadBytes](int, void*, size_t count, off_t) { + // Store the number of successfully read bytes + successfullyReadBytes = count; + return count; + })); + EXPECT_CALL(fsimpl, read(0, _, _, Ne(0))) + .WillRepeatedly(Throw(FuseErrnoException(GetParam()))); + + char *buf = new char[READCOUNT]; + auto retval = ReadFileReturnError(FILENAME, buf, READCOUNT, 0); + EXPECT_EQ(0, retval.error); + EXPECT_EQ(successfullyReadBytes, retval.read_bytes); // Check that we're getting the number of successfully read bytes (the first read call) returned + delete[] buf; +} diff --git a/test/fspp/fuse/read/FuseReadFileDescriptorTest.cpp b/test/fspp/fuse/read/FuseReadFileDescriptorTest.cpp new file mode 100644 index 00000000..3170af4c --- /dev/null +++ b/test/fspp/fuse/read/FuseReadFileDescriptorTest.cpp @@ -0,0 +1,29 @@ +#include "testutils/FuseReadTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Throw; + +using namespace fspp::fuse; + +class FuseReadFileDescriptorTest: public FuseReadTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseReadFileDescriptorTest, FuseReadFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024)); + + +TEST_P(FuseReadFileDescriptorTest, FileDescriptorIsCorrect) { + ReturnIsFileOnLstatWithSize(FILENAME, 1); + OnOpenReturnFileDescriptor(FILENAME, GetParam()); + EXPECT_CALL(fsimpl, read(Eq(GetParam()), _, _, _)) + .Times(1).WillOnce(ReturnSuccessfulRead); + + char buf[1]; + ReadFile(FILENAME, buf, 1, 0); +} diff --git a/test/fspp/fuse/read/FuseReadOverflowTest.cpp b/test/fspp/fuse/read/FuseReadOverflowTest.cpp new file mode 100644 index 00000000..78175d65 --- /dev/null +++ b/test/fspp/fuse/read/FuseReadOverflowTest.cpp @@ -0,0 +1,40 @@ +#include "testutils/FuseReadTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +using std::min; + +using namespace fspp::fuse; + +class FuseReadOverflowTest: public FuseReadTest { +public: + const size_t FILESIZE = 1000; + const size_t READSIZE = 2000; + const size_t OFFSET = 500; + + void SetUp() override { + ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, read(0, _, _, _)).WillRepeatedly(ReturnSuccessfulReadRegardingSize(FILESIZE)); + } +}; + + +TEST_F(FuseReadOverflowTest, ReadMoreThanFileSizeFromBeginning) { + char buf[READSIZE]; + auto retval = ReadFileReturnError(FILENAME, buf, READSIZE, 0); + EXPECT_EQ(FILESIZE, retval.read_bytes); +} + +TEST_F(FuseReadOverflowTest, ReadMoreThanFileSizeFromMiddle) { + char buf[READSIZE]; + auto retval = ReadFileReturnError(FILENAME, buf, READSIZE, OFFSET); + EXPECT_EQ(FILESIZE-OFFSET, retval.read_bytes); +} diff --git a/test/fspp/fuse/read/FuseReadReturnedDataTest.cpp b/test/fspp/fuse/read/FuseReadReturnedDataTest.cpp new file mode 100644 index 00000000..0c0e503b --- /dev/null +++ b/test/fspp/fuse/read/FuseReadReturnedDataTest.cpp @@ -0,0 +1,75 @@ +#include +#include +#include "../../testutils/InMemoryFile.h" +#include "testutils/FuseReadTest.h" +#include + +#include "fspp/fuse/FuseErrnoException.h" + +#include +#include + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +using std::tuple; +using std::get; +using std::min; +using cpputils::Data; +using cpputils::DataFixture; + +using namespace fspp::fuse; + +// We can't test the count or size parameter directly, because fuse doesn't pass them 1:1. +// It usually asks to read bigger blocks (probably does some caching). +// But we can test that the data returned from the ::read syscall is the correct data region. + +struct TestData { + TestData(): count(0), offset(0), additional_bytes_at_end_of_file(0) {} + TestData(const tuple &data): count(get<0>(data)), offset(get<1>(data)), additional_bytes_at_end_of_file(get<2>(data)) {} + size_t count; + off_t offset; + //How many more bytes does the file have after the read block? + size_t additional_bytes_at_end_of_file; + size_t fileSize() { + return count + offset + additional_bytes_at_end_of_file; + } +}; + +// The testcase creates random data in memory, offers a mock read() implementation to read from this +// memory region and check methods to check for data equality of a region. +class FuseReadReturnedDataTest: public FuseReadTest, public WithParamInterface> { +public: + std::unique_ptr testFile; + TestData testData; + + FuseReadReturnedDataTest() + : testFile(nullptr), + testData(GetParam()) { + testFile = std::make_unique(DataFixture::generate(testData.fileSize())); + ReturnIsFileOnLstatWithSize(FILENAME, testData.fileSize()); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, read(0, _, _, _)) + .WillRepeatedly(ReadFromFile); + } + + // This read() mock implementation reads from the stored virtual file (testFile). + Action ReadFromFile = Invoke([this](int, void *buf, size_t count, off_t offset) { + return testFile->read(buf, count, offset); + }); +}; +INSTANTIATE_TEST_CASE_P(FuseReadReturnedDataTest, FuseReadReturnedDataTest, Combine(Values(0,1,10,1000,1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024))); + + +TEST_P(FuseReadReturnedDataTest, ReturnedDataRangeIsCorrect) { + Data buf(testData.count); + ReadFile(FILENAME, buf.data(), testData.count, testData.offset); + EXPECT_TRUE(testFile->fileContentEquals(buf, testData.offset)); +} diff --git a/test/fspp/fuse/read/testutils/FuseReadTest.cpp b/test/fspp/fuse/read/testutils/FuseReadTest.cpp new file mode 100644 index 00000000..20345393 --- /dev/null +++ b/test/fspp/fuse/read/testutils/FuseReadTest.cpp @@ -0,0 +1,26 @@ +#include "FuseReadTest.h" + +void FuseReadTest::ReadFile(const char *filename, void *buf, size_t count, off_t offset) { + auto retval = ReadFileReturnError(filename, buf, count, offset); + EXPECT_EQ(0, retval.error); + EXPECT_EQ(count, retval.read_bytes); +} + +FuseReadTest::ReadError FuseReadTest::ReadFileReturnError(const char *filename, void *buf, size_t count, off_t offset) { + auto fs = TestFS(); + + int fd = OpenFile(fs.get(), filename); + + ReadError result; + errno = 0; + result.read_bytes = ::pread(fd, buf, count, offset); + result.error = errno; + return result; +} + +int FuseReadTest::OpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_RDONLY); + EXPECT_GE(fd, 0) << "Error opening file"; + return fd; +} diff --git a/test/fspp/fuse/read/testutils/FuseReadTest.h b/test/fspp/fuse/read/testutils/FuseReadTest.h new file mode 100644 index 00000000..f74bb474 --- /dev/null +++ b/test/fspp/fuse/read/testutils/FuseReadTest.h @@ -0,0 +1,35 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_READ_TESTUTILS_FUSEREADTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_READ_TESTUTILS_FUSEREADTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseReadTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + struct ReadError { + int error; + size_t read_bytes; + }; + + void ReadFile(const char *filename, void *buf, size_t count, off_t offset); + ReadError ReadFileReturnError(const char *filename, void *buf, size_t count, off_t offset); + + ::testing::Action ReturnSuccessfulRead = + ::testing::Invoke([](int, void *, size_t count, off_t) { + return count; + }); + + ::testing::Action ReturnSuccessfulReadRegardingSize(size_t filesize) { + return ::testing::Invoke([filesize](int, void *, size_t count, off_t offset) { + size_t ableToReadCount = std::min(count, (size_t)(filesize - offset)); + return ableToReadCount; + }); + } + +private: + int OpenFile(const TempTestFS *fs, const char *filename); +}; + +#endif diff --git a/test/fspp/fuse/readDir/FuseReadDirDirnameTest.cpp b/test/fspp/fuse/readDir/FuseReadDirDirnameTest.cpp new file mode 100644 index 00000000..c0556c7d --- /dev/null +++ b/test/fspp/fuse/readDir/FuseReadDirDirnameTest.cpp @@ -0,0 +1,46 @@ +#include "testutils/FuseReadDirTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +using std::vector; +using std::string; + +class FuseReadDirDirnameTest: public FuseReadDirTest { +public: +}; + +TEST_F(FuseReadDirDirnameTest, ReadRootDir) { + EXPECT_CALL(fsimpl, readDir(StrEq("/"))) + .Times(1).WillOnce(ReturnDirEntries({})); + + ReadDir("/"); +} + +TEST_F(FuseReadDirDirnameTest, ReadDir) { + ReturnIsDirOnLstat("/mydir"); + EXPECT_CALL(fsimpl, readDir(StrEq("/mydir"))) + .Times(1).WillOnce(ReturnDirEntries({})); + + ReadDir("/mydir"); +} + +TEST_F(FuseReadDirDirnameTest, ReadDirNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + EXPECT_CALL(fsimpl, readDir(StrEq("/mydir/mydir2"))) + .Times(1).WillOnce(ReturnDirEntries({})); + + ReadDir("/mydir/mydir2"); +} + +TEST_F(FuseReadDirDirnameTest, ReadDirNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsDirOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, readDir(StrEq("/mydir/mydir2/mydir3"))) + .Times(1).WillOnce(ReturnDirEntries({})); + + ReadDir("/mydir/mydir2/mydir3"); +} diff --git a/test/fspp/fuse/readDir/FuseReadDirErrorTest.cpp b/test/fspp/fuse/readDir/FuseReadDirErrorTest.cpp new file mode 100644 index 00000000..6fe313c4 --- /dev/null +++ b/test/fspp/fuse/readDir/FuseReadDirErrorTest.cpp @@ -0,0 +1,38 @@ +#include "testutils/FuseReadDirTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using std::vector; +using std::string; + +using namespace fspp::fuse; + +class FuseReadDirErrorTest: public FuseReadDirTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseReadDirErrorTest, FuseReadDirErrorTest, Values(EACCES, EBADF, EMFILE, ENFILE, ENOMEM, ENOTDIR, EFAULT, EINVAL)); + +//TODO On ENOENT, libfuse doesn't return the ENOENT error, but returns a success response with an empty directory. Why? + +TEST_F(FuseReadDirErrorTest, NoError) { + ReturnIsDirOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, readDir(StrEq(DIRNAME))) + .Times(1).WillOnce(ReturnDirEntries({})); + + int error = ReadDirReturnError(DIRNAME); + EXPECT_EQ(0, error); +} + +TEST_P(FuseReadDirErrorTest, ReturnedErrorCodeIsCorrect) { + ReturnIsDirOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, readDir(StrEq(DIRNAME))) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = ReadDirReturnError(DIRNAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/readDir/FuseReadDirReturnTest.cpp b/test/fspp/fuse/readDir/FuseReadDirReturnTest.cpp new file mode 100644 index 00000000..3723ba36 --- /dev/null +++ b/test/fspp/fuse/readDir/FuseReadDirReturnTest.cpp @@ -0,0 +1,64 @@ +#include "testutils/FuseReadDirTest.h" +#include +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::vector; +using std::string; + +using namespace fspp::fuse; +using fspp::Dir; + +unique_ref> LARGE_DIR(int num_entries) { + auto result = make_unique_ref>(); + result->reserve(num_entries); + for(int i=0; ipush_back("File "+std::to_string(i)+" file"); + } + return result; +} + +class FuseReadDirReturnTest: public FuseReadDirTest, public WithParamInterface> { +public: + void testDirEntriesAreCorrect(const vector &direntries) { + ReturnIsDirOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, readDir(StrEq(DIRNAME))) + .Times(1).WillOnce(ReturnDirEntries(direntries)); + + auto returned_dir_entries = ReadDir(DIRNAME); + EXPECT_EQ(direntries, *returned_dir_entries); + } +}; +INSTANTIATE_TEST_CASE_P(FuseReadDirReturnTest, FuseReadDirReturnTest, Values( + vector({}), + vector({"oneentry"}), + vector({"twoentries_1", "twoentries_2"}), + vector({"file1", "file with spaces"}), + vector({"file1", ".dotfile"}) +)); + +TEST_P(FuseReadDirReturnTest, ReturnedDirEntriesAreCorrect) { + testDirEntriesAreCorrect(GetParam()); +} + +// If using this with GTest Value-Parametrized TEST_P, it breaks some other unrelated tests +// (probably because it is doing a lot of construction work on the start of the test program) +TEST_F(FuseReadDirReturnTest, ReturnedDirEntriesAreCorrect_LargeDir1000) { + auto direntries = LARGE_DIR(1000); + testDirEntriesAreCorrect(*direntries); +} + +// If using this with GTest Value-Parametrized TEST_P, it breaks some other unrelated tests +// (probably because it is doing a lot of construction work on the start of the test program) +// DISABLED, because it uses a lot of memory +TEST_F(FuseReadDirReturnTest, DISABLED_ReturnedDirEntriesAreCorrect_LargeDir1000000) { + auto direntries = LARGE_DIR(1000000); + testDirEntriesAreCorrect(*direntries); +} diff --git a/test/fspp/fuse/readDir/testutils/FuseReadDirTest.cpp b/test/fspp/fuse/readDir/testutils/FuseReadDirTest.cpp new file mode 100644 index 00000000..0fe0723a --- /dev/null +++ b/test/fspp/fuse/readDir/testutils/FuseReadDirTest.cpp @@ -0,0 +1,88 @@ +#include "FuseReadDirTest.h" + +using cpputils::unique_ref; +using cpputils::make_unique_ref; +using std::vector; +using std::string; +using std::initializer_list; + +using ::testing::Action; +using ::testing::Return; + +unique_ref> FuseReadDirTest::ReadDir(const char *dirname) { + auto fs = TestFS(); + + DIR *dir = openDir(fs.get(), dirname); + + auto result = make_unique_ref>(); + readDirEntries(dir, result.get()); + closeDir(dir); + return result; +} + +int FuseReadDirTest::ReadDirReturnError(const char *dirname) { + auto fs = TestFS(); + + errno = 0; + DIR *dir = openDirAllowError(fs.get(), dirname); + EXPECT_EQ(errno!=0, dir==nullptr) << "errno should exactly be != 0 if opendir returned nullptr"; + if (errno != 0) { + return errno; + } + + auto result = make_unique_ref>(); + int error = readDirEntriesAllowError(dir, result.get()); + closeDir(dir); + return error; +} + +DIR *FuseReadDirTest::openDir(TempTestFS *fs, const char *dirname) { + DIR *dir = openDirAllowError(fs, dirname); + EXPECT_NE(nullptr, dir) << "Opening directory failed"; + return dir; +} + +DIR *FuseReadDirTest::openDirAllowError(TempTestFS *fs, const char *dirname) { + auto realpath = fs->mountDir() / dirname; + return ::opendir(realpath.c_str()); +} + +void FuseReadDirTest::readDirEntries(DIR *dir, vector *result) { + int error = readDirEntriesAllowError(dir, result); + EXPECT_EQ(0, error); +} + +int FuseReadDirTest::readDirEntriesAllowError(DIR *dir, vector *result) { + struct dirent *entry = nullptr; + int error = readNextDirEntryAllowError(dir, &entry); + if (error != 0) { + return error; + } + while(entry != nullptr) { + result->push_back(entry->d_name); + int error = readNextDirEntryAllowError(dir, &entry); + if (error != 0) { + return error; + } + } + return 0; +} + +int FuseReadDirTest::readNextDirEntryAllowError(DIR *dir, struct dirent **result) { + errno = 0; + *result = ::readdir(dir); + return errno; +} + +void FuseReadDirTest::closeDir(DIR *dir) { + int retval = ::closedir(dir); + EXPECT_EQ(0, retval) << "Closing dir failed"; +} + +Action*(const char*)> FuseReadDirTest::ReturnDirEntries(vector entries) { + vector *direntries = new vector(entries.size(), fspp::Dir::Entry(fspp::Dir::EntryType::FILE, "")); + for(unsigned int i = 0; i < entries.size(); ++i) { + (*direntries)[i].name = entries[i]; + } + return Return(direntries); +} diff --git a/test/fspp/fuse/readDir/testutils/FuseReadDirTest.h b/test/fspp/fuse/readDir/testutils/FuseReadDirTest.h new file mode 100644 index 00000000..19787aca --- /dev/null +++ b/test/fspp/fuse/readDir/testutils/FuseReadDirTest.h @@ -0,0 +1,27 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_READDIR_TESTUTILS_FUSEREADDIRTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_READDIR_TESTUTILS_FUSEREADDIRTEST_H_ + +#include "../../../testutils/FuseTest.h" +#include +#include "fspp/fs_interface/Dir.h" + +class FuseReadDirTest: public FuseTest { +public: + const char *DIRNAME = "/mydir"; + + cpputils::unique_ref> ReadDir(const char *dirname); + int ReadDirReturnError(const char *dirname); + + static ::testing::Action*(const char*)> ReturnDirEntries(std::vector entries); + +private: + DIR *openDir(TempTestFS *fs, const char *dirname); + DIR *openDirAllowError(TempTestFS *fs, const char *dirname); + void readDirEntries(DIR *dir, std::vector *result); + int readDirEntriesAllowError(DIR *dir, std::vector *result); + int readNextDirEntryAllowError(DIR *dir, struct dirent **result); + void closeDir(DIR *dir); +}; + +#endif diff --git a/test/fspp/fuse/rename/FuseRenameErrorTest.cpp b/test/fspp/fuse/rename/FuseRenameErrorTest.cpp new file mode 100644 index 00000000..15079d53 --- /dev/null +++ b/test/fspp/fuse/rename/FuseRenameErrorTest.cpp @@ -0,0 +1,24 @@ +#include "testutils/FuseRenameTest.h" +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseRenameErrorTest: public FuseRenameTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseRenameErrorTest, FuseRenameErrorTest, Values(EACCES, EBUSY, EDQUOT, EFAULT, EINVAL, EISDIR, ELOOP, EMLINK, ENAMETOOLONG, ENOENT, ENOMEM, ENOSPC, ENOTDIR, ENOTEMPTY, EEXIST, EPERM, EROFS, EXDEV, EBADF, ENOTDIR)); + +TEST_P(FuseRenameErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME1); + ReturnDoesntExistOnLstat(FILENAME2); + EXPECT_CALL(fsimpl, rename(StrEq(FILENAME1), StrEq(FILENAME2))) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = RenameReturnError(FILENAME1, FILENAME2); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/rename/FuseRenameFilenameTest.cpp b/test/fspp/fuse/rename/FuseRenameFilenameTest.cpp new file mode 100644 index 00000000..c1cc05d6 --- /dev/null +++ b/test/fspp/fuse/rename/FuseRenameFilenameTest.cpp @@ -0,0 +1,132 @@ +#include "testutils/FuseRenameTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +class FuseRenameFilenameTest: public FuseRenameTest { +}; + +TEST_F(FuseRenameFilenameTest, RenameFileRootToRoot) { + ReturnIsFileOnLstat("/myfile"); + ReturnDoesntExistOnLstat("/myrenamedfile"); + EXPECT_CALL(fsimpl, rename(StrEq("/myfile"), StrEq("/myrenamedfile"))) + .Times(1).WillOnce(Return()); + + Rename("/myfile", "/myrenamedfile"); +} + +TEST_F(FuseRenameFilenameTest, RenameFileRootToNested) { + ReturnIsFileOnLstat("/myfile"); + ReturnIsDirOnLstat("/mydir"); + ReturnDoesntExistOnLstat("/mydir/myrenamedfile"); + EXPECT_CALL(fsimpl, rename(StrEq("/myfile"), StrEq("/mydir/myrenamedfile"))) + .Times(1).WillOnce(Return()); + + Rename("/myfile", "/mydir/myrenamedfile"); +} + +TEST_F(FuseRenameFilenameTest, RenameFileNestedToRoot) { + ReturnDoesntExistOnLstat("/myrenamedfile"); + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/myfile"); + EXPECT_CALL(fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/myrenamedfile"))) + .Times(1).WillOnce(Return()); + + Rename("/mydir/myfile", "/myrenamedfile"); +} + +TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/myfile"); + ReturnDoesntExistOnLstat("/mydir/myrenamedfile"); + EXPECT_CALL(fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/mydir/myrenamedfile"))) + .Times(1).WillOnce(Return()); + + Rename("/mydir/myfile", "/mydir/myrenamedfile"); +} + +TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + ReturnDoesntExistOnLstat("/mydir/mydir2/myrenamedfile"); + EXPECT_CALL(fsimpl, rename(StrEq("/mydir/mydir2/myfile"), StrEq("/mydir/mydir2/myrenamedfile"))) + .Times(1).WillOnce(Return()); + + Rename("/mydir/mydir2/myfile", "/mydir/mydir2/myrenamedfile"); +} + +TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested_DifferentFolder) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir2"); + ReturnIsFileOnLstat("/mydir/myfile"); + ReturnDoesntExistOnLstat("/mydir2/myrenamedfile"); + EXPECT_CALL(fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/mydir2/myrenamedfile"))) + .Times(1).WillOnce(Return()); + + Rename("/mydir/myfile", "/mydir2/myrenamedfile"); +} + +TEST_F(FuseRenameFilenameTest, RenameDirRootToRoot) { + ReturnIsDirOnLstat("/mydir"); + ReturnDoesntExistOnLstat("/myrenameddir"); + EXPECT_CALL(fsimpl, rename(StrEq("/mydir"), StrEq("/myrenameddir"))) + .Times(1).WillOnce(Return()); + + Rename("/mydir", "/myrenameddir"); +} + +TEST_F(FuseRenameFilenameTest, RenameDirRootToNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/myrootdir"); + ReturnDoesntExistOnLstat("/myrootdir/myrenameddir"); + EXPECT_CALL(fsimpl, rename(StrEq("/mydir"), StrEq("/myrootdir/myrenameddir"))) + .Times(1).WillOnce(Return()); + + Rename("/mydir", "/myrootdir/myrenameddir"); +} + +TEST_F(FuseRenameFilenameTest, RenameDirNestedToRoot) { + ReturnDoesntExistOnLstat("/myrenameddir"); + ReturnIsDirOnLstat("/myrootdir"); + ReturnIsDirOnLstat("/myrootdir/mydir"); + EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrenameddir"))) + .Times(1).WillOnce(Return()); + + Rename("/myrootdir/mydir", "/myrenameddir"); +} + +TEST_F(FuseRenameFilenameTest, RenameDirNestedToNested) { + ReturnIsDirOnLstat("/myrootdir"); + ReturnIsDirOnLstat("/myrootdir/mydir"); + ReturnDoesntExistOnLstat("/myrootdir/myrenameddir"); + EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrootdir/myrenameddir"))) + .Times(1).WillOnce(Return()); + + Rename("/myrootdir/mydir", "/myrootdir/myrenameddir"); +} + +TEST_F(FuseRenameFilenameTest, RenameDirNestedToNested2) { + ReturnIsDirOnLstat("/myrootdir"); + ReturnIsDirOnLstat("/myrootdir/myrootdir2"); + ReturnIsDirOnLstat("/myrootdir/myrootdir2/mydir"); + ReturnDoesntExistOnLstat("/myrootdir/myrootdir2/myrenameddir"); + EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/myrootdir2/mydir"), StrEq("/myrootdir/myrootdir2/myrenameddir"))) + .Times(1).WillOnce(Return()); + + Rename("/myrootdir/myrootdir2/mydir", "/myrootdir/myrootdir2/myrenameddir"); +} + +TEST_F(FuseRenameFilenameTest, RenameDirNestedToNested_DifferentFolder) { + ReturnIsDirOnLstat("/myrootdir"); + ReturnIsDirOnLstat("/myrootdir2"); + ReturnIsDirOnLstat("/myrootdir/mydir"); + ReturnDoesntExistOnLstat("/myrootdir2/myrenameddir"); + EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrootdir2/myrenameddir"))) + .Times(1).WillOnce(Return()); + + Rename("/myrootdir/mydir", "/myrootdir2/myrenameddir"); +} diff --git a/test/fspp/fuse/rename/testutils/FuseRenameTest.cpp b/test/fspp/fuse/rename/testutils/FuseRenameTest.cpp new file mode 100644 index 00000000..b0e1732c --- /dev/null +++ b/test/fspp/fuse/rename/testutils/FuseRenameTest.cpp @@ -0,0 +1,22 @@ +#include "FuseRenameTest.h" + +using ::testing::Action; +using ::testing::Invoke; + +void FuseRenameTest::Rename(const char *from, const char *to) { + int error = RenameReturnError(from, to); + EXPECT_EQ(0, error); +} + +int FuseRenameTest::RenameReturnError(const char *from, const char *to) { + auto fs = TestFS(); + + auto realfrom = fs->mountDir() / from; + auto realto = fs->mountDir() / to; + int retval = ::rename(realfrom.c_str(), realto.c_str()); + if (0 == retval) { + return 0; + } else { + return errno; + } +} diff --git a/test/fspp/fuse/rename/testutils/FuseRenameTest.h b/test/fspp/fuse/rename/testutils/FuseRenameTest.h new file mode 100644 index 00000000..34192915 --- /dev/null +++ b/test/fspp/fuse/rename/testutils/FuseRenameTest.h @@ -0,0 +1,16 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_RENAME_TESTUTILS_FUSERENAMETEST_H_ +#define MESSMER_FSPP_TEST_FUSE_RENAME_TESTUTILS_FUSERENAMETEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseRenameTest: public FuseTest { +public: + const char *FILENAME1 = "/myfile1"; + const char *FILENAME2 = "/myfile2"; + + void Rename(const char *from, const char *to); + int RenameReturnError(const char *from, const char *to); +}; + +#endif diff --git a/test/fspp/fuse/rmdir/FuseRmdirDirnameTest.cpp b/test/fspp/fuse/rmdir/FuseRmdirDirnameTest.cpp new file mode 100644 index 00000000..5dcc5d0b --- /dev/null +++ b/test/fspp/fuse/rmdir/FuseRmdirDirnameTest.cpp @@ -0,0 +1,43 @@ +#include "testutils/FuseRmdirTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +class FuseRmdirDirnameTest: public FuseRmdirTest { +}; + +TEST_F(FuseRmdirDirnameTest, Rmdir) { + ReturnIsDirOnLstat("/mydir"); + EXPECT_CALL(fsimpl, rmdir(StrEq("/mydir"))) + // After rmdir was called, lstat should return that it doesn't exist anymore + // This is needed to make the ::rmdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat()); + + Rmdir("/mydir"); +} + +TEST_F(FuseRmdirDirnameTest, RmdirNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mysubdir"); + EXPECT_CALL(fsimpl, rmdir(StrEq("/mydir/mysubdir"))) + // After rmdir was called, lstat should return that it doesn't exist anymore + // This is needed to make the ::rmdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat()); + + Rmdir("/mydir/mysubdir"); +} + +TEST_F(FuseRmdirDirnameTest, RmdirNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsDirOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, rmdir(StrEq("/mydir/mydir2/mydir3"))) + // After rmdir was called, lstat should return that it doesn't exist anymore + // This is needed to make the ::rmdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat()); + + Rmdir("/mydir/mydir2/mydir3"); +} diff --git a/test/fspp/fuse/rmdir/FuseRmdirErrorTest.cpp b/test/fspp/fuse/rmdir/FuseRmdirErrorTest.cpp new file mode 100644 index 00000000..37d281c1 --- /dev/null +++ b/test/fspp/fuse/rmdir/FuseRmdirErrorTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseRmdirTest.h" +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseRmdirErrorTest: public FuseRmdirTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseRmdirErrorTest, FuseRmdirErrorTest, Values(EACCES, EBUSY, EFAULT, EINVAL, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, ENOTEMPTY, EPERM, EROFS)); + +TEST_P(FuseRmdirErrorTest, ReturnedErrorIsCorrect) { + ReturnIsDirOnLstat(DIRNAME); + EXPECT_CALL(fsimpl, rmdir(StrEq(DIRNAME))) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = RmdirReturnError(DIRNAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/rmdir/testutils/FuseRmdirTest.cpp b/test/fspp/fuse/rmdir/testutils/FuseRmdirTest.cpp new file mode 100644 index 00000000..a655594c --- /dev/null +++ b/test/fspp/fuse/rmdir/testutils/FuseRmdirTest.cpp @@ -0,0 +1,27 @@ +#include "FuseRmdirTest.h" + +using ::testing::Action; +using ::testing::Invoke; + +void FuseRmdirTest::Rmdir(const char *dirname) { + int error = RmdirReturnError(dirname); + EXPECT_EQ(0, error); +} + +int FuseRmdirTest::RmdirReturnError(const char *dirname) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / dirname; + int retval = ::rmdir(realpath.c_str()); + if (retval == 0) { + return 0; + } else { + return errno; + } +} + +Action FuseRmdirTest::FromNowOnReturnDoesntExistOnLstat() { + return Invoke([this](const char *dirname) { + ReturnDoesntExistOnLstat(dirname); + }); +} diff --git a/test/fspp/fuse/rmdir/testutils/FuseRmdirTest.h b/test/fspp/fuse/rmdir/testutils/FuseRmdirTest.h new file mode 100644 index 00000000..6f547d49 --- /dev/null +++ b/test/fspp/fuse/rmdir/testutils/FuseRmdirTest.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_RMDIR_TESTUTILS_FUSERMDIRTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_RMDIR_TESTUTILS_FUSERMDIRTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseRmdirTest: public FuseTest { +public: + const char *DIRNAME = "/mydir"; + + void Rmdir(const char *dirname); + int RmdirReturnError(const char *dirname); + + ::testing::Action FromNowOnReturnDoesntExistOnLstat(); +}; + +#endif diff --git a/test/fspp/fuse/statfs/FuseStatfsErrorTest.cpp b/test/fspp/fuse/statfs/FuseStatfsErrorTest.cpp new file mode 100644 index 00000000..44d24f4a --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsErrorTest.cpp @@ -0,0 +1,31 @@ +#include "testutils/FuseStatfsTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::StrEq; +using ::testing::_; +using ::testing::Throw; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +using fspp::fuse::FuseErrnoException; + +class FuseStatfsErrorTest: public FuseStatfsTest, public WithParamInterface { +public: +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsErrorTest, FuseStatfsErrorTest, Values(EACCES, EBADF, EFAULT, EINTR, EIO, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS, ENOTDIR, EOVERFLOW)); + +TEST_F(FuseStatfsErrorTest, ReturnNoError) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, statfs(StrEq(FILENAME), _)).Times(1).WillOnce(Return()); + int error = StatfsReturnError(FILENAME); + EXPECT_EQ(0, error); +} + +TEST_P(FuseStatfsErrorTest, ReturnError) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, statfs(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + int error = StatfsReturnError(FILENAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsPathParameterTest.cpp b/test/fspp/fuse/statfs/FuseStatfsPathParameterTest.cpp new file mode 100644 index 00000000..ecc42704 --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsPathParameterTest.cpp @@ -0,0 +1,41 @@ +#include "testutils/FuseStatfsTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseStatfsPathParameterTest: public FuseStatfsTest { +}; + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectRoot) { + EXPECT_CALL(fsimpl, statfs(StrEq("/"), _)).Times(1).WillOnce(Return()); + Statfs("/"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectSimpleFile) { + ReturnIsFileOnLstat("/myfile"); + EXPECT_CALL(fsimpl, statfs(StrEq("/myfile"), _)).Times(1).WillOnce(Return()); + Statfs("/myfile"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectSimpleDir) { + ReturnIsDirOnLstat("/mydir"); + EXPECT_CALL(fsimpl, statfs(StrEq("/mydir"), _)).Times(1).WillOnce(Return()); + Statfs("/mydir"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectNestedFile) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, statfs(StrEq("/mydir/mydir2/myfile"), _)).Times(1).WillOnce(Return()); + Statfs("/mydir/mydir2/myfile"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectNestedDir) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsDirOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, statfs(StrEq("/mydir/mydir2/mydir3"), _)).Times(1).WillOnce(Return()); + Statfs("/mydir/mydir2/mydir3"); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnBavailTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnBavailTest.cpp new file mode 100644 index 00000000..2c2f1f8e --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnBavailTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBavailTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsblkcnt_t value) override { + stat->f_bavail = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBavailTest, FuseStatfsReturnBavailTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBavailTest, ReturnedBavailIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_bavail); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnBfreeTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnBfreeTest.cpp new file mode 100644 index 00000000..8529a27c --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnBfreeTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBfreeTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsblkcnt_t value) override { + stat->f_bfree = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBfreeTest, FuseStatfsReturnBfreeTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBfreeTest, ReturnedBfreeIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_bfree); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnBlocksTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnBlocksTest.cpp new file mode 100644 index 00000000..b9f59912 --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnBlocksTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBlocksTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsblkcnt_t value) override { + stat->f_blocks = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBlocksTest, FuseStatfsReturnBlocksTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBlocksTest, ReturnedBlocksIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_blocks); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnBsizeTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnBsizeTest.cpp new file mode 100644 index 00000000..ccdb1278 --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnBsizeTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBsizeTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, unsigned long value) override { + stat->f_bsize = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBsizeTest, FuseStatfsReturnBsizeTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBsizeTest, ReturnedBsizeIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_bsize); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnFfreeTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnFfreeTest.cpp new file mode 100644 index 00000000..e490810f --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnFfreeTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnFfreeTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsfilcnt_t value) override { + stat->f_ffree = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnFfreeTest, FuseStatfsReturnFfreeTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnFfreeTest, ReturnedFfreeIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_ffree); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnFilesTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnFilesTest.cpp new file mode 100644 index 00000000..1a11ab54 --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnFilesTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnFilesTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsfilcnt_t value) override { + stat->f_files = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnFilesTest, FuseStatfsReturnFilesTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnFilesTest, ReturnedFilesIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_files); +} diff --git a/test/fspp/fuse/statfs/FuseStatfsReturnNamemaxTest.cpp b/test/fspp/fuse/statfs/FuseStatfsReturnNamemaxTest.cpp new file mode 100644 index 00000000..c663e586 --- /dev/null +++ b/test/fspp/fuse/statfs/FuseStatfsReturnNamemaxTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnNamemaxTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, unsigned long value) override { + stat->f_namemax = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnNamemaxTest, FuseStatfsReturnNamemaxTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnNamemaxTest, ReturnedNamemaxIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_namemax); +} diff --git a/test/fspp/fuse/statfs/testutils/FuseStatfsReturnTest.h b/test/fspp/fuse/statfs/testutils/FuseStatfsReturnTest.h new file mode 100644 index 00000000..b6cec026 --- /dev/null +++ b/test/fspp/fuse/statfs/testutils/FuseStatfsReturnTest.h @@ -0,0 +1,36 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_STATFS_TESTUTILS_FUSESTATFSRETURNTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_STATFS_TESTUTILS_FUSESTATFSRETURNTEST_H_ + +#include "FuseStatfsTest.h" + +// This class offers test helpers for testing (struct statfs) entries. We return them from +// our mock filesystem, set up a temporary filesystem, call statfs syscall on it, and +// then check the return value. +template +class FuseStatfsReturnTest: public FuseStatfsTest { +public: + // Set the specified (struct statfs) entry to the given value, and test whether it is correctly returned from the syscall. + struct ::statvfs CallStatfsWithValue(Property value); + +private: + std::function SetPropertyImpl(Property value); + + // Override this function to specify, how to set the specified (struct statfs) entry on the passed (struct statfs *) object. + virtual void set(struct ::statvfs *statfs, Property value) = 0; +}; + +template +struct ::statvfs FuseStatfsReturnTest::CallStatfsWithValue(Property value) { + return CallStatfsWithImpl(SetPropertyImpl(value)); +} + +template +std::function FuseStatfsReturnTest::SetPropertyImpl(Property value) { + return [this, value] (struct ::statvfs *stat) { + set(stat, value); + }; +} + + +#endif diff --git a/test/fspp/fuse/statfs/testutils/FuseStatfsTest.cpp b/test/fspp/fuse/statfs/testutils/FuseStatfsTest.cpp new file mode 100644 index 00000000..7d0e09bd --- /dev/null +++ b/test/fspp/fuse/statfs/testutils/FuseStatfsTest.cpp @@ -0,0 +1,46 @@ +#include "FuseStatfsTest.h" + +using std::function; +using ::testing::StrEq; +using ::testing::_; +using ::testing::Invoke; + +void FuseStatfsTest::Statfs(const std::string &path) { + struct ::statvfs dummy; + Statfs(path, &dummy); +} + +int FuseStatfsTest::StatfsReturnError(const std::string &path) { + struct ::statvfs dummy; + return StatfsReturnError(path, &dummy); +} + +void FuseStatfsTest::Statfs(const std::string &path, struct ::statvfs *result) { + int error = StatfsReturnError(path, result); + EXPECT_EQ(0, error) << "lstat syscall failed. errno: " << errno; +} + +int FuseStatfsTest::StatfsReturnError(const std::string &path, struct ::statvfs *result) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / path; + int retval = ::statvfs(realpath.c_str(), result); + if (retval == 0) { + return 0; + } else { + return errno; + } +} + +struct ::statvfs FuseStatfsTest::CallStatfsWithImpl(function implementation) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, statfs(StrEq(FILENAME), _)).WillRepeatedly(Invoke([implementation](const char*, struct ::statvfs *stat) { + implementation(stat); + })); + + struct ::statvfs result; + Statfs(FILENAME, &result); + + return result; +} + diff --git a/test/fspp/fuse/statfs/testutils/FuseStatfsTest.h b/test/fspp/fuse/statfs/testutils/FuseStatfsTest.h new file mode 100644 index 00000000..65278bd1 --- /dev/null +++ b/test/fspp/fuse/statfs/testutils/FuseStatfsTest.h @@ -0,0 +1,34 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_STATFS_TESTUTILS_FUSESTATFSTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_STATFS_TESTUTILS_FUSESTATFSTEST_H_ + +#include +#include +#include + +#include "../../../testutils/FuseTest.h" + +// This class offers some utility functions for testing statfs(). +class FuseStatfsTest: public FuseTest { +protected: + const char *FILENAME = "/myfile"; + + // Set up a temporary filesystem (using the fsimpl mock in FuseTest as filesystem implementation) + // and call the statfs syscall on the given (filesystem-relative) path. + void Statfs(const std::string &path); + // Same as Statfs above, but also return the result of the statfs syscall. + void Statfs(const std::string &path, struct ::statvfs *result); + + // These two functions are the same as Statfs above, but they don't fail the test when the statfs syscall + // crashes. Instead, they return the result value of the statfs syscall. + int StatfsReturnError(const std::string &path); + int StatfsReturnError(const std::string &path, struct ::statvfs *result); + + // You can specify an implementation, which can modify the (struct statfs *) result, + // our fuse mock filesystem implementation will then return this to fuse on an statfs call. + // This functions then set up a temporary filesystem with this mock, calls statfs on a filesystem node + // and returns the (struct statfs) returned from an statfs syscall to this filesystem. + struct ::statvfs CallStatfsWithImpl(std::function implementation); +}; + +#endif diff --git a/test/fspp/fuse/truncate/FuseTruncateErrorTest.cpp b/test/fspp/fuse/truncate/FuseTruncateErrorTest.cpp new file mode 100644 index 00000000..582cc9df --- /dev/null +++ b/test/fspp/fuse/truncate/FuseTruncateErrorTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseTruncateTest.h" +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseTruncateErrorTest: public FuseTruncateTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseTruncateErrorTest, FuseTruncateErrorTest, Values(EACCES, EFAULT, EFBIG, EINTR, EINVAL, EIO, EISDIR, ELOOP, ENAMETOOLONG, ENOENT, ENOTDIR, EPERM, EROFS, ETXTBSY)); + +TEST_P(FuseTruncateErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, truncate(StrEq(FILENAME), _)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = TruncateFileReturnError(FILENAME, 0); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/truncate/FuseTruncateFilenameTest.cpp b/test/fspp/fuse/truncate/FuseTruncateFilenameTest.cpp new file mode 100644 index 00000000..7adc5a3a --- /dev/null +++ b/test/fspp/fuse/truncate/FuseTruncateFilenameTest.cpp @@ -0,0 +1,35 @@ +#include "testutils/FuseTruncateTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseTruncateFilenameTest: public FuseTruncateTest { +}; + +TEST_F(FuseTruncateFilenameTest, TruncateFile) { + ReturnIsFileOnLstat("/myfile"); + EXPECT_CALL(fsimpl, truncate(StrEq("/myfile"), _)) + .Times(1).WillOnce(Return()); + + TruncateFile("/myfile", 0); +} + +TEST_F(FuseTruncateFilenameTest, TruncateFileNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/myfile"); + EXPECT_CALL(fsimpl, truncate(StrEq("/mydir/myfile"), _)) + .Times(1).WillOnce(Return()); + + TruncateFile("/mydir/myfile", 0); +} + +TEST_F(FuseTruncateFilenameTest, TruncateFileNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, truncate(StrEq("/mydir/mydir2/myfile"), _)) + .Times(1).WillOnce(Return()); + + TruncateFile("/mydir/mydir2/myfile", 0); +} diff --git a/test/fspp/fuse/truncate/FuseTruncateSizeTest.cpp b/test/fspp/fuse/truncate/FuseTruncateSizeTest.cpp new file mode 100644 index 00000000..d1ca019f --- /dev/null +++ b/test/fspp/fuse/truncate/FuseTruncateSizeTest.cpp @@ -0,0 +1,20 @@ +#include "testutils/FuseTruncateTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseTruncateSizeTest: public FuseTruncateTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseTruncateSizeTest, FuseTruncateSizeTest, Values(0, 1, 10, 1024, 1024*1024*1024)); + + +TEST_P(FuseTruncateSizeTest, TruncateFile) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, truncate(StrEq(FILENAME), GetParam())) + .Times(1).WillOnce(Return()); + + TruncateFile(FILENAME, GetParam()); +} diff --git a/test/fspp/fuse/truncate/testutils/FuseTruncateTest.cpp b/test/fspp/fuse/truncate/testutils/FuseTruncateTest.cpp new file mode 100644 index 00000000..49516025 --- /dev/null +++ b/test/fspp/fuse/truncate/testutils/FuseTruncateTest.cpp @@ -0,0 +1,18 @@ +#include "FuseTruncateTest.h" + +void FuseTruncateTest::TruncateFile(const char *filename, off_t size) { + int error = TruncateFileReturnError(filename, size); + EXPECT_EQ(0, error); +} + +int FuseTruncateTest::TruncateFileReturnError(const char *filename, off_t size) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / filename; + int retval = ::truncate(realpath.c_str(), size); + if (retval == 0) { + return 0; + } else { + return errno; + } +} diff --git a/test/fspp/fuse/truncate/testutils/FuseTruncateTest.h b/test/fspp/fuse/truncate/testutils/FuseTruncateTest.h new file mode 100644 index 00000000..5b34322c --- /dev/null +++ b/test/fspp/fuse/truncate/testutils/FuseTruncateTest.h @@ -0,0 +1,15 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_TRUNCATE_TESTUTILS_FUSETRUNCATETEST_H_ +#define MESSMER_FSPP_TEST_FUSE_TRUNCATE_TESTUTILS_FUSETRUNCATETEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseTruncateTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + void TruncateFile(const char *filename, off_t size); + int TruncateFileReturnError(const char *filename, off_t size); +}; + +#endif diff --git a/test/fspp/fuse/unlink/FuseUnlinkErrorTest.cpp b/test/fspp/fuse/unlink/FuseUnlinkErrorTest.cpp new file mode 100644 index 00000000..dc568e73 --- /dev/null +++ b/test/fspp/fuse/unlink/FuseUnlinkErrorTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseUnlinkTest.h" +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseUnlinkErrorTest: public FuseUnlinkTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseUnlinkErrorTest, FuseUnlinkErrorTest, Values(EACCES, EBUSY, EFAULT, EIO, EISDIR, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EPERM, EROFS, EINVAL)); + +TEST_P(FuseUnlinkErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, unlink(StrEq(FILENAME))) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = UnlinkReturnError(FILENAME); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/unlink/FuseUnlinkFilenameTest.cpp b/test/fspp/fuse/unlink/FuseUnlinkFilenameTest.cpp new file mode 100644 index 00000000..8d56388f --- /dev/null +++ b/test/fspp/fuse/unlink/FuseUnlinkFilenameTest.cpp @@ -0,0 +1,43 @@ +#include "testutils/FuseUnlinkTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +class FuseUnlinkFilenameTest: public FuseUnlinkTest { +}; + +TEST_F(FuseUnlinkFilenameTest, Unlink) { + ReturnIsFileOnLstat("/mydir"); + EXPECT_CALL(fsimpl, unlink(StrEq("/mydir"))) + // After rmdir was called, lstat should return that it doesn't exist anymore + // This is needed to make the ::rmdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat()); + + Unlink("/mydir"); +} + +TEST_F(FuseUnlinkFilenameTest, UnlinkNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/mysubdir"); + EXPECT_CALL(fsimpl, unlink(StrEq("/mydir/mysubdir"))) + // After rmdir was called, lstat should return that it doesn't exist anymore + // This is needed to make the ::rmdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat()); + + Unlink("/mydir/mysubdir"); +} + +TEST_F(FuseUnlinkFilenameTest, UnlinkNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, unlink(StrEq("/mydir/mydir2/mydir3"))) + // After rmdir was called, lstat should return that it doesn't exist anymore + // This is needed to make the ::rmdir() syscall pass. + .Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat()); + + Unlink("/mydir/mydir2/mydir3"); +} diff --git a/test/fspp/fuse/unlink/testutils/FuseUnlinkTest.cpp b/test/fspp/fuse/unlink/testutils/FuseUnlinkTest.cpp new file mode 100644 index 00000000..7eac8961 --- /dev/null +++ b/test/fspp/fuse/unlink/testutils/FuseUnlinkTest.cpp @@ -0,0 +1,27 @@ +#include "FuseUnlinkTest.h" + +using ::testing::Action; +using ::testing::Invoke; + +void FuseUnlinkTest::Unlink(const char *filename) { + int error = UnlinkReturnError(filename); + EXPECT_EQ(0, error); +} + +int FuseUnlinkTest::UnlinkReturnError(const char *filename) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / filename; + int retval = ::unlink(realpath.c_str()); + if (0 == retval) { + return 0; + } else { + return errno; + } +} + +Action FuseUnlinkTest::FromNowOnReturnDoesntExistOnLstat() { + return Invoke([this](const char *filename) { + ReturnDoesntExistOnLstat(filename); + }); +} diff --git a/test/fspp/fuse/unlink/testutils/FuseUnlinkTest.h b/test/fspp/fuse/unlink/testutils/FuseUnlinkTest.h new file mode 100644 index 00000000..cfea0bb6 --- /dev/null +++ b/test/fspp/fuse/unlink/testutils/FuseUnlinkTest.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_UNLINK_TESTUTILS_FUSEUNLINKTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_UNLINK_TESTUTILS_FUSEUNLINKTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseUnlinkTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + void Unlink(const char *filename); + int UnlinkReturnError(const char *filename); + + ::testing::Action FromNowOnReturnDoesntExistOnLstat(); +}; + +#endif diff --git a/test/fspp/fuse/utimens/FuseUtimensErrorTest.cpp b/test/fspp/fuse/utimens/FuseUtimensErrorTest.cpp new file mode 100644 index 00000000..1389ede4 --- /dev/null +++ b/test/fspp/fuse/utimens/FuseUtimensErrorTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseUtimensTest.h" +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Throw; +using ::testing::WithParamInterface; +using ::testing::Values; + +using namespace fspp::fuse; + +class FuseUtimensErrorTest: public FuseUtimensTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseUtimensErrorTest, FuseUtimensErrorTest, Values(EACCES, ENOENT, EPERM, EROFS)); + +TEST_P(FuseUtimensErrorTest, ReturnedErrorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, utimens(StrEq(FILENAME), _, _)) + .Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + + int error = UtimensReturnError(FILENAME, TIMEVALUE, TIMEVALUE); + EXPECT_EQ(GetParam(), error); +} diff --git a/test/fspp/fuse/utimens/FuseUtimensFilenameTest.cpp b/test/fspp/fuse/utimens/FuseUtimensFilenameTest.cpp new file mode 100644 index 00000000..058f3621 --- /dev/null +++ b/test/fspp/fuse/utimens/FuseUtimensFilenameTest.cpp @@ -0,0 +1,62 @@ +#include "testutils/FuseUtimensTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseUtimensFilenameTest: public FuseUtimensTest { +}; + +TEST_F(FuseUtimensFilenameTest, UtimensFile) { + ReturnIsFileOnLstat("/myfile"); + EXPECT_CALL(fsimpl, utimens(StrEq("/myfile"), _, _)) + .Times(1).WillOnce(Return()); + + Utimens("/myfile", TIMEVALUE, TIMEVALUE); +} + +TEST_F(FuseUtimensFilenameTest, UtimensFileNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsFileOnLstat("/mydir/myfile"); + EXPECT_CALL(fsimpl, utimens(StrEq("/mydir/myfile"), _, _)) + .Times(1).WillOnce(Return()); + + Utimens("/mydir/myfile", TIMEVALUE, TIMEVALUE); +} + +TEST_F(FuseUtimensFilenameTest, UtimensFileNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, utimens(StrEq("/mydir/mydir2/myfile"), _, _)) + .Times(1).WillOnce(Return()); + + Utimens("/mydir/mydir2/myfile", TIMEVALUE, TIMEVALUE); +} + +TEST_F(FuseUtimensFilenameTest, UtimensDir) { + ReturnIsDirOnLstat("/mydir"); + EXPECT_CALL(fsimpl, utimens(StrEq("/mydir"), _, _)) + .Times(1).WillOnce(Return()); + + Utimens("/mydir", TIMEVALUE, TIMEVALUE); +} + +TEST_F(FuseUtimensFilenameTest, UtimensDirNested) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + EXPECT_CALL(fsimpl, utimens(StrEq("/mydir/mydir2"), _, _)) + .Times(1).WillOnce(Return()); + + Utimens("/mydir/mydir2", TIMEVALUE, TIMEVALUE); +} + +TEST_F(FuseUtimensFilenameTest, UtimensDirNested2) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsDirOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, utimens(StrEq("/mydir/mydir2/mydir3"), _, _)) + .Times(1).WillOnce(Return()); + + Utimens("/mydir/mydir2/mydir3", TIMEVALUE, TIMEVALUE); +} diff --git a/test/fspp/fuse/utimens/FuseUtimensTimeParameterTest.cpp b/test/fspp/fuse/utimens/FuseUtimensTimeParameterTest.cpp new file mode 100644 index 00000000..807a52d4 --- /dev/null +++ b/test/fspp/fuse/utimens/FuseUtimensTimeParameterTest.cpp @@ -0,0 +1,31 @@ +#include "testutils/FuseUtimensTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseUtimensTimeParameterTest: public FuseUtimensTest, public WithParamInterface { +}; +const timespec TIMEVAL1[2] = {FuseUtimensTest::makeTimespec(0,0), FuseUtimensTest::makeTimespec(0,0)}; +const timespec TIMEVAL2[2] = {FuseUtimensTest::makeTimespec(1000,0), FuseUtimensTest::makeTimespec(0,0)}; +const timespec TIMEVAL3[2] = {FuseUtimensTest::makeTimespec(0,1000), FuseUtimensTest::makeTimespec(0,0)}; +const timespec TIMEVAL4[2] = {FuseUtimensTest::makeTimespec(1000,1000), FuseUtimensTest::makeTimespec(0,0)}; +const timespec TIMEVAL5[2] = {FuseUtimensTest::makeTimespec(0,0), FuseUtimensTest::makeTimespec(0,0)}; +const timespec TIMEVAL6[2] = {FuseUtimensTest::makeTimespec(0,0), FuseUtimensTest::makeTimespec(1000,0)}; +const timespec TIMEVAL7[2] = {FuseUtimensTest::makeTimespec(0,0), FuseUtimensTest::makeTimespec(0,1000)}; +const timespec TIMEVAL8[2] = {FuseUtimensTest::makeTimespec(0,0), FuseUtimensTest::makeTimespec(1000,1000)}; +const timespec TIMEVAL9[2] = {FuseUtimensTest::makeTimespec(1417196126,123000), FuseUtimensTest::makeTimespec(1417109713,321000)}; // current timestamp and the day before as of writing this test case +const timespec TIMEVAL10[2] = {FuseUtimensTest::makeTimespec(UINT64_C(1024)*1024*1024*1024,999000), FuseUtimensTest::makeTimespec(UINT64_C(2*1024)*1024*1024*1024,321000)}; // needs 64bit for timestamp representation +INSTANTIATE_TEST_CASE_P(FuseUtimensTimeParameterTest, FuseUtimensTimeParameterTest, + Values(TIMEVAL1, TIMEVAL2, TIMEVAL3, TIMEVAL4, TIMEVAL5, TIMEVAL6, TIMEVAL7, TIMEVAL8, TIMEVAL9, TIMEVAL10)); + + +TEST_P(FuseUtimensTimeParameterTest, Utimens) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, utimens(StrEq(FILENAME), TimeSpecEq(GetParam()[0]), TimeSpecEq(GetParam()[1]))) + .Times(1).WillOnce(Return()); + + Utimens(FILENAME, GetParam()[0], GetParam()[1]); +} diff --git a/test/fspp/fuse/utimens/testutils/FuseUtimensTest.cpp b/test/fspp/fuse/utimens/testutils/FuseUtimensTest.cpp new file mode 100644 index 00000000..bd75dc99 --- /dev/null +++ b/test/fspp/fuse/utimens/testutils/FuseUtimensTest.cpp @@ -0,0 +1,32 @@ +#include "FuseUtimensTest.h" + +#include +#include + +void FuseUtimensTest::Utimens(const char *filename, timespec lastAccessTime, timespec lastModificationTime) { + int error = UtimensReturnError(filename, lastAccessTime, lastModificationTime); + EXPECT_EQ(0, error); +} + +int FuseUtimensTest::UtimensReturnError(const char *filename, timespec lastAccessTime, timespec lastModificationTime) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / filename; + + struct timeval casted_times[2]; + TIMESPEC_TO_TIMEVAL(&casted_times[0], &lastAccessTime); + TIMESPEC_TO_TIMEVAL(&casted_times[1], &lastModificationTime); + int retval = ::utimes(realpath.c_str(), casted_times); + if (0 == retval) { + return 0; + } else { + return errno; + } +} + +struct timespec FuseUtimensTest::makeTimespec(time_t tv_sec, long tv_nsec) { + struct timespec result; + result.tv_sec = tv_sec; + result.tv_nsec = tv_nsec; + return result; +} diff --git a/test/fspp/fuse/utimens/testutils/FuseUtimensTest.h b/test/fspp/fuse/utimens/testutils/FuseUtimensTest.h new file mode 100644 index 00000000..7306ab53 --- /dev/null +++ b/test/fspp/fuse/utimens/testutils/FuseUtimensTest.h @@ -0,0 +1,22 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_UTIMENS_TESTUTILS_FUSEUTIMENSTEST_H_ +#define MESSMER_FSPP_TEST_FUSE_UTIMENS_TESTUTILS_FUSEUTIMENSTEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseUtimensTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + timespec TIMEVALUE = makeTimespec(0,0); + + void Utimens(const char *filename, timespec lastAccessTime, timespec lastModificationTime); + int UtimensReturnError(const char *filename, timespec lastAccessTime, timespec lastModificationTime); + + static struct timespec makeTimespec(time_t tv_sec, long tv_nsec); +}; + +MATCHER_P(TimeSpecEq, expected, "") { + return expected.tv_sec == arg.tv_sec && expected.tv_nsec == arg.tv_nsec; +} + +#endif diff --git a/test/fspp/fuse/write/FuseWriteDataTest.cpp b/test/fspp/fuse/write/FuseWriteDataTest.cpp new file mode 100644 index 00000000..270effb5 --- /dev/null +++ b/test/fspp/fuse/write/FuseWriteDataTest.cpp @@ -0,0 +1,82 @@ +#include +#include "testutils/FuseWriteTest.h" +#include "../../testutils/InMemoryFile.h" + +#include "fspp/fuse/FuseErrnoException.h" + +#include +#include + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +using std::tuple; +using std::get; +using std::min; +using cpputils::Data; +using cpputils::DataFixture; + +using namespace fspp::fuse; + +// We can't test the count or size parameter directly, because fuse doesn't pass them 1:1. +// But we can test that the data passed to the ::write syscall is correctly written. + +struct TestData { + TestData(): count(0), offset(0), additional_bytes_at_end_of_file(0) {} + TestData(const tuple &data): count(get<0>(data)), offset(get<1>(data)), additional_bytes_at_end_of_file(get<2>(data)) {} + size_t count; + off_t offset; + //How many more bytes does the file have after the read block? + size_t additional_bytes_at_end_of_file; + size_t fileSize() { + return count + offset + additional_bytes_at_end_of_file; + } +}; + +// The testcase creates random data in memory, offers a mock write() implementation to write to this +// memory region and check methods to check for data equality of a region. +class FuseWriteDataTest: public FuseWriteTest, public WithParamInterface> { +public: + std::unique_ptr testFile; + TestData testData; + + FuseWriteDataTest() + : testFile(nullptr), + testData(GetParam()) { + testFile = std::make_unique(DataFixture::generate(testData.fileSize(), 1)); + ReturnIsFileOnLstatWithSize(FILENAME, testData.fileSize()); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, write(0, _, _, _)) + .WillRepeatedly(WriteToFile); + } + + // This write() mock implementation writes to the stored virtual file. + Action WriteToFile = Invoke([this](int, const void *buf, size_t count, off_t offset) { + testFile->write(buf, count, offset); + }); +}; +INSTANTIATE_TEST_CASE_P(FuseWriteDataTest, FuseWriteDataTest, Combine(Values(0,1,10,1000,1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024))); + + +TEST_P(FuseWriteDataTest, DataWasCorrectlyWritten) { + Data randomWriteData = DataFixture::generate(testData.count, 2); + WriteFile(FILENAME, randomWriteData.data(), testData.count, testData.offset); + + EXPECT_TRUE(testFile->fileContentEquals(randomWriteData, testData.offset)); +} + +TEST_P(FuseWriteDataTest, RestOfFileIsUnchanged) { + Data randomWriteData = DataFixture::generate(testData.count, 2); + WriteFile(FILENAME, randomWriteData.data(), testData.count, testData.offset); + + EXPECT_TRUE(testFile->sizeUnchanged()); + EXPECT_TRUE(testFile->regionUnchanged(0, testData.offset)); + EXPECT_TRUE(testFile->regionUnchanged(testData.offset + testData.count, testData.additional_bytes_at_end_of_file)); +} diff --git a/test/fspp/fuse/write/FuseWriteErrorTest.cpp b/test/fspp/fuse/write/FuseWriteErrorTest.cpp new file mode 100644 index 00000000..a3031aa9 --- /dev/null +++ b/test/fspp/fuse/write/FuseWriteErrorTest.cpp @@ -0,0 +1,61 @@ +#include "testutils/FuseWriteTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Throw; + +using namespace fspp::fuse; + +class FuseWriteErrorTest: public FuseWriteTest, public WithParamInterface { +public: + size_t FILESIZE = 64*1024*1024; + size_t WRITECOUNT = 32*1024*1024; + + void SetUp() override { + //Make the file size big enough that fuse should issue at least two writes + ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE); + OnOpenReturnFileDescriptor(FILENAME, 0); + } +}; +INSTANTIATE_TEST_CASE_P(FuseWriteErrorTest, FuseWriteErrorTest, Values(EAGAIN, EBADF, EDESTADDRREQ, EDQUOT, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENOSPC, EPIPE, EOVERFLOW, ESPIPE, ENXIO)); + + +TEST_P(FuseWriteErrorTest, ReturnErrorOnFirstWriteCall) { + EXPECT_CALL(fsimpl, write(0, _, _, _)) + .WillRepeatedly(Throw(FuseErrnoException(GetParam()))); + + char *buf = new char[WRITECOUNT]; + auto retval = WriteFileReturnError(FILENAME, buf, WRITECOUNT, 0); + EXPECT_EQ(GetParam(), retval.error); + delete[] buf; +} + +TEST_P(FuseWriteErrorTest, ReturnErrorOnSecondWriteCall) { + // The first write request is from the beginning of the file and works, but the later ones fail. + // We store the number of bytes the first call could successfully write and check later that our + // write syscall returns exactly this number of bytes + size_t successfullyWrittenBytes = -1; + EXPECT_CALL(fsimpl, write(0, _, _, Eq(0))) + .Times(1) + .WillOnce(Invoke([&successfullyWrittenBytes](int, const void*, size_t count, off_t) { + // Store the number of successfully written bytes + successfullyWrittenBytes = count; + })); + EXPECT_CALL(fsimpl, write(0, _, _, Ne(0))) + .WillRepeatedly(Throw(FuseErrnoException(GetParam()))); + + char *buf = new char[WRITECOUNT]; + auto retval = WriteFileReturnError(FILENAME, buf, WRITECOUNT, 0); + EXPECT_EQ(0, retval.error); + EXPECT_EQ(successfullyWrittenBytes, retval.written_bytes); // Check that we're getting the number of successfully written bytes (the first write call) returned + delete[] buf; +} + diff --git a/test/fspp/fuse/write/FuseWriteFileDescriptorTest.cpp b/test/fspp/fuse/write/FuseWriteFileDescriptorTest.cpp new file mode 100644 index 00000000..6a18087a --- /dev/null +++ b/test/fspp/fuse/write/FuseWriteFileDescriptorTest.cpp @@ -0,0 +1,29 @@ +#include "testutils/FuseWriteTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::WithParamInterface; +using ::testing::Values; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Throw; + +using namespace fspp::fuse; + +class FuseWriteFileDescriptorTest: public FuseWriteTest, public WithParamInterface { +}; +INSTANTIATE_TEST_CASE_P(FuseWriteFileDescriptorTest, FuseWriteFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024)); + + +TEST_P(FuseWriteFileDescriptorTest, FileDescriptorIsCorrect) { + ReturnIsFileOnLstat(FILENAME); + OnOpenReturnFileDescriptor(FILENAME, GetParam()); + EXPECT_CALL(fsimpl, write(Eq(GetParam()), _, _, _)) + .Times(1).WillOnce(Return()); + + char buf[1]; + WriteFile(FILENAME, buf, 1, 0); +} diff --git a/test/fspp/fuse/write/FuseWriteOverflowTest.cpp b/test/fspp/fuse/write/FuseWriteOverflowTest.cpp new file mode 100644 index 00000000..5c4ed422 --- /dev/null +++ b/test/fspp/fuse/write/FuseWriteOverflowTest.cpp @@ -0,0 +1,88 @@ +#include +#include "testutils/FuseWriteTest.h" +#include "../../testutils/InMemoryFile.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Eq; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Action; + +using std::min; +using cpputils::DataFixture; +using cpputils::Data; + +using namespace fspp::fuse; + +class FuseWriteOverflowTest: public FuseWriteTest { +public: + size_t FILESIZE; + size_t WRITESIZE; + size_t OFFSET; + + WriteableInMemoryFile testFile; + Data writeData; + + FuseWriteOverflowTest(size_t filesize, size_t writesize, size_t offset) + : FILESIZE(filesize), WRITESIZE(writesize), OFFSET(offset), testFile(DataFixture::generate(FILESIZE)), writeData(DataFixture::generate(WRITESIZE)) { + ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE); + OnOpenReturnFileDescriptor(FILENAME, 0); + EXPECT_CALL(fsimpl, write(0, _, _, _)).WillRepeatedly(WriteToFile); + } + + // This write() mock implementation writes to the stored virtual file. + Action WriteToFile = + Invoke([this](int, const void *buf, size_t count, off_t offset) { + testFile.write(buf, count, offset); + }); +}; + +class FuseWriteOverflowTestWithNonemptyFile: public FuseWriteOverflowTest { +public: + FuseWriteOverflowTestWithNonemptyFile(): FuseWriteOverflowTest(1000, 2000, 500) {} +}; + +TEST_F(FuseWriteOverflowTestWithNonemptyFile, WriteMoreThanFileSizeFromBeginning) { + WriteFile(FILENAME, writeData.data(), WRITESIZE, 0); + + EXPECT_EQ(WRITESIZE, testFile.size()); + EXPECT_TRUE(testFile.fileContentEquals(writeData, 0)); +} + +TEST_F(FuseWriteOverflowTestWithNonemptyFile, WriteMoreThanFileSizeFromMiddle) { + WriteFile(FILENAME, writeData.data(), WRITESIZE, OFFSET); + + EXPECT_EQ(OFFSET + WRITESIZE, testFile.size()); + EXPECT_TRUE(testFile.regionUnchanged(0, OFFSET)); + EXPECT_TRUE(testFile.fileContentEquals(writeData, OFFSET)); +} + +TEST_F(FuseWriteOverflowTestWithNonemptyFile, WriteAfterFileEnd) { + WriteFile(FILENAME, writeData.data(), WRITESIZE, FILESIZE + OFFSET); + + EXPECT_EQ(FILESIZE + OFFSET + WRITESIZE, testFile.size()); + EXPECT_TRUE(testFile.regionUnchanged(0, FILESIZE)); + EXPECT_TRUE(testFile.fileContentEquals(writeData, FILESIZE + OFFSET)); +} + +class FuseWriteOverflowTestWithEmptyFile: public FuseWriteOverflowTest { +public: + FuseWriteOverflowTestWithEmptyFile(): FuseWriteOverflowTest(0, 2000, 500) {} +}; + +TEST_F(FuseWriteOverflowTestWithEmptyFile, WriteToBeginOfEmptyFile) { + WriteFile(FILENAME, writeData.data(), WRITESIZE, 0); + + EXPECT_EQ(WRITESIZE, testFile.size()); + EXPECT_TRUE(testFile.fileContentEquals(writeData, 0)); +} + +TEST_F(FuseWriteOverflowTestWithEmptyFile, WriteAfterFileEnd) { + WriteFile(FILENAME, writeData.data(), WRITESIZE, OFFSET); + + EXPECT_EQ(OFFSET + WRITESIZE, testFile.size()); + EXPECT_TRUE(testFile.fileContentEquals(writeData, OFFSET)); +} diff --git a/test/fspp/fuse/write/testutils/FuseWriteTest.cpp b/test/fspp/fuse/write/testutils/FuseWriteTest.cpp new file mode 100644 index 00000000..9f464fdb --- /dev/null +++ b/test/fspp/fuse/write/testutils/FuseWriteTest.cpp @@ -0,0 +1,26 @@ +#include "FuseWriteTest.h" + +void FuseWriteTest::WriteFile(const char *filename, const void *buf, size_t count, off_t offset) { + auto retval = WriteFileReturnError(filename, buf, count, offset); + EXPECT_EQ(0, retval.error); + EXPECT_EQ(count, retval.written_bytes); +} + +FuseWriteTest::WriteError FuseWriteTest::WriteFileReturnError(const char *filename, const void *buf, size_t count, off_t offset) { + auto fs = TestFS(); + + int fd = OpenFile(fs.get(), filename); + + WriteError result; + errno = 0; + result.written_bytes = ::pwrite(fd, buf, count, offset); + result.error = errno; + return result; +} + +int FuseWriteTest::OpenFile(const TempTestFS *fs, const char *filename) { + auto realpath = fs->mountDir() / filename; + int fd = ::open(realpath.c_str(), O_WRONLY); + EXPECT_GE(fd, 0) << "Error opening file"; + return fd; +} diff --git a/test/fspp/fuse/write/testutils/FuseWriteTest.h b/test/fspp/fuse/write/testutils/FuseWriteTest.h new file mode 100644 index 00000000..8da26a67 --- /dev/null +++ b/test/fspp/fuse/write/testutils/FuseWriteTest.h @@ -0,0 +1,23 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_FUSE_WRITE_TESTUTILS_FUSEWRITETEST_H_ +#define MESSMER_FSPP_TEST_FUSE_WRITE_TESTUTILS_FUSEWRITETEST_H_ + +#include "../../../testutils/FuseTest.h" + +class FuseWriteTest: public FuseTest { +public: + const char *FILENAME = "/myfile"; + + struct WriteError { + int error; + size_t written_bytes; + }; + + void WriteFile(const char *filename, const void *buf, size_t count, off_t offset); + WriteError WriteFileReturnError(const char *filename, const void *buf, size_t count, off_t offset); + +private: + int OpenFile(const TempTestFS *fs, const char *filename); +}; + +#endif diff --git a/test/fspp/impl/FuseOpenFileListTest.cpp b/test/fspp/impl/FuseOpenFileListTest.cpp new file mode 100644 index 00000000..be104fd8 --- /dev/null +++ b/test/fspp/impl/FuseOpenFileListTest.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include "fspp/impl/FuseOpenFileList.h" + +#include + +using cpputils::make_unique_ref; + +using namespace fspp; + +class MockOpenFile: public OpenFile { +public: + MockOpenFile(int fileid_, int flags_): fileid(fileid_), flags(flags_), destructed(false) {} + int fileid, flags; + bool destructed; + + ~MockOpenFile() {destructed = true;} + + MOCK_CONST_METHOD1(stat, void(struct ::stat*)); + MOCK_CONST_METHOD1(truncate, void(off_t)); + MOCK_CONST_METHOD3(read, size_t(void*, size_t, off_t)); + MOCK_METHOD3(write, void(const void*, size_t, off_t)); + MOCK_METHOD0(flush, void()); + MOCK_METHOD0(fsync, void()); + MOCK_METHOD0(fdatasync, void()); +}; + +struct FuseOpenFileListTest: public ::testing::Test { + static constexpr int FILEID1 = 4; + static constexpr int FLAGS1 = 5; + static constexpr int FILEID2 = 6; + static constexpr int FLAGS2 = 7; + static constexpr int FILEID3 = 8; + static constexpr int FLAGS3 = 9; + + FuseOpenFileListTest(): list() {} + + FuseOpenFileList list; + + int open(int fileid, int flags) { + return list.open(make_unique_ref(fileid, flags)); + } + int open() { + return open(FILEID1, FILEID2); + } + void check(int id, int fileid, int flags) { + MockOpenFile *openFile = dynamic_cast(list.get(id)); + EXPECT_EQ(fileid, openFile->fileid); + EXPECT_EQ(flags, openFile->flags); + } +}; + +TEST_F(FuseOpenFileListTest, EmptyList1) { + ASSERT_THROW(list.get(0), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, EmptyList2) { + ASSERT_THROW(list.get(3), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, InvalidId) { + int valid_id = open(); + int invalid_id = valid_id + 1; + ASSERT_THROW(list.get(invalid_id), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, Open1AndGet) { + int id = open(FILEID1, FLAGS1); + check(id, FILEID1, FLAGS1); +} + +TEST_F(FuseOpenFileListTest, Open2AndGet) { + int id1 = open(FILEID1, FLAGS1); + int id2 = open(FILEID2, FLAGS2); + + check(id1, FILEID1, FLAGS1); + check(id2, FILEID2, FLAGS2); +} + +TEST_F(FuseOpenFileListTest, Open3AndGet) { + int id1 = open(FILEID1, FLAGS1); + int id2 = open(FILEID2, FLAGS2); + int id3 = open(FILEID3, FLAGS3); + + check(id1, FILEID1, FLAGS1); + check(id3, FILEID3, FLAGS3); + check(id2, FILEID2, FLAGS2); +} + +//TODO Test case fails. Disabled it. Figure out why and reenable. +/*TEST_F(FuseOpenFileListTest, DestructOnClose) { + int id = open(); + + MockOpenFile *openFile = dynamic_cast(list.get(id)); + + EXPECT_FALSE(openFile->destructed); + list.close(id); + EXPECT_TRUE(openFile->destructed); +}*/ + +TEST_F(FuseOpenFileListTest, GetClosedItemOnEmptyList) { + int id = open(); + + ASSERT_NO_THROW(list.get(id)); + list.close(id); + ASSERT_THROW(list.get(id), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, GetClosedItemOnNonEmptyList) { + int id = open(); + open(); + + ASSERT_NO_THROW(list.get(id)); + list.close(id); + ASSERT_THROW(list.get(id), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, CloseOnEmptyList1) { + ASSERT_THROW(list.close(0), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, CloseOnEmptyList2) { + ASSERT_THROW(list.close(4), std::out_of_range); +} + +TEST_F(FuseOpenFileListTest, RemoveInvalidId) { + int valid_id = open(); + int invalid_id = valid_id + 1; + ASSERT_THROW(list.close(invalid_id), std::out_of_range); +} diff --git a/test/fspp/impl/IdListTest.cpp b/test/fspp/impl/IdListTest.cpp new file mode 100644 index 00000000..dc6b289d --- /dev/null +++ b/test/fspp/impl/IdListTest.cpp @@ -0,0 +1,109 @@ +#include + +#include "fspp/impl/IdList.h" +#include + +using cpputils::make_unique_ref; + +using namespace fspp; + +class MyObj { +public: + MyObj(int val_): val(val_) {} + int val; +}; + +struct IdListTest: public ::testing::Test { + static constexpr int OBJ1 = 3; + static constexpr int OBJ2 = 10; + static constexpr int OBJ3 = 8; + + IdListTest(): list() {} + + IdList list; + + int add(int num) { + return list.add(make_unique_ref(num)); + } + int add() { + return add(OBJ1); + } + void check(int id, int num) { + EXPECT_EQ(num, list.get(id)->val); + } + void checkConst(int id, int num) { + const IdList &constList = list; + EXPECT_EQ(num, constList.get(id)->val); + } +}; + +TEST_F(IdListTest, EmptyList1) { + ASSERT_THROW(list.get(0), std::out_of_range); +} + +TEST_F(IdListTest, EmptyList2) { + ASSERT_THROW(list.get(3), std::out_of_range); +} + +TEST_F(IdListTest, InvalidId) { + int valid_id = add(); + int invalid_id = valid_id + 1; + ASSERT_THROW(list.get(invalid_id), std::out_of_range); +} + +TEST_F(IdListTest, GetRemovedItemOnEmptyList) { + int id = add(); + list.remove(id); + ASSERT_THROW(list.get(id), std::out_of_range); +} + +TEST_F(IdListTest, GetRemovedItemOnNonEmptyList) { + int id = add(); + add(); + list.remove(id); + ASSERT_THROW(list.get(id), std::out_of_range); +} + +TEST_F(IdListTest, RemoveOnEmptyList1) { + ASSERT_THROW(list.remove(0), std::out_of_range); +} + +TEST_F(IdListTest, RemoveOnEmptyList2) { + ASSERT_THROW(list.remove(4), std::out_of_range); +} + +TEST_F(IdListTest, RemoveInvalidId) { + int valid_id = add(); + int invalid_id = valid_id + 1; + ASSERT_THROW(list.remove(invalid_id), std::out_of_range); +} + +TEST_F(IdListTest, Add1AndGet) { + int id = add(OBJ1); + check(id, OBJ1); +} + +TEST_F(IdListTest, Add2AndGet) { + int id1 = add(OBJ1); + int id2 = add(OBJ2); + check(id1, OBJ1); + check(id2, OBJ2); +} + +TEST_F(IdListTest, Add3AndGet) { + int id1 = add(OBJ1); + int id2 = add(OBJ2); + int id3 = add(OBJ3); + check(id1, OBJ1); + check(id3, OBJ3); + check(id2, OBJ2); +} + +TEST_F(IdListTest, Add3AndConstGet) { + int id1 = add(OBJ1); + int id2 = add(OBJ2); + int id3 = add(OBJ3); + checkConst(id1, OBJ1); + checkConst(id3, OBJ3); + checkConst(id2, OBJ2); +} diff --git a/test/fspp/testutils/FuseTest.cpp b/test/fspp/testutils/FuseTest.cpp new file mode 100644 index 00000000..86c14451 --- /dev/null +++ b/test/fspp/testutils/FuseTest.cpp @@ -0,0 +1,127 @@ +#include "FuseTest.h" + +using ::testing::StrEq; +using ::testing::_; +using ::testing::Return; +using ::testing::Throw; +using ::testing::Action; +using ::testing::Invoke; + +using cpputils::unique_ref; +using cpputils::make_unique_ref; + +namespace bf = boost::filesystem; + +using namespace fspp::fuse; + +MockFilesystem::MockFilesystem() {} +MockFilesystem::~MockFilesystem() {} + +FuseTest::FuseTest(): fsimpl() { + auto defaultAction = Throw(FuseErrnoException(EIO)); + ON_CALL(fsimpl, openFile(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, closeFile(_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, lstat(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, fstat(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, truncate(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, ftruncate(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, read(_,_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, write(_,_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, fsync(_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, fdatasync(_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, access(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, createAndOpenFile(_,_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, mkdir(_,_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, rmdir(_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, unlink(_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, rename(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, readDir(_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, utimens(_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, statfs(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, chmod(_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, chown(_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, createSymlink(_,_,_,_)).WillByDefault(defaultAction); + ON_CALL(fsimpl, readSymlink(_,_,_)).WillByDefault(defaultAction); +} + +unique_ref FuseTest::TestFS() { + return make_unique_ref(&fsimpl); +} + +FuseTest::TempTestFS::TempTestFS(MockFilesystem *fsimpl): _mountDir(), _fuse(fsimpl), _fuse_thread(&_fuse) { + std::string dirpath = _mountDir.path().native(); + int argc = 3; + const char *argv[] = {"test", "-f", dirpath.c_str()}; + + _fuse_thread.start(argc, const_cast(argv)); +} + +FuseTest::TempTestFS::~TempTestFS() { + _fuse_thread.stop(); +} + +const bf::path &FuseTest::TempTestFS::mountDir() const { + return _mountDir.path(); +} + +Action FuseTest::ReturnIsFileWithSize(size_t size) { + return Invoke([size](const char*, struct ::stat* result) { + result->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + result->st_nlink = 1; + result->st_size = size; + }); +} + +//TODO Combine ReturnIsFile and ReturnIsFileFstat. This should be possible in gmock by either (a) using ::testing::Undefined as parameter type or (b) using action macros +Action FuseTest::ReturnIsFile = ReturnIsFileWithSize(0); + +Action FuseTest::ReturnIsFileFstat = + Invoke([](int, struct ::stat* result) { + result->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + result->st_nlink = 1; + }); + +Action FuseTest::ReturnIsFileFstatWithSize(size_t size) { + return Invoke([size](int, struct ::stat *result) { + result->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + result->st_nlink = 1; + result->st_size = size; + std::cout << "Return size: " << size < FuseTest::ReturnIsDir = + Invoke([](const char*, struct ::stat* result) { + result->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH; + result->st_nlink = 1; + }); + +Action FuseTest::ReturnDoesntExist = Throw(fspp::fuse::FuseErrnoException(ENOENT)); + +void FuseTest::OnOpenReturnFileDescriptor(const char *filename, int descriptor) { + EXPECT_CALL(fsimpl, openFile(StrEq(filename), _)).Times(1).WillOnce(Return(descriptor)); +} + +void FuseTest::ReturnIsFileOnLstat(const bf::path &path) { + EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsFile); +} + +void FuseTest::ReturnIsFileOnLstatWithSize(const bf::path &path, const size_t size) { + EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsFileWithSize(size)); +} + +void FuseTest::ReturnIsDirOnLstat(const bf::path &path) { + EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsDir); +} + +void FuseTest::ReturnDoesntExistOnLstat(const bf::path &path) { + EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnDoesntExist); +} + +void FuseTest::ReturnIsFileOnFstat(int descriptor) { + EXPECT_CALL(fsimpl, fstat(descriptor, _)).WillRepeatedly(ReturnIsFileFstat); +} + +void FuseTest::ReturnIsFileOnFstatWithSize(int descriptor, size_t size) { + EXPECT_CALL(fsimpl, fstat(descriptor, _)).WillRepeatedly(ReturnIsFileFstatWithSize(size)); +} \ No newline at end of file diff --git a/test/fspp/testutils/FuseTest.h b/test/fspp/testutils/FuseTest.h new file mode 100644 index 00000000..1cf3d345 --- /dev/null +++ b/test/fspp/testutils/FuseTest.h @@ -0,0 +1,125 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_TESTUTILS_FUSETEST_H_ +#define MESSMER_FSPP_TEST_TESTUTILS_FUSETEST_H_ + +#include +#include + +#include "fspp/fuse/Filesystem.h" +#include "fspp/fuse/FuseErrnoException.h" +#include "fspp/fuse/Fuse.h" +#include "fspp/fs_interface/Dir.h" + +#include + +#include +#include "FuseThread.h" + +#define MOCK_PATH_METHOD1(NAME, RETURNTYPE) \ + RETURNTYPE NAME(const boost::filesystem::path &path) override { \ + return NAME(path.c_str()); \ + } \ + MOCK_METHOD1(NAME, RETURNTYPE(const char*)); \ + +#define MOCK_PATH_METHOD2(NAME, RETURNTYPE, PARAM1) \ + RETURNTYPE NAME(const boost::filesystem::path &path, PARAM1 param1) override { \ + return NAME(path.c_str(), param1); \ + } \ + MOCK_METHOD2(NAME, RETURNTYPE(const char*, PARAM1)); \ + +#define MOCK_PATH_METHOD3(NAME, RETURNTYPE, PARAM1, PARAM2) \ + RETURNTYPE NAME(const boost::filesystem::path &path, PARAM1 p1, PARAM2 p2) override { \ + return NAME(path.c_str(), p1, p2); \ + } \ + MOCK_METHOD3(NAME, RETURNTYPE(const char*, PARAM1, PARAM2)); \ + +#define MOCK_PATH_METHOD4(NAME, RETURNTYPE, PARAM1, PARAM2, PARAM3) \ + RETURNTYPE NAME(const boost::filesystem::path &path, PARAM1 p1, PARAM2 p2, PARAM3 p3) override { \ + return NAME(path.c_str(), p1, p2, p3); \ + } \ + MOCK_METHOD4(NAME, RETURNTYPE(const char*, PARAM1, PARAM2, PARAM3)); \ + +class MockFilesystem: public fspp::fuse::Filesystem { +public: + MockFilesystem(); + virtual ~MockFilesystem(); + + MOCK_PATH_METHOD2(openFile, int, int); + MOCK_METHOD1(closeFile, void(int)); + MOCK_PATH_METHOD2(lstat, void, struct ::stat*); + MOCK_METHOD2(fstat, void(int, struct ::stat*)); + MOCK_PATH_METHOD2(truncate, void, off_t); + MOCK_METHOD2(ftruncate, void(int, off_t)); + MOCK_METHOD4(read, size_t(int, void*, size_t, off_t)); + MOCK_METHOD4(write, void(int, const void*, size_t, off_t)); + MOCK_METHOD1(flush, void(int)); + MOCK_METHOD1(fsync, void(int)); + MOCK_METHOD1(fdatasync, void(int)); + MOCK_PATH_METHOD2(access, void, int); + MOCK_PATH_METHOD4(createAndOpenFile, int, mode_t, uid_t, gid_t); + MOCK_PATH_METHOD4(mkdir, void, mode_t, uid_t, gid_t); + MOCK_PATH_METHOD1(rmdir, void); + MOCK_PATH_METHOD1(unlink, void); + void rename(const boost::filesystem::path &from, const boost::filesystem::path &to) override { + return rename(from.c_str(), to.c_str()); + } + MOCK_METHOD2(rename, void(const char*, const char*)); + cpputils::unique_ref> readDir(const boost::filesystem::path &path) override { + return cpputils::nullcheck(std::unique_ptr>(readDir(path.c_str()))).value(); + } + MOCK_METHOD1(readDir, std::vector*(const char*)); + void utimens(const boost::filesystem::path &path, timespec lastAccessTime, timespec lastModificationTime) override { + return utimens(path.c_str(), lastAccessTime, lastModificationTime); + } + MOCK_METHOD3(utimens, void(const char*, timespec, timespec)); + MOCK_PATH_METHOD2(statfs, void, struct statvfs*); + void createSymlink(const boost::filesystem::path &to, const boost::filesystem::path &from, uid_t uid, gid_t gid) override { + return createSymlink(to.c_str(), from.c_str(), uid, gid); + } + MOCK_PATH_METHOD2(chmod, void, mode_t); + MOCK_PATH_METHOD3(chown, void, uid_t, gid_t); + MOCK_METHOD4(createSymlink, void(const char*, const char*, uid_t, gid_t)); + MOCK_PATH_METHOD3(readSymlink, void, char*, size_t); +}; + +class FuseTest: public ::testing::Test { +public: + static constexpr const char* FILENAME = "/myfile"; + + FuseTest(); + + class TempTestFS { + public: + TempTestFS(MockFilesystem *fsimpl); + virtual ~TempTestFS(); + public: + const boost::filesystem::path &mountDir() const; + private: + cpputils::TempDir _mountDir; + fspp::fuse::Fuse _fuse; + FuseThread _fuse_thread; + }; + + cpputils::unique_ref TestFS(); + + MockFilesystem fsimpl; + + + //TODO Combine ReturnIsFile and ReturnIsFileFstat. This should be possible in gmock by either (a) using ::testing::Undefined as parameter type or (b) using action macros + static ::testing::Action ReturnIsFile; + static ::testing::Action ReturnIsFileWithSize(size_t size); + static ::testing::Action ReturnIsFileFstat; + static ::testing::Action ReturnIsFileFstatWithSize(size_t size); + static ::testing::Action ReturnIsDir; + static ::testing::Action ReturnDoesntExist; + + void ReturnIsFileOnLstat(const boost::filesystem::path &path); + void ReturnIsFileOnLstatWithSize(const boost::filesystem::path &path, const size_t size); + void ReturnIsDirOnLstat(const boost::filesystem::path &path); + void ReturnDoesntExistOnLstat(const boost::filesystem::path &path); + void OnOpenReturnFileDescriptor(const char *filename, int descriptor); + void ReturnIsFileOnFstat(int descriptor); + void ReturnIsFileOnFstatWithSize(int descriptor, const size_t size); +}; + +#endif diff --git a/test/fspp/testutils/FuseThread.cpp b/test/fspp/testutils/FuseThread.cpp new file mode 100644 index 00000000..4c6e95f2 --- /dev/null +++ b/test/fspp/testutils/FuseThread.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include "FuseThread.h" +#include +#include +#include "fspp/fuse/Fuse.h" + +using boost::thread; +using boost::chrono::seconds; +using std::string; + +using fspp::fuse::Fuse; + +FuseThread::FuseThread(Fuse *fuse) + :_fuse(fuse), _child() { +} + +void FuseThread::start(int argc, char *argv[]) { + _child = thread([this, argc, argv] () { + _fuse->run(argc, argv); + }); + //Wait until it is running (busy waiting is simple and doesn't hurt much here) + while(!_fuse->running()) {} +} + +void FuseThread::stop() { + pthread_kill(_child.native_handle(), SIGINT); + bool thread_stopped = _child.try_join_for(seconds(5)); + ASSERT(thread_stopped, "FuseThread could not be stopped"); + //Wait until it is properly shutdown (busy waiting is simple and doesn't hurt much here) + while (_fuse->running()) {} +} diff --git a/test/fspp/testutils/FuseThread.h b/test/fspp/testutils/FuseThread.h new file mode 100644 index 00000000..73666840 --- /dev/null +++ b/test/fspp/testutils/FuseThread.h @@ -0,0 +1,28 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_TESTUTILS_FUSETHREAD_H_ +#define MESSMER_FSPP_TEST_TESTUTILS_FUSETHREAD_H_ + +#include +#include +#include + +namespace fspp { +namespace fuse { + class Fuse; +} +} + +class FuseThread { +public: + FuseThread(fspp::fuse::Fuse *fuse); + void start(int argc, char *argv[]); + void stop(); + +private: + fspp::fuse::Fuse *_fuse; + boost::thread _child; + + DISALLOW_COPY_AND_ASSIGN(FuseThread); +}; + +#endif diff --git a/test/fspp/testutils/InMemoryFile.cpp b/test/fspp/testutils/InMemoryFile.cpp new file mode 100644 index 00000000..78df5f7d --- /dev/null +++ b/test/fspp/testutils/InMemoryFile.cpp @@ -0,0 +1,56 @@ +#include "InMemoryFile.h" + +using cpputils::Data; + +InMemoryFile::InMemoryFile(Data data): _data(std::move(data)) { +} + +InMemoryFile::~InMemoryFile() { +} + +int InMemoryFile::read(void *buf, size_t count, off_t offset) const { + size_t realCount = std::min(count, (size_t)(_data.size() - offset)); + std::memcpy(buf, (uint8_t*)_data.data() + offset, realCount); + return realCount; +} + +const void *InMemoryFile::data() const { + return _data.data(); +} + +bool InMemoryFile::fileContentEquals(const Data &expected, off_t offset) const { + return 0 == std::memcmp((uint8_t*)expected.data(), (uint8_t*)_data.data() + offset, expected.size()); +} + +size_t InMemoryFile::size() const { + return _data.size(); +} + +WriteableInMemoryFile::WriteableInMemoryFile(Data data): InMemoryFile(std::move(data)), _originalData(_data.copy()) { +} + +void WriteableInMemoryFile::write(const void *buf, size_t count, off_t offset) { + _extendFileSizeIfNecessary(count + offset); + + std::memcpy((uint8_t*)_data.data() + offset, buf, count); +} + +void WriteableInMemoryFile::_extendFileSizeIfNecessary(size_t size) { + if (size > _data.size()) { + _extendFileSize(size); + } +} + +void WriteableInMemoryFile::_extendFileSize(size_t size) { + Data newfile(size); + std::memcpy(newfile.data(), _data.data(), _data.size()); + _data = std::move(newfile); +} + +bool WriteableInMemoryFile::sizeUnchanged() const { + return _data.size() == _originalData.size(); +} + +bool WriteableInMemoryFile::regionUnchanged(off_t offset, size_t count) const { + return 0 == std::memcmp((uint8_t*)_data.data() + offset, (uint8_t*)_originalData.data() + offset, count); +} diff --git a/test/fspp/testutils/InMemoryFile.h b/test/fspp/testutils/InMemoryFile.h new file mode 100644 index 00000000..58adb9c0 --- /dev/null +++ b/test/fspp/testutils/InMemoryFile.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef MESSMER_FSPP_TEST_TESTUTILS_INMEMORYFILE_H_ +#define MESSMER_FSPP_TEST_TESTUTILS_INMEMORYFILE_H_ + +#include + +class InMemoryFile { +public: + InMemoryFile(cpputils::Data data); + virtual ~InMemoryFile(); + + int read(void *buf, size_t count, off_t offset) const; + + const void *data() const; + size_t size() const; + + bool fileContentEquals(const cpputils::Data &expected, off_t offset) const; + +protected: + cpputils::Data _data; +}; + +class WriteableInMemoryFile: public InMemoryFile { +public: + WriteableInMemoryFile(cpputils::Data data); + + void write(const void *buf, size_t count, off_t offset); + + bool sizeUnchanged() const; + bool regionUnchanged(off_t offset, size_t count) const; + +private: + void _extendFileSizeIfNecessary(size_t size); + void _extendFileSize(size_t size); + + cpputils::Data _originalData; +}; + + +#endif diff --git a/test/main.cpp b/test/main.cpp deleted file mode 100644 index ec4a5e33..00000000 --- a/test/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "google/gtest/gtest.h" -#include - -int main(int argc, char **argv) { - cpputils::showBacktraceOnSigSegv(); - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/parallelaccessstore/CMakeLists.txt b/test/parallelaccessstore/CMakeLists.txt new file mode 100644 index 00000000..fab6afcf --- /dev/null +++ b/test/parallelaccessstore/CMakeLists.txt @@ -0,0 +1,13 @@ +project (parallelaccessstore-test) + +set(SOURCES + ParallelAccessBaseStoreTest.cpp + DummyTest.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} googletest) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/parallelaccessstore/DummyTest.cpp b/test/parallelaccessstore/DummyTest.cpp new file mode 100644 index 00000000..bafa57e5 --- /dev/null +++ b/test/parallelaccessstore/DummyTest.cpp @@ -0,0 +1,5 @@ +#include + +TEST(Dummy, DummyTest) { +} + diff --git a/test/parallelaccessstore/ParallelAccessBaseStoreTest.cpp b/test/parallelaccessstore/ParallelAccessBaseStoreTest.cpp new file mode 100644 index 00000000..2766b1d7 --- /dev/null +++ b/test/parallelaccessstore/ParallelAccessBaseStoreTest.cpp @@ -0,0 +1,3 @@ +#include "parallelaccessstore/ParallelAccessBaseStore.h" + +// Test that ParallelAccessBaseStore.h can be included without errors diff --git a/utils.cmake b/utils.cmake new file mode 100644 index 00000000..18a0edf2 --- /dev/null +++ b/utils.cmake @@ -0,0 +1,64 @@ +include(CheckCXXCompilerFlag) + +################################################### +# Activate C++14 +# +# Uses: target_activate_cpp14(buildtarget) +################################################### +function(target_activate_cpp14 TARGET) + check_cxx_compiler_flag("-std=c++14" COMPILER_HAS_CPP14_SUPPORT) + IF (COMPILER_HAS_CPP14_SUPPORT) + target_compile_options(${TARGET} PUBLIC -std=c++14) + ELSE() + check_cxx_compiler_flag("-std=c++1y" COMPILER_HAS_CPP14_PARTIAL_SUPPORT) + IF (COMPILER_HAS_CPP14_PARTIAL_SUPPORT) + target_compile_options(${TARGET} PUBLIC -std=c++1y) + ELSE() + message(FATAL_ERROR "Compiler doesn't support C++14") + ENDIF() + ENDIF() + IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${TARGET} PUBLIC -stdlib=libc++) + ENDIF() +endfunction(target_activate_cpp14) + +################################################# +# Enable style compiler warnings +# +# Uses: target_enable_style_warnings(buildtarget) +################################################# +function(target_enable_style_warnings TARGET) + target_compile_options(${TARGET} PRIVATE -Wall -Wextra) +endfunction(target_enable_style_warnings) + +################################################## +# Add boost to the project +# +# Uses: +# target_add_boost(buildtarget) # if you're only using header-only boost libs +# target_add_boost(buildtarget system filesystem) # list all libraries to link against in the dependencies +################################################## +function(target_add_boost TARGET) + # Load boost libraries + find_package(Boost 1.56.0 + REQUIRED + COMPONENTS ${ARGN}) + set(Boost_USE_STATIC_LIBS ON) + target_include_directories(${TARGET} SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}) + target_link_libraries(${TARGET} PRIVATE ${Boost_LIBRARIES}) +endfunction(target_add_boost) + +################################################## +# Specify that a specific minimal version of gcc is required +# +# Uses: +# require_gcc_version(4.9) +################################################## +function(require_gcc_version) + if (CMAKE_COMPILER_IS_GNUCXX) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_LESS ${ARGN}) + message(FATAL_ERROR "Needs at least gcc version ${ARGN}, found gcc ${GCC_VERSION}") + endif (GCC_VERSION VERSION_LESS ${ARGN}) + endif (CMAKE_COMPILER_IS_GNUCXX) +endfunction(require_gcc_version) diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 00000000..3bc0a86f --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(googletest) +add_subdirectory(scrypt) +add_subdirectory(spdlog) +add_subdirectory(gitversion) \ No newline at end of file diff --git a/vendor/README b/vendor/README new file mode 100644 index 00000000..fc544479 --- /dev/null +++ b/vendor/README @@ -0,0 +1,5 @@ +This directory contains external projects, taken from the following locations: +scrypt: http://www.tarsnap.com/scrypt.html +googletest: https://github.com/google/googletest +spdlog: https://github.com/gabime/spdlog/commit/0c7beb2e36008598cf80d0e8eb8635ac403febb9 (commit 0c7beb2e36008598cf80d0e8eb8635ac403febb9) +gitversion: https://github.com/smessmer/gitversion \ No newline at end of file diff --git a/vendor/gitversion/CMakeLists.txt b/vendor/gitversion/CMakeLists.txt new file mode 100644 index 00000000..4c287120 --- /dev/null +++ b/vendor/gitversion/CMakeLists.txt @@ -0,0 +1 @@ +include(gitversion-1.7/cmake.cmake) diff --git a/vendor/gitversion/gitversion-1.8/.gitignore b/vendor/gitversion/gitversion-1.8/.gitignore new file mode 100644 index 00000000..5afdf67d --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/.gitignore @@ -0,0 +1,61 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +.idea +MANIFEST +Version.py diff --git a/vendor/gitversion/gitversion-1.8/LICENSE b/vendor/gitversion/gitversion-1.8/LICENSE new file mode 100644 index 00000000..733c0723 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/vendor/gitversion/gitversion-1.8/README.md b/vendor/gitversion/gitversion-1.8/README.md new file mode 100644 index 00000000..d8180318 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/README.md @@ -0,0 +1,163 @@ +# git-version [![Build Status](https://travis-ci.org/smessmer/gitversion.svg?branch=master)](https://travis-ci.org/smessmer/gitversion) +Make git version information (e.g. git tag name, git commit id, ...) available to your source files. +A simple use case scenario is to output this version information when the application is called with "--version". + +This repository contains + - A python script to generate C++ headers or python modules with this version information. You can add the python script to your build process to autogenerate the files on each build. + - A CMake script which can be directly included into a CMake projects. It will then automatically be run on each build and you only have to #include the generated file. + + +Use with cmake (only C++) +================ + +Copy this repository into a subfolder of your project and include the cmake.cmake file in your CMakeLists.txt + + INCLUDE(gitversion/cmake.cmake) + TARGET_GIT_VERSION_INIT(buildtarget) + +Then, you can write in your source file: + + #include + cout << version::VERSION_STRING << endl; + cout << version::IS_STABLE_VERSION << endl; + cout << version::GIT_COMMIT_ID << endl; + cout << version::GIT_COMMITS_SINCE_TAG << endl; + // ... (see below for more variables) + +That's it already. Have fun :) + +Use manually (C++ and Python) +================ + +Install from PyPi +---------------- + +To install the tool: + + pip install git-version + +To generate a version.h file containing C++ version information for the git repository located in myrepositorydir: + + python -m gitversionbuilder --dir myrepositorydir --lang cpp version.h + +Or to generate a module with version information for python: + + python -m gitversionbuilder --dir myrepositorydir --lang python version.py + + +Run script from source tree +------------------------- + +If you don't want to use PyPi, you can run the script directly from the source tree. +Clone this repository and go to the src directory (or alternatively add the src directory to the PYTHONPATH environment variable), then call for example + + python -m gitversionbuilder --dir myrepositorydir --lang cpp version.h + +If you want to build a distribution of the package to use it somewhere else, you can use the standard python [setuptools](https://pythonhosted.org/setuptools/). +A corresponding setup.py is available in the directory. + + +Available Information +================= + +Basic Information +----------------- +The following table shows the basic variables that are always available. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VERSION_STRING1.0Built from git tag "1.0".
v0.8alphaBuilt from git tag "v0.8alpha".
0.8.dev3+rev4fa254c + Built from 3 commits after git tag "0.8". The current git commit has commit id 4fa254c. +
dev2+rev4fa254cThe repository doesn't have any git tags yet. There are 2 commits since the repository started and the current git commit has commit id 4fa254c.
0.8-modifiedThe suffix "-modified" will be used if there have been modifications since the last commit.
0.8.dev3+rev4fa254c-modified
GIT_TAG_NAMEThe name of the last git tag. If there is no git tag, then this is the name of the git branch.
GIT_COMMITS_SINCE_TAGThe number of git commits since the last git tag. If the repository doesn't have any git tags, then this is the number of git commits since the repository started
GIT_COMMIT_IDThe commit id of the git commit this was built from.
MODIFIED_SINCE_COMMITTrue, if there are uncommitted changes in the git working directory or index since the last commit; i.e. untracked (and not ignored) files, or modified files in the working directory or the index.
IS_DEV_VERSIONTrue, if this is a development version; i.e. there are no tags yet or GIT_COMMITS_SINCE_TAG > 0 or MODIFIED_SINCE_COMMIT.
+ +Additional Information +---------------------- + +We will parse the git tag name and provide additional information if you use the following versioning scheme for your git tag names: + + + /^v?[0-9]+(\.[0-9]+)*(-?((alpha|beta|rc|pre|m)[0-9]?|stable|final))?$/ + +In words, we support a set of numeric version components separated by a dot, then optionally a version tag like "alpha", "beta", "beta2", "rc2", "M3", "pre2", "stable", "final". The version tag can optionally be separated with a dash and the version number can optionally be prefixed with "v". +The version tag is matched case insensitive. It is for example also allowed to write "RC" instead of "rc". + +Examples for supported version numbers: + + - 0.8.1 + - v3.0 + - 1.1-alpha + - 1.2alpha + - 1.4.3beta + - 1.4.3beta2 + - 2.0-M2 + - 4-RC2 + - 3.0final + - 2.1-stable + - ... + +If you use a version scheme supported by this, we will provide the following additional information + + + + + + + + + + + + + + + + +
IS_STABLE_VERSIONTrue, if built from a final tag; i.e. IS_DEV_VERSION == false and GIT_COMMITS_SINCE_TAG == 0 and VERSION_TAG in {"", "stable", "final"}
VERSION_COMPONENTSAn array containing the version number split at the dots. That is, git tag "1.02.3alpha" will have VERSION_COMPONENTS=["1","02","3"].
VERSION_TAGThe version tag ("alpha", "beta", "rc4", "M2", "stable", "final", "", ...) that follows after the version number. If the version tag is separated by a dash, the dash is not included.
+ + diff --git a/vendor/gitversion/gitversion-1.8/cmake.cmake b/vendor/gitversion/gitversion-1.8/cmake.cmake new file mode 100644 index 00000000..587196ef --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/cmake.cmake @@ -0,0 +1,28 @@ +set(DIR_OF_GITVERSION_TOOL "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "DIR_OF_GITVERSION_TOOL") + +################################################# +# Add git version information +# Uses: +# TARGET_GIT_VERSION_INIT(buildtarget) +# Then, you can write in your source file: +# #include +# cout << gitversion::VERSION.toString() << endl; +################################################# +function(TARGET_GIT_VERSION_INIT TARGET) + FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/messmer_gitversion") + FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/messmer_gitversion/gitversion") + + SET(ENV{PYTHONPATH} "${DIR_OF_GITVERSION_TOOL}/src") + EXECUTE_PROCESS(COMMAND /usr/bin/env python -m gitversionbuilder --lang cpp --dir "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/messmer_gitversion/gitversion/version.h" + RESULT_VARIABLE result) + IF(NOT ${result} EQUAL 0) + MESSAGE(FATAL_ERROR "Error running messmer/git-version tool. Return code is: ${result}") + ENDIF() + TARGET_INCLUDE_DIRECTORIES(${TARGET} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/messmer_gitversion") + + # Load version string and write it to a cmake variable so it can be accessed from cmake. + FILE(READ "${CMAKE_CURRENT_BINARY_DIR}/messmer_gitversion/gitversion/version.h" VERSION_H_FILE_CONTENT) + STRING(REGEX REPLACE ".*VERSION_STRING = \"([^\"]*)\".*" "\\1" VERSION_STRING "${VERSION_H_FILE_CONTENT}") + MESSAGE(STATUS "Version from git: ${VERSION_STRING}") + SET(GITVERSION_VERSION_STRING "${VERSION_STRING}" PARENT_SCOPE) +endfunction(TARGET_GIT_VERSION_INIT) diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/DummyVersion.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/DummyVersion.py new file mode 100644 index 00000000..07e33a55 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/DummyVersion.py @@ -0,0 +1,13 @@ + +# --------------------------------------------------- +# This file is autogenerated by git-version-builder. +# DO NOT MODIFY! +# --------------------------------------------------- + +VERSION_STRING = "dummy" +TAG_NAME = "dummy" +COMMITS_SINCE_TAG = 0 +GIT_COMMIT_ID = "dummy" +MODIFIED_SINCE_COMMIT = False +IS_DEV_VERSION = True +IS_STABLE_VERSION = False diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/__init__.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/__main__.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/__main__.py new file mode 100755 index 00000000..b34fb968 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/__main__.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import argparse +import importlib +from gitversionbuilder import main + + +try: + Version = importlib.import_module(".Version", package="gitversionbuilder") +except ImportError: + Version = importlib.import_module(".DummyVersion", package="gitversionbuilder") + + +def run_main(): + parser = argparse.ArgumentParser(description="Create a source file containing git version information.") + parser.add_argument('--version', action='version', version=Version.VERSION_STRING) + parser.add_argument('--lang', choices=['cpp', 'python'], required=True) + parser.add_argument('--dir', default='.') + parser.add_argument('file') + args = parser.parse_args() + + print("Creating git version information from %s" % args.dir) + + main.create_version_file(git_directory=args.dir, output_file=args.file, lang=args.lang) + + +if __name__ == '__main__': + run_main() diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/main.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/main.py new file mode 100644 index 00000000..a7dfa6dd --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/main.py @@ -0,0 +1,25 @@ +from gitversionbuilder import versioninforeader, versioninfooutputter + + +def get_version(git_directory): + return versioninforeader.from_git(git_directory) + + +def create_version_file(git_directory, output_file, lang): + version_info = get_version(git_directory) + output = _output(version_info, lang=lang) + _write_to_file(output_file, output) + + +def _output(version_info, lang): + if lang == "cpp": + return versioninfooutputter.to_cpp(version_info) + elif lang == "python": + return versioninfooutputter.to_python(version_info) + else: + raise ValueError("Unknown language") + + +def _write_to_file(output_file, output): + with open(output_file, 'w') as file: + file.write(output) diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/utils.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/utils.py new file mode 100644 index 00000000..73dc27d3 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/utils.py @@ -0,0 +1,38 @@ +import os +import sys + +# Use this like +# > with ChDir(my_dir): +# > do_something() +# Then, the working directory will be set to my_dir, do_something() will be called, +# and the working directory will be set back. +class ChDir(object): + def __init__(self, dir): + self.dir = dir + + def __enter__(self): + self.old_dir = os.getcwd() + os.chdir(self.dir) + + def __exit__(self, type, value, traceback): + os.chdir(self.old_dir) + + +class EqualityMixin(object): + def __eq__(self, other): + return (isinstance(other, self.__class__) + and self.__dict__ == other.__dict__) + + def __ne__(self, other): + return not self.__eq__(other) + + +IS_PYTHON_2 = sys.version_info[0] == 2 +IS_PYTHON_3 = sys.version_info[0] == 3 + + +def isstring(obj): + if IS_PYTHON_2: + return isinstance(obj, basestring) + else: + return isinstance(obj, str) diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninfo.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninfo.py new file mode 100644 index 00000000..a551b74b --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninfo.py @@ -0,0 +1,58 @@ +from gitversionbuilder.utils import EqualityMixin, isstring +import re + + +class TagInterpretation(EqualityMixin): + def __init__(self, version_components, version_tag, is_dev_version): + assert (isinstance(version_components, list)) + assert (all(isstring(item) for item in version_components)) + assert (isstring(version_tag)) + self.version_components = version_components + self.version_tag = version_tag + self.is_stable = (not is_dev_version) and self.version_tag in ["", "stable", "final"] + + def __repr__(self): + return "%s(%r)" % (self.__class__, self.__dict__) + + +class VersionInfo(EqualityMixin): + def __init__(self, git_tag_name, git_commits_since_tag, git_commit_id, git_tag_exists, modified_since_commit): + assert (isstring(git_tag_name)) + assert (isinstance(git_commits_since_tag, int)) + assert (isstring(git_commit_id)) + assert (isinstance(git_tag_exists, bool)) + assert (isinstance(modified_since_commit, bool)) + self.git_tag_name = git_tag_name + self.git_commits_since_tag = git_commits_since_tag + self.git_commit_id = git_commit_id + self.git_tag_exists = git_tag_exists + self.modified_since_commit = modified_since_commit + self.is_dev = modified_since_commit or (not git_tag_exists) or (git_commits_since_tag != 0) + + def interpret_tag_name(self): + matched = re.match("^v?([0-9]+(?:\.[0-9]+)*)(?:-?((alpha|beta|rc|pre|m)[0-9]?|stable|final))?$", + self.git_tag_name, re.IGNORECASE) + if matched: + version_components = matched.group(1).split('.') + version_tag = matched.group(2) + if version_tag is None: + version_tag = "" + return TagInterpretation(version_components, version_tag, self.is_dev) + else: + return None + + @property + def version_string(self): + result = "" + if self.git_tag_exists: + result += self.git_tag_name + if self.git_commits_since_tag > 0: + if result != "": + result += "." + result += "dev%d+rev%s" % (self.git_commits_since_tag, self.git_commit_id) + if self.modified_since_commit: + result += "-modified" + return result + + def __repr__(self): + return "%s(%r)" % (self.__class__, self.__dict__) diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninfooutputter.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninfooutputter.py new file mode 100644 index 00000000..c8ccf4f0 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninfooutputter.py @@ -0,0 +1,118 @@ + + +def to_cpp(version_info): + return _CppFormatter().format(version_info) + + +def to_python(version_info): + return _PythonFormatter().format(version_info) + + +class _Formatter(object): + def __init__(self): + pass + + def format(self, version_info): + return self.main_formatter(version_info, + self._format_is_stable(version_info) + self._format_tag_interpretation(version_info)) + + def _format_is_stable(self, version_info): + if not version_info.git_tag_exists: + return self.is_stable_formatter(False) + else: + tag_interpretation = version_info.interpret_tag_name() + if tag_interpretation is not None: + return self.is_stable_formatter(tag_interpretation.is_stable) + else: + return "" + + def _format_tag_interpretation(self, version_info): + tag_interpretation = version_info.interpret_tag_name() + if tag_interpretation is None: + return "" + else: + formatted_version_components = self.version_components_formatter(tag_interpretation.version_components) + return self.tag_interpretation_formatter(tag_interpretation, formatted_version_components) + + +# ---------------------------------------- +# C++ Formatter +# ---------------------------------------- + +class _CppFormatter(_Formatter): + def main_formatter(self, version_info, other_variables): + return """ +// --------------------------------------------------- +// This file is autogenerated by git-version. +// DO NOT MODIFY! +// --------------------------------------------------- + +#pragma once +#ifndef MESSMER_GITVERSION_VERSION_H +#define MESSMER_GITVERSION_VERSION_H + +namespace version { + constexpr const char *VERSION_STRING = "%s"; + constexpr const char *GIT_TAG_NAME = "%s"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = %d; + constexpr const char *GIT_COMMIT_ID = "%s"; + constexpr bool MODIFIED_SINCE_COMMIT = %s; + constexpr bool IS_DEV_VERSION = %s; +%s +} + +#endif +""" % (version_info.version_string, version_info.git_tag_name, version_info.git_commits_since_tag, + version_info.git_commit_id, str(version_info.modified_since_commit).lower(), str(version_info.is_dev).lower(), + other_variables) + + def is_stable_formatter(self, is_stable): + return """ + constexpr bool IS_STABLE_VERSION = %s; +""" % str(is_stable).lower() + + def tag_interpretation_formatter(self, tag_interpretation, version_components): + return """ + constexpr const char *VERSION_COMPONENTS[] = %s; + constexpr const char *VERSION_TAG = "%s"; +""" % (version_components, tag_interpretation.version_tag) + + def version_components_formatter(self, version_components): + return "{\"" + "\", \"".join(version_components) + "\"}" + + +# ---------------------------------------- +# Python Formatter +# ---------------------------------------- + +class _PythonFormatter(_Formatter): + def main_formatter(self, version_info, other_variables): + return """ +# --------------------------------------------------- +# This file is autogenerated by git-version. +# DO NOT MODIFY! +# --------------------------------------------------- + +VERSION_STRING = "%s" +GIT_TAG_NAME = "%s" +GIT_COMMITS_SINCE_TAG = %d +GIT_COMMIT_ID = "%s" +MODIFIED_SINCE_COMMIT = %s +IS_DEV_VERSION = %s +%s +""" % (version_info.version_string, version_info.git_tag_name, version_info.git_commits_since_tag, + version_info.git_commit_id, version_info.modified_since_commit, version_info.is_dev, other_variables) + + def is_stable_formatter(self, is_stable): + return """ +IS_STABLE_VERSION = %s +""" % is_stable + + def tag_interpretation_formatter(self, tag_interpretation, version_components): + return """ +VERSION_COMPONENTS = %s +VERSION_TAG = "%s" +""" % (version_components, tag_interpretation.version_tag) + + def version_components_formatter(self, version_components): + return "[\"" + "\", \"".join(version_components) + "\"]" diff --git a/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninforeader.py b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninforeader.py new file mode 100644 index 00000000..434195e1 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/gitversionbuilder/versioninforeader.py @@ -0,0 +1,103 @@ +import subprocess +import os +import re +from gitversionbuilder import versioninfo, utils +from gitversionbuilder.utils import isstring + + +def from_git(git_directory): + with utils.ChDir(git_directory): + try: + with open(os.devnull, 'w') as devnull: + version_string = subprocess.check_output(["git", "describe", "--tags", "--long", "--abbrev=7"], + stderr=devnull).decode() + return _parse_git_version(version_string, _is_modified_since_commit_in_cwd()) + except subprocess.CalledProcessError: + # If there is no git tag, then the commits_since_tag returned by git is wrong + # (because they consider the branch HEAD the tag and there are 0 commits since the branch head). + # We want to return the total number of commits in the branch if there is no tag. + total_num_commits = _total_number_of_commits_in_cwd() + if total_num_commits > 0: + # There is no git tag, but there are commits + branch_name = _branch_name_in_cwd() + commit_id = _commit_id_in_cwd() + return versioninfo.VersionInfo(git_tag_name=branch_name, + git_commits_since_tag=total_num_commits, + git_commit_id=commit_id, + git_tag_exists=False, + modified_since_commit=_is_modified_since_commit_in_cwd()) + else: + # There are no commits yet + branch_name = "HEAD" + commit_id = "0" + return versioninfo.VersionInfo(git_tag_name=branch_name, + git_commits_since_tag=total_num_commits, + git_commit_id=commit_id, + git_tag_exists=False, + modified_since_commit=_cwd_is_not_empty()) + + +def _total_number_of_commits_in_cwd(): + try: + with open('/dev/null', 'w') as devnull: + return int(subprocess.check_output(["git", "rev-list", "HEAD", "--count"], stderr=devnull)) + except subprocess.CalledProcessError: + return 0 + + +def _branch_name_in_cwd(): + return subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).strip().decode() + + +def _commit_id_in_cwd(): + return subprocess.check_output(["git", "log", "--format=%h", "-n", "1"]).strip().decode() + + +def _is_modified_since_commit_in_cwd(): + return _there_are_modified_files_in_cwd() or _there_are_untracked_files_in_cwd() + + +def _there_are_untracked_files_in_cwd(): + return subprocess.check_output(["git", "ls-files", "--exclude-standard", "--others"]).strip().decode() != "" + + +def _there_are_modified_files_in_cwd(): + # Usually we'd like to use "git diff-index" here. + # But there seems to be a bug that when we run "chmod 755 file" on a file that already has 755 and is committed to git as such, the next run of "git diff-index" will show it as a difference. + # "git diff" seams to work + return (0 != subprocess.call(["git", "diff", "--exit-code", "--quiet", "HEAD"])) or (0 != subprocess.call(["git", "diff", "--cached", "--exit-code", "--quiet", "HEAD"])) + + +def _cwd_is_not_empty(): + all_entries = os.listdir(os.getcwd()) + nongit_entries = [entry for entry in all_entries if entry != ".git"] + return len(nongit_entries) != 0 + + +def _remove_prefix(prefix, string): + if string.startswith(prefix): + return string[len(prefix):] + else: + return string + + +class VersionParseError(Exception): + def __init__(self, version_string): + self.version_string = version_string + + def __str__(self): + return "Version not parseable: %s" % self.version_string + + +def _parse_git_version(git_version_string, modified_since_commit): + assert(isstring(git_version_string)) + matched = re.match("^([a-zA-Z0-9\.\-/]+)-([0-9]+)-g([0-9a-f]+)$", git_version_string) + if matched: + tag = matched.group(1) + commits_since_tag = int(matched.group(2)) + commit_id = matched.group(3) + return versioninfo.VersionInfo(git_tag_name=tag, git_commits_since_tag=commits_since_tag, + git_commit_id=commit_id, git_tag_exists=True, + modified_since_commit=modified_since_commit) + else: + raise VersionParseError(git_version_string) diff --git a/vendor/gitversion/gitversion-1.8/src/setup.py b/vendor/gitversion/gitversion-1.8/src/setup.py new file mode 100644 index 00000000..1c382854 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/setup.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +from setuptools import setup +from gitversionbuilder import main + +main.create_version_file(git_directory=".", output_file="gitversionbuilder/Version.py", lang="python") +version = main.get_version(git_directory=".") + +setup(name='git-version', + version=version.version_string, + description='Make git version information (e.g. git tag name, git commit id, ...) available to your C++ or python source files. A simple use case scenario is to output this version information when the application is called with "--version".', + author='Sebastian Messmer', + author_email='heinzisoft@web.de', + license='GPLv3', + url='https://github.com/smessmer/gitversion', + packages=['gitversionbuilder'], + tests_require=['tempdir'], + test_suite='test', + entry_points = { + 'console_scripts': [ + "git-version = gitversionbuilder.__main__:run_main" + ] + }, + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Programming Language :: Python", + "Programming Language :: C++", + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Code Generators", + "Topic :: Software Development :: Version Control" + ] + ) diff --git a/vendor/gitversion/gitversion-1.8/src/test/__init__.py b/vendor/gitversion/gitversion-1.8/src/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vendor/gitversion/gitversion-1.8/src/test/main_test.py b/vendor/gitversion/gitversion-1.8/src/test/main_test.py new file mode 100644 index 00000000..221c3997 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/test/main_test.py @@ -0,0 +1,237 @@ +import unittest +import subprocess +import os +import sys +from gitversionbuilder import main, Version +from gitversionbuilder.utils import ChDir +from test import test_utils +from test.test_utils import GitDir, TempFile + + +class IntegrationTest(unittest.TestCase, test_utils.CodeAsserts): + def test_call_version(self): + result = subprocess.check_output([sys.executable, "-m", "gitversionbuilder", "--version"], stderr=subprocess.STDOUT).strip().decode() + self.assertEqual(Version.VERSION_STRING, result) + + def test_call_help(self): + result = subprocess.check_output([sys.executable, "-m", "gitversionbuilder", "--help"], stderr=subprocess.STDOUT).decode() + self.assertRegexpMatches(result, "usage:") + + def test_call_python(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "dev1+rev%s" + GIT_TAG_NAME = "master" + GIT_COMMITS_SINCE_TAG = 1 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + """ % (commit_id[0:7], commit_id[0:7]) + with open("/dev/null", 'w') as devnull: + subprocess.check_call([sys.executable, "-m", "gitversionbuilder", "--lang", "python", "--dir", git.dir, out_file], + stdout=devnull) + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_call_cpp(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "dev1+rev%s"; + constexpr const char *GIT_TAG_NAME = "master"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 1; + constexpr const char *GIT_COMMIT_ID = "%s"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + } + + #endif + """ % (commit_id[0:7], commit_id[0:7]) + with open("/dev/null", 'w') as devnull: + subprocess.check_call([sys.executable, "-m", "gitversionbuilder", "--lang", "cpp", "--dir", git.dir, out_file], + stdout=devnull) + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_call_without_specifying_dir(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "dev1+rev%s" + GIT_TAG_NAME = "master" + GIT_COMMITS_SINCE_TAG = 1 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + """ % (commit_id[0:7], commit_id[0:7]) + script_dir = os.getcwd() + with ChDir(git.dir), open("/dev/null", 'w') as devnull: + subprocess.check_call([sys.executable, "-m", "gitversionbuilder", "--lang", "python", out_file], + stdout=devnull, env={"PYTHONPATH": script_dir}) + self.assertCodeEqual(expected, self.read_file(out_file)) + + +class MainTest(unittest.TestCase, test_utils.CodeAsserts): + def test_empty_git_python(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "dev1+rev%s" + GIT_TAG_NAME = "master" + GIT_COMMITS_SINCE_TAG = 1 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + """ % (commit_id[0:7], commit_id[0:7]) + main.create_version_file(git_directory=git.dir, output_file=out_file, lang="python") + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_empty_git_cpp(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "dev1+rev%s"; + constexpr const char *GIT_TAG_NAME = "master"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 1; + constexpr const char *GIT_COMMIT_ID = "%s"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + } + + #endif + """ % (commit_id[0:7], commit_id[0:7]) + main.create_version_file(git_directory=git.dir, output_file=out_file, lang="cpp") + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_tagged_git(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + git.create_git_tag("1.0.1") + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "1.0.1" + GIT_TAG_NAME = "1.0.1" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = False + IS_STABLE_VERSION = True + + VERSION_COMPONENTS = ["1", "0", "1"] + VERSION_TAG = "" + """ % commit_id[0:7] + main.create_version_file(git_directory=git.dir, output_file=out_file, lang="python") + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_tagged_git_with_commits_after_tag(self): + with GitDir() as git, TempFile() as out_file: + git.create_git_commit() + git.create_git_tag("1.0.1") + commit_id = git.create_git_commit() + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "1.0.1.dev1+rev%s" + GIT_TAG_NAME = "1.0.1" + GIT_COMMITS_SINCE_TAG = 1 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + + VERSION_COMPONENTS = ["1", "0", "1"] + VERSION_TAG = "" + """ % (commit_id[0:7], commit_id[0:7]) + main.create_version_file(git_directory=git.dir, output_file=out_file, lang="python") + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_tagged_git_with_different_tagname_scheme(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + git.create_git_tag("versionone") + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "versionone" + GIT_TAG_NAME = "versionone" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = False + """ % commit_id[0:7] + main.create_version_file(git_directory=git.dir, output_file=out_file, lang="python") + self.assertCodeEqual(expected, self.read_file(out_file)) + + def test_tagged_git_with_different_tagname_scheme_modified(self): + with GitDir() as git, TempFile() as out_file: + commit_id = git.create_git_commit() + git.create_git_tag("versionone") + git.add_untracked_file() + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "versionone-modified" + GIT_TAG_NAME = "versionone" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "%s" + MODIFIED_SINCE_COMMIT = True + IS_DEV_VERSION = True + """ % commit_id[0:7] + main.create_version_file(git_directory=git.dir, output_file=out_file, lang="python") + self.assertCodeEqual(expected, self.read_file(out_file)) + + +if __name__ == '__main__': + unittest.main() diff --git a/vendor/gitversion/gitversion-1.8/src/test/test_utils.py b/vendor/gitversion/gitversion-1.8/src/test/test_utils.py new file mode 100644 index 00000000..2bc362bc --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/test/test_utils.py @@ -0,0 +1,102 @@ +from tempfile import NamedTemporaryFile, mkdtemp +import os +import shutil +import subprocess +import random +import string +from gitversionbuilder.utils import ChDir + + +class CodeAsserts(object): + def assertCodeEqual(self, expected, actual): + self.assertEqual(self._normalize(expected), self._normalize(actual)) + + def read_file(self, filename): + with open(filename, 'r') as open_file: + return open_file.read() + + def _normalize(self, string): + lines = string.splitlines() + normalized_lines = map(self._normalize_line, lines) + without_empty_lines = filter(None, normalized_lines) + return "\n".join(without_empty_lines) + + def _normalize_line(self, line): + tokens = line.split() + return " ".join(tokens) + + +class TempFile(object): + def __enter__(self): + f = NamedTemporaryFile() + f.close() # This also deletes the file + self.filename = f.name + return f.name + + def __exit__(self, type, value, tb): + if os.path.isfile(self.filename): + os.remove(self.filename) + + +class GitDir(object): + def __enter__(self): + self.dir = mkdtemp() + self._setup_git() + return self + + def __exit__(self, type, value, traceback): + shutil.rmtree(self.dir) + + def _setup_git(self): + with ChDir(self.dir): + self._silent_call(["git", "init"]) + self._silent_call(["git", "config", "user.email", "you@example.com"]); + self._silent_call(["git", "config", "user.name", "Your Name"]); + + def create_git_commit(self): + self.add_tracked_file() + with ChDir(self.dir): + self._silent_call(["git", "commit", "-m", "message"]) + commit_id = self._silent_call(["git", "rev-parse", "--short", "HEAD"]).strip() + return commit_id + + def add_untracked_file(self): + filename = self._random_string(10) + with ChDir(self.dir): + self._silent_call(["touch", filename]) + return filename + + def add_tracked_file(self): + filename = self.add_untracked_file() + with ChDir(self.dir): + self._silent_call(["git", "add", filename]) + return filename + + def modify_file(self, filename): + content = self._random_string(10) + with ChDir(self.dir): + with open(filename, 'w') as file: + file.write(content) + + def _random_string(self, length): + return ''.join(random.choice(string.ascii_lowercase) for _ in range(length)) + + def _silent_call(self, command): + with open(os.devnull, 'w') as devnull: + return subprocess.check_output(command, stderr=devnull).decode() + + def create_git_branch(self, branch_name): + with ChDir(self.dir): + self._silent_call(["git", "checkout", "-b", branch_name]) + + def switch_git_branch(self, branch_name): + with ChDir(self.dir): + self._silent_call(["git", "checkout", branch_name]) + + def checkout_git_commit(self, commit_id): + with ChDir(self.dir): + self._silent_call(["git", "checkout", commit_id]) + + def create_git_tag(self, tag_name): + with ChDir(self.dir): + self._silent_call(["git", "tag", tag_name]) diff --git a/vendor/gitversion/gitversion-1.8/src/test/utils_test.py b/vendor/gitversion/gitversion-1.8/src/test/utils_test.py new file mode 100644 index 00000000..4e1d3a35 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/test/utils_test.py @@ -0,0 +1,21 @@ +import unittest +import os +from gitversionbuilder import utils + + +class UtilsTest(unittest.TestCase): + def test_chdir_to_root(self): + curdir = os.getcwd() + with utils.ChDir('/'): + self.assertEqual('/', os.getcwd()) + self.assertEqual(curdir, os.getcwd()) + + def test_chdir_to_parent(self): + curdir = os.getcwd() + with utils.ChDir('..'): + self.assertEqual(os.path.abspath(os.path.join(curdir, '..')), os.getcwd()) + self.assertEqual(curdir, os.getcwd()) + + +if __name__ == '__main__': + unittest.main() diff --git a/vendor/gitversion/gitversion-1.8/src/test/versioninfo_test.py b/vendor/gitversion/gitversion-1.8/src/test/versioninfo_test.py new file mode 100644 index 00000000..8339b30b --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/test/versioninfo_test.py @@ -0,0 +1,194 @@ +import unittest + +from gitversionbuilder.versioninfo import VersionInfo, TagInterpretation + + +class VersionInfoTest(unittest.TestCase): + def test_equals(self): + self.assertEqual(VersionInfo("v1.6.0", 20, "23fa", True, False), + VersionInfo("v1.6.0", 20, "23fa", True, False)) + + def test_not_equals_tag(self): + self.assertNotEqual(VersionInfo("v1.6.0", 20, "23fa", True, False), + VersionInfo("v1.6.1", 20, "23fa", True, False)) + + def test_not_equals_commits_since_tag(self): + self.assertNotEqual(VersionInfo("v1.6.1", 20, "23fa", True, False), + VersionInfo("v1.6.1", 21, "23fa", True, False)) + + def test_not_equals_commit_id(self): + self.assertNotEqual(VersionInfo("v1.6.1", 20, "23fa", True, False), + VersionInfo("v1.6.1", 20, "23fb", True, False)) + + def test_not_equals_is_tag(self): + self.assertNotEqual(VersionInfo("v1.6.1", 20, "23fa", True, False), + VersionInfo("v1.6.1", 20, "23fa", False, False)) + + def test_not_equals_modified_since_commit(self): + self.assertNotEqual(VersionInfo("v1.6.1", 20, "23fa", True, False), + VersionInfo("v1.6.1", 20, "23fa", True, True)) + + def test_version_string_for_tag(self): + self.assertEqual("v1.5", VersionInfo("v1.5", 0, "23fa", True, False).version_string) + + def test_version_string_for_tag_modified(self): + self.assertEqual("v1.5-modified", VersionInfo("v1.5", 0, "23fa", True, True).version_string) + + def test_version_string_with_no_tag(self): + self.assertEqual("dev2+rev23fa", VersionInfo("develop", 2, "23fa", False, False).version_string) + + def test_version_string_with_no_tag_modified(self): + self.assertEqual("dev2+rev23fa-modified", VersionInfo("develop", 2, "23fa", False, True).version_string) + + def test_version_string_with_commits_since_tag(self): + self.assertEqual("v1.5.dev2+rev23fa", VersionInfo("v1.5", 2, "23fa", True, False).version_string) + + def test_version_string_with_commits_since_tag_modified(self): + self.assertEqual("v1.5.dev2+rev23fa-modified", VersionInfo("v1.5", 2, "23fa", True, True).version_string) + + def test_is_dev_1(self): + self.assertTrue(VersionInfo("1.0", 1, "23fa", True, False).is_dev) + + def test_is_dev_123(self): + self.assertTrue(VersionInfo("1.0", 123, "23fa", True, False).is_dev) + + def test_is_dev_no_commits(self): + self.assertTrue(VersionInfo("1.0", 0, "23fa", False, False).is_dev) + + def test_is_dev_modified(self): + self.assertTrue(VersionInfo("1.0", 0, "23fa", True, True).is_dev) + + def test_is_not_dev(self): + self.assertFalse(VersionInfo("1.0", 0, "23fa", True, False).is_dev) + + def test_interpret_valid_tag_name(self): + self.assertEqual(TagInterpretation(["1"], "", False), + VersionInfo("1", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_plain(self): + self.assertEqual(TagInterpretation(["1", "0"], "", False), + VersionInfo("1.0", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_alpha(self): + self.assertEqual(TagInterpretation(["1", "0"], "alpha", False), + VersionInfo("1.0alpha", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_alpha_number(self): + self.assertEqual(TagInterpretation(["1", "0"], "alpha2", False), + VersionInfo("1.0alpha2", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_beta(self): + self.assertEqual(TagInterpretation(["1", "0"], "beta", False), + VersionInfo("1.0beta", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_beta_number(self): + self.assertEqual(TagInterpretation(["1", "0"], "beta3", False), + VersionInfo("1.0beta3", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_with_dash(self): + self.assertEqual(TagInterpretation(["1", "02", "3"], "beta", False), + VersionInfo("1.02.3-beta", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_with_zeroes_in_component(self): + self.assertEqual(TagInterpretation(["1", "020", "3"], "beta", False), + VersionInfo("1.020.3-beta", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_stable(self): + self.assertEqual(TagInterpretation(["1", "02"], "stable", False), + VersionInfo("1.02-stable", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_final(self): + self.assertEqual(TagInterpretation(["0", "8"], "final", False), + VersionInfo("0.8final", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_M3(self): + self.assertEqual(TagInterpretation(["0", "8"], "M3", False), + VersionInfo("0.8-M3", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_m3(self): + self.assertEqual(TagInterpretation(["0", "8"], "m3", False), + VersionInfo("0.8m3", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_rc2(self): + self.assertEqual(TagInterpretation(["0", "8"], "rc2", False), + VersionInfo("0.8rc2", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_RC2(self): + self.assertEqual(TagInterpretation(["0", "8"], "RC2", False), + VersionInfo("0.8-RC2", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_pre2(self): + self.assertEqual(TagInterpretation(["0", "8"], "pre2", False), + VersionInfo("0.8-pre2", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_of_dev_version_1(self): + self.assertEqual(TagInterpretation(["0", "8"], "", True), + VersionInfo("0.8", 1, "23fa", True, False).interpret_tag_name()) + + def test_interpret_valid_tag_name_of_dev_version_2(self): + self.assertEqual(TagInterpretation(["0", "8"], "", True), + VersionInfo("0.8", 123, "23fa", True, False).interpret_tag_name()) + + def test_interpret_invalid_tag_name(self): + self.assertEqual(None, VersionInfo("develop", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_invalid_tag_name_invalid_tag(self): + self.assertEqual(None, VersionInfo("1.0invalid", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_invalid_tag_name_invalid_tag_with_dash(self): + self.assertEqual(None, VersionInfo("1.0-invalid", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_invalid_tag_name_invalid_number(self): + self.assertEqual(None, VersionInfo("develop-alpha", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_invalid_tag_name_invalid_component_separator(self): + self.assertEqual(None, VersionInfo("1,0-alpha", 0, "23fa", True, False).interpret_tag_name()) + + def test_interpret_invalid_tag_name_invalid_missing_component(self): + self.assertEqual(None, VersionInfo("1,-alpha", 0, "23fa", True, False).interpret_tag_name()) + + +class TagInterpretationTest(unittest.TestCase): + def test_equals(self): + self.assertEqual(TagInterpretation(["1", "2"], "alpha", False), + TagInterpretation(["1", "2"], "alpha", False)) + + def test_not_equals_version_tag(self): + self.assertNotEqual(TagInterpretation(["1", "2"], "beta", False), + TagInterpretation(["1", "2"], "alpha", False)) + + def test_not_equals_components_1(self): + self.assertNotEqual(TagInterpretation(["1"], "alpha", False), + TagInterpretation(["1", "2"], "alpha", False)) + + def test_not_equals_components_2(self): + self.assertNotEqual(TagInterpretation(["1", "3"], "alpha", False), + TagInterpretation(["1", "2"], "alpha", False)) + + def test_alpha_is_not_stable(self): + self.assertFalse(TagInterpretation(["1"], "alpha", False).is_stable) + + def test_beta_is_not_stable(self): + self.assertFalse(TagInterpretation(["1"], "beta", False).is_stable) + + def test_rc3_is_not_stable(self): + self.assertFalse(TagInterpretation(["1"], "rc3", False).is_stable) + + def test_M3_is_not_stable(self): + self.assertFalse(TagInterpretation(["1"], "M3", False).is_stable) + + def test_stable_is_stable(self): + self.assertTrue(TagInterpretation(["1"], "stable", False).is_stable) + + def test_final_is_stable(self): + self.assertTrue(TagInterpretation(["1"], "final", False).is_stable) + + def test_plain_is_stable(self): + self.assertTrue(TagInterpretation(["1"], "", False).is_stable) + + def test_dev_is_not_stable(self): + self.assertFalse(TagInterpretation(["1"], "", True).is_stable) + + +if __name__ == '__main__': + unittest.main() diff --git a/vendor/gitversion/gitversion-1.8/src/test/versioninfooutputter_test.py b/vendor/gitversion/gitversion-1.8/src/test/versioninfooutputter_test.py new file mode 100644 index 00000000..b38eaa64 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/test/versioninfooutputter_test.py @@ -0,0 +1,341 @@ +import unittest + +from gitversionbuilder import versioninfo, versioninfooutputter +from test import test_utils + + +class VersionInfoOutputterTest(unittest.TestCase, test_utils.CodeAsserts): + def test_output_cpp(self): + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "versionone.dev2+rev230a"; + constexpr const char *GIT_TAG_NAME = "versionone"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 2; + constexpr const char *GIT_COMMIT_ID = "230a"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + } + + #endif + """ + actual = versioninfooutputter.to_cpp(versioninfo.VersionInfo("versionone", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_cpp_without_tag(self): + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "dev2+rev230a"; + constexpr const char *GIT_TAG_NAME = "develop"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 2; + constexpr const char *GIT_COMMIT_ID = "230a"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + } + + #endif + """ + actual = versioninfooutputter.to_cpp(versioninfo.VersionInfo("develop", 2, "230a", False, False)) + self.assertCodeEqual(expected, actual) + + def test_output_cpp_with_version_info(self): + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "1.6.dev2+rev230a"; + constexpr const char *GIT_TAG_NAME = "1.6"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 2; + constexpr const char *GIT_COMMIT_ID = "230a"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + + constexpr const char *VERSION_COMPONENTS[] = {"1", "6"}; + constexpr const char *VERSION_TAG = ""; + } + + #endif + """ + actual = versioninfooutputter.to_cpp(versioninfo.VersionInfo("1.6", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_cpp_with_version_info_with_leading_zero(self): + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "1.06.dev2+rev230a"; + constexpr const char *GIT_TAG_NAME = "1.06"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 2; + constexpr const char *GIT_COMMIT_ID = "230a"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + + constexpr const char *VERSION_COMPONENTS[] = {"1", "06"}; + constexpr const char *VERSION_TAG = ""; + } + + #endif + """ + actual = versioninfooutputter.to_cpp(versioninfo.VersionInfo("1.06", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_cpp_with_version_info_and_version_tag(self): + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "1.6alpha.dev2+rev230a"; + constexpr const char *GIT_TAG_NAME = "1.6alpha"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 2; + constexpr const char *GIT_COMMIT_ID = "230a"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + + constexpr const char *VERSION_COMPONENTS[] = {"1", "6"}; + constexpr const char *VERSION_TAG = "alpha"; + } + + #endif + """ + actual = versioninfooutputter.to_cpp(versioninfo.VersionInfo("1.6alpha", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_cpp_with_version_info_and_dashed_version_tag(self): + expected = """ + // --------------------------------------------------- + // This file is autogenerated by git-version. + // DO NOT MODIFY! + // --------------------------------------------------- + + #pragma once + #ifndef MESSMER_GITVERSION_VERSION_H + #define MESSMER_GITVERSION_VERSION_H + + namespace version { + constexpr const char *VERSION_STRING = "1.6-alpha.dev2+rev230a"; + constexpr const char *GIT_TAG_NAME = "1.6-alpha"; + constexpr const unsigned int GIT_COMMITS_SINCE_TAG = 2; + constexpr const char *GIT_COMMIT_ID = "230a"; + constexpr bool MODIFIED_SINCE_COMMIT = false; + constexpr bool IS_DEV_VERSION = true; + constexpr bool IS_STABLE_VERSION = false; + + constexpr const char *VERSION_COMPONENTS[] = {"1", "6"}; + constexpr const char *VERSION_TAG = "alpha"; + } + + #endif + """ + actual = versioninfooutputter.to_cpp(versioninfo.VersionInfo("1.6-alpha", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "versiontwo.dev2+rev230a" + GIT_TAG_NAME = "versiontwo" + GIT_COMMITS_SINCE_TAG = 2 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("versiontwo", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python_with_version_info(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "0.8.dev2+rev230a" + GIT_TAG_NAME = "0.8" + GIT_COMMITS_SINCE_TAG = 2 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + + VERSION_COMPONENTS = ["0", "8"] + VERSION_TAG = "" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("0.8", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python_with_version_info_and_version_tag(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "v1.0alpha.dev2+rev230a" + GIT_TAG_NAME = "v1.0alpha" + GIT_COMMITS_SINCE_TAG = 2 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + + VERSION_COMPONENTS = ["1", "0"] + VERSION_TAG = "alpha" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("v1.0alpha", 2, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python_with_version_info_and_version_tag_modified(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "v1.0alpha.dev2+rev230a-modified" + GIT_TAG_NAME = "v1.0alpha" + GIT_COMMITS_SINCE_TAG = 2 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = True + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + + VERSION_COMPONENTS = ["1", "0"] + VERSION_TAG = "alpha" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("v1.0alpha", 2, "230a", True, True)) + self.assertCodeEqual(expected, actual) + + def test_output_python_instable_nondev(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "v1.0alpha" + GIT_TAG_NAME = "v1.0alpha" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = False + IS_STABLE_VERSION = False + + VERSION_COMPONENTS = ["1", "0"] + VERSION_TAG = "alpha" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("v1.0alpha", 0, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python_stable_nondev_plain(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "v1.0" + GIT_TAG_NAME = "v1.0" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = False + IS_STABLE_VERSION = True + + VERSION_COMPONENTS = ["1", "0"] + VERSION_TAG = "" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("v1.0", 0, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python_stable_stable(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "v1.0-stable" + GIT_TAG_NAME = "v1.0-stable" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = False + IS_DEV_VERSION = False + IS_STABLE_VERSION = True + + VERSION_COMPONENTS = ["1", "0"] + VERSION_TAG = "stable" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("v1.0-stable", 0, "230a", True, False)) + self.assertCodeEqual(expected, actual) + + def test_output_python_modified(self): + expected = """ + # --------------------------------------------------- + # This file is autogenerated by git-version. + # DO NOT MODIFY! + # --------------------------------------------------- + + VERSION_STRING = "v1.0-stable-modified" + GIT_TAG_NAME = "v1.0-stable" + GIT_COMMITS_SINCE_TAG = 0 + GIT_COMMIT_ID = "230a" + MODIFIED_SINCE_COMMIT = True + IS_DEV_VERSION = True + IS_STABLE_VERSION = False + + VERSION_COMPONENTS = ["1", "0"] + VERSION_TAG = "stable" + """ + actual = versioninfooutputter.to_python(versioninfo.VersionInfo("v1.0-stable", 0, "230a", True, True)) + self.assertCodeEqual(expected, actual) + + +if __name__ == '__main__': + unittest.main() diff --git a/vendor/gitversion/gitversion-1.8/src/test/versioninforeader_test.py b/vendor/gitversion/gitversion-1.8/src/test/versioninforeader_test.py new file mode 100644 index 00000000..58d2dca3 --- /dev/null +++ b/vendor/gitversion/gitversion-1.8/src/test/versioninforeader_test.py @@ -0,0 +1,286 @@ +import unittest + +from gitversionbuilder import versioninforeader +from gitversionbuilder.versioninfo import VersionInfo +from test.test_utils import GitDir + + +class ParseGitVersionTest(unittest.TestCase): + def test_parse_git_version_simple(self): + obj = versioninforeader._parse_git_version("v1.6-0-g3f2a", False) + self.assertEqual(VersionInfo("v1.6", 0, "3f2a", True, False), obj) + + def test_parse_git_version_with_commits_since_tag(self): + obj = versioninforeader._parse_git_version("v1.6.3-23-g49302", False) + self.assertEqual(VersionInfo("v1.6.3", 23, "49302", True, False), obj) + + def test_parse_git_version_with_dashes_in_tag(self): + obj = versioninforeader._parse_git_version("v1.6.3-23-20-gfade", False) + self.assertEqual(VersionInfo("v1.6.3-23", 20, "fade", True, False), obj) + + def test_parse_git_version_with_slashes_in_tag(self): + obj = versioninforeader._parse_git_version("/heads/develop-20-gfade", False) + self.assertEqual(VersionInfo("/heads/develop", 20, "fade", True, False), obj) + + def test_parse_git_version_missing_tag(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "23-gfade", False) + + def test_parse_git_version_empty_tag(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "-23-gfade", False) + + def test_parse_git_version_missing_commits_since_tag(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "v2.3-gfade", + False) + + def test_parse_git_version_empty_commits_since_tag(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "v2.3--gfade", + False) + + def test_parse_git_version_commits_since_tag_not_int(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "v2.3-a2-gfade", + False) + + def test_parse_git_version_missing_commit_id(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "v2.3-20", False) + + def test_parse_git_version_empty_commit_id(self): + self.assertRaises(versioninforeader.VersionParseError, versioninforeader._parse_git_version, "v2.3-20-", False) + + +class VersionInfoReaderTest(unittest.TestCase): + def test_empty(self): + with GitDir() as dir: + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("HEAD", 0, "0", False, False), version_info) + + def test_commit(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 1, commit_id, False, False), version_info) + + def test_commit_commit(self): + with GitDir() as dir: + dir.create_git_commit() + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 2, commit_id, False, False), version_info) + + def test_commit_tag(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_tag("tagname") + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("tagname", 0, commit_id, True, False), version_info) + + def test_commit_tag_commit(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("tagname") + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("tagname", 1, commit_id, True, False), version_info) + + def test_commit_tag_commit_commit(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("tagname") + dir.create_git_commit() + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("tagname", 2, commit_id, True, False), version_info) + + def test_commit_tag_commit_tag_commit(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("tagname") + dir.create_git_commit() + dir.create_git_tag("mytag2") + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag2", 1, commit_id, True, False), version_info) + + def test_commit_commit_tag_rewind(self): + with GitDir() as dir: + dir.create_git_commit() + commit_id = dir.create_git_commit() + dir.create_git_commit() + dir.create_git_tag("tagname") + dir.checkout_git_commit(commit_id) + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("HEAD", 2, commit_id, False, False), version_info) + + def test_commit_tag_commit_commit_tag_rewind(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("tagname") + commit_id = dir.create_git_commit() + dir.create_git_commit() + dir.create_git_tag("mytag2") + dir.checkout_git_commit(commit_id) + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("tagname", 1, commit_id, True, False), version_info) + + def test_commit_branch(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_branch("newbranch") + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("newbranch", 1, commit_id, False, False), version_info) + + def test_commit_branch_commit(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_branch("newbranch") + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("newbranch", 2, commit_id, False, False), version_info) + + def test_commit_tag_commit_branch_commit(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("mytag") + dir.create_git_commit() + dir.create_git_branch("newbranch") + commit_id = dir.create_git_commit() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 2, commit_id, True, False), version_info) + + def test_commit_branchedcommit(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_branch("newbranch") + dir.create_git_commit() + dir.switch_git_branch("master") + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 1, commit_id, False, False), version_info) + + def test_commit_branchedtaggedcommit(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_branch("newbranch") + dir.create_git_commit() + dir.create_git_tag("mytag") + dir.switch_git_branch("master") + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 1, commit_id, False, False), version_info) + + def test_commit_tag_branchedtaggedcommit(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_tag("originaltag") + dir.create_git_branch("newbranch") + dir.create_git_commit() + dir.create_git_tag("newtag") + dir.switch_git_branch("master") + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("originaltag", 0, commit_id, True, False), version_info) + + def test_commit_tag_commit_branchedtaggedcommit(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("originaltag") + commit_id = dir.create_git_commit() + dir.create_git_branch("newbranch") + dir.create_git_commit() + dir.create_git_tag("newtag") + dir.switch_git_branch("master") + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("originaltag", 1, commit_id, True, False), version_info) + + + + # ------------------------------------------------------------- + # Test that local uncommitted changes are recognized correctly + # ------------------------------------------------------------- + + def test_empty_with_untracked_file(self): + with GitDir() as dir: + dir.add_untracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("HEAD", 0, "0", False, True), version_info) + + def test_empty_with_tracked_file(self): + with GitDir() as dir: + dir.add_tracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("HEAD", 0, "0", False, True), version_info) + + def test_commit_with_untracked_file(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.add_untracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 1, commit_id, False, True), version_info) + + def test_commit_with_tracked_file(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.add_tracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 1, commit_id, False, True), version_info) + + def test_commit_with_modified_file(self): + with GitDir() as dir: + filename = dir.add_tracked_file() + commit_id = dir.create_git_commit() + dir.modify_file(filename) + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("master", 1, commit_id, False, True), version_info) + + def test_tag_with_untracked_file(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_tag("mytag") + dir.add_untracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 0, commit_id, True, True), version_info) + + def test_tag_with_tracked_file(self): + with GitDir() as dir: + commit_id = dir.create_git_commit() + dir.create_git_tag("mytag") + dir.add_tracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 0, commit_id, True, True), version_info) + + def test_tag_with_modified_file(self): + with GitDir() as dir: + filename = dir.add_tracked_file() + commit_id = dir.create_git_commit() + dir.create_git_tag("mytag") + dir.modify_file(filename) + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 0, commit_id, True, True), version_info) + + def test_tag_commit_with_untracked_file(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("mytag") + commit_id = dir.create_git_commit() + dir.add_untracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 1, commit_id, True, True), version_info) + + def test_tag_commit_with_tracked_file(self): + with GitDir() as dir: + dir.create_git_commit() + dir.create_git_tag("mytag") + commit_id = dir.create_git_commit() + dir.add_tracked_file() + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 1, commit_id, True, True), version_info) + + def test_tag_commit_with_modified_file(self): + with GitDir() as dir: + filename = dir.add_tracked_file() + dir.create_git_commit() + dir.create_git_tag("mytag") + commit_id = dir.create_git_commit() + dir.modify_file(filename) + version_info = versioninforeader.from_git(dir.dir) + self.assertEqual(VersionInfo("mytag", 1, commit_id, True, True), version_info) + + +if __name__ == '__main__': + unittest.main() diff --git a/vendor/googletest/CMakeLists.txt b/vendor/googletest/CMakeLists.txt new file mode 100644 index 00000000..28fd07c9 --- /dev/null +++ b/vendor/googletest/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(gtest-1.7.0) + +project (googletest) +add_library(${PROJECT_NAME} dummy.cpp) +target_link_libraries(${PROJECT_NAME} gtest gmock gmock_main) +target_include_directories(${PROJECT_NAME} SYSTEM INTERFACE ${gtest_INCLUDE_DIRS}/include SYSTEM ${gmock_INCLUDE_DIRS}/include) diff --git a/vendor/googletest/dummy.cpp b/vendor/googletest/dummy.cpp new file mode 100644 index 00000000..e69de29b diff --git a/vendor/googletest/gtest-1.7.0/CMakeLists.txt b/vendor/googletest/gtest-1.7.0/CMakeLists.txt new file mode 100644 index 00000000..8d2b552e --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.6.2) + +project( googletest-distribution ) + +enable_testing() + +option(BUILD_GTEST "Builds the googletest subproject" OFF) + +#Note that googlemock target already builds googletest +option(BUILD_GMOCK "Builds the googlemock subproject" ON) + +if(BUILD_GMOCK) + add_subdirectory( googlemock ) +elseif(BUILD_GTEST) + add_subdirectory( googletest ) +endif() diff --git a/vendor/googletest/gtest-1.7.0/README.md b/vendor/googletest/gtest-1.7.0/README.md new file mode 100644 index 00000000..20728771 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/README.md @@ -0,0 +1,141 @@ + +# Google Test # + +[![Build Status](https://travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest) + +Welcome to **Google Test**, Google's C++ test framework! + +This repository is a merger of the formerly separate GoogleTest and +GoogleMock projects. These were so closely related that it makes sense to +maintain and release them together. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Getting started information for **Google Test** is available in the +[Google Test Primer](googletest/docs/Primer.md) documentation. + +**Google Mock** is an extension to Google Test for writing and using C++ mock +classes. See the separate [Google Mock documentation](googlemock/README.md). + +More detailed documentation for googletest (including build instructions) are +in its interior [googletest/README.md](googletest/README.md) file. + +## Features ## + + * An [XUnit](https://en.wikipedia.org/wiki/XUnit) test framework. + * Test discovery. + * A rich set of assertions. + * User-defined assertions. + * Death tests. + * Fatal and non-fatal failures. + * Value-parameterized tests. + * Type-parameterized tests. + * Various options for running the tests. + * XML test report generation. + +## Platforms ## + +Google test has been used on a variety of platforms: + + * Linux + * Mac OS X + * Windows + * Cygwin + * MinGW + * Windows Mobile + * Symbian + +## Who Is Using Google Test? ## + +In addition to many internal projects at Google, Google Test is also used by +the following notable projects: + + * The [Chromium projects](http://www.chromium.org/) (behind the Chrome + browser and Chrome OS). + * The [LLVM](http://llvm.org/) compiler. + * [Protocol Buffers](https://github.com/google/protobuf), Google's data + interchange format. + * The [OpenCV](http://opencv.org/) computer vision library. + +## Related Open Source Projects ## + +[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that runs +your test binary, allows you to track its progress via a progress bar, and +displays a list of test failures. Clicking on one shows failure text. Google +Test UI is written in C#. + +[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event +listener for Google Test that implements the +[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test +result output. If your test runner understands TAP, you may find it useful. + +## Requirements ## + +Google Test is designed to have fairly minimal requirements to build +and use with your projects, but there are some. Currently, we support +Linux, Windows, Mac OS X, and Cygwin. We will also make our best +effort to support other platforms (e.g. Solaris, AIX, and z/OS). +However, since core members of the Google Test project have no access +to these platforms, Google Test may have outstanding issues there. If +you notice any problems on your platform, please notify +. Patches for fixing them are +even more welcome! + +### Linux Requirements ### + +These are the base requirements to build and use Google Test from a source +package (as described below): + + * GNU-compatible Make or gmake + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98-standard-compliant compiler + +### Windows Requirements ### + + * Microsoft Visual C++ v7.1 or newer + +### Cygwin Requirements ### + + * Cygwin v1.5.25-14 or newer + +### Mac OS X Requirements ### + + * Mac OS X v10.4 Tiger or newer + * XCode Developer Tools + +### Requirements for Contributors ### + +We welcome patches. If you plan to contribute a patch, you need to +build Google Test and its own tests from a git checkout (described +below), which has further requirements: + + * [Python](https://www.python.org/) v2.3 or newer (for running some of + the tests and re-generating certain source files from templates) + * [CMake](https://cmake.org/) v2.6.4 or newer + +## Regenerating Source Files ## + +Some of Google Test's source files are generated from templates (not +in the C++ sense) using a script. +For example, the +file include/gtest/internal/gtest-type-util.h.pump is used to generate +gtest-type-util.h in the same directory. + +You don't need to worry about regenerating the source files +unless you need to modify them. You would then modify the +corresponding `.pump` files and run the '[pump.py](googletest/scripts/pump.py)' +generator script. See the [Pump Manual](googletest/docs/PumpManual.md). + +### Contributing Code ### + +We welcome patches. Please read the +[Developer's Guide](googletest/docs/DevGuide.md) +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + +Happy testing! diff --git a/vendor/googletest/gtest-1.7.0/googlemock/CHANGES b/vendor/googletest/gtest-1.7.0/googlemock/CHANGES new file mode 100644 index 00000000..d6f2f760 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/CHANGES @@ -0,0 +1,126 @@ +Changes for 1.7.0: + +* All new improvements in Google Test 1.7.0. +* New feature: matchers DoubleNear(), FloatNear(), + NanSensitiveDoubleNear(), NanSensitiveFloatNear(), + UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(), + WhenSortedBy(), IsEmpty(), and SizeIs(). +* Improvement: Google Mock can now be built as a DLL. +* Improvement: when compiled by a C++11 compiler, matchers AllOf() + and AnyOf() can accept an arbitrary number of matchers. +* Improvement: when compiled by a C++11 compiler, matchers + ElementsAreArray() can accept an initializer list. +* Improvement: when exceptions are enabled, a mock method with no + default action now throws instead crashing the test. +* Improvement: added class testing::StringMatchResultListener to aid + definition of composite matchers. +* Improvement: function return types used in MOCK_METHOD*() macros can + now contain unprotected commas. +* Improvement (potentially breaking): EXPECT_THAT() and ASSERT_THAT() + are now more strict in ensuring that the value type and the matcher + type are compatible, catching potential bugs in tests. +* Improvement: Pointee() now works on an optional. +* Improvement: the ElementsAreArray() matcher can now take a vector or + iterator range as input, and makes a copy of its input elements + before the conversion to a Matcher. +* Improvement: the Google Mock Generator can now generate mocks for + some class templates. +* Bug fix: mock object destruction triggerred by another mock object's + destruction no longer hangs. +* Improvement: Google Mock Doctor works better with newer Clang and + GCC now. +* Compatibility fixes. +* Bug/warning fixes. + +Changes for 1.6.0: + +* Compilation is much faster and uses much less memory, especially + when the constructor and destructor of a mock class are moved out of + the class body. +* New matchers: Pointwise(), Each(). +* New actions: ReturnPointee() and ReturnRefOfCopy(). +* CMake support. +* Project files for Visual Studio 2010. +* AllOf() and AnyOf() can handle up-to 10 arguments now. +* Google Mock doctor understands Clang error messages now. +* SetArgPointee<> now accepts string literals. +* gmock_gen.py handles storage specifier macros and template return + types now. +* Compatibility fixes. +* Bug fixes and implementation clean-ups. +* Potentially incompatible changes: disables the harmful 'make install' + command in autotools. + +Potentially breaking changes: + +* The description string for MATCHER*() changes from Python-style + interpolation to an ordinary C++ string expression. +* SetArgumentPointee is deprecated in favor of SetArgPointee. +* Some non-essential project files for Visual Studio 2005 are removed. + +Changes for 1.5.0: + + * New feature: Google Mock can be safely used in multi-threaded tests + on platforms having pthreads. + * New feature: function for printing a value of arbitrary type. + * New feature: function ExplainMatchResult() for easy definition of + composite matchers. + * The new matcher API lets user-defined matchers generate custom + explanations more directly and efficiently. + * Better failure messages all around. + * NotNull() and IsNull() now work with smart pointers. + * Field() and Property() now work when the matcher argument is a pointer + passed by reference. + * Regular expression matchers on all platforms. + * Added GCC 4.0 support for Google Mock Doctor. + * Added gmock_all_test.cc for compiling most Google Mock tests + in a single file. + * Significantly cleaned up compiler warnings. + * Bug fixes, better test coverage, and implementation clean-ups. + + Potentially breaking changes: + + * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher() + need to be updated after upgrading to Google Mock 1.5.0; matchers defined + using MATCHER or MATCHER_P* aren't affected. + * Dropped support for 'make install'. + +Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of +Google Test): + + * Works in more environments: Symbian and minGW, Visual C++ 7.1. + * Lighter weight: comes with our own implementation of TR1 tuple (no + more dependency on Boost!). + * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks. + * New feature: ACTION_TEMPLATE for defining templatized actions. + * New feature: the .After() clause for specifying expectation order. + * New feature: the .With() clause for for specifying inter-argument + constraints. + * New feature: actions ReturnArg(), ReturnNew(...), and + DeleteArg(). + * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(), + and Contains(). + * New feature: utility class MockFunction, useful for checkpoints, etc. + * New feature: functions Value(x, m) and SafeMatcherCast(m). + * New feature: copying a mock object is rejected at compile time. + * New feature: a script for fusing all Google Mock and Google Test + source files for easy deployment. + * Improved the Google Mock doctor to diagnose more diseases. + * Improved the Google Mock generator script. + * Compatibility fixes for Mac OS X and gcc. + * Bug fixes and implementation clean-ups. + +Changes for 1.1.0: + + * New feature: ability to use Google Mock with any testing framework. + * New feature: macros for easily defining new matchers + * New feature: macros for easily defining new actions. + * New feature: more container matchers. + * New feature: actions for accessing function arguments and throwing + exceptions. + * Improved the Google Mock doctor script for diagnosing compiler errors. + * Bug fixes and implementation clean-ups. + +Changes for 1.0.0: + + * Initial Open Source release of Google Mock diff --git a/vendor/googletest/gtest-1.7.0/googlemock/CMakeLists.txt b/vendor/googletest/gtest-1.7.0/googlemock/CMakeLists.txt new file mode 100644 index 00000000..beb259a2 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/CMakeLists.txt @@ -0,0 +1,202 @@ +######################################################################## +# CMake build script for Google Mock. +# +# To run the tests for Google Mock itself on Linux, use 'make test' or +# ctest. You can select which tests to run using 'ctest -R regex'. +# For more options, run 'ctest --help'. + +# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to +# make it prominent in the GUI. +option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) + +option(gmock_build_tests "Build all of Google Mock's own tests." OFF) + +# A directory to find Google Test sources. +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gtest/CMakeLists.txt") + set(gtest_dir gtest) +else() + set(gtest_dir ../googletest) +endif() + +# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build(). +include("${gtest_dir}/cmake/hermetic_build.cmake" OPTIONAL) + +if (COMMAND pre_project_set_up_hermetic_build) + # Google Test also calls hermetic setup functions from add_subdirectory, + # although its changes will not affect things at the current scope. + pre_project_set_up_hermetic_build() +endif() + +######################################################################## +# +# Project-wide settings + +# Name of the project. +# +# CMake files in this project can refer to the root source directory +# as ${gmock_SOURCE_DIR} and to the root binary directory as +# ${gmock_BINARY_DIR}. +# Language "C" is required for find_package(Threads). +project(gmock CXX C) +cmake_minimum_required(VERSION 2.6.2) + +if (COMMAND set_up_hermetic_build) + set_up_hermetic_build() +endif() + +# Instructs CMake to process Google Test's CMakeLists.txt and add its +# targets to the current scope. We are placing Google Test's binary +# directory in a subdirectory of our own as VC compilation may break +# if they are the same (the default). +add_subdirectory("${gtest_dir}" "${gmock_BINARY_DIR}/gtest") + +# Although Google Test's CMakeLists.txt calls this function, the +# changes there don't affect the current scope. Therefore we have to +# call it again here. +config_compiler_and_linker() # from ${gtest_dir}/cmake/internal_utils.cmake + +# Adds Google Mock's and Google Test's header directories to the search path. +include_directories("${gmock_SOURCE_DIR}/include" + "${gmock_SOURCE_DIR}" + "${gtest_SOURCE_DIR}/include" + # This directory is needed to build directly from Google + # Test sources. + "${gtest_SOURCE_DIR}") + +# Summary of tuple support for Microsoft Visual Studio: +# Compiler version(MS) version(cmake) Support +# ---------- ----------- -------------- ----------------------------- +# <= VS 2010 <= 10 <= 1600 Use Google Tests's own tuple. +# VS 2012 11 1700 std::tr1::tuple + _VARIADIC_MAX=10 +# VS 2013 12 1800 std::tr1::tuple +if (MSVC AND MSVC_VERSION EQUAL 1700) + add_definitions(/D _VARIADIC_MAX=10) +endif() + +######################################################################## +# +# Defines the gmock & gmock_main libraries. User tests should link +# with one of them. + +# Google Mock libraries. We build them using more strict warnings than what +# are used for other targets, to ensure that Google Mock can be compiled by +# a user aggressive about warnings. +cxx_library(gmock + "${cxx_strict}" + "${gtest_dir}/src/gtest-all.cc" + src/gmock-all.cc) + +cxx_library(gmock_main + "${cxx_strict}" + "${gtest_dir}/src/gtest-all.cc" + src/gmock-all.cc + src/gmock_main.cc) + +# If the CMake version supports it, attach header directory information +# to the targets for when we are part of a parent build (ie being pulled +# in via add_subdirectory() rather than being a standalone build). +if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") + target_include_directories(gmock INTERFACE "${gmock_SOURCE_DIR}/include") + target_include_directories(gmock_main INTERFACE "${gmock_SOURCE_DIR}/include") +endif() + +######################################################################## +# +# Install rules +install(TARGETS gmock gmock_main + DESTINATION lib) +install(DIRECTORY ${gmock_SOURCE_DIR}/include/gmock + DESTINATION include) + +######################################################################## +# +# Google Mock's own tests. +# +# You can skip this section if you aren't interested in testing +# Google Mock itself. +# +# The tests are not built by default. To build them, set the +# gmock_build_tests option to ON. You can do it by running ccmake +# or specifying the -Dgmock_build_tests=ON flag when running cmake. + +if (gmock_build_tests) + # This must be set in the root directory for the tests to be run by + # 'make test' or ctest. + enable_testing() + + ############################################################ + # C++ tests built with standard compiler flags. + + cxx_test(gmock-actions_test gmock_main) + cxx_test(gmock-cardinalities_test gmock_main) + cxx_test(gmock_ex_test gmock_main) + cxx_test(gmock-generated-actions_test gmock_main) + cxx_test(gmock-generated-function-mockers_test gmock_main) + cxx_test(gmock-generated-internal-utils_test gmock_main) + cxx_test(gmock-generated-matchers_test gmock_main) + cxx_test(gmock-internal-utils_test gmock_main) + cxx_test(gmock-matchers_test gmock_main) + cxx_test(gmock-more-actions_test gmock_main) + cxx_test(gmock-nice-strict_test gmock_main) + cxx_test(gmock-port_test gmock_main) + cxx_test(gmock-spec-builders_test gmock_main) + cxx_test(gmock_link_test gmock_main test/gmock_link2_test.cc) + cxx_test(gmock_test gmock_main) + + if (CMAKE_USE_PTHREADS_INIT) + cxx_test(gmock_stress_test gmock) + endif() + + # gmock_all_test is commented to save time building and running tests. + # Uncomment if necessary. + # cxx_test(gmock_all_test gmock_main) + + ############################################################ + # C++ tests built with non-standard compiler flags. + + cxx_library(gmock_main_no_exception "${cxx_no_exception}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + cxx_library(gmock_main_no_rtti "${cxx_no_rtti}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + if (NOT MSVC OR MSVC_VERSION LESS 1600) # 1600 is Visual Studio 2010. + # Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that + # conflict with our own definitions. Therefore using our own tuple does not + # work on those compilers. + cxx_library(gmock_main_use_own_tuple "${cxx_use_own_tuple}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + cxx_test_with_flags(gmock_use_own_tuple_test "${cxx_use_own_tuple}" + gmock_main_use_own_tuple test/gmock-spec-builders_test.cc) + endif() + + cxx_test_with_flags(gmock-more-actions_no_exception_test "${cxx_no_exception}" + gmock_main_no_exception test/gmock-more-actions_test.cc) + + cxx_test_with_flags(gmock_no_rtti_test "${cxx_no_rtti}" + gmock_main_no_rtti test/gmock-spec-builders_test.cc) + + cxx_shared_library(shared_gmock_main "${cxx_default}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + # Tests that a binary can be built with Google Mock as a shared library. On + # some system configurations, it may not possible to run the binary without + # knowing more details about the system configurations. We do not try to run + # this binary. To get a more robust shared library coverage, configure with + # -DBUILD_SHARED_LIBS=ON. + cxx_executable_with_flags(shared_gmock_test_ "${cxx_default}" + shared_gmock_main test/gmock-spec-builders_test.cc) + set_target_properties(shared_gmock_test_ + PROPERTIES + COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") + + ############################################################ + # Python tests. + + cxx_executable(gmock_leak_test_ test gmock_main) + py_test(gmock_leak_test) + + cxx_executable(gmock_output_test_ test gmock) + py_test(gmock_output_test) +endif() diff --git a/vendor/googletest/gtest-1.7.0/googlemock/CONTRIBUTORS b/vendor/googletest/gtest-1.7.0/googlemock/CONTRIBUTORS new file mode 100644 index 00000000..6e9ae362 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/CONTRIBUTORS @@ -0,0 +1,40 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Mocking Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Benoit Sigoure +Bogdan Piloca +Chandler Carruth +Dave MacLachlan +David Anderson +Dean Sturtevant +Gene Volovich +Hal Burch +Jeffrey Yasskin +Jim Keller +Joe Walnes +Jon Wray +Keir Mierle +Keith Ray +Kostya Serebryany +Lev Makhlis +Manuel Klimek +Mario Tanev +Mark Paskin +Markus Heule +Matthew Simmons +Mike Bland +Neal Norwitz +Nermin Ozkiranartli +Owen Carlsen +Paneendra Ba +Paul Menage +Piotr Kaminski +Russ Rufer +Sverre Sundsdal +Takeshi Yoshino +Vadim Berman +Vlad Losev +Wolfgang Klier +Zhanyong Wan diff --git a/vendor/googletest/gtest-1.7.0/googlemock/LICENSE b/vendor/googletest/gtest-1.7.0/googlemock/LICENSE new file mode 100644 index 00000000..1941a11f --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/LICENSE @@ -0,0 +1,28 @@ +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. diff --git a/vendor/googletest/gtest-1.7.0/googlemock/Makefile.am b/vendor/googletest/gtest-1.7.0/googlemock/Makefile.am new file mode 100644 index 00000000..9adbc516 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/Makefile.am @@ -0,0 +1,224 @@ +# Automake file + +# Nonstandard package files for distribution. +EXTRA_DIST = LICENSE + +# We may need to build our internally packaged gtest. If so, it will be +# included in the 'subdirs' variable. +SUBDIRS = $(subdirs) + +# This is generated by the configure script, so clean it for distribution. +DISTCLEANFILES = scripts/gmock-config + +# We define the global AM_CPPFLAGS as everything we compile includes from these +# directories. +AM_CPPFLAGS = $(GTEST_CPPFLAGS) -I$(srcdir)/include + +# Modifies compiler and linker flags for pthreads compatibility. +if HAVE_PTHREADS + AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1 + AM_LIBS = @PTHREAD_LIBS@ +endif + +# Build rules for libraries. +lib_LTLIBRARIES = lib/libgmock.la lib/libgmock_main.la + +lib_libgmock_la_SOURCES = src/gmock-all.cc + +pkginclude_HEADERS = \ + include/gmock/gmock-actions.h \ + include/gmock/gmock-cardinalities.h \ + include/gmock/gmock-generated-actions.h \ + include/gmock/gmock-generated-function-mockers.h \ + include/gmock/gmock-generated-matchers.h \ + include/gmock/gmock-generated-nice-strict.h \ + include/gmock/gmock-matchers.h \ + include/gmock/gmock-more-actions.h \ + include/gmock/gmock-more-matchers.h \ + include/gmock/gmock-spec-builders.h \ + include/gmock/gmock.h + +pkginclude_internaldir = $(pkgincludedir)/internal +pkginclude_internal_HEADERS = \ + include/gmock/internal/gmock-generated-internal-utils.h \ + include/gmock/internal/gmock-internal-utils.h \ + include/gmock/internal/gmock-port.h \ + include/gmock/internal/custom/gmock-generated-actions.h \ + include/gmock/internal/custom/gmock-matchers.h \ + include/gmock/internal/custom/gmock-port.h + +lib_libgmock_main_la_SOURCES = src/gmock_main.cc +lib_libgmock_main_la_LIBADD = lib/libgmock.la + +# Build rules for tests. Automake's naming for some of these variables isn't +# terribly obvious, so this is a brief reference: +# +# TESTS -- Programs run automatically by "make check" +# check_PROGRAMS -- Programs built by "make check" but not necessarily run + +TESTS= +check_PROGRAMS= +AM_LDFLAGS = $(GTEST_LDFLAGS) + +# This exercises all major components of Google Mock. It also +# verifies that libgmock works. +TESTS += test/gmock-spec-builders_test +check_PROGRAMS += test/gmock-spec-builders_test +test_gmock_spec_builders_test_SOURCES = test/gmock-spec-builders_test.cc +test_gmock_spec_builders_test_LDADD = $(GTEST_LIBS) lib/libgmock.la + +# This tests using Google Mock in multiple translation units. It also +# verifies that libgmock_main and libgmock work. +TESTS += test/gmock_link_test +check_PROGRAMS += test/gmock_link_test +test_gmock_link_test_SOURCES = \ + test/gmock_link2_test.cc \ + test/gmock_link_test.cc \ + test/gmock_link_test.h +test_gmock_link_test_LDADD = $(GTEST_LIBS) lib/libgmock_main.la lib/libgmock.la + +if HAVE_PYTHON + # Tests that fused gmock files compile and work. + TESTS += test/gmock_fused_test + check_PROGRAMS += test/gmock_fused_test + test_gmock_fused_test_SOURCES = \ + fused-src/gmock-gtest-all.cc \ + fused-src/gmock/gmock.h \ + fused-src/gmock_main.cc \ + fused-src/gtest/gtest.h \ + test/gmock_test.cc + test_gmock_fused_test_CPPFLAGS = -I"$(srcdir)/fused-src" +endif + +# Google Mock source files that we don't compile directly. +GMOCK_SOURCE_INGLUDES = \ + src/gmock-cardinalities.cc \ + src/gmock-internal-utils.cc \ + src/gmock-matchers.cc \ + src/gmock-spec-builders.cc \ + src/gmock.cc + +EXTRA_DIST += $(GMOCK_SOURCE_INGLUDES) + +# C++ tests that we don't compile using autotools. +EXTRA_DIST += \ + test/gmock-actions_test.cc \ + test/gmock_all_test.cc \ + test/gmock-cardinalities_test.cc \ + test/gmock_ex_test.cc \ + test/gmock-generated-actions_test.cc \ + test/gmock-generated-function-mockers_test.cc \ + test/gmock-generated-internal-utils_test.cc \ + test/gmock-generated-matchers_test.cc \ + test/gmock-internal-utils_test.cc \ + test/gmock-matchers_test.cc \ + test/gmock-more-actions_test.cc \ + test/gmock-nice-strict_test.cc \ + test/gmock-port_test.cc \ + test/gmock_stress_test.cc + +# Python tests, which we don't run using autotools. +EXTRA_DIST += \ + test/gmock_leak_test.py \ + test/gmock_leak_test_.cc \ + test/gmock_output_test.py \ + test/gmock_output_test_.cc \ + test/gmock_output_test_golden.txt \ + test/gmock_test_utils.py + +# Nonstandard package files for distribution. +EXTRA_DIST += \ + CHANGES \ + CONTRIBUTORS \ + make/Makefile + +# Pump scripts for generating Google Mock headers. +# TODO(chandlerc@google.com): automate the generation of *.h from *.h.pump. +EXTRA_DIST += \ + include/gmock/gmock-generated-actions.h.pump \ + include/gmock/gmock-generated-function-mockers.h.pump \ + include/gmock/gmock-generated-matchers.h.pump \ + include/gmock/gmock-generated-nice-strict.h.pump \ + include/gmock/internal/gmock-generated-internal-utils.h.pump \ + include/gmock/internal/custom/gmock-generated-actions.h.pump + +# Script for fusing Google Mock and Google Test source files. +EXTRA_DIST += scripts/fuse_gmock_files.py + +# The Google Mock Generator tool from the cppclean project. +EXTRA_DIST += \ + scripts/generator/LICENSE \ + scripts/generator/README \ + scripts/generator/README.cppclean \ + scripts/generator/cpp/__init__.py \ + scripts/generator/cpp/ast.py \ + scripts/generator/cpp/gmock_class.py \ + scripts/generator/cpp/keywords.py \ + scripts/generator/cpp/tokenize.py \ + scripts/generator/cpp/utils.py \ + scripts/generator/gmock_gen.py + +# Script for diagnosing compiler errors in programs that use Google +# Mock. +EXTRA_DIST += scripts/gmock_doctor.py + +# CMake scripts. +EXTRA_DIST += \ + CMakeLists.txt + +# Microsoft Visual Studio 2005 projects. +EXTRA_DIST += \ + msvc/2005/gmock.sln \ + msvc/2005/gmock.vcproj \ + msvc/2005/gmock_config.vsprops \ + msvc/2005/gmock_main.vcproj \ + msvc/2005/gmock_test.vcproj + +# Microsoft Visual Studio 2010 projects. +EXTRA_DIST += \ + msvc/2010/gmock.sln \ + msvc/2010/gmock.vcxproj \ + msvc/2010/gmock_config.props \ + msvc/2010/gmock_main.vcxproj \ + msvc/2010/gmock_test.vcxproj + +if HAVE_PYTHON +# gmock_test.cc does not really depend on files generated by the +# fused-gmock-internal rule. However, gmock_test.o does, and it is +# important to include test/gmock_test.cc as part of this rule in order to +# prevent compiling gmock_test.o until all dependent files have been +# generated. +$(test_gmock_fused_test_SOURCES): fused-gmock-internal + +# TODO(vladl@google.com): Find a way to add Google Tests's sources here. +fused-gmock-internal: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \ + $(lib_libgmock_la_SOURCES) $(GMOCK_SOURCE_INGLUDES) \ + $(lib_libgmock_main_la_SOURCES) \ + scripts/fuse_gmock_files.py + mkdir -p "$(srcdir)/fused-src" + chmod -R u+w "$(srcdir)/fused-src" + rm -f "$(srcdir)/fused-src/gtest/gtest.h" + rm -f "$(srcdir)/fused-src/gmock/gmock.h" + rm -f "$(srcdir)/fused-src/gmock-gtest-all.cc" + "$(srcdir)/scripts/fuse_gmock_files.py" "$(srcdir)/fused-src" + cp -f "$(srcdir)/src/gmock_main.cc" "$(srcdir)/fused-src" + +maintainer-clean-local: + rm -rf "$(srcdir)/fused-src" +endif + +# Death tests may produce core dumps in the build directory. In case +# this happens, clean them to keep distcleancheck happy. +CLEANFILES = core + +# Disables 'make install' as installing a compiled version of Google +# Mock can lead to undefined behavior due to violation of the +# One-Definition Rule. + +install-exec-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Mock into your build system." + false + +install-data-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Mock into your build system." + false diff --git a/vendor/googletest/gtest-1.7.0/googlemock/README.md b/vendor/googletest/gtest-1.7.0/googlemock/README.md new file mode 100644 index 00000000..332beab3 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/README.md @@ -0,0 +1,333 @@ +## Google Mock ## + +The Google C++ mocking framework. + +### Overview ### + +Google's framework for writing and using C++ mock classes. +It can help you derive better designs of your system and write better tests. + +It is inspired by: + + * [jMock](http://www.jmock.org/), + * [EasyMock](http://www.easymock.org/), and + * [Hamcrest](http://code.google.com/p/hamcrest/), + +and designed with C++'s specifics in mind. + +Google mock: + + * lets you create mock classes trivially using simple macros. + * supports a rich set of matchers and actions. + * handles unordered, partially ordered, or completely ordered expectations. + * is extensible by users. + +We hope you find it useful! + +### Features ### + + * Provides a declarative syntax for defining mocks. + * Can easily define partial (hybrid) mocks, which are a cross of real + and mock objects. + * Handles functions of arbitrary types and overloaded functions. + * Comes with a rich set of matchers for validating function arguments. + * Uses an intuitive syntax for controlling the behavior of a mock. + * Does automatic verification of expectations (no record-and-replay needed). + * Allows arbitrary (partial) ordering constraints on + function calls to be expressed,. + * Lets a user extend it by defining new matchers and actions. + * Does not use exceptions. + * Is easy to learn and use. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Please note that code under [scripts/generator](scripts/generator/) is +from [cppclean](http://code.google.com/p/cppclean/) and released under +the Apache License, which is different from Google Mock's license. + +## Getting Started ## + +If you are new to the project, we suggest that you read the user +documentation in the following order: + + * Learn the [basics](../googletest/docs/Primer.md) of + Google Test, if you choose to use Google Mock with it (recommended). + * Read [Google Mock for Dummies](docs/ForDummies.md). + * Read the instructions below on how to build Google Mock. + +You can also watch Zhanyong's [talk](http://www.youtube.com/watch?v=sYpCyLI47rM) on Google Mock's usage and implementation. + +Once you understand the basics, check out the rest of the docs: + + * [CheatSheet](docs/CheatSheet.md) - all the commonly used stuff + at a glance. + * [CookBook](docs/CookBook.md) - recipes for getting things done, + including advanced techniques. + +If you need help, please check the +[KnownIssues](docs/KnownIssues.md) and +[FrequentlyAskedQuestions](docs/FrequentlyAskedQuestions.md) before +posting a question on the +[discussion group](http://groups.google.com/group/googlemock). + + +### Using Google Mock Without Google Test ### + +Google Mock is not a testing framework itself. Instead, it needs a +testing framework for writing tests. Google Mock works seamlessly +with [Google Test](http://code.google.com/p/googletest/), but +you can also use it with [any C++ testing framework](googlemock/ForDummies.md#Using_Google_Mock_with_Any_Testing_Framework). + +### Requirements for End Users ### + +Google Mock is implemented on top of [Google Test]( +http://github.com/google/googletest/), and depends on it. +You must use the bundled version of Google Test when using Google Mock. + +You can also easily configure Google Mock to work with another testing +framework, although it will still need Google Test. Please read +["Using_Google_Mock_with_Any_Testing_Framework"]( + docs/ForDummies.md#Using_Google_Mock_with_Any_Testing_Framework) +for instructions. + +Google Mock depends on advanced C++ features and thus requires a more +modern compiler. The following are needed to use Google Mock: + +#### Linux Requirements #### + + * GNU-compatible Make or "gmake" + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * C++98-standard-compliant compiler (e.g. GCC 3.4 or newer) + +#### Windows Requirements #### + + * Microsoft Visual C++ 8.0 SP1 or newer + +#### Mac OS X Requirements #### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +### Requirements for Contributors ### + +We welcome patches. If you plan to contribute a patch, you need to +build Google Mock and its tests, which has further requirements: + + * Automake version 1.9 or newer + * Autoconf version 2.59 or newer + * Libtool / Libtoolize + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + +### Building Google Mock ### + +#### Preparing to Build (Unix only) #### + +If you are using a Unix system and plan to use the GNU Autotools build +system to build Google Mock (described below), you'll need to +configure it now. + +To prepare the Autotools build system: + + cd googlemock + autoreconf -fvi + +To build Google Mock and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +This section shows how you can integrate Google Mock into your +existing build system. + +Suppose you put Google Mock in directory `${GMOCK_DIR}` and Google Test +in `${GTEST_DIR}` (the latter is `${GMOCK_DIR}/gtest` by default). To +build Google Mock, create a library build target (or a project as +called by Visual Studio and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc and ${GMOCK_DIR}/src/gmock-all.cc + +with + + ${GTEST_DIR}/include and ${GMOCK_DIR}/include + +in the system header search path, and + + ${GTEST_DIR} and ${GMOCK_DIR} + +in the normal header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \ + -pthread -c ${GTEST_DIR}/src/gtest-all.cc + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \ + -pthread -c ${GMOCK_DIR}/src/gmock-all.cc + ar -rv libgmock.a gtest-all.o gmock-all.o + +(We need -pthread as Google Test and Google Mock use threads.) + +Next, you should compile your test source file with +${GTEST\_DIR}/include and ${GMOCK\_DIR}/include in the header search +path, and link it with gmock and any other necessary libraries: + + g++ -isystem ${GTEST_DIR}/include -isystem ${GMOCK_DIR}/include \ + -pthread path/to/your_test.cc libgmock.a -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Mock on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Mock's own tests. Instead, it just builds the Google Mock library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GMOCK_DIR}/make + make + ./gmock_test + +If you see errors, try to tweak the contents of +[make/Makefile](make/Makefile) to make them go away. + +### Windows ### + +The msvc/2005 directory contains VC++ 2005 projects and the msvc/2010 +directory contains VC++ 2010 projects for building Google Mock and +selected tests. + +Change to the appropriate directory and run "msbuild gmock.sln" to +build the library and tests (or open the gmock.sln in the MSVC IDE). +If you want to create your own project to use with Google Mock, you'll +have to configure it to use the `gmock_config` propety sheet. For that: + + * Open the Property Manager window (View | Other Windows | Property Manager) + * Right-click on your project and select "Add Existing Property Sheet..." + * Navigate to `gmock_config.vsprops` or `gmock_config.props` and select it. + * In Project Properties | Configuration Properties | General | Additional + Include Directories, type /include. + +### Tweaking Google Mock ### + +Google Mock can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Mock by +defining control macros on the compiler command line. Generally, +these macros are named like `GTEST_XYZ` and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file [${GTEST\_DIR}/include/gtest/internal/gtest-port.h]( +../googletest/include/gtest/internal/gtest-port.h). + +### Choosing a TR1 Tuple Library ### + +Google Mock uses the C++ Technical Report 1 (TR1) tuple library +heavily. Unfortunately TR1 tuple is not yet widely available with all +compilers. The good news is that Google Test 1.4.0+ implements a +subset of TR1 tuple that's enough for Google Mock's need. Google Mock +will automatically use that implementation when the compiler doesn't +provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +and Google Mock use. However, if your project already uses TR1 tuple, +you need to tell Google Test and Google Mock to use the same TR1 tuple +library the rest of your project uses, or the two tuple +implementations will clash. To do that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test, Google Mock, and +your tests. If you want to force Google Test and Google Mock to use +their own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you want to use Boost's TR1 tuple library with Google Mock, please +refer to the Boost website (http://www.boost.org/) for how to obtain +it and set it up. + +### As a Shared Library (DLL) ### + +Google Mock is compact, so most users can build and link it as a static +library for the simplicity. Google Mock can be used as a DLL, but the +same DLL must contain Google Test as well. See +[Google Test's README][gtest_readme] +for instructions on how to set up necessary compiler settings. + +### Tweaking Google Mock ### + +Most of Google Test's control macros apply to Google Mock as well. +Please see [Google Test's README][gtest_readme] for how to tweak them. + +### Upgrading from an Earlier Version ### + +We strive to keep Google Mock releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Mock. + +#### Upgrading from 1.1.0 or Earlier #### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "[Choosing a TR1 Tuple +Library](../googletest/#choosing-a-tr1-tuple-library)". + +#### Upgrading from 1.4.0 or Earlier #### + +On platforms where the pthread library is available, Google Test and +Google Mock use it in order to be thread-safe. For this to work, you +may need to tweak your compiler and/or linker flags. Please see the +"[Multi-threaded Tests](../googletest#multi-threaded-tests +)" section in file Google Test's README for what you may need to do. + +If you have custom matchers defined using `MatcherInterface` or +`MakePolymorphicMatcher()`, you'll need to update their definitions to +use the new matcher API ( +[monomorphic](http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Monomorphic_Matchers), +[polymorphic](http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Polymorphic_Matchers)). +Matchers defined using `MATCHER()` or `MATCHER_P*()` aren't affected. + +### Developing Google Mock ### + +This section discusses how to make your own changes to Google Mock. + +#### Testing Google Mock Itself #### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you'll need Autotools. First, make sure you have followed +the instructions above to configure Google Mock. +Then, create a build output directory and enter it. Next, + + ${GMOCK_DIR}/configure # try --help for more info + +Once you have successfully configured Google Mock, the build steps are +standard for GNU-style OSS packages. + + make # Standard makefile following GNU conventions + make check # Builds and runs all tests - all should pass. + +Note that when building your project against Google Mock, you are building +against Google Test as well. There is no need to configure Google Test +separately. + +#### Contributing a Patch #### + +We welcome patches. +Please read the [Developer's Guide](docs/DevGuide.md) +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + +Happy testing! + +[gtest_readme]: ../googletest/README.md "googletest" diff --git a/vendor/googletest/gtest-1.7.0/googlemock/build-aux/.keep b/vendor/googletest/gtest-1.7.0/googlemock/build-aux/.keep new file mode 100644 index 00000000..e69de29b diff --git a/vendor/googletest/gtest-1.7.0/googlemock/configure.ac b/vendor/googletest/gtest-1.7.0/googlemock/configure.ac new file mode 100644 index 00000000..3b740f20 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/configure.ac @@ -0,0 +1,146 @@ +m4_include(../googletest/m4/acx_pthread.m4) + +AC_INIT([Google C++ Mocking Framework], + [1.7.0], + [googlemock@googlegroups.com], + [gmock]) + +# Provide various options to initialize the Autoconf and configure processes. +AC_PREREQ([2.59]) +AC_CONFIG_SRCDIR([./LICENSE]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_HEADERS([build-aux/config.h]) +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([scripts/gmock-config], [chmod +x scripts/gmock-config]) + +# Initialize Automake with various options. We require at least v1.9, prevent +# pedantic complaints about package files, and enable various distribution +# targets. +AM_INIT_AUTOMAKE([1.9 dist-bzip2 dist-zip foreign subdir-objects]) + +# Check for programs used in building Google Test. +AC_PROG_CC +AC_PROG_CXX +AC_LANG([C++]) +AC_PROG_LIBTOOL + +# TODO(chandlerc@google.com): Currently we aren't running the Python tests +# against the interpreter detected by AM_PATH_PYTHON, and so we condition +# HAVE_PYTHON by requiring "python" to be in the PATH, and that interpreter's +# version to be >= 2.3. This will allow the scripts to use a "/usr/bin/env" +# hashbang. +PYTHON= # We *do not* allow the user to specify a python interpreter +AC_PATH_PROG([PYTHON],[python],[:]) +AS_IF([test "$PYTHON" != ":"], + [AM_PYTHON_CHECK_VERSION([$PYTHON],[2.3],[:],[PYTHON=":"])]) +AM_CONDITIONAL([HAVE_PYTHON],[test "$PYTHON" != ":"]) + +# TODO(chandlerc@google.com) Check for the necessary system headers. + +# Configure pthreads. +AC_ARG_WITH([pthreads], + [AS_HELP_STRING([--with-pthreads], + [use pthreads (default is yes)])], + [with_pthreads=$withval], + [with_pthreads=check]) + +have_pthreads=no +AS_IF([test "x$with_pthreads" != "xno"], + [ACX_PTHREAD( + [], + [AS_IF([test "x$with_pthreads" != "xcheck"], + [AC_MSG_FAILURE( + [--with-pthreads was specified, but unable to be used])])]) + have_pthreads="$acx_pthread_ok"]) +AM_CONDITIONAL([HAVE_PTHREADS],[test "x$have_pthreads" == "xyes"]) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_LIBS) + +# GoogleMock currently has hard dependencies upon GoogleTest above and beyond +# running its own test suite, so we both provide our own version in +# a subdirectory and provide some logic to use a custom version or a system +# installed version. +AC_ARG_WITH([gtest], + [AS_HELP_STRING([--with-gtest], + [Specifies how to find the gtest package. If no + arguments are given, the default behavior, a + system installed gtest will be used if present, + and an internal version built otherwise. If a + path is provided, the gtest built or installed at + that prefix will be used.])], + [], + [with_gtest=yes]) +AC_ARG_ENABLE([external-gtest], + [AS_HELP_STRING([--disable-external-gtest], + [Disables any detection or use of a system + installed or user provided gtest. Any option to + '--with-gtest' is ignored. (Default is enabled.)]) + ], [], [enable_external_gtest=yes]) +AS_IF([test "x$with_gtest" == "xno"], + [AC_MSG_ERROR([dnl +Support for GoogleTest was explicitly disabled. Currently GoogleMock has a hard +dependency upon GoogleTest to build, please provide a version, or allow +GoogleMock to use any installed version and fall back upon its internal +version.])]) + +# Setup various GTEST variables. TODO(chandlerc@google.com): When these are +# used below, they should be used such that any pre-existing values always +# trump values we set them to, so that they can be used to selectively override +# details of the detection process. +AC_ARG_VAR([GTEST_CONFIG], + [The exact path of Google Test's 'gtest-config' script.]) +AC_ARG_VAR([GTEST_CPPFLAGS], + [C-like preprocessor flags for Google Test.]) +AC_ARG_VAR([GTEST_CXXFLAGS], + [C++ compile flags for Google Test.]) +AC_ARG_VAR([GTEST_LDFLAGS], + [Linker path and option flags for Google Test.]) +AC_ARG_VAR([GTEST_LIBS], + [Library linking flags for Google Test.]) +AC_ARG_VAR([GTEST_VERSION], + [The version of Google Test available.]) +HAVE_BUILT_GTEST="no" + +GTEST_MIN_VERSION="1.7.0" + +AS_IF([test "x${enable_external_gtest}" = "xyes"], + [# Begin filling in variables as we are able. + AS_IF([test "x${with_gtest}" != "xyes"], + [AS_IF([test -x "${with_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${with_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${with_gtest}/bin/gtest-config"]) + AS_IF([test -x "${GTEST_CONFIG}"], [], + [AC_MSG_ERROR([dnl +Unable to locate either a built or installed Google Test at '${with_gtest}'.]) + ])]) + + AS_IF([test -x "${GTEST_CONFIG}"], [], + [AC_PATH_PROG([GTEST_CONFIG], [gtest-config])]) + AS_IF([test -x "${GTEST_CONFIG}"], + [AC_MSG_CHECKING([for Google Test version >= ${GTEST_MIN_VERSION}]) + AS_IF([${GTEST_CONFIG} --min-version=${GTEST_MIN_VERSION}], + [AC_MSG_RESULT([yes]) + HAVE_BUILT_GTEST="yes"], + [AC_MSG_RESULT([no])])])]) + +AS_IF([test "x${HAVE_BUILT_GTEST}" = "xyes"], + [GTEST_CPPFLAGS=`${GTEST_CONFIG} --cppflags` + GTEST_CXXFLAGS=`${GTEST_CONFIG} --cxxflags` + GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags` + GTEST_LIBS=`${GTEST_CONFIG} --libs` + GTEST_VERSION=`${GTEST_CONFIG} --version`], + [AC_CONFIG_SUBDIRS([../googletest]) + # GTEST_CONFIG needs to be executable both in a Makefile environmont and + # in a shell script environment, so resolve an absolute path for it here. + GTEST_CONFIG="`pwd -P`/../googletest/scripts/gtest-config" + GTEST_CPPFLAGS='-I$(top_srcdir)/../googletest/include' + GTEST_CXXFLAGS='-g' + GTEST_LDFLAGS='' + GTEST_LIBS='$(top_builddir)/../googletest/lib/libgtest.la' + GTEST_VERSION="${GTEST_MIN_VERSION}"]) + +# TODO(chandlerc@google.com) Check the types, structures, and other compiler +# and architecture characteristics. + +# Output the generated files. No further autoconf macros may be used. +AC_OUTPUT diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/CheatSheet.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/CheatSheet.md new file mode 100644 index 00000000..ef4451b8 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/CheatSheet.md @@ -0,0 +1,562 @@ + + +# Defining a Mock Class # + +## Mocking a Normal Class ## + +Given +``` +class Foo { + ... + virtual ~Foo(); + virtual int GetSize() const = 0; + virtual string Describe(const char* name) = 0; + virtual string Describe(int type) = 0; + virtual bool Process(Bar elem, int count) = 0; +}; +``` +(note that `~Foo()` **must** be virtual) we can define its mock as +``` +#include "gmock/gmock.h" + +class MockFoo : public Foo { + MOCK_CONST_METHOD0(GetSize, int()); + MOCK_METHOD1(Describe, string(const char* name)); + MOCK_METHOD1(Describe, string(int type)); + MOCK_METHOD2(Process, bool(Bar elem, int count)); +}; +``` + +To create a "nice" mock object which ignores all uninteresting calls, +or a "strict" mock object, which treats them as failures: +``` +NiceMock nice_foo; // The type is a subclass of MockFoo. +StrictMock strict_foo; // The type is a subclass of MockFoo. +``` + +## Mocking a Class Template ## + +To mock +``` +template +class StackInterface { + public: + ... + virtual ~StackInterface(); + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; +``` +(note that `~StackInterface()` **must** be virtual) just append `_T` to the `MOCK_*` macros: +``` +template +class MockStack : public StackInterface { + public: + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Specifying Calling Conventions for Mock Functions ## + +If your mock function doesn't use the default calling convention, you +can specify it by appending `_WITH_CALLTYPE` to any of the macros +described in the previous two sections and supplying the calling +convention as the first argument to the macro. For example, +``` + MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n)); + MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Bar, int(double x, double y)); +``` +where `STDMETHODCALLTYPE` is defined by `` on Windows. + +# Using Mocks in Tests # + +The typical flow is: + 1. Import the Google Mock names you need to use. All Google Mock names are in the `testing` namespace unless they are macros or otherwise noted. + 1. Create the mock objects. + 1. Optionally, set the default actions of the mock objects. + 1. Set your expectations on the mock objects (How will they be called? What wil they do?). + 1. Exercise code that uses the mock objects; if necessary, check the result using [Google Test](../../googletest/) assertions. + 1. When a mock objects is destructed, Google Mock automatically verifies that all expectations on it have been satisfied. + +Here is an example: +``` +using ::testing::Return; // #1 + +TEST(BarTest, DoesThis) { + MockFoo foo; // #2 + + ON_CALL(foo, GetSize()) // #3 + .WillByDefault(Return(1)); + // ... other default actions ... + + EXPECT_CALL(foo, Describe(5)) // #4 + .Times(3) + .WillRepeatedly(Return("Category 5")); + // ... other expectations ... + + EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 +} // #6 +``` + +# Setting Default Actions # + +Google Mock has a **built-in default action** for any function that +returns `void`, `bool`, a numeric value, or a pointer. + +To customize the default action for functions with return type `T` globally: +``` +using ::testing::DefaultValue; + +// Sets the default value to be returned. T must be CopyConstructible. +DefaultValue::Set(value); +// Sets a factory. Will be invoked on demand. T must be MoveConstructible. +// T MakeT(); +DefaultValue::SetFactory(&MakeT); +// ... use the mocks ... +// Resets the default value. +DefaultValue::Clear(); +``` + +To customize the default action for a particular method, use `ON_CALL()`: +``` +ON_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .WillByDefault(action); +``` + +# Setting Expectations # + +`EXPECT_CALL()` sets **expectations** on a mock method (How will it be +called? What will it do?): +``` +EXPECT_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .Times(cardinality) ? + .InSequence(sequences) * + .After(expectations) * + .WillOnce(action) * + .WillRepeatedly(action) ? + .RetiresOnSaturation(); ? +``` + +If `Times()` is omitted, the cardinality is assumed to be: + + * `Times(1)` when there is neither `WillOnce()` nor `WillRepeatedly()`; + * `Times(n)` when there are `n WillOnce()`s but no `WillRepeatedly()`, where `n` >= 1; or + * `Times(AtLeast(n))` when there are `n WillOnce()`s and a `WillRepeatedly()`, where `n` >= 0. + +A method with no `EXPECT_CALL()` is free to be invoked _any number of times_, and the default action will be taken each time. + +# Matchers # + +A **matcher** matches a _single_ argument. You can use it inside +`ON_CALL()` or `EXPECT_CALL()`, or use it to validate a value +directly: + +| `EXPECT_THAT(value, matcher)` | Asserts that `value` matches `matcher`. | +|:------------------------------|:----------------------------------------| +| `ASSERT_THAT(value, matcher)` | The same as `EXPECT_THAT(value, matcher)`, except that it generates a **fatal** failure. | + +Built-in matchers (where `argument` is the function argument) are +divided into several categories: + +## Wildcard ## +|`_`|`argument` can be any value of the correct type.| +|:--|:-----------------------------------------------| +|`A()` or `An()`|`argument` can be any value of type `type`. | + +## Generic Comparison ## + +|`Eq(value)` or `value`|`argument == value`| +|:---------------------|:------------------| +|`Ge(value)` |`argument >= value`| +|`Gt(value)` |`argument > value` | +|`Le(value)` |`argument <= value`| +|`Lt(value)` |`argument < value` | +|`Ne(value)` |`argument != value`| +|`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| +|`NotNull()` |`argument` is a non-null pointer (raw or smart).| +|`Ref(variable)` |`argument` is a reference to `variable`.| +|`TypedEq(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| + +Except `Ref()`, these matchers make a _copy_ of `value` in case it's +modified or destructed later. If the compiler complains that `value` +doesn't have a public copy constructor, try wrap it in `ByRef()`, +e.g. `Eq(ByRef(non_copyable_value))`. If you do that, make sure +`non_copyable_value` is not changed afterwards, or the meaning of your +matcher will be changed. + +## Floating-Point Matchers ## + +|`DoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal.| +|:-------------------|:----------------------------------------------------------------------------------------------| +|`FloatEq(a_float)` |`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. | +|`NanSensitiveDoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. | +|`NanSensitiveFloatEq(a_float)`|`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. | + +The above matchers use ULP-based comparison (the same as used in +[Google Test](../../googletest/)). They +automatically pick a reasonable error bound based on the absolute +value of the expected value. `DoubleEq()` and `FloatEq()` conform to +the IEEE standard, which requires comparing two NaNs for equality to +return false. The `NanSensitive*` version instead treats two NaNs as +equal, which is often what a user wants. + +|`DoubleNear(a_double, max_abs_error)`|`argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal.| +|:------------------------------------|:--------------------------------------------------------------------------------------------------------------------| +|`FloatNear(a_float, max_abs_error)` |`argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. | +|`NanSensitiveDoubleNear(a_double, max_abs_error)`|`argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. | +|`NanSensitiveFloatNear(a_float, max_abs_error)`|`argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. | + +## String Matchers ## + +The `argument` can be either a C string or a C++ string object: + +|`ContainsRegex(string)`|`argument` matches the given regular expression.| +|:----------------------|:-----------------------------------------------| +|`EndsWith(suffix)` |`argument` ends with string `suffix`. | +|`HasSubstr(string)` |`argument` contains `string` as a sub-string. | +|`MatchesRegex(string)` |`argument` matches the given regular expression with the match starting at the first character and ending at the last character.| +|`StartsWith(prefix)` |`argument` starts with string `prefix`. | +|`StrCaseEq(string)` |`argument` is equal to `string`, ignoring case. | +|`StrCaseNe(string)` |`argument` is not equal to `string`, ignoring case.| +|`StrEq(string)` |`argument` is equal to `string`. | +|`StrNe(string)` |`argument` is not equal to `string`. | + +`ContainsRegex()` and `MatchesRegex()` use the regular expression +syntax defined +[here](../../googletest/docs/AdvancedGuide.md#regular-expression-syntax). +`StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide +strings as well. + +## Container Matchers ## + +Most STL-style containers support `==`, so you can use +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. If you want to write the elements in-line, +match them more flexibly, or get more informative messages, you can use: + +| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | +|:-------------------------|:---------------------------------------------------------------------------------------------------------------------------------| +| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | +| `Each(e)` | `argument` is a container where _every_ element matches `e`, which can be either a value or a matcher. | +| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the i-th element matches `ei`, which can be a value or a matcher. 0 to 10 arguments are allowed. | +| `ElementsAreArray({ e0, e1, ..., en })`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. | +| `IsEmpty()` | `argument` is an empty container (`container.empty()`). | +| `Pointwise(m, container)` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. | +| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. | +| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under some permutation each element matches an `ei` (for a different `i`), which can be a value or a matcher. 0 to 10 arguments are allowed. | +| `UnorderedElementsAreArray({ e0, e1, ..., en })`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. | +| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(UnorderedElementsAre(1, 2, 3))` verifies that `argument` contains elements `1`, `2`, and `3`, ignoring order. | +| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. | + +Notes: + + * These matchers can also match: + 1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`), and + 1. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer, int len)` -- see [Multi-argument Matchers](#Multiargument_Matchers.md)). + * The array being matched may be multi-dimensional (i.e. its elements can be arrays). + * `m` in `Pointwise(m, ...)` should be a matcher for `::testing::tuple` where `T` and `U` are the element type of the actual container and the expected container, respectively. For example, to compare two `Foo` containers where `Foo` doesn't support `operator==` but has an `Equals()` method, one might write: + +``` +using ::testing::get; +MATCHER(FooEq, "") { + return get<0>(arg).Equals(get<1>(arg)); +} +... +EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos)); +``` + +## Member Matchers ## + +|`Field(&class::field, m)`|`argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| +|:------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| +|`Key(e)` |`argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`.| +|`Pair(m1, m2)` |`argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. | +|`Property(&class::property, m)`|`argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| + +## Matching the Result of a Function or Functor ## + +|`ResultOf(f, m)`|`f(argument)` matches matcher `m`, where `f` is a function or functor.| +|:---------------|:---------------------------------------------------------------------| + +## Pointer Matchers ## + +|`Pointee(m)`|`argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`.| +|:-----------|:-----------------------------------------------------------------------------------------------| +|`WhenDynamicCastTo(m)`| when `argument` is passed through `dynamic_cast()`, it matches matcher `m`. | + +## Multiargument Matchers ## + +Technically, all matchers match a _single_ value. A "multi-argument" +matcher is just one that matches a _tuple_. The following matchers can +be used to match a tuple `(x, y)`: + +|`Eq()`|`x == y`| +|:-----|:-------| +|`Ge()`|`x >= y`| +|`Gt()`|`x > y` | +|`Le()`|`x <= y`| +|`Lt()`|`x < y` | +|`Ne()`|`x != y`| + +You can use the following selectors to pick a subset of the arguments +(or reorder them) to participate in the matching: + +|`AllArgs(m)`|Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`.| +|:-----------|:-------------------------------------------------------------------| +|`Args(m)`|The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`.| + +## Composite Matchers ## + +You can make a matcher from one or more other matchers: + +|`AllOf(m1, m2, ..., mn)`|`argument` matches all of the matchers `m1` to `mn`.| +|:-----------------------|:---------------------------------------------------| +|`AnyOf(m1, m2, ..., mn)`|`argument` matches at least one of the matchers `m1` to `mn`.| +|`Not(m)` |`argument` doesn't match matcher `m`. | + +## Adapters for Matchers ## + +|`MatcherCast(m)`|casts matcher `m` to type `Matcher`.| +|:------------------|:--------------------------------------| +|`SafeMatcherCast(m)`| [safely casts](CookBook.md#casting-matchers) matcher `m` to type `Matcher`. | +|`Truly(predicate)` |`predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor.| + +## Matchers as Predicates ## + +|`Matches(m)(value)`|evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor.| +|:------------------|:---------------------------------------------------------------------------------------------| +|`ExplainMatchResult(m, value, result_listener)`|evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. | +|`Value(value, m)` |evaluates to `true` if `value` matches `m`. | + +## Defining Matchers ## + +| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | +|:-------------------------------------------------|:------------------------------------------------------| +| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a macher `IsDivisibleBy(n)` to match a number divisible by `n`. | +| `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | + +**Notes:** + + 1. The `MATCHER*` macros cannot be used inside a function or class. + 1. The matcher body must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). + 1. You can use `PrintToString(x)` to convert a value `x` of any type to a string. + +## Matchers as Test Assertions ## + +|`ASSERT_THAT(expression, m)`|Generates a [fatal failure](../../googletest/docs/Primer.md#assertions) if the value of `expression` doesn't match matcher `m`.| +|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------| +|`EXPECT_THAT(expression, m)`|Generates a non-fatal failure if the value of `expression` doesn't match matcher `m`. | + +# Actions # + +**Actions** specify what a mock function should do when invoked. + +## Returning a Value ## + +|`Return()`|Return from a `void` mock function.| +|:---------|:----------------------------------| +|`Return(value)`|Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type at the time the expectation is set, not when the action is executed.| +|`ReturnArg()`|Return the `N`-th (0-based) argument.| +|`ReturnNew(a1, ..., ak)`|Return `new T(a1, ..., ak)`; a different object is created each time.| +|`ReturnNull()`|Return a null pointer. | +|`ReturnPointee(ptr)`|Return the value pointed to by `ptr`.| +|`ReturnRef(variable)`|Return a reference to `variable`. | +|`ReturnRefOfCopy(value)`|Return a reference to a copy of `value`; the copy lives as long as the action.| + +## Side Effects ## + +|`Assign(&variable, value)`|Assign `value` to variable.| +|:-------------------------|:--------------------------| +| `DeleteArg()` | Delete the `N`-th (0-based) argument, which must be a pointer. | +| `SaveArg(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. | +| `SaveArgPointee(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. | +| `SetArgReferee(value)` | Assign value to the variable referenced by the `N`-th (0-based) argument. | +|`SetArgPointee(value)` |Assign `value` to the variable pointed by the `N`-th (0-based) argument.| +|`SetArgumentPointee(value)`|Same as `SetArgPointee(value)`. Deprecated. Will be removed in v1.7.0.| +|`SetArrayArgument(first, last)`|Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range.| +|`SetErrnoAndReturn(error, value)`|Set `errno` to `error` and return `value`.| +|`Throw(exception)` |Throws the given exception, which can be any copyable value. Available since v1.1.0.| + +## Using a Function or a Functor as an Action ## + +|`Invoke(f)`|Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor.| +|:----------|:-----------------------------------------------------------------------------------------------------------------| +|`Invoke(object_pointer, &class::method)`|Invoke the {method on the object with the arguments passed to the mock function. | +|`InvokeWithoutArgs(f)`|Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | +|`InvokeWithoutArgs(object_pointer, &class::method)`|Invoke the method on the object, which takes no arguments. | +|`InvokeArgument(arg1, arg2, ..., argk)`|Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments.| + +The return value of the invoked function is used as the return value +of the action. + +When defining a function or functor to be used with `Invoke*()`, you can declare any unused parameters as `Unused`: +``` + double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } + ... + EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance)); +``` + +In `InvokeArgument(...)`, if an argument needs to be passed by reference, wrap it inside `ByRef()`. For example, +``` + InvokeArgument<2>(5, string("Hi"), ByRef(foo)) +``` +calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by value, and `foo` by reference. + +## Default Action ## + +|`DoDefault()`|Do the default action (specified by `ON_CALL()` or the built-in one).| +|:------------|:--------------------------------------------------------------------| + +**Note:** due to technical reasons, `DoDefault()` cannot be used inside a composite action - trying to do so will result in a run-time error. + +## Composite Actions ## + +|`DoAll(a1, a2, ..., an)`|Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void. | +|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------| +|`IgnoreResult(a)` |Perform action `a` and ignore its result. `a` must not return void. | +|`WithArg(a)` |Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. | +|`WithArgs(a)`|Pass the selected (0-based) arguments of the mock function to action `a` and perform it. | +|`WithoutArgs(a)` |Perform action `a` without any arguments. | + +## Defining Actions ## + +| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | +|:--------------------------------------|:---------------------------------------------------------------------------------------| +| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | +| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. | + +The `ACTION*` macros cannot be used inside a function or class. + +# Cardinalities # + +These are used in `Times()` to specify how many times a mock function will be called: + +|`AnyNumber()`|The function can be called any number of times.| +|:------------|:----------------------------------------------| +|`AtLeast(n)` |The call is expected at least `n` times. | +|`AtMost(n)` |The call is expected at most `n` times. | +|`Between(m, n)`|The call is expected between `m` and `n` (inclusive) times.| +|`Exactly(n) or n`|The call is expected exactly `n` times. In particular, the call should never happen when `n` is 0.| + +# Expectation Order # + +By default, the expectations can be matched in _any_ order. If some +or all expectations must be matched in a given order, there are two +ways to specify it. They can be used either independently or +together. + +## The After Clause ## + +``` +using ::testing::Expectation; +... +Expectation init_x = EXPECT_CALL(foo, InitX()); +Expectation init_y = EXPECT_CALL(foo, InitY()); +EXPECT_CALL(foo, Bar()) + .After(init_x, init_y); +``` +says that `Bar()` can be called only after both `InitX()` and +`InitY()` have been called. + +If you don't know how many pre-requisites an expectation has when you +write it, you can use an `ExpectationSet` to collect them: + +``` +using ::testing::ExpectationSet; +... +ExpectationSet all_inits; +for (int i = 0; i < element_count; i++) { + all_inits += EXPECT_CALL(foo, InitElement(i)); +} +EXPECT_CALL(foo, Bar()) + .After(all_inits); +``` +says that `Bar()` can be called only after all elements have been +initialized (but we don't care about which elements get initialized +before the others). + +Modifying an `ExpectationSet` after using it in an `.After()` doesn't +affect the meaning of the `.After()`. + +## Sequences ## + +When you have a long chain of sequential expectations, it's easier to +specify the order using **sequences**, which don't require you to given +each expectation in the chain a different name. All expected
+calls
in the same sequence must occur in the order they are +specified. + +``` +using ::testing::Sequence; +Sequence s1, s2; +... +EXPECT_CALL(foo, Reset()) + .InSequence(s1, s2) + .WillOnce(Return(true)); +EXPECT_CALL(foo, GetSize()) + .InSequence(s1) + .WillOnce(Return(1)); +EXPECT_CALL(foo, Describe(A())) + .InSequence(s2) + .WillOnce(Return("dummy")); +``` +says that `Reset()` must be called before _both_ `GetSize()` _and_ +`Describe()`, and the latter two can occur in any order. + +To put many expectations in a sequence conveniently: +``` +using ::testing::InSequence; +{ + InSequence dummy; + + EXPECT_CALL(...)...; + EXPECT_CALL(...)...; + ... + EXPECT_CALL(...)...; +} +``` +says that all expected calls in the scope of `dummy` must occur in +strict order. The name `dummy` is irrelevant.) + +# Verifying and Resetting a Mock # + +Google Mock will verify the expectations on a mock object when it is destructed, or you can do it earlier: +``` +using ::testing::Mock; +... +// Verifies and removes the expectations on mock_obj; +// returns true iff successful. +Mock::VerifyAndClearExpectations(&mock_obj); +... +// Verifies and removes the expectations on mock_obj; +// also removes the default actions set by ON_CALL(); +// returns true iff successful. +Mock::VerifyAndClear(&mock_obj); +``` + +You can also tell Google Mock that a mock object can be leaked and doesn't +need to be verified: +``` +Mock::AllowLeak(&mock_obj); +``` + +# Mock Classes # + +Google Mock defines a convenient mock class template +``` +class MockFunction { + public: + MOCK_METHODn(Call, R(A1, ..., An)); +}; +``` +See this [recipe](CookBook.md#using-check-points) for one application of it. + +# Flags # + +| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. | +|:-------------------------------|:----------------------------------------------| +| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. | diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/CookBook.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/CookBook.md new file mode 100644 index 00000000..c215b551 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/CookBook.md @@ -0,0 +1,3675 @@ + + +You can find recipes for using Google Mock here. If you haven't yet, +please read the [ForDummies](ForDummies.md) document first to make sure you understand +the basics. + +**Note:** Google Mock lives in the `testing` name space. For +readability, it is recommended to write `using ::testing::Foo;` once in +your file before using the name `Foo` defined by Google Mock. We omit +such `using` statements in this page for brevity, but you should do it +in your own code. + +# Creating Mock Classes # + +## Mocking Private or Protected Methods ## + +You must always put a mock method definition (`MOCK_METHOD*`) in a +`public:` section of the mock class, regardless of the method being +mocked being `public`, `protected`, or `private` in the base class. +This allows `ON_CALL` and `EXPECT_CALL` to reference the mock function +from outside of the mock class. (Yes, C++ allows a subclass to change +the access level of a virtual function in the base class.) Example: + +``` +class Foo { + public: + ... + virtual bool Transform(Gadget* g) = 0; + + protected: + virtual void Resume(); + + private: + virtual int GetTimeOut(); +}; + +class MockFoo : public Foo { + public: + ... + MOCK_METHOD1(Transform, bool(Gadget* g)); + + // The following must be in the public section, even though the + // methods are protected or private in the base class. + MOCK_METHOD0(Resume, void()); + MOCK_METHOD0(GetTimeOut, int()); +}; +``` + +## Mocking Overloaded Methods ## + +You can mock overloaded functions as usual. No special attention is required: + +``` +class Foo { + ... + + // Must be virtual as we'll inherit from Foo. + virtual ~Foo(); + + // Overloaded on the types and/or numbers of arguments. + virtual int Add(Element x); + virtual int Add(int times, Element x); + + // Overloaded on the const-ness of this object. + virtual Bar& GetBar(); + virtual const Bar& GetBar() const; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Add, int(Element x)); + MOCK_METHOD2(Add, int(int times, Element x); + + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +``` + +**Note:** if you don't mock all versions of the overloaded method, the +compiler will give you a warning about some methods in the base class +being hidden. To fix that, use `using` to bring them in scope: + +``` +class MockFoo : public Foo { + ... + using Foo::Add; + MOCK_METHOD1(Add, int(Element x)); + // We don't want to mock int Add(int times, Element x); + ... +}; +``` + +## Mocking Class Templates ## + +To mock a class template, append `_T` to the `MOCK_*` macros: + +``` +template +class StackInterface { + ... + // Must be virtual as we'll inherit from StackInterface. + virtual ~StackInterface(); + + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; + +template +class MockStack : public StackInterface { + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Mocking Nonvirtual Methods ## + +Google Mock can mock non-virtual functions to be used in what we call _hi-perf +dependency injection_. + +In this case, instead of sharing a common base class with the real +class, your mock class will be _unrelated_ to the real class, but +contain methods with the same signatures. The syntax for mocking +non-virtual methods is the _same_ as mocking virtual methods: + +``` +// A simple packet stream class. None of its members is virtual. +class ConcretePacketStream { + public: + void AppendPacket(Packet* new_packet); + const Packet* GetPacket(size_t packet_number) const; + size_t NumberOfPackets() const; + ... +}; + +// A mock packet stream class. It inherits from no other, but defines +// GetPacket() and NumberOfPackets(). +class MockPacketStream { + public: + MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number)); + MOCK_CONST_METHOD0(NumberOfPackets, size_t()); + ... +}; +``` + +Note that the mock class doesn't define `AppendPacket()`, unlike the +real class. That's fine as long as the test doesn't need to call it. + +Next, you need a way to say that you want to use +`ConcretePacketStream` in production code, and use `MockPacketStream` +in tests. Since the functions are not virtual and the two classes are +unrelated, you must specify your choice at _compile time_ (as opposed +to run time). + +One way to do it is to templatize your code that needs to use a packet +stream. More specifically, you will give your code a template type +argument for the type of the packet stream. In production, you will +instantiate your template with `ConcretePacketStream` as the type +argument. In tests, you will instantiate the same template with +`MockPacketStream`. For example, you may write: + +``` +template +void CreateConnection(PacketStream* stream) { ... } + +template +class PacketReader { + public: + void ReadPackets(PacketStream* stream, size_t packet_num); +}; +``` + +Then you can use `CreateConnection()` and +`PacketReader` in production code, and use +`CreateConnection()` and +`PacketReader` in tests. + +``` + MockPacketStream mock_stream; + EXPECT_CALL(mock_stream, ...)...; + .. set more expectations on mock_stream ... + PacketReader reader(&mock_stream); + ... exercise reader ... +``` + +## Mocking Free Functions ## + +It's possible to use Google Mock to mock a free function (i.e. a +C-style function or a static method). You just need to rewrite your +code to use an interface (abstract class). + +Instead of calling a free function (say, `OpenFile`) directly, +introduce an interface for it and have a concrete subclass that calls +the free function: + +``` +class FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) = 0; +}; + +class File : public FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) { + return OpenFile(path, mode); + } +}; +``` + +Your code should talk to `FileInterface` to open a file. Now it's +easy to mock out the function. + +This may seem much hassle, but in practice you often have multiple +related functions that you can put in the same interface, so the +per-function syntactic overhead will be much lower. + +If you are concerned about the performance overhead incurred by +virtual functions, and profiling confirms your concern, you can +combine this with the recipe for [mocking non-virtual methods](#Mocking_Nonvirtual_Methods.md). + +## The Nice, the Strict, and the Naggy ## + +If a mock method has no `EXPECT_CALL` spec but is called, Google Mock +will print a warning about the "uninteresting call". The rationale is: + + * New methods may be added to an interface after a test is written. We shouldn't fail a test just because a method it doesn't know about is called. + * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, he can add an `EXPECT_CALL()` to suppress the warning. + +However, sometimes you may want to suppress all "uninteresting call" +warnings, while sometimes you may want the opposite, i.e. to treat all +of them as errors. Google Mock lets you make the decision on a +per-mock-object basis. + +Suppose your test uses a mock class `MockFoo`: + +``` +TEST(...) { + MockFoo mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +If a method of `mock_foo` other than `DoThis()` is called, it will be +reported by Google Mock as a warning. However, if you rewrite your +test to use `NiceMock` instead, the warning will be gone, +resulting in a cleaner test output: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +`NiceMock` is a subclass of `MockFoo`, so it can be used +wherever `MockFoo` is accepted. + +It also works if `MockFoo`'s constructor takes some arguments, as +`NiceMock` "inherits" `MockFoo`'s constructors: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +The usage of `StrictMock` is similar, except that it makes all +uninteresting calls failures: + +``` +using ::testing::StrictMock; + +TEST(...) { + StrictMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... + + // The test will fail if a method of mock_foo other than DoThis() + // is called. +} +``` + +There are some caveats though (I don't like them just as much as the +next guy, but sadly they are side effects of C++'s limitations): + + 1. `NiceMock` and `StrictMock` only work for mock methods defined using the `MOCK_METHOD*` family of macros **directly** in the `MockFoo` class. If a mock method is defined in a **base class** of `MockFoo`, the "nice" or "strict" modifier may not affect it, depending on the compiler. In particular, nesting `NiceMock` and `StrictMock` (e.g. `NiceMock >`) is **not** supported. + 1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). + 1. During the constructor or destructor of `MockFoo`, the mock object is _not_ nice or strict. This may cause surprises if the constructor or destructor calls a mock method on `this` object. (This behavior, however, is consistent with C++'s general rule: if a constructor or destructor calls a virtual method of `this` object, that method is treated as non-virtual. In other words, to the base class's constructor or destructor, `this` object behaves like an instance of the base class, not the derived class. This rule is required for safety. Otherwise a base constructor may use members of a derived class before they are initialized, or a base destructor may use members of a derived class after they have been destroyed.) + +Finally, you should be **very cautious** about when to use naggy or strict mocks, as they tend to make tests more brittle and harder to maintain. When you refactor your code without changing its externally visible behavior, ideally you should't need to update any tests. If your code interacts with a naggy mock, however, you may start to get spammed with warnings as the result of your change. Worse, if your code interacts with a strict mock, your tests may start to fail and you'll be forced to fix them. Our general recommendation is to use nice mocks (not yet the default) most of the time, use naggy mocks (the current default) when developing or debugging tests, and use strict mocks only as the last resort. + +## Simplifying the Interface without Breaking Existing Code ## + +Sometimes a method has a long list of arguments that is mostly +uninteresting. For example, + +``` +class LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) = 0; +}; +``` + +This method's argument list is lengthy and hard to work with (let's +say that the `message` argument is not even 0-terminated). If we mock +it as is, using the mock will be awkward. If, however, we try to +simplify this interface, we'll need to fix all clients depending on +it, which is often infeasible. + +The trick is to re-dispatch the method in the mock class: + +``` +class ScopedMockLog : public LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, const tm* tm_time, + const char* message, size_t message_len) { + // We are only interested in the log severity, full file name, and + // log message. + Log(severity, full_filename, std::string(message, message_len)); + } + + // Implements the mock method: + // + // void Log(LogSeverity severity, + // const string& file_path, + // const string& message); + MOCK_METHOD3(Log, void(LogSeverity severity, const string& file_path, + const string& message)); +}; +``` + +By defining a new mock method with a trimmed argument list, we make +the mock class much more user-friendly. + +## Alternative to Mocking Concrete Classes ## + +Often you may find yourself using classes that don't implement +interfaces. In order to test your code that uses such a class (let's +call it `Concrete`), you may be tempted to make the methods of +`Concrete` virtual and then mock it. + +Try not to do that. + +Making a non-virtual function virtual is a big decision. It creates an +extension point where subclasses can tweak your class' behavior. This +weakens your control on the class because now it's harder to maintain +the class' invariants. You should make a function virtual only when +there is a valid reason for a subclass to override it. + +Mocking concrete classes directly is problematic as it creates a tight +coupling between the class and the tests - any small change in the +class may invalidate your tests and make test maintenance a pain. + +To avoid such problems, many programmers have been practicing "coding +to interfaces": instead of talking to the `Concrete` class, your code +would define an interface and talk to it. Then you implement that +interface as an adaptor on top of `Concrete`. In tests, you can easily +mock that interface to observe how your code is doing. + +This technique incurs some overhead: + + * You pay the cost of virtual function calls (usually not a problem). + * There is more abstraction for the programmers to learn. + +However, it can also bring significant benefits in addition to better +testability: + + * `Concrete`'s API may not fit your problem domain very well, as you may not be the only client it tries to serve. By designing your own interface, you have a chance to tailor it to your need - you may add higher-level functionalities, rename stuff, etc instead of just trimming the class. This allows you to write your code (user of the interface) in a more natural way, which means it will be more readable, more maintainable, and you'll be more productive. + * If `Concrete`'s implementation ever has to change, you don't have to rewrite everywhere it is used. Instead, you can absorb the change in your implementation of the interface, and your other code and tests will be insulated from this change. + +Some people worry that if everyone is practicing this technique, they +will end up writing lots of redundant code. This concern is totally +understandable. However, there are two reasons why it may not be the +case: + + * Different projects may need to use `Concrete` in different ways, so the best interfaces for them will be different. Therefore, each of them will have its own domain-specific interface on top of `Concrete`, and they will not be the same code. + * If enough projects want to use the same interface, they can always share it, just like they have been sharing `Concrete`. You can check in the interface and the adaptor somewhere near `Concrete` (perhaps in a `contrib` sub-directory) and let many projects use it. + +You need to weigh the pros and cons carefully for your particular +problem, but I'd like to assure you that the Java community has been +practicing this for a long time and it's a proven effective technique +applicable in a wide variety of situations. :-) + +## Delegating Calls to a Fake ## + +Some times you have a non-trivial fake implementation of an +interface. For example: + +``` +class Foo { + public: + virtual ~Foo() {} + virtual char DoThis(int n) = 0; + virtual void DoThat(const char* s, int* p) = 0; +}; + +class FakeFoo : public Foo { + public: + virtual char DoThis(int n) { + return (n > 0) ? '+' : + (n < 0) ? '-' : '0'; + } + + virtual void DoThat(const char* s, int* p) { + *p = strlen(s); + } +}; +``` + +Now you want to mock this interface such that you can set expectations +on it. However, you also want to use `FakeFoo` for the default +behavior, as duplicating it in the mock object is, well, a lot of +work. + +When you define the mock class using Google Mock, you can have it +delegate its default action to a fake class you already have, using +this pattern: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + // Normal mock method definitions using Google Mock. + MOCK_METHOD1(DoThis, char(int n)); + MOCK_METHOD2(DoThat, void(const char* s, int* p)); + + // Delegates the default actions of the methods to a FakeFoo object. + // This must be called *before* the custom ON_CALL() statements. + void DelegateToFake() { + ON_CALL(*this, DoThis(_)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThis)); + ON_CALL(*this, DoThat(_, _)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThat)); + } + private: + FakeFoo fake_; // Keeps an instance of the fake in the mock. +}; +``` + +With that, you can use `MockFoo` in your tests as usual. Just remember +that if you don't explicitly set an action in an `ON_CALL()` or +`EXPECT_CALL()`, the fake will be called upon to do it: + +``` +using ::testing::_; + +TEST(AbcTest, Xyz) { + MockFoo foo; + foo.DelegateToFake(); // Enables the fake for delegation. + + // Put your ON_CALL(foo, ...)s here, if any. + + // No action specified, meaning to use the default action. + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(foo, DoThat(_, _)); + + int n = 0; + EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked. + foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. + EXPECT_EQ(2, n); +} +``` + +**Some tips:** + + * If you want, you can still override the default action by providing your own `ON_CALL()` or using `.WillOnce()` / `.WillRepeatedly()` in `EXPECT_CALL()`. + * In `DelegateToFake()`, you only need to delegate the methods whose fake implementation you intend to use. + * The general technique discussed here works for overloaded methods, but you'll need to tell the compiler which version you mean. To disambiguate a mock function (the one you specify inside the parentheses of `ON_CALL()`), see the "Selecting Between Overloaded Functions" section on this page; to disambiguate a fake function (the one you place inside `Invoke()`), use a `static_cast` to specify the function's type. For instance, if class `Foo` has methods `char DoThis(int n)` and `bool DoThis(double x) const`, and you want to invoke the latter, you need to write `Invoke(&fake_, static_cast(&FakeFoo::DoThis))` instead of `Invoke(&fake_, &FakeFoo::DoThis)` (The strange-looking thing inside the angled brackets of `static_cast` is the type of a function pointer to the second `DoThis()` method.). + * Having to mix a mock and a fake is often a sign of something gone wrong. Perhaps you haven't got used to the interaction-based way of testing yet. Or perhaps your interface is taking on too many roles and should be split up. Therefore, **don't abuse this**. We would only recommend to do it as an intermediate step when you are refactoring your code. + +Regarding the tip on mixing a mock and a fake, here's an example on +why it may be a bad sign: Suppose you have a class `System` for +low-level system operations. In particular, it does file and I/O +operations. And suppose you want to test how your code uses `System` +to do I/O, and you just want the file operations to work normally. If +you mock out the entire `System` class, you'll have to provide a fake +implementation for the file operation part, which suggests that +`System` is taking on too many roles. + +Instead, you can define a `FileOps` interface and an `IOOps` interface +and split `System`'s functionalities into the two. Then you can mock +`IOOps` without mocking `FileOps`. + +## Delegating Calls to a Real Object ## + +When using testing doubles (mocks, fakes, stubs, and etc), sometimes +their behaviors will differ from those of the real objects. This +difference could be either intentional (as in simulating an error such +that you can test the error handling code) or unintentional. If your +mocks have different behaviors than the real objects by mistake, you +could end up with code that passes the tests but fails in production. + +You can use the _delegating-to-real_ technique to ensure that your +mock has the same behavior as the real object while retaining the +ability to validate calls. This technique is very similar to the +delegating-to-fake technique, the difference being that we use a real +object instead of a fake. Here's an example: + +``` +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MockFoo() { + // By default, all calls are delegated to the real object. + ON_CALL(*this, DoThis()) + .WillByDefault(Invoke(&real_, &Foo::DoThis)); + ON_CALL(*this, DoThat(_)) + .WillByDefault(Invoke(&real_, &Foo::DoThat)); + ... + } + MOCK_METHOD0(DoThis, ...); + MOCK_METHOD1(DoThat, ...); + ... + private: + Foo real_; +}; +... + + MockFoo mock; + + EXPECT_CALL(mock, DoThis()) + .Times(3); + EXPECT_CALL(mock, DoThat("Hi")) + .Times(AtLeast(1)); + ... use mock in test ... +``` + +With this, Google Mock will verify that your code made the right calls +(with the right arguments, in the right order, called the right number +of times, etc), and a real object will answer the calls (so the +behavior will be the same as in production). This gives you the best +of both worlds. + +## Delegating Calls to a Parent Class ## + +Ideally, you should code to interfaces, whose methods are all pure +virtual. In reality, sometimes you do need to mock a virtual method +that is not pure (i.e, it already has an implementation). For example: + +``` +class Foo { + public: + virtual ~Foo(); + + virtual void Pure(int n) = 0; + virtual int Concrete(const char* str) { ... } +}; + +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); +}; +``` + +Sometimes you may want to call `Foo::Concrete()` instead of +`MockFoo::Concrete()`. Perhaps you want to do it as part of a stub +action, or perhaps your test doesn't need to mock `Concrete()` at all +(but it would be oh-so painful to have to define a new mock class +whenever you don't need to mock one of its methods). + +The trick is to leave a back door in your mock class for accessing the +real methods in the base class: + +``` +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); + + // Use this to call Concrete() defined in Foo. + int FooConcrete(const char* str) { return Foo::Concrete(str); } +}; +``` + +Now, you can call `Foo::Concrete()` inside an action by: + +``` +using ::testing::_; +using ::testing::Invoke; +... + EXPECT_CALL(foo, Concrete(_)) + .WillOnce(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +or tell the mock object that you don't want to mock `Concrete()`: + +``` +using ::testing::Invoke; +... + ON_CALL(foo, Concrete(_)) + .WillByDefault(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +(Why don't we just write `Invoke(&foo, &Foo::Concrete)`? If you do +that, `MockFoo::Concrete()` will be called (and cause an infinite +recursion) since `Foo::Concrete()` is virtual. That's just how C++ +works.) + +# Using Matchers # + +## Matching Argument Values Exactly ## + +You can specify exactly which arguments a mock method is expecting: + +``` +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(5)) + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", bar)); +``` + +## Using Simple Matchers ## + +You can use matchers to match arguments that have a certain property: + +``` +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", NotNull())); + // The second argument must not be NULL. +``` + +A frequently used matcher is `_`, which matches anything: + +``` +using ::testing::_; +using ::testing::NotNull; +... + EXPECT_CALL(foo, DoThat(_, NotNull())); +``` + +## Combining Matchers ## + +You can build complex matchers from existing ones using `AllOf()`, +`AnyOf()`, and `Not()`: + +``` +using ::testing::AllOf; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +... + // The argument must be > 5 and != 10. + EXPECT_CALL(foo, DoThis(AllOf(Gt(5), + Ne(10)))); + + // The first argument must not contain sub-string "blah". + EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), + NULL)); +``` + +## Casting Matchers ## + +Google Mock matchers are statically typed, meaning that the compiler +can catch your mistake if you use a matcher of the wrong type (for +example, if you use `Eq(5)` to match a `string` argument). Good for +you! + +Sometimes, however, you know what you're doing and want the compiler +to give you some slack. One example is that you have a matcher for +`long` and the argument you want to match is `int`. While the two +types aren't exactly the same, there is nothing really wrong with +using a `Matcher` to match an `int` - after all, we can first +convert the `int` argument to a `long` before giving it to the +matcher. + +To support this need, Google Mock gives you the +`SafeMatcherCast(m)` function. It casts a matcher `m` to type +`Matcher`. To ensure safety, Google Mock checks that (let `U` be the +type `m` accepts): + + 1. Type `T` can be implicitly cast to type `U`; + 1. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and floating-point numbers), the conversion from `T` to `U` is not lossy (in other words, any value representable by `T` can also be represented by `U`); and + 1. When `U` is a reference, `T` must also be a reference (as the underlying matcher may be interested in the address of the `U` value). + +The code won't compile if any of these conditions isn't met. + +Here's one example: + +``` +using ::testing::SafeMatcherCast; + +// A base class and a child class. +class Base { ... }; +class Derived : public Base { ... }; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(DoThis, void(Derived* derived)); +}; +... + + MockFoo foo; + // m is a Matcher we got from somewhere. + EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); +``` + +If you find `SafeMatcherCast(m)` too limiting, you can use a similar +function `MatcherCast(m)`. The difference is that `MatcherCast` works +as long as you can `static_cast` type `T` to type `U`. + +`MatcherCast` essentially lets you bypass C++'s type system +(`static_cast` isn't always safe as it could throw away information, +for example), so be careful not to misuse/abuse it. + +## Selecting Between Overloaded Functions ## + +If you expect an overloaded function to be called, the compiler may +need some help on which overloaded version it is. + +To disambiguate functions overloaded on the const-ness of this object, +use the `Const()` argument wrapper. + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + ... + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +... + + MockFoo foo; + Bar bar1, bar2; + EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). + .WillOnce(ReturnRef(bar1)); + EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). + .WillOnce(ReturnRef(bar2)); +``` + +(`Const()` is defined by Google Mock and returns a `const` reference +to its argument.) + +To disambiguate overloaded functions with the same number of arguments +but different argument types, you may need to specify the exact type +of a matcher, either by wrapping your matcher in `Matcher()`, or +using a matcher whose type is fixed (`TypedEq`, `An()`, +etc): + +``` +using ::testing::An; +using ::testing::Lt; +using ::testing::Matcher; +using ::testing::TypedEq; + +class MockPrinter : public Printer { + public: + MOCK_METHOD1(Print, void(int n)); + MOCK_METHOD1(Print, void(char c)); +}; + +TEST(PrinterTest, Print) { + MockPrinter printer; + + EXPECT_CALL(printer, Print(An())); // void Print(int); + EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); + EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); + + printer.Print(3); + printer.Print(6); + printer.Print('a'); +} +``` + +## Performing Different Actions Based on the Arguments ## + +When a mock method is called, the _last_ matching expectation that's +still active will be selected (think "newer overrides older"). So, you +can make a method do different things depending on its argument values +like this: + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Return; +... + // The default case. + EXPECT_CALL(foo, DoThis(_)) + .WillRepeatedly(Return('b')); + + // The more specific case. + EXPECT_CALL(foo, DoThis(Lt(5))) + .WillRepeatedly(Return('a')); +``` + +Now, if `foo.DoThis()` is called with a value less than 5, `'a'` will +be returned; otherwise `'b'` will be returned. + +## Matching Multiple Arguments as a Whole ## + +Sometimes it's not enough to match the arguments individually. For +example, we may want to say that the first argument must be less than +the second argument. The `With()` clause allows us to match +all arguments of a mock function as a whole. For example, + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Ne; +... + EXPECT_CALL(foo, InRange(Ne(0), _)) + .With(Lt()); +``` + +says that the first argument of `InRange()` must not be 0, and must be +less than the second argument. + +The expression inside `With()` must be a matcher of type +`Matcher< ::testing::tuple >`, where `A1`, ..., `An` are the +types of the function arguments. + +You can also write `AllArgs(m)` instead of `m` inside `.With()`. The +two forms are equivalent, but `.With(AllArgs(Lt()))` is more readable +than `.With(Lt())`. + +You can use `Args(m)` to match the `n` selected arguments +(as a tuple) against `m`. For example, + +``` +using ::testing::_; +using ::testing::AllOf; +using ::testing::Args; +using ::testing::Lt; +... + EXPECT_CALL(foo, Blah(_, _, _)) + .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); +``` + +says that `Blah()` will be called with arguments `x`, `y`, and `z` where +`x < y < z`. + +As a convenience and example, Google Mock provides some matchers for +2-tuples, including the `Lt()` matcher above. See the [CheatSheet](CheatSheet.md) for +the complete list. + +Note that if you want to pass the arguments to a predicate of your own +(e.g. `.With(Args<0, 1>(Truly(&MyPredicate)))`), that predicate MUST be +written to take a `::testing::tuple` as its argument; Google Mock will pass the `n` selected arguments as _one_ single tuple to the predicate. + +## Using Matchers as Predicates ## + +Have you noticed that a matcher is just a fancy predicate that also +knows how to describe itself? Many existing algorithms take predicates +as arguments (e.g. those defined in STL's `` header), and +it would be a shame if Google Mock matchers are not allowed to +participate. + +Luckily, you can use a matcher where a unary predicate functor is +expected by wrapping it inside the `Matches()` function. For example, + +``` +#include +#include + +std::vector v; +... +// How many elements in v are >= 10? +const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); +``` + +Since you can build complex matchers from simpler ones easily using +Google Mock, this gives you a way to conveniently construct composite +predicates (doing the same using STL's `` header is just +painful). For example, here's a predicate that's satisfied by any +number that is >= 0, <= 100, and != 50: + +``` +Matches(AllOf(Ge(0), Le(100), Ne(50))) +``` + +## Using Matchers in Google Test Assertions ## + +Since matchers are basically predicates that also know how to describe +themselves, there is a way to take advantage of them in +[Google Test](../../googletest/) assertions. It's +called `ASSERT_THAT` and `EXPECT_THAT`: + +``` + ASSERT_THAT(value, matcher); // Asserts that value matches matcher. + EXPECT_THAT(value, matcher); // The non-fatal version. +``` + +For example, in a Google Test test you can write: + +``` +#include "gmock/gmock.h" + +using ::testing::AllOf; +using ::testing::Ge; +using ::testing::Le; +using ::testing::MatchesRegex; +using ::testing::StartsWith; +... + + EXPECT_THAT(Foo(), StartsWith("Hello")); + EXPECT_THAT(Bar(), MatchesRegex("Line \\d+")); + ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10))); +``` + +which (as you can probably guess) executes `Foo()`, `Bar()`, and +`Baz()`, and verifies that: + + * `Foo()` returns a string that starts with `"Hello"`. + * `Bar()` returns a string that matches regular expression `"Line \\d+"`. + * `Baz()` returns a number in the range [5, 10]. + +The nice thing about these macros is that _they read like +English_. They generate informative messages too. For example, if the +first `EXPECT_THAT()` above fails, the message will be something like: + +``` +Value of: Foo() + Actual: "Hi, world!" +Expected: starts with "Hello" +``` + +**Credit:** The idea of `(ASSERT|EXPECT)_THAT` was stolen from the +[Hamcrest](https://github.com/hamcrest/) project, which adds +`assertThat()` to JUnit. + +## Using Predicates as Matchers ## + +Google Mock provides a built-in set of matchers. In case you find them +lacking, you can use an arbitray unary predicate function or functor +as a matcher - as long as the predicate accepts a value of the type +you want. You do this by wrapping the predicate inside the `Truly()` +function, for example: + +``` +using ::testing::Truly; + +int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } +... + + // Bar() must be called with an even number. + EXPECT_CALL(foo, Bar(Truly(IsEven))); +``` + +Note that the predicate function / functor doesn't have to return +`bool`. It works as long as the return value can be used as the +condition in statement `if (condition) ...`. + +## Matching Arguments that Are Not Copyable ## + +When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, Google Mock saves +away a copy of `bar`. When `Foo()` is called later, Google Mock +compares the argument to `Foo()` with the saved copy of `bar`. This +way, you don't need to worry about `bar` being modified or destroyed +after the `EXPECT_CALL()` is executed. The same is true when you use +matchers like `Eq(bar)`, `Le(bar)`, and so on. + +But what if `bar` cannot be copied (i.e. has no copy constructor)? You +could define your own matcher function and use it with `Truly()`, as +the previous couple of recipes have shown. Or, you may be able to get +away from it if you can guarantee that `bar` won't be changed after +the `EXPECT_CALL()` is executed. Just tell Google Mock that it should +save a reference to `bar`, instead of a copy of it. Here's how: + +``` +using ::testing::Eq; +using ::testing::ByRef; +using ::testing::Lt; +... + // Expects that Foo()'s argument == bar. + EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar)))); + + // Expects that Foo()'s argument < bar. + EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar)))); +``` + +Remember: if you do this, don't change `bar` after the +`EXPECT_CALL()`, or the result is undefined. + +## Validating a Member of an Object ## + +Often a mock function takes a reference to object as an argument. When +matching the argument, you may not want to compare the entire object +against a fixed object, as that may be over-specification. Instead, +you may need to validate a certain member variable or the result of a +certain getter method of the object. You can do this with `Field()` +and `Property()`. More specifically, + +``` +Field(&Foo::bar, m) +``` + +is a matcher that matches a `Foo` object whose `bar` member variable +satisfies matcher `m`. + +``` +Property(&Foo::baz, m) +``` + +is a matcher that matches a `Foo` object whose `baz()` method returns +a value that satisfies matcher `m`. + +For example: + +> | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | +|:-----------------------------|:-----------------------------------| +> | `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | + +Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no +argument and be declared as `const`. + +BTW, `Field()` and `Property()` can also match plain pointers to +objects. For instance, + +``` +Field(&Foo::number, Ge(3)) +``` + +matches a plain pointer `p` where `p->number >= 3`. If `p` is `NULL`, +the match will always fail regardless of the inner matcher. + +What if you want to validate more than one members at the same time? +Remember that there is `AllOf()`. + +## Validating the Value Pointed to by a Pointer Argument ## + +C++ functions often take pointers as arguments. You can use matchers +like `IsNull()`, `NotNull()`, and other comparison matchers to match a +pointer, but what if you want to make sure the value _pointed to_ by +the pointer, instead of the pointer itself, has a certain property? +Well, you can use the `Pointee(m)` matcher. + +`Pointee(m)` matches a pointer iff `m` matches the value the pointer +points to. For example: + +``` +using ::testing::Ge; +using ::testing::Pointee; +... + EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); +``` + +expects `foo.Bar()` to be called with a pointer that points to a value +greater than or equal to 3. + +One nice thing about `Pointee()` is that it treats a `NULL` pointer as +a match failure, so you can write `Pointee(m)` instead of + +``` + AllOf(NotNull(), Pointee(m)) +``` + +without worrying that a `NULL` pointer will crash your test. + +Also, did we tell you that `Pointee()` works with both raw pointers +**and** smart pointers (`linked_ptr`, `shared_ptr`, `scoped_ptr`, and +etc)? + +What if you have a pointer to pointer? You guessed it - you can use +nested `Pointee()` to probe deeper inside the value. For example, +`Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer +that points to a number less than 3 (what a mouthful...). + +## Testing a Certain Property of an Object ## + +Sometimes you want to specify that an object argument has a certain +property, but there is no existing matcher that does this. If you want +good error messages, you should define a matcher. If you want to do it +quick and dirty, you could get away with writing an ordinary function. + +Let's say you have a mock function that takes an object of type `Foo`, +which has an `int bar()` method and an `int baz()` method, and you +want to constrain that the argument's `bar()` value plus its `baz()` +value is a given number. Here's how you can define a matcher to do it: + +``` +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class BarPlusBazEqMatcher : public MatcherInterface { + public: + explicit BarPlusBazEqMatcher(int expected_sum) + : expected_sum_(expected_sum) {} + + virtual bool MatchAndExplain(const Foo& foo, + MatchResultListener* listener) const { + return (foo.bar() + foo.baz()) == expected_sum_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "bar() + baz() equals " << expected_sum_; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "bar() + baz() does not equal " << expected_sum_; + } + private: + const int expected_sum_; +}; + +inline Matcher BarPlusBazEq(int expected_sum) { + return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); +} + +... + + EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; +``` + +## Matching Containers ## + +Sometimes an STL container (e.g. list, vector, map, ...) is passed to +a mock function and you may want to validate it. Since most STL +containers support the `==` operator, you can write +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. + +Sometimes, though, you may want to be more flexible (for example, the +first element must be an exact match, but the second element can be +any positive number, and so on). Also, containers used in tests often +have a small number of elements, and having to define the expected +container out-of-line is a bit of a hassle. + +You can use the `ElementsAre()` or `UnorderedElementsAre()` matcher in +such cases: + +``` +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); +``` + +The above matcher says that the container must have 4 elements, which +must be 1, greater than 0, anything, and 5 respectively. + +If you instead write: + +``` +using ::testing::_; +using ::testing::Gt; +using ::testing::UnorderedElementsAre; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(UnorderedElementsAre(1, Gt(0), _, 5))); +``` + +It means that the container must have 4 elements, which under some +permutation must be 1, greater than 0, anything, and 5 respectively. + +`ElementsAre()` and `UnorderedElementsAre()` are overloaded to take 0 +to 10 arguments. If more are needed, you can place them in a C-style +array and use `ElementsAreArray()` or `UnorderedElementsAreArray()` +instead: + +``` +using ::testing::ElementsAreArray; +... + + // ElementsAreArray accepts an array of element values. + const int expected_vector1[] = { 1, 5, 2, 4, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); + + // Or, an array of element matchers. + Matcher expected_vector2 = { 1, Gt(2), _, 3, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); +``` + +In case the array needs to be dynamically created (and therefore the +array size cannot be inferred by the compiler), you can give +`ElementsAreArray()` an additional argument to specify the array size: + +``` +using ::testing::ElementsAreArray; +... + int* const expected_vector3 = new int[count]; + ... fill expected_vector3 with values ... + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); +``` + +**Tips:** + + * `ElementsAre*()` can be used to match _any_ container that implements the STL iterator pattern (i.e. it has a `const_iterator` type and supports `begin()/end()`), not just the ones defined in STL. It will even work with container types yet to be written - as long as they follows the above pattern. + * You can use nested `ElementsAre*()` to match nested (multi-dimensional) containers. + * If the container is passed by pointer instead of by reference, just write `Pointee(ElementsAre*(...))`. + * The order of elements _matters_ for `ElementsAre*()`. Therefore don't use it with containers whose element order is undefined (e.g. `hash_map`). + +## Sharing Matchers ## + +Under the hood, a Google Mock matcher object consists of a pointer to +a ref-counted implementation object. Copying matchers is allowed and +very efficient, as only the pointer is copied. When the last matcher +that references the implementation object dies, the implementation +object will be deleted. + +Therefore, if you have some complex matcher that you want to use again +and again, there is no need to build it everytime. Just assign it to a +matcher variable and use that variable repeatedly! For example, + +``` + Matcher in_range = AllOf(Gt(5), Le(10)); + ... use in_range as a matcher in multiple EXPECT_CALLs ... +``` + +# Setting Expectations # + +## Knowing When to Expect ## + +`ON_CALL` is likely the single most under-utilized construct in Google Mock. + +There are basically two constructs for defining the behavior of a mock object: `ON_CALL` and `EXPECT_CALL`. The difference? `ON_CALL` defines what happens when a mock method is called, but _doesn't imply any expectation on the method being called._ `EXPECT_CALL` not only defines the behavior, but also sets an expectation that _the method will be called with the given arguments, for the given number of times_ (and _in the given order_ when you specify the order too). + +Since `EXPECT_CALL` does more, isn't it better than `ON_CALL`? Not really. Every `EXPECT_CALL` adds a constraint on the behavior of the code under test. Having more constraints than necessary is _baaad_ - even worse than not having enough constraints. + +This may be counter-intuitive. How could tests that verify more be worse than tests that verify less? Isn't verification the whole point of tests? + +The answer, lies in _what_ a test should verify. **A good test verifies the contract of the code.** If a test over-specifies, it doesn't leave enough freedom to the implementation. As a result, changing the implementation without breaking the contract (e.g. refactoring and optimization), which should be perfectly fine to do, can break such tests. Then you have to spend time fixing them, only to see them broken again the next time the implementation is changed. + +Keep in mind that one doesn't have to verify more than one property in one test. In fact, **it's a good style to verify only one thing in one test.** If you do that, a bug will likely break only one or two tests instead of dozens (which case would you rather debug?). If you are also in the habit of giving tests descriptive names that tell what they verify, you can often easily guess what's wrong just from the test log itself. + +So use `ON_CALL` by default, and only use `EXPECT_CALL` when you actually intend to verify that the call is made. For example, you may have a bunch of `ON_CALL`s in your test fixture to set the common mock behavior shared by all tests in the same group, and write (scarcely) different `EXPECT_CALL`s in different `TEST_F`s to verify different aspects of the code's behavior. Compared with the style where each `TEST` has many `EXPECT_CALL`s, this leads to tests that are more resilient to implementational changes (and thus less likely to require maintenance) and makes the intent of the tests more obvious (so they are easier to maintain when you do need to maintain them). + +If you are bothered by the "Uninteresting mock function call" message printed when a mock method without an `EXPECT_CALL` is called, you may use a `NiceMock` instead to suppress all such messages for the mock object, or suppress the message for specific methods by adding `EXPECT_CALL(...).Times(AnyNumber())`. DO NOT suppress it by blindly adding an `EXPECT_CALL(...)`, or you'll have a test that's a pain to maintain. + +## Ignoring Uninteresting Calls ## + +If you are not interested in how a mock method is called, just don't +say anything about it. In this case, if the method is ever called, +Google Mock will perform its default action to allow the test program +to continue. If you are not happy with the default action taken by +Google Mock, you can override it using `DefaultValue::Set()` +(described later in this document) or `ON_CALL()`. + +Please note that once you expressed interest in a particular mock +method (via `EXPECT_CALL()`), all invocations to it must match some +expectation. If this function is called but the arguments don't match +any `EXPECT_CALL()` statement, it will be an error. + +## Disallowing Unexpected Calls ## + +If a mock method shouldn't be called at all, explicitly say so: + +``` +using ::testing::_; +... + EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +If some calls to the method are allowed, but the rest are not, just +list all the expected calls: + +``` +using ::testing::AnyNumber; +using ::testing::Gt; +... + EXPECT_CALL(foo, Bar(5)); + EXPECT_CALL(foo, Bar(Gt(10))) + .Times(AnyNumber()); +``` + +A call to `foo.Bar()` that doesn't match any of the `EXPECT_CALL()` +statements will be an error. + +## Understanding Uninteresting vs Unexpected Calls ## + +_Uninteresting_ calls and _unexpected_ calls are different concepts in Google Mock. _Very_ different. + +A call `x.Y(...)` is **uninteresting** if there's _not even a single_ `EXPECT_CALL(x, Y(...))` set. In other words, the test isn't interested in the `x.Y()` method at all, as evident in that the test doesn't care to say anything about it. + +A call `x.Y(...)` is **unexpected** if there are some `EXPECT_CALL(x, Y(...))s` set, but none of them matches the call. Put another way, the test is interested in the `x.Y()` method (therefore it _explicitly_ sets some `EXPECT_CALL` to verify how it's called); however, the verification fails as the test doesn't expect this particular call to happen. + +**An unexpected call is always an error,** as the code under test doesn't behave the way the test expects it to behave. + +**By default, an uninteresting call is not an error,** as it violates no constraint specified by the test. (Google Mock's philosophy is that saying nothing means there is no constraint.) However, it leads to a warning, as it _might_ indicate a problem (e.g. the test author might have forgotten to specify a constraint). + +In Google Mock, `NiceMock` and `StrictMock` can be used to make a mock class "nice" or "strict". How does this affect uninteresting calls and unexpected calls? + +A **nice mock** suppresses uninteresting call warnings. It is less chatty than the default mock, but otherwise is the same. If a test fails with a default mock, it will also fail using a nice mock instead. And vice versa. Don't expect making a mock nice to change the test's result. + +A **strict mock** turns uninteresting call warnings into errors. So making a mock strict may change the test's result. + +Let's look at an example: + +``` +TEST(...) { + NiceMock mock_registry; + EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) + .WillRepeatedly(Return("Larry Page")); + + // Use mock_registry in code under test. + ... &mock_registry ... +} +``` + +The sole `EXPECT_CALL` here says that all calls to `GetDomainOwner()` must have `"google.com"` as the argument. If `GetDomainOwner("yahoo.com")` is called, it will be an unexpected call, and thus an error. Having a nice mock doesn't change the severity of an unexpected call. + +So how do we tell Google Mock that `GetDomainOwner()` can be called with some other arguments as well? The standard technique is to add a "catch all" `EXPECT_CALL`: + +``` + EXPECT_CALL(mock_registry, GetDomainOwner(_)) + .Times(AnyNumber()); // catches all other calls to this method. + EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) + .WillRepeatedly(Return("Larry Page")); +``` + +Remember that `_` is the wildcard matcher that matches anything. With this, if `GetDomainOwner("google.com")` is called, it will do what the second `EXPECT_CALL` says; if it is called with a different argument, it will do what the first `EXPECT_CALL` says. + +Note that the order of the two `EXPECT_CALLs` is important, as a newer `EXPECT_CALL` takes precedence over an older one. + +For more on uninteresting calls, nice mocks, and strict mocks, read ["The Nice, the Strict, and the Naggy"](#the-nice-the-strict-and-the-naggy). + +## Expecting Ordered Calls ## + +Although an `EXPECT_CALL()` statement defined earlier takes precedence +when Google Mock tries to match a function call with an expectation, +by default calls don't have to happen in the order `EXPECT_CALL()` +statements are written. For example, if the arguments match the +matchers in the third `EXPECT_CALL()`, but not those in the first two, +then the third expectation will be used. + +If you would rather have all calls occur in the order of the +expectations, put the `EXPECT_CALL()` statements in a block where you +define a variable of type `InSequence`: + +``` + using ::testing::_; + using ::testing::InSequence; + + { + InSequence s; + + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(bar, DoThat(_)) + .Times(2); + EXPECT_CALL(foo, DoThis(6)); + } +``` + +In this example, we expect a call to `foo.DoThis(5)`, followed by two +calls to `bar.DoThat()` where the argument can be anything, which are +in turn followed by a call to `foo.DoThis(6)`. If a call occurred +out-of-order, Google Mock will report an error. + +## Expecting Partially Ordered Calls ## + +Sometimes requiring everything to occur in a predetermined order can +lead to brittle tests. For example, we may care about `A` occurring +before both `B` and `C`, but aren't interested in the relative order +of `B` and `C`. In this case, the test should reflect our real intent, +instead of being overly constraining. + +Google Mock allows you to impose an arbitrary DAG (directed acyclic +graph) on the calls. One way to express the DAG is to use the +[After](CheatSheet.md#the-after-clause) clause of `EXPECT_CALL`. + +Another way is via the `InSequence()` clause (not the same as the +`InSequence` class), which we borrowed from jMock 2. It's less +flexible than `After()`, but more convenient when you have long chains +of sequential calls, as it doesn't require you to come up with +different names for the expectations in the chains. Here's how it +works: + +If we view `EXPECT_CALL()` statements as nodes in a graph, and add an +edge from node A to node B wherever A must occur before B, we can get +a DAG. We use the term "sequence" to mean a directed path in this +DAG. Now, if we decompose the DAG into sequences, we just need to know +which sequences each `EXPECT_CALL()` belongs to in order to be able to +reconstruct the orginal DAG. + +So, to specify the partial order on the expectations we need to do two +things: first to define some `Sequence` objects, and then for each +`EXPECT_CALL()` say which `Sequence` objects it is part +of. Expectations in the same sequence must occur in the order they are +written. For example, + +``` + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(foo, A()) + .InSequence(s1, s2); + EXPECT_CALL(bar, B()) + .InSequence(s1); + EXPECT_CALL(bar, C()) + .InSequence(s2); + EXPECT_CALL(foo, D()) + .InSequence(s2); +``` + +specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> +C -> D`): + +``` + +---> B + | + A ---| + | + +---> C ---> D +``` + +This means that A must occur before B and C, and C must occur before +D. There's no restriction about the order other than these. + +## Controlling When an Expectation Retires ## + +When a mock method is called, Google Mock only consider expectations +that are still active. An expectation is active when created, and +becomes inactive (aka _retires_) when a call that has to occur later +has occurred. For example, in + +``` + using ::testing::_; + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 + .Times(AnyNumber()) + .InSequence(s1, s2); + EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 + .InSequence(s1); + EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 + .InSequence(s2); +``` + +as soon as either #2 or #3 is matched, #1 will retire. If a warning +`"File too large."` is logged after this, it will be an error. + +Note that an expectation doesn't retire automatically when it's +saturated. For example, + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 +``` + +says that there will be exactly one warning with the message `"File +too large."`. If the second warning contains this message too, #2 will +match again and result in an upper-bound-violated error. + +If this is not what you want, you can ask an expectation to retire as +soon as it becomes saturated: + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 + .RetiresOnSaturation(); +``` + +Here #2 can be used only once, so if you have two warnings with the +message `"File too large."`, the first will match #2 and the second +will match #1 - there will be no error. + +# Using Actions # + +## Returning References from Mock Methods ## + +If a mock function's return type is a reference, you need to use +`ReturnRef()` instead of `Return()` to return a result: + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetBar, Bar&()); +}; +... + + MockFoo foo; + Bar bar; + EXPECT_CALL(foo, GetBar()) + .WillOnce(ReturnRef(bar)); +``` + +## Returning Live Values from Mock Methods ## + +The `Return(x)` action saves a copy of `x` when the action is +_created_, and always returns the same value whenever it's +executed. Sometimes you may want to instead return the _live_ value of +`x` (i.e. its value at the time when the action is _executed_.). + +If the mock function's return type is a reference, you can do it using +`ReturnRef(x)`, as shown in the previous recipe ("Returning References +from Mock Methods"). However, Google Mock doesn't let you use +`ReturnRef()` in a mock function whose return type is not a reference, +as doing that usually indicates a user error. So, what shall you do? + +You may be tempted to try `ByRef()`: + +``` +using testing::ByRef; +using testing::Return; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetValue, int()); +}; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(Return(ByRef(x))); + x = 42; + EXPECT_EQ(42, foo.GetValue()); +``` + +Unfortunately, it doesn't work here. The above code will fail with error: + +``` +Value of: foo.GetValue() + Actual: 0 +Expected: 42 +``` + +The reason is that `Return(value)` converts `value` to the actual +return type of the mock function at the time when the action is +_created_, not when it is _executed_. (This behavior was chosen for +the action to be safe when `value` is a proxy object that references +some temporary objects.) As a result, `ByRef(x)` is converted to an +`int` value (instead of a `const int&`) when the expectation is set, +and `Return(ByRef(x))` will always return 0. + +`ReturnPointee(pointer)` was provided to solve this problem +specifically. It returns the value pointed to by `pointer` at the time +the action is _executed_: + +``` +using testing::ReturnPointee; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(ReturnPointee(&x)); // Note the & here. + x = 42; + EXPECT_EQ(42, foo.GetValue()); // This will succeed now. +``` + +## Combining Actions ## + +Want to do more than one thing when a function is called? That's +fine. `DoAll()` allow you to do sequence of actions every time. Only +the return value of the last action in the sequence will be used. + +``` +using ::testing::DoAll; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Bar, bool(int n)); +}; +... + + EXPECT_CALL(foo, Bar(_)) + .WillOnce(DoAll(action_1, + action_2, + ... + action_n)); +``` + +## Mocking Side Effects ## + +Sometimes a method exhibits its effect not via returning a value but +via side effects. For example, it may change some global state or +modify an output argument. To mock side effects, in general you can +define your own action by implementing `::testing::ActionInterface`. + +If all you need to do is to change an output argument, the built-in +`SetArgPointee()` action is convenient: + +``` +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + MOCK_METHOD2(Mutate, void(bool mutate, int* value)); + ... +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, Mutate(true, _)) + .WillOnce(SetArgPointee<1>(5)); +``` + +In this example, when `mutator.Mutate()` is called, we will assign 5 +to the `int` variable pointed to by argument #1 +(0-based). + +`SetArgPointee()` conveniently makes an internal copy of the +value you pass to it, removing the need to keep the value in scope and +alive. The implication however is that the value must have a copy +constructor and assignment operator. + +If the mock method also needs to return a value as well, you can chain +`SetArgPointee()` with `Return()` using `DoAll()`: + +``` +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + ... + MOCK_METHOD1(MutateInt, bool(int* value)); +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, MutateInt(_)) + .WillOnce(DoAll(SetArgPointee<0>(5), + Return(true))); +``` + +If the output argument is an array, use the +`SetArrayArgument(first, last)` action instead. It copies the +elements in source range `[first, last)` to the array pointed to by +the `N`-th (0-based) argument: + +``` +using ::testing::NotNull; +using ::testing::SetArrayArgument; + +class MockArrayMutator : public ArrayMutator { + public: + MOCK_METHOD2(Mutate, void(int* values, int num_values)); + ... +}; +... + + MockArrayMutator mutator; + int values[5] = { 1, 2, 3, 4, 5 }; + EXPECT_CALL(mutator, Mutate(NotNull(), 5)) + .WillOnce(SetArrayArgument<0>(values, values + 5)); +``` + +This also works when the argument is an output iterator: + +``` +using ::testing::_; +using ::testing::SeArrayArgument; + +class MockRolodex : public Rolodex { + public: + MOCK_METHOD1(GetNames, void(std::back_insert_iterator >)); + ... +}; +... + + MockRolodex rolodex; + vector names; + names.push_back("George"); + names.push_back("John"); + names.push_back("Thomas"); + EXPECT_CALL(rolodex, GetNames(_)) + .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); +``` + +## Changing a Mock Object's Behavior Based on the State ## + +If you expect a call to change the behavior of a mock object, you can use `::testing::InSequence` to specify different behaviors before and after the call: + +``` +using ::testing::InSequence; +using ::testing::Return; + +... + { + InSequence seq; + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(my_mock, Flush()); + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(false)); + } + my_mock.FlushIfDirty(); +``` + +This makes `my_mock.IsDirty()` return `true` before `my_mock.Flush()` is called and return `false` afterwards. + +If the behavior change is more complex, you can store the effects in a variable and make a mock method get its return value from that variable: + +``` +using ::testing::_; +using ::testing::SaveArg; +using ::testing::Return; + +ACTION_P(ReturnPointee, p) { return *p; } +... + int previous_value = 0; + EXPECT_CALL(my_mock, GetPrevValue()) + .WillRepeatedly(ReturnPointee(&previous_value)); + EXPECT_CALL(my_mock, UpdateValue(_)) + .WillRepeatedly(SaveArg<0>(&previous_value)); + my_mock.DoSomethingToUpdateValue(); +``` + +Here `my_mock.GetPrevValue()` will always return the argument of the last `UpdateValue()` call. + +## Setting the Default Value for a Return Type ## + +If a mock method's return type is a built-in C++ type or pointer, by +default it will return 0 when invoked. Also, in C++ 11 and above, a mock +method whose return type has a default constructor will return a default-constructed +value by default. You only need to specify an +action if this default value doesn't work for you. + +Sometimes, you may want to change this default value, or you may want +to specify a default value for types Google Mock doesn't know +about. You can do this using the `::testing::DefaultValue` class +template: + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD0(CalculateBar, Bar()); +}; +... + + Bar default_bar; + // Sets the default return value for type Bar. + DefaultValue::Set(default_bar); + + MockFoo foo; + + // We don't need to specify an action here, as the default + // return value works for us. + EXPECT_CALL(foo, CalculateBar()); + + foo.CalculateBar(); // This should return default_bar. + + // Unsets the default return value. + DefaultValue::Clear(); +``` + +Please note that changing the default value for a type can make you +tests hard to understand. We recommend you to use this feature +judiciously. For example, you may want to make sure the `Set()` and +`Clear()` calls are right next to the code that uses your mock. + +## Setting the Default Actions for a Mock Method ## + +You've learned how to change the default value of a given +type. However, this may be too coarse for your purpose: perhaps you +have two mock methods with the same return type and you want them to +have different behaviors. The `ON_CALL()` macro allows you to +customize your mock's behavior at the method level: + +``` +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Gt; +using ::testing::Return; +... + ON_CALL(foo, Sign(_)) + .WillByDefault(Return(-1)); + ON_CALL(foo, Sign(0)) + .WillByDefault(Return(0)); + ON_CALL(foo, Sign(Gt(0))) + .WillByDefault(Return(1)); + + EXPECT_CALL(foo, Sign(_)) + .Times(AnyNumber()); + + foo.Sign(5); // This should return 1. + foo.Sign(-9); // This should return -1. + foo.Sign(0); // This should return 0. +``` + +As you may have guessed, when there are more than one `ON_CALL()` +statements, the news order take precedence over the older ones. In +other words, the **last** one that matches the function arguments will +be used. This matching order allows you to set up the common behavior +in a mock object's constructor or the test fixture's set-up phase and +specialize the mock's behavior later. + +## Using Functions/Methods/Functors as Actions ## + +If the built-in actions don't suit you, you can easily use an existing +function, method, or functor as an action: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(Sum, int(int x, int y)); + MOCK_METHOD1(ComplexJob, bool(int x)); +}; + +int CalculateSum(int x, int y) { return x + y; } + +class Helper { + public: + bool ComplexJob(int x); +}; +... + + MockFoo foo; + Helper helper; + EXPECT_CALL(foo, Sum(_, _)) + .WillOnce(Invoke(CalculateSum)); + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(Invoke(&helper, &Helper::ComplexJob)); + + foo.Sum(5, 6); // Invokes CalculateSum(5, 6). + foo.ComplexJob(10); // Invokes helper.ComplexJob(10); +``` + +The only requirement is that the type of the function, etc must be +_compatible_ with the signature of the mock function, meaning that the +latter's arguments can be implicitly converted to the corresponding +arguments of the former, and the former's return type can be +implicitly converted to that of the latter. So, you can invoke +something whose type is _not_ exactly the same as the mock function, +as long as it's safe to do so - nice, huh? + +## Invoking a Function/Method/Functor Without Arguments ## + +`Invoke()` is very useful for doing actions that are more complex. It +passes the mock function's arguments to the function or functor being +invoked such that the callee has the full context of the call to work +with. If the invoked function is not interested in some or all of the +arguments, it can simply ignore them. + +Yet, a common pattern is that a test author wants to invoke a function +without the arguments of the mock function. `Invoke()` allows her to +do that using a wrapper function that throws away the arguments before +invoking an underlining nullary function. Needless to say, this can be +tedious and obscures the intent of the test. + +`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except +that it doesn't pass the mock function's arguments to the +callee. Here's an example: + +``` +using ::testing::_; +using ::testing::InvokeWithoutArgs; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(ComplexJob, bool(int n)); +}; + +bool Job1() { ... } +... + + MockFoo foo; + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(InvokeWithoutArgs(Job1)); + + foo.ComplexJob(10); // Invokes Job1(). +``` + +## Invoking an Argument of the Mock Function ## + +Sometimes a mock function will receive a function pointer or a functor +(in other words, a "callable") as an argument, e.g. + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, bool(int n, bool (*fp)(int))); +}; +``` + +and you may want to invoke this callable argument: + +``` +using ::testing::_; +... + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(...); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +Arghh, you need to refer to a mock function argument but C++ has no +lambda (yet), so you have to define your own action. :-( Or do you +really? + +Well, Google Mock has an action to solve _exactly_ this problem: + +``` + InvokeArgument(arg_1, arg_2, ..., arg_m) +``` + +will invoke the `N`-th (0-based) argument the mock function receives, +with `arg_1`, `arg_2`, ..., and `arg_m`. No matter if the argument is +a function pointer or a functor, Google Mock handles them both. + +With that, you could write: + +``` +using ::testing::_; +using ::testing::InvokeArgument; +... + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(InvokeArgument<1>(5)); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +What if the callable takes an argument by reference? No problem - just +wrap it inside `ByRef()`: + +``` +... + MOCK_METHOD1(Bar, bool(bool (*fp)(int, const Helper&))); +... +using ::testing::_; +using ::testing::ByRef; +using ::testing::InvokeArgument; +... + + MockFoo foo; + Helper helper; + ... + EXPECT_CALL(foo, Bar(_)) + .WillOnce(InvokeArgument<0>(5, ByRef(helper))); + // ByRef(helper) guarantees that a reference to helper, not a copy of it, + // will be passed to the callable. +``` + +What if the callable takes an argument by reference and we do **not** +wrap the argument in `ByRef()`? Then `InvokeArgument()` will _make a +copy_ of the argument, and pass a _reference to the copy_, instead of +a reference to the original value, to the callable. This is especially +handy when the argument is a temporary value: + +``` +... + MOCK_METHOD1(DoThat, bool(bool (*f)(const double& x, const string& s))); +... +using ::testing::_; +using ::testing::InvokeArgument; +... + + MockFoo foo; + ... + EXPECT_CALL(foo, DoThat(_)) + .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); + // Will execute (*f)(5.0, string("Hi")), where f is the function pointer + // DoThat() receives. Note that the values 5.0 and string("Hi") are + // temporary and dead once the EXPECT_CALL() statement finishes. Yet + // it's fine to perform this action later, since a copy of the values + // are kept inside the InvokeArgument action. +``` + +## Ignoring an Action's Result ## + +Sometimes you have an action that returns _something_, but you need an +action that returns `void` (perhaps you want to use it in a mock +function that returns `void`, or perhaps it needs to be used in +`DoAll()` and it's not the last in the list). `IgnoreResult()` lets +you do that. For example: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; + +int Process(const MyData& data); +string DoSomething(); + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Abc, void(const MyData& data)); + MOCK_METHOD0(Xyz, bool()); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, Abc(_)) + // .WillOnce(Invoke(Process)); + // The above line won't compile as Process() returns int but Abc() needs + // to return void. + .WillOnce(IgnoreResult(Invoke(Process))); + + EXPECT_CALL(foo, Xyz()) + .WillOnce(DoAll(IgnoreResult(Invoke(DoSomething)), + // Ignores the string DoSomething() returns. + Return(true))); +``` + +Note that you **cannot** use `IgnoreResult()` on an action that already +returns `void`. Doing so will lead to ugly compiler errors. + +## Selecting an Action's Arguments ## + +Say you have a mock function `Foo()` that takes seven arguments, and +you have a custom action that you want to invoke when `Foo()` is +called. Trouble is, the custom action only wants three arguments: + +``` +using ::testing::_; +using ::testing::Invoke; +... + MOCK_METHOD7(Foo, bool(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight)); +... + +bool IsVisibleInQuadrant1(bool visible, int x, int y) { + return visible && x >= 0 && y >= 0; +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( +``` + +To please the compiler God, you can to define an "adaptor" that has +the same signature as `Foo()` and calls the custom action with the +right arguments: + +``` +using ::testing::_; +using ::testing::Invoke; + +bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight) { + return IsVisibleInQuadrant1(visible, x, y); +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. +``` + +But isn't this awkward? + +Google Mock provides a generic _action adaptor_, so you can spend your +time minding more important business than writing your own +adaptors. Here's the syntax: + +``` + WithArgs(action) +``` + +creates an action that passes the arguments of the mock function at +the given indices (0-based) to the inner `action` and performs +it. Using `WithArgs`, our original example can be written as: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::WithArgs; +... + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); + // No need to define your own adaptor. +``` + +For better readability, Google Mock also gives you: + + * `WithoutArgs(action)` when the inner `action` takes _no_ argument, and + * `WithArg(action)` (no `s` after `Arg`) when the inner `action` takes _one_ argument. + +As you may have realized, `InvokeWithoutArgs(...)` is just syntactic +sugar for `WithoutArgs(Inovke(...))`. + +Here are more tips: + + * The inner action used in `WithArgs` and friends does not have to be `Invoke()` -- it can be anything. + * You can repeat an argument in the argument list if necessary, e.g. `WithArgs<2, 3, 3, 5>(...)`. + * You can change the order of the arguments, e.g. `WithArgs<3, 2, 1>(...)`. + * The types of the selected arguments do _not_ have to match the signature of the inner action exactly. It works as long as they can be implicitly converted to the corresponding arguments of the inner action. For example, if the 4-th argument of the mock function is an `int` and `my_action` takes a `double`, `WithArg<4>(my_action)` will work. + +## Ignoring Arguments in Action Functions ## + +The selecting-an-action's-arguments recipe showed us one way to make a +mock function and an action with incompatible argument lists fit +together. The downside is that wrapping the action in +`WithArgs<...>()` can get tedious for people writing the tests. + +If you are defining a function, method, or functor to be used with +`Invoke*()`, and you are not interested in some of its arguments, an +alternative to `WithArgs` is to declare the uninteresting arguments as +`Unused`. This makes the definition less cluttered and less fragile in +case the types of the uninteresting arguments change. It could also +increase the chance the action function can be reused. For example, +given + +``` + MOCK_METHOD3(Foo, double(const string& label, double x, double y)); + MOCK_METHOD3(Bar, double(int index, double x, double y)); +``` + +instead of + +``` +using ::testing::_; +using ::testing::Invoke; + +double DistanceToOriginWithLabel(const string& label, double x, double y) { + return sqrt(x*x + y*y); +} + +double DistanceToOriginWithIndex(int index, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOriginWithLabel)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOriginWithIndex)); +``` + +you could write + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Unused; + +double DistanceToOrigin(Unused, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOrigin)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOrigin)); +``` + +## Sharing Actions ## + +Just like matchers, a Google Mock action object consists of a pointer +to a ref-counted implementation object. Therefore copying actions is +also allowed and very efficient. When the last action that references +the implementation object dies, the implementation object will be +deleted. + +If you have some complex action that you want to use again and again, +you may not have to build it from scratch everytime. If the action +doesn't have an internal state (i.e. if it always does the same thing +no matter how many times it has been called), you can assign it to an +action variable and use that variable repeatedly. For example: + +``` + Action set_flag = DoAll(SetArgPointee<0>(5), + Return(true)); + ... use set_flag in .WillOnce() and .WillRepeatedly() ... +``` + +However, if the action has its own state, you may be surprised if you +share the action object. Suppose you have an action factory +`IncrementCounter(init)` which creates an action that increments and +returns a counter whose initial value is `init`, using two actions +created from the same expression and using a shared action will +exihibit different behaviors. Example: + +``` + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(IncrementCounter(0)); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(IncrementCounter(0)); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 1 - Blah() uses a different + // counter than Bar()'s. +``` + +versus + +``` + Action increment = IncrementCounter(0); + + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(increment); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(increment); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 3 - the counter is shared. +``` + +# Misc Recipes on Using Google Mock # + +## Mocking Methods That Use Move-Only Types ## + +C++11 introduced move-only types. A move-only-typed value can be moved from one object to another, but cannot be copied. `std::unique_ptr` is probably the most commonly used move-only type. + +Mocking a method that takes and/or returns move-only types presents some challenges, but nothing insurmountable. This recipe shows you how you can do it. + +Let’s say we are working on a fictional project that lets one post and share snippets called “buzzes”. Your code uses these types: + +``` +enum class AccessLevel { kInternal, kPublic }; + +class Buzz { + public: + explicit Buzz(AccessLevel access) { … } + ... +}; + +class Buzzer { + public: + virtual ~Buzzer() {} + virtual std::unique_ptr MakeBuzz(const std::string& text) = 0; + virtual bool ShareBuzz(std::unique_ptr buzz, Time timestamp) = 0; + ... +}; +``` + +A `Buzz` object represents a snippet being posted. A class that implements the `Buzzer` interface is capable of creating and sharing `Buzz`. Methods in `Buzzer` may return a `unique_ptr` or take a `unique_ptr`. Now we need to mock `Buzzer` in our tests. + +To mock a method that returns a move-only type, you just use the familiar `MOCK_METHOD` syntax as usual: + +``` +class MockBuzzer : public Buzzer { + public: + MOCK_METHOD1(MakeBuzz, std::unique_ptr(const std::string& text)); + … +}; +``` + +However, if you attempt to use the same `MOCK_METHOD` pattern to mock a method that takes a move-only parameter, you’ll get a compiler error currently: + +``` + // Does NOT compile! + MOCK_METHOD2(ShareBuzz, bool(std::unique_ptr buzz, Time timestamp)); +``` + +While it’s highly desirable to make this syntax just work, it’s not trivial and the work hasn’t been done yet. Fortunately, there is a trick you can apply today to get something that works nearly as well as this. + +The trick, is to delegate the `ShareBuzz()` method to a mock method (let’s call it `DoShareBuzz()`) that does not take move-only parameters: + +``` +class MockBuzzer : public Buzzer { + public: + MOCK_METHOD1(MakeBuzz, std::unique_ptr(const std::string& text)); + MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp)); + bool ShareBuzz(std::unique_ptr buzz, Time timestamp) { + return DoShareBuzz(buzz.get(), timestamp); + } +}; +``` + +Note that there's no need to define or declare `DoShareBuzz()` in a base class. You only need to define it as a `MOCK_METHOD` in the mock class. + +Now that we have the mock class defined, we can use it in tests. In the following code examples, we assume that we have defined a `MockBuzzer` object named `mock_buzzer_`: + +``` + MockBuzzer mock_buzzer_; +``` + +First let’s see how we can set expectations on the `MakeBuzz()` method, which returns a `unique_ptr`. + +As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or `.WillRepeated()` clause), when that expectation fires, the default action for that method will be taken. Since `unique_ptr<>` has a default constructor that returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an action: + +``` + // Use the default action. + EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")); + + // Triggers the previous EXPECT_CALL. + EXPECT_EQ(nullptr, mock_buzzer_.MakeBuzz("hello")); +``` + +If you are not happy with the default action, you can tweak it. Depending on what you need, you may either tweak the default action for a specific (mock object, mock method) combination using `ON_CALL()`, or you may tweak the default action for all mock methods that return a specific type. The usage of `ON_CALL()` is similar to `EXPECT_CALL()`, so we’ll skip it and just explain how to do the latter (tweaking the default action for a specific return type). You do this via the `DefaultValue<>::SetFactory()` and `DefaultValue<>::Clear()` API: + +``` + // Sets the default action for return type std::unique_ptr to + // creating a new Buzz every time. + DefaultValue>::SetFactory( + [] { return MakeUnique(AccessLevel::kInternal); }); + + // When this fires, the default action of MakeBuzz() will run, which + // will return a new Buzz object. + EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber()); + + auto buzz1 = mock_buzzer_.MakeBuzz("hello"); + auto buzz2 = mock_buzzer_.MakeBuzz("hello"); + EXPECT_NE(nullptr, buzz1); + EXPECT_NE(nullptr, buzz2); + EXPECT_NE(buzz1, buzz2); + + // Resets the default action for return type std::unique_ptr, + // to avoid interfere with other tests. + DefaultValue>::Clear(); +``` + +What if you want the method to do something other than the default action? If you just need to return a pre-defined move-only value, you can use the `Return(ByMove(...))` action: + +``` + // When this fires, the unique_ptr<> specified by ByMove(...) will + // be returned. + EXPECT_CALL(mock_buzzer_, MakeBuzz("world")) + .WillOnce(Return(ByMove(MakeUnique(AccessLevel::kInternal)))); + + EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("world")); +``` + +Note that `ByMove()` is essential here - if you drop it, the code won’t compile. + +Quiz time! What do you think will happen if a `Return(ByMove(...))` action is performed more than once (e.g. you write `….WillRepeatedly(Return(ByMove(...)));`)? Come think of it, after the first time the action runs, the source value will be consumed (since it’s a move-only value), so the next time around, there’s no value to move from -- you’ll get a run-time error that `Return(ByMove(...))` can only be run once. + +If you need your mock method to do more than just moving a pre-defined value, remember that you can always use `Invoke()` to call a lambda or a callable object, which can do pretty much anything you want: + +``` + EXPECT_CALL(mock_buzzer_, MakeBuzz("x")) + .WillRepeatedly(Invoke([](const std::string& text) { + return std::make_unique(AccessLevel::kInternal); + })); + + EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); + EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); +``` + +Every time this `EXPECT_CALL` fires, a new `unique_ptr` will be created and returned. You cannot do this with `Return(ByMove(...))`. + +Now there’s one topic we haven’t covered: how do you set expectations on `ShareBuzz()`, which takes a move-only-typed parameter? The answer is you don’t. Instead, you set expectations on the `DoShareBuzz()` mock method (remember that we defined a `MOCK_METHOD` for `DoShareBuzz()`, not `ShareBuzz()`): + +``` + EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)); + + // When one calls ShareBuzz() on the MockBuzzer like this, the call is + // forwarded to DoShareBuzz(), which is mocked. Therefore this statement + // will trigger the above EXPECT_CALL. + mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal), + ::base::Now()); +``` + +Some of you may have spotted one problem with this approach: the `DoShareBuzz()` mock method differs from the real `ShareBuzz()` method in that it cannot take ownership of the buzz parameter - `ShareBuzz()` will always delete buzz after `DoShareBuzz()` returns. What if you need to save the buzz object somewhere for later use when `ShareBuzz()` is called? Indeed, you'd be stuck. + +Another problem with the `DoShareBuzz()` we had is that it can surprise people reading or maintaining the test, as one would expect that `DoShareBuzz()` has (logically) the same contract as `ShareBuzz()`. + +Fortunately, these problems can be fixed with a bit more code. Let's try to get it right this time: + +``` +class MockBuzzer : public Buzzer { + public: + MockBuzzer() { + // Since DoShareBuzz(buzz, time) is supposed to take ownership of + // buzz, define a default behavior for DoShareBuzz(buzz, time) to + // delete buzz. + ON_CALL(*this, DoShareBuzz(_, _)) + .WillByDefault(Invoke([](Buzz* buzz, Time timestamp) { + delete buzz; + return true; + })); + } + + MOCK_METHOD1(MakeBuzz, std::unique_ptr(const std::string& text)); + + // Takes ownership of buzz. + MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp)); + bool ShareBuzz(std::unique_ptr buzz, Time timestamp) { + return DoShareBuzz(buzz.release(), timestamp); + } +}; +``` + +Now, the mock `DoShareBuzz()` method is free to save the buzz argument for later use if this is what you want: + +``` + std::unique_ptr intercepted_buzz; + EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)) + .WillOnce(Invoke([&intercepted_buzz](Buzz* buzz, Time timestamp) { + // Save buzz in intercepted_buzz for analysis later. + intercepted_buzz.reset(buzz); + return false; + })); + + mock_buzzer_.ShareBuzz(std::make_unique(AccessLevel::kInternal), + Now()); + EXPECT_NE(nullptr, intercepted_buzz); +``` + +Using the tricks covered in this recipe, you are now able to mock methods that take and/or return move-only types. Put your newly-acquired power to good use - when you design a new API, you can now feel comfortable using `unique_ptrs` as appropriate, without fearing that doing so will compromise your tests. + +## Making the Compilation Faster ## + +Believe it or not, the _vast majority_ of the time spent on compiling +a mock class is in generating its constructor and destructor, as they +perform non-trivial tasks (e.g. verification of the +expectations). What's more, mock methods with different signatures +have different types and thus their constructors/destructors need to +be generated by the compiler separately. As a result, if you mock many +different types of methods, compiling your mock class can get really +slow. + +If you are experiencing slow compilation, you can move the definition +of your mock class' constructor and destructor out of the class body +and into a `.cpp` file. This way, even if you `#include` your mock +class in N files, the compiler only needs to generate its constructor +and destructor once, resulting in a much faster compilation. + +Let's illustrate the idea using an example. Here's the definition of a +mock class before applying this recipe: + +``` +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // Since we don't declare the constructor or the destructor, + // the compiler will generate them in every translation unit + // where this mock class is used. + + MOCK_METHOD0(DoThis, int()); + MOCK_METHOD1(DoThat, bool(const char* str)); + ... more mock methods ... +}; +``` + +After the change, it would look like: + +``` +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // The constructor and destructor are declared, but not defined, here. + MockFoo(); + virtual ~MockFoo(); + + MOCK_METHOD0(DoThis, int()); + MOCK_METHOD1(DoThat, bool(const char* str)); + ... more mock methods ... +}; +``` +and +``` +// File mock_foo.cpp. +#include "path/to/mock_foo.h" + +// The definitions may appear trivial, but the functions actually do a +// lot of things through the constructors/destructors of the member +// variables used to implement the mock methods. +MockFoo::MockFoo() {} +MockFoo::~MockFoo() {} +``` + +## Forcing a Verification ## + +When it's being destoyed, your friendly mock object will automatically +verify that all expectations on it have been satisfied, and will +generate [Google Test](../../googletest/) failures +if not. This is convenient as it leaves you with one less thing to +worry about. That is, unless you are not sure if your mock object will +be destoyed. + +How could it be that your mock object won't eventually be destroyed? +Well, it might be created on the heap and owned by the code you are +testing. Suppose there's a bug in that code and it doesn't delete the +mock object properly - you could end up with a passing test when +there's actually a bug. + +Using a heap checker is a good idea and can alleviate the concern, but +its implementation may not be 100% reliable. So, sometimes you do want +to _force_ Google Mock to verify a mock object before it is +(hopefully) destructed. You can do this with +`Mock::VerifyAndClearExpectations(&mock_object)`: + +``` +TEST(MyServerTest, ProcessesRequest) { + using ::testing::Mock; + + MockFoo* const foo = new MockFoo; + EXPECT_CALL(*foo, ...)...; + // ... other expectations ... + + // server now owns foo. + MyServer server(foo); + server.ProcessRequest(...); + + // In case that server's destructor will forget to delete foo, + // this will verify the expectations anyway. + Mock::VerifyAndClearExpectations(foo); +} // server is destroyed when it goes out of scope here. +``` + +**Tip:** The `Mock::VerifyAndClearExpectations()` function returns a +`bool` to indicate whether the verification was successful (`true` for +yes), so you can wrap that function call inside a `ASSERT_TRUE()` if +there is no point going further when the verification has failed. + +## Using Check Points ## + +Sometimes you may want to "reset" a mock object at various check +points in your test: at each check point, you verify that all existing +expectations on the mock object have been satisfied, and then you set +some new expectations on it as if it's newly created. This allows you +to work with a mock object in "phases" whose sizes are each +manageable. + +One such scenario is that in your test's `SetUp()` function, you may +want to put the object you are testing into a certain state, with the +help from a mock object. Once in the desired state, you want to clear +all expectations on the mock, such that in the `TEST_F` body you can +set fresh expectations on it. + +As you may have figured out, the `Mock::VerifyAndClearExpectations()` +function we saw in the previous recipe can help you here. Or, if you +are using `ON_CALL()` to set default actions on the mock object and +want to clear the default actions as well, use +`Mock::VerifyAndClear(&mock_object)` instead. This function does what +`Mock::VerifyAndClearExpectations(&mock_object)` does and returns the +same `bool`, **plus** it clears the `ON_CALL()` statements on +`mock_object` too. + +Another trick you can use to achieve the same effect is to put the +expectations in sequences and insert calls to a dummy "check-point" +function at specific places. Then you can verify that the mock +function calls do happen at the right time. For example, if you are +exercising code: + +``` +Foo(1); +Foo(2); +Foo(3); +``` + +and want to verify that `Foo(1)` and `Foo(3)` both invoke +`mock.Bar("a")`, but `Foo(2)` doesn't invoke anything. You can write: + +``` +using ::testing::MockFunction; + +TEST(FooTest, InvokesBarCorrectly) { + MyMock mock; + // Class MockFunction has exactly one mock method. It is named + // Call() and has type F. + MockFunction check; + { + InSequence s; + + EXPECT_CALL(mock, Bar("a")); + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(mock, Bar("a")); + } + Foo(1); + check.Call("1"); + Foo(2); + check.Call("2"); + Foo(3); +} +``` + +The expectation spec says that the first `Bar("a")` must happen before +check point "1", the second `Bar("a")` must happen after check point "2", +and nothing should happen between the two check points. The explicit +check points make it easy to tell which `Bar("a")` is called by which +call to `Foo()`. + +## Mocking Destructors ## + +Sometimes you want to make sure a mock object is destructed at the +right time, e.g. after `bar->A()` is called but before `bar->B()` is +called. We already know that you can specify constraints on the order +of mock function calls, so all we need to do is to mock the destructor +of the mock function. + +This sounds simple, except for one problem: a destructor is a special +function with special syntax and special semantics, and the +`MOCK_METHOD0` macro doesn't work for it: + +``` + MOCK_METHOD0(~MockFoo, void()); // Won't compile! +``` + +The good news is that you can use a simple pattern to achieve the same +effect. First, add a mock function `Die()` to your mock class and call +it in the destructor, like this: + +``` +class MockFoo : public Foo { + ... + // Add the following two lines to the mock class. + MOCK_METHOD0(Die, void()); + virtual ~MockFoo() { Die(); } +}; +``` + +(If the name `Die()` clashes with an existing symbol, choose another +name.) Now, we have translated the problem of testing when a `MockFoo` +object dies to testing when its `Die()` method is called: + +``` + MockFoo* foo = new MockFoo; + MockBar* bar = new MockBar; + ... + { + InSequence s; + + // Expects *foo to die after bar->A() and before bar->B(). + EXPECT_CALL(*bar, A()); + EXPECT_CALL(*foo, Die()); + EXPECT_CALL(*bar, B()); + } +``` + +And that's that. + +## Using Google Mock and Threads ## + +**IMPORTANT NOTE:** What we describe in this recipe is **ONLY** true on +platforms where Google Mock is thread-safe. Currently these are only +platforms that support the pthreads library (this includes Linux and Mac). +To make it thread-safe on other platforms we only need to implement +some synchronization operations in `"gtest/internal/gtest-port.h"`. + +In a **unit** test, it's best if you could isolate and test a piece of +code in a single-threaded context. That avoids race conditions and +dead locks, and makes debugging your test much easier. + +Yet many programs are multi-threaded, and sometimes to test something +we need to pound on it from more than one thread. Google Mock works +for this purpose too. + +Remember the steps for using a mock: + + 1. Create a mock object `foo`. + 1. Set its default actions and expectations using `ON_CALL()` and `EXPECT_CALL()`. + 1. The code under test calls methods of `foo`. + 1. Optionally, verify and reset the mock. + 1. Destroy the mock yourself, or let the code under test destroy it. The destructor will automatically verify it. + +If you follow the following simple rules, your mocks and threads can +live happily together: + + * Execute your _test code_ (as opposed to the code being tested) in _one_ thread. This makes your test easy to follow. + * Obviously, you can do step #1 without locking. + * When doing step #2 and #5, make sure no other thread is accessing `foo`. Obvious too, huh? + * #3 and #4 can be done either in one thread or in multiple threads - anyway you want. Google Mock takes care of the locking, so you don't have to do any - unless required by your test logic. + +If you violate the rules (for example, if you set expectations on a +mock while another thread is calling its methods), you get undefined +behavior. That's not fun, so don't do it. + +Google Mock guarantees that the action for a mock function is done in +the same thread that called the mock function. For example, in + +``` + EXPECT_CALL(mock, Foo(1)) + .WillOnce(action1); + EXPECT_CALL(mock, Foo(2)) + .WillOnce(action2); +``` + +if `Foo(1)` is called in thread 1 and `Foo(2)` is called in thread 2, +Google Mock will execute `action1` in thread 1 and `action2` in thread +2. + +Google Mock does _not_ impose a sequence on actions performed in +different threads (doing so may create deadlocks as the actions may +need to cooperate). This means that the execution of `action1` and +`action2` in the above example _may_ interleave. If this is a problem, +you should add proper synchronization logic to `action1` and `action2` +to make the test thread-safe. + + +Also, remember that `DefaultValue` is a global resource that +potentially affects _all_ living mock objects in your +program. Naturally, you won't want to mess with it from multiple +threads or when there still are mocks in action. + +## Controlling How Much Information Google Mock Prints ## + +When Google Mock sees something that has the potential of being an +error (e.g. a mock function with no expectation is called, a.k.a. an +uninteresting call, which is allowed but perhaps you forgot to +explicitly ban the call), it prints some warning messages, including +the arguments of the function and the return value. Hopefully this +will remind you to take a look and see if there is indeed a problem. + +Sometimes you are confident that your tests are correct and may not +appreciate such friendly messages. Some other times, you are debugging +your tests or learning about the behavior of the code you are testing, +and wish you could observe every mock call that happens (including +argument values and the return value). Clearly, one size doesn't fit +all. + +You can control how much Google Mock tells you using the +`--gmock_verbose=LEVEL` command-line flag, where `LEVEL` is a string +with three possible values: + + * `info`: Google Mock will print all informational messages, warnings, and errors (most verbose). At this setting, Google Mock will also log any calls to the `ON_CALL/EXPECT_CALL` macros. + * `warning`: Google Mock will print both warnings and errors (less verbose). This is the default. + * `error`: Google Mock will print errors only (least verbose). + +Alternatively, you can adjust the value of that flag from within your +tests like so: + +``` + ::testing::FLAGS_gmock_verbose = "error"; +``` + +Now, judiciously use the right flag to enable Google Mock serve you better! + +## Gaining Super Vision into Mock Calls ## + +You have a test using Google Mock. It fails: Google Mock tells you +that some expectations aren't satisfied. However, you aren't sure why: +Is there a typo somewhere in the matchers? Did you mess up the order +of the `EXPECT_CALL`s? Or is the code under test doing something +wrong? How can you find out the cause? + +Won't it be nice if you have X-ray vision and can actually see the +trace of all `EXPECT_CALL`s and mock method calls as they are made? +For each call, would you like to see its actual argument values and +which `EXPECT_CALL` Google Mock thinks it matches? + +You can unlock this power by running your test with the +`--gmock_verbose=info` flag. For example, given the test program: + +``` +using testing::_; +using testing::HasSubstr; +using testing::Return; + +class MockFoo { + public: + MOCK_METHOD2(F, void(const string& x, const string& y)); +}; + +TEST(Foo, Bar) { + MockFoo mock; + EXPECT_CALL(mock, F(_, _)).WillRepeatedly(Return()); + EXPECT_CALL(mock, F("a", "b")); + EXPECT_CALL(mock, F("c", HasSubstr("d"))); + + mock.F("a", "good"); + mock.F("a", "b"); +} +``` + +if you run it with `--gmock_verbose=info`, you will see this output: + +``` +[ RUN ] Foo.Bar + +foo_test.cc:14: EXPECT_CALL(mock, F(_, _)) invoked +foo_test.cc:15: EXPECT_CALL(mock, F("a", "b")) invoked +foo_test.cc:16: EXPECT_CALL(mock, F("c", HasSubstr("d"))) invoked +foo_test.cc:14: Mock function call matches EXPECT_CALL(mock, F(_, _))... + Function call: F(@0x7fff7c8dad40"a", @0x7fff7c8dad10"good") +foo_test.cc:15: Mock function call matches EXPECT_CALL(mock, F("a", "b"))... + Function call: F(@0x7fff7c8dada0"a", @0x7fff7c8dad70"b") +foo_test.cc:16: Failure +Actual function call count doesn't match EXPECT_CALL(mock, F("c", HasSubstr("d")))... + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] Foo.Bar +``` + +Suppose the bug is that the `"c"` in the third `EXPECT_CALL` is a typo +and should actually be `"a"`. With the above message, you should see +that the actual `F("a", "good")` call is matched by the first +`EXPECT_CALL`, not the third as you thought. From that it should be +obvious that the third `EXPECT_CALL` is written wrong. Case solved. + +## Running Tests in Emacs ## + +If you build and run your tests in Emacs, the source file locations of +Google Mock and [Google Test](../../googletest/) +errors will be highlighted. Just press `` on one of them and +you'll be taken to the offending line. Or, you can just type `C-x `` +to jump to the next error. + +To make it even easier, you can add the following lines to your +`~/.emacs` file: + +``` +(global-set-key "\M-m" 'compile) ; m is for make +(global-set-key [M-down] 'next-error) +(global-set-key [M-up] '(lambda () (interactive) (next-error -1))) +``` + +Then you can type `M-m` to start a build, or `M-up`/`M-down` to move +back and forth between errors. + +## Fusing Google Mock Source Files ## + +Google Mock's implementation consists of dozens of files (excluding +its own tests). Sometimes you may want them to be packaged up in +fewer files instead, such that you can easily copy them to a new +machine and start hacking there. For this we provide an experimental +Python script `fuse_gmock_files.py` in the `scripts/` directory +(starting with release 1.2.0). Assuming you have Python 2.4 or above +installed on your machine, just go to that directory and run +``` +python fuse_gmock_files.py OUTPUT_DIR +``` + +and you should see an `OUTPUT_DIR` directory being created with files +`gtest/gtest.h`, `gmock/gmock.h`, and `gmock-gtest-all.cc` in it. +These three files contain everything you need to use Google Mock (and +Google Test). Just copy them to anywhere you want and you are ready +to write tests and use mocks. You can use the +[scrpts/test/Makefile](../scripts/test/Makefile) file as an example on how to compile your tests +against them. + +# Extending Google Mock # + +## Writing New Matchers Quickly ## + +The `MATCHER*` family of macros can be used to define custom matchers +easily. The syntax: + +``` +MATCHER(name, description_string_expression) { statements; } +``` + +will define a matcher with the given name that executes the +statements, which must return a `bool` to indicate if the match +succeeds. Inside the statements, you can refer to the value being +matched by `arg`, and refer to its type by `arg_type`. + +The description string is a `string`-typed expression that documents +what the matcher does, and is used to generate the failure message +when the match fails. It can (and should) reference the special +`bool` variable `negation`, and should evaluate to the description of +the matcher when `negation` is `false`, or that of the matcher's +negation when `negation` is `true`. + +For convenience, we allow the description string to be empty (`""`), +in which case Google Mock will use the sequence of words in the +matcher name as the description. + +For example: +``` +MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } +``` +allows you to write +``` + // Expects mock_foo.Bar(n) to be called where n is divisible by 7. + EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); +``` +or, +``` +using ::testing::Not; +... + EXPECT_THAT(some_expression, IsDivisibleBy7()); + EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7())); +``` +If the above assertions fail, they will print something like: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 +... + Value of: some_other_expression + Expected: not (is divisible by 7) + Actual: 21 +``` +where the descriptions `"is divisible by 7"` and `"not (is divisible +by 7)"` are automatically calculated from the matcher name +`IsDivisibleBy7`. + +As you may have noticed, the auto-generated descriptions (especially +those for the negation) may not be so great. You can always override +them with a string expression of your own: +``` +MATCHER(IsDivisibleBy7, std::string(negation ? "isn't" : "is") + + " divisible by 7") { + return (arg % 7) == 0; +} +``` + +Optionally, you can stream additional information to a hidden argument +named `result_listener` to explain the match result. For example, a +better definition of `IsDivisibleBy7` is: +``` +MATCHER(IsDivisibleBy7, "") { + if ((arg % 7) == 0) + return true; + + *result_listener << "the remainder is " << (arg % 7); + return false; +} +``` + +With this definition, the above assertion will give a better message: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 (the remainder is 6) +``` + +You should let `MatchAndExplain()` print _any additional information_ +that can help a user understand the match result. Note that it should +explain why the match succeeds in case of a success (unless it's +obvious) - this is useful when the matcher is used inside +`Not()`. There is no need to print the argument value itself, as +Google Mock already prints it for you. + +**Notes:** + + 1. The type of the value being matched (`arg_type`) is determined by the context in which you use the matcher and is supplied to you by the compiler, so you don't need to worry about declaring it (nor can you). This allows the matcher to be polymorphic. For example, `IsDivisibleBy7()` can be used to match any type where the value of `(arg % 7) == 0` can be implicitly converted to a `bool`. In the `Bar(IsDivisibleBy7())` example above, if method `Bar()` takes an `int`, `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be `unsigned long`; and so on. + 1. Google Mock doesn't guarantee when or how many times a matcher will be invoked. Therefore the matcher logic must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). This requirement must be satisfied no matter how you define the matcher (e.g. using one of the methods described in the following recipes). In particular, a matcher can never call a mock function, as that will affect the state of the mock object and Google Mock. + +## Writing New Parameterized Matchers Quickly ## + +Sometimes you'll want to define a matcher that has parameters. For that you +can use the macro: +``` +MATCHER_P(name, param_name, description_string) { statements; } +``` +where the description string can be either `""` or a string expression +that references `negation` and `param_name`. + +For example: +``` +MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +``` +will allow you to write: +``` + EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +``` +which may lead to this message (assuming `n` is 10): +``` + Value of: Blah("a") + Expected: has absolute value 10 + Actual: -9 +``` + +Note that both the matcher description and its parameter are +printed, making the message human-friendly. + +In the matcher definition body, you can write `foo_type` to +reference the type of a parameter named `foo`. For example, in the +body of `MATCHER_P(HasAbsoluteValue, value)` above, you can write +`value_type` to refer to the type of `value`. + +Google Mock also provides `MATCHER_P2`, `MATCHER_P3`, ..., up to +`MATCHER_P10` to support multi-parameter matchers: +``` +MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; } +``` + +Please note that the custom description string is for a particular +**instance** of the matcher, where the parameters have been bound to +actual values. Therefore usually you'll want the parameter values to +be part of the description. Google Mock lets you do that by +referencing the matcher parameters in the description string +expression. + +For example, +``` + using ::testing::PrintToString; + MATCHER_P2(InClosedRange, low, hi, + std::string(negation ? "isn't" : "is") + " in range [" + + PrintToString(low) + ", " + PrintToString(hi) + "]") { + return low <= arg && arg <= hi; + } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the message: +``` + Expected: is in range [4, 6] +``` + +If you specify `""` as the description, the failure message will +contain the sequence of words in the matcher name followed by the +parameter values printed as a tuple. For example, +``` + MATCHER_P2(InClosedRange, low, hi, "") { ... } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the text: +``` + Expected: in closed range (4, 6) +``` + +For the purpose of typing, you can view +``` +MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +``` +as shorthand for +``` +template +FooMatcherPk +Foo(p1_type p1, ..., pk_type pk) { ... } +``` + +When you write `Foo(v1, ..., vk)`, the compiler infers the types of +the parameters `v1`, ..., and `vk` for you. If you are not happy with +the result of the type inference, you can specify the types by +explicitly instantiating the template, as in `Foo(5, false)`. +As said earlier, you don't get to (or need to) specify +`arg_type` as that's determined by the context in which the matcher +is used. + +You can assign the result of expression `Foo(p1, ..., pk)` to a +variable of type `FooMatcherPk`. This can be +useful when composing matchers. Matchers that don't have a parameter +or have only one parameter have special types: you can assign `Foo()` +to a `FooMatcher`-typed variable, and assign `Foo(p)` to a +`FooMatcherP`-typed variable. + +While you can instantiate a matcher template with reference types, +passing the parameters by pointer usually makes your code more +readable. If, however, you still want to pass a parameter by +reference, be aware that in the failure message generated by the +matcher you will see the value of the referenced object but not its +address. + +You can overload matchers with different numbers of parameters: +``` +MATCHER_P(Blah, a, description_string_1) { ... } +MATCHER_P2(Blah, a, b, description_string_2) { ... } +``` + +While it's tempting to always use the `MATCHER*` macros when defining +a new matcher, you should also consider implementing +`MatcherInterface` or using `MakePolymorphicMatcher()` instead (see +the recipes that follow), especially if you need to use the matcher a +lot. While these approaches require more work, they give you more +control on the types of the value being matched and the matcher +parameters, which in general leads to better compiler error messages +that pay off in the long run. They also allow overloading matchers +based on parameter types (as opposed to just based on the number of +parameters). + +## Writing New Monomorphic Matchers ## + +A matcher of argument type `T` implements +`::testing::MatcherInterface` and does two things: it tests whether a +value of type `T` matches the matcher, and can describe what kind of +values it matches. The latter ability is used for generating readable +error messages when expectations are violated. + +The interface looks like this: + +``` +class MatchResultListener { + public: + ... + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x); + + // Returns the underlying ostream. + ::std::ostream* stream(); +}; + +template +class MatcherInterface { + public: + virtual ~MatcherInterface(); + + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Describes this matcher to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. + virtual void DescribeNegationTo(::std::ostream* os) const; +}; +``` + +If you need a custom matcher but `Truly()` is not a good option (for +example, you may not be happy with the way `Truly(predicate)` +describes itself, or you may want your matcher to be polymorphic as +`Eq(value)` is), you can define a matcher to do whatever you want in +two steps: first implement the matcher interface, and then define a +factory function to create a matcher instance. The second step is not +strictly needed but it makes the syntax of using the matcher nicer. + +For example, you can define a matcher to test whether an `int` is +divisible by 7 and then use it like this: +``` +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, MatchResultListener* listener) const { + return (n % 7) == 0; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "is divisible by 7"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "is not divisible by 7"; + } +}; + +inline Matcher DivisibleBy7() { + return MakeMatcher(new DivisibleBy7Matcher); +} +... + + EXPECT_CALL(foo, Bar(DivisibleBy7())); +``` + +You may improve the matcher message by streaming additional +information to the `listener` argument in `MatchAndExplain()`: + +``` +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, + MatchResultListener* listener) const { + const int remainder = n % 7; + if (remainder != 0) { + *listener << "the remainder is " << remainder; + } + return remainder == 0; + } + ... +}; +``` + +Then, `EXPECT_THAT(x, DivisibleBy7());` may general a message like this: +``` +Value of: x +Expected: is divisible by 7 + Actual: 23 (the remainder is 2) +``` + +## Writing New Polymorphic Matchers ## + +You've learned how to write your own matchers in the previous +recipe. Just one problem: a matcher created using `MakeMatcher()` only +works for one particular type of arguments. If you want a +_polymorphic_ matcher that works with arguments of several types (for +instance, `Eq(x)` can be used to match a `value` as long as `value` == +`x` compiles -- `value` and `x` don't have to share the same type), +you can learn the trick from `"gmock/gmock-matchers.h"` but it's a bit +involved. + +Fortunately, most of the time you can define a polymorphic matcher +easily with the help of `MakePolymorphicMatcher()`. Here's how you can +define `NotNull()` as an example: + +``` +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +using ::testing::NotNull; +using ::testing::PolymorphicMatcher; + +class NotNullMatcher { + public: + // To implement a polymorphic matcher, first define a COPYABLE class + // that has three members MatchAndExplain(), DescribeTo(), and + // DescribeNegationTo(), like the following. + + // In this example, we want to use NotNull() with any pointer, so + // MatchAndExplain() accepts a pointer of any type as its first argument. + // In general, you can define MatchAndExplain() as an ordinary method or + // a method template, or even overload it. + template + bool MatchAndExplain(T* p, + MatchResultListener* /* listener */) const { + return p != NULL; + } + + // Describes the property of a value matching this matcher. + void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } + + // Describes the property of a value NOT matching this matcher. + void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } +}; + +// To construct a polymorphic matcher, pass an instance of the class +// to MakePolymorphicMatcher(). Note the return type. +inline PolymorphicMatcher NotNull() { + return MakePolymorphicMatcher(NotNullMatcher()); +} +... + + EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. +``` + +**Note:** Your polymorphic matcher class does **not** need to inherit from +`MatcherInterface` or any other class, and its methods do **not** need +to be virtual. + +Like in a monomorphic matcher, you may explain the match result by +streaming additional information to the `listener` argument in +`MatchAndExplain()`. + +## Writing New Cardinalities ## + +A cardinality is used in `Times()` to tell Google Mock how many times +you expect a call to occur. It doesn't have to be exact. For example, +you can say `AtLeast(5)` or `Between(2, 4)`. + +If the built-in set of cardinalities doesn't suit you, you are free to +define your own by implementing the following interface (in namespace +`testing`): + +``` +class CardinalityInterface { + public: + virtual ~CardinalityInterface(); + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; +``` + +For example, to specify that a call must occur even number of times, +you can write + +``` +using ::testing::Cardinality; +using ::testing::CardinalityInterface; +using ::testing::MakeCardinality; + +class EvenNumberCardinality : public CardinalityInterface { + public: + virtual bool IsSatisfiedByCallCount(int call_count) const { + return (call_count % 2) == 0; + } + + virtual bool IsSaturatedByCallCount(int call_count) const { + return false; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "called even number of times"; + } +}; + +Cardinality EvenNumber() { + return MakeCardinality(new EvenNumberCardinality); +} +... + + EXPECT_CALL(foo, Bar(3)) + .Times(EvenNumber()); +``` + +## Writing New Actions Quickly ## + +If the built-in actions don't work for you, and you find it +inconvenient to use `Invoke()`, you can use a macro from the `ACTION*` +family to quickly define a new action that can be used in your code as +if it's a built-in action. + +By writing +``` +ACTION(name) { statements; } +``` +in a namespace scope (i.e. not inside a class or function), you will +define an action with the given name that executes the statements. +The value returned by `statements` will be used as the return value of +the action. Inside the statements, you can refer to the K-th +(0-based) argument of the mock function as `argK`. For example: +``` +ACTION(IncrementArg1) { return ++(*arg1); } +``` +allows you to write +``` +... WillOnce(IncrementArg1()); +``` + +Note that you don't need to specify the types of the mock function +arguments. Rest assured that your code is type-safe though: +you'll get a compiler error if `*arg1` doesn't support the `++` +operator, or if the type of `++(*arg1)` isn't compatible with the mock +function's return type. + +Another example: +``` +ACTION(Foo) { + (*arg2)(5); + Blah(); + *arg1 = 0; + return arg0; +} +``` +defines an action `Foo()` that invokes argument #2 (a function pointer) +with 5, calls function `Blah()`, sets the value pointed to by argument +#1 to 0, and returns argument #0. + +For more convenience and flexibility, you can also use the following +pre-defined symbols in the body of `ACTION`: + +| `argK_type` | The type of the K-th (0-based) argument of the mock function | +|:------------|:-------------------------------------------------------------| +| `args` | All arguments of the mock function as a tuple | +| `args_type` | The type of all arguments of the mock function as a tuple | +| `return_type` | The return type of the mock function | +| `function_type` | The type of the mock function | + +For example, when using an `ACTION` as a stub action for mock function: +``` +int DoSomething(bool flag, int* ptr); +``` +we have: +| **Pre-defined Symbol** | **Is Bound To** | +|:-----------------------|:----------------| +| `arg0` | the value of `flag` | +| `arg0_type` | the type `bool` | +| `arg1` | the value of `ptr` | +| `arg1_type` | the type `int*` | +| `args` | the tuple `(flag, ptr)` | +| `args_type` | the type `::testing::tuple` | +| `return_type` | the type `int` | +| `function_type` | the type `int(bool, int*)` | + +## Writing New Parameterized Actions Quickly ## + +Sometimes you'll want to parameterize an action you define. For that +we have another macro +``` +ACTION_P(name, param) { statements; } +``` + +For example, +``` +ACTION_P(Add, n) { return arg0 + n; } +``` +will allow you to write +``` +// Returns argument #0 + 5. +... WillOnce(Add(5)); +``` + +For convenience, we use the term _arguments_ for the values used to +invoke the mock function, and the term _parameters_ for the values +used to instantiate an action. + +Note that you don't need to provide the type of the parameter either. +Suppose the parameter is named `param`, you can also use the +Google-Mock-defined symbol `param_type` to refer to the type of the +parameter as inferred by the compiler. For example, in the body of +`ACTION_P(Add, n)` above, you can write `n_type` for the type of `n`. + +Google Mock also provides `ACTION_P2`, `ACTION_P3`, and etc to support +multi-parameter actions. For example, +``` +ACTION_P2(ReturnDistanceTo, x, y) { + double dx = arg0 - x; + double dy = arg1 - y; + return sqrt(dx*dx + dy*dy); +} +``` +lets you write +``` +... WillOnce(ReturnDistanceTo(5.0, 26.5)); +``` + +You can view `ACTION` as a degenerated parameterized action where the +number of parameters is 0. + +You can also easily define actions overloaded on the number of parameters: +``` +ACTION_P(Plus, a) { ... } +ACTION_P2(Plus, a, b) { ... } +``` + +## Restricting the Type of an Argument or Parameter in an ACTION ## + +For maximum brevity and reusability, the `ACTION*` macros don't ask +you to provide the types of the mock function arguments and the action +parameters. Instead, we let the compiler infer the types for us. + +Sometimes, however, we may want to be more explicit about the types. +There are several tricks to do that. For example: +``` +ACTION(Foo) { + // Makes sure arg0 can be converted to int. + int n = arg0; + ... use n instead of arg0 here ... +} + +ACTION_P(Bar, param) { + // Makes sure the type of arg1 is const char*. + ::testing::StaticAssertTypeEq(); + + // Makes sure param can be converted to bool. + bool flag = param; +} +``` +where `StaticAssertTypeEq` is a compile-time assertion in Google Test +that verifies two types are the same. + +## Writing New Action Templates Quickly ## + +Sometimes you want to give an action explicit template parameters that +cannot be inferred from its value parameters. `ACTION_TEMPLATE()` +supports that and can be viewed as an extension to `ACTION()` and +`ACTION_P*()`. + +The syntax: +``` +ACTION_TEMPLATE(ActionName, + HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), + AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +``` + +defines an action template that takes _m_ explicit template parameters +and _n_ value parameters, where _m_ is between 1 and 10, and _n_ is +between 0 and 10. `name_i` is the name of the i-th template +parameter, and `kind_i` specifies whether it's a `typename`, an +integral constant, or a template. `p_i` is the name of the i-th value +parameter. + +Example: +``` +// DuplicateArg(output) converts the k-th argument of the mock +// function to type T and copies it to *output. +ACTION_TEMPLATE(DuplicateArg, + // Note the comma between int and k: + HAS_2_TEMPLATE_PARAMS(int, k, typename, T), + AND_1_VALUE_PARAMS(output)) { + *output = T(::testing::get(args)); +} +``` + +To create an instance of an action template, write: +``` + ActionName(v1, ..., v_n) +``` +where the `t`s are the template arguments and the +`v`s are the value arguments. The value argument +types are inferred by the compiler. For example: +``` +using ::testing::_; +... + int n; + EXPECT_CALL(mock, Foo(_, _)) + .WillOnce(DuplicateArg<1, unsigned char>(&n)); +``` + +If you want to explicitly specify the value argument types, you can +provide additional template arguments: +``` + ActionName(v1, ..., v_n) +``` +where `u_i` is the desired type of `v_i`. + +`ACTION_TEMPLATE` and `ACTION`/`ACTION_P*` can be overloaded on the +number of value parameters, but not on the number of template +parameters. Without the restriction, the meaning of the following is +unclear: + +``` + OverloadedAction(x); +``` + +Are we using a single-template-parameter action where `bool` refers to +the type of `x`, or a two-template-parameter action where the compiler +is asked to infer the type of `x`? + +## Using the ACTION Object's Type ## + +If you are writing a function that returns an `ACTION` object, you'll +need to know its type. The type depends on the macro used to define +the action and the parameter types. The rule is relatively simple: +| **Given Definition** | **Expression** | **Has Type** | +|:---------------------|:---------------|:-------------| +| `ACTION(Foo)` | `Foo()` | `FooAction` | +| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | +| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | +| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `FooActionP` | +| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | +| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz(bool_value, int_value)` | `FooActionP2` | +| ... | ... | ... | + +Note that we have to pick different suffixes (`Action`, `ActionP`, +`ActionP2`, and etc) for actions with different numbers of value +parameters, or the action definitions cannot be overloaded on the +number of them. + +## Writing New Monomorphic Actions ## + +While the `ACTION*` macros are very convenient, sometimes they are +inappropriate. For example, despite the tricks shown in the previous +recipes, they don't let you directly specify the types of the mock +function arguments and the action parameters, which in general leads +to unoptimized compiler error messages that can baffle unfamiliar +users. They also don't allow overloading actions based on parameter +types without jumping through some hoops. + +An alternative to the `ACTION*` macros is to implement +`::testing::ActionInterface`, where `F` is the type of the mock +function in which the action will be used. For example: + +``` +template class ActionInterface { + public: + virtual ~ActionInterface(); + + // Performs the action. Result is the return type of function type + // F, and ArgumentTuple is the tuple of arguments of F. + // + // For example, if F is int(bool, const string&), then Result would + // be int, and ArgumentTuple would be ::testing::tuple. + virtual Result Perform(const ArgumentTuple& args) = 0; +}; + +using ::testing::_; +using ::testing::Action; +using ::testing::ActionInterface; +using ::testing::MakeAction; + +typedef int IncrementMethod(int*); + +class IncrementArgumentAction : public ActionInterface { + public: + virtual int Perform(const ::testing::tuple& args) { + int* p = ::testing::get<0>(args); // Grabs the first argument. + return *p++; + } +}; + +Action IncrementArgument() { + return MakeAction(new IncrementArgumentAction); +} +... + + EXPECT_CALL(foo, Baz(_)) + .WillOnce(IncrementArgument()); + + int n = 5; + foo.Baz(&n); // Should return 5 and change n to 6. +``` + +## Writing New Polymorphic Actions ## + +The previous recipe showed you how to define your own action. This is +all good, except that you need to know the type of the function in +which the action will be used. Sometimes that can be a problem. For +example, if you want to use the action in functions with _different_ +types (e.g. like `Return()` and `SetArgPointee()`). + +If an action can be used in several types of mock functions, we say +it's _polymorphic_. The `MakePolymorphicAction()` function template +makes it easy to define such an action: + +``` +namespace testing { + +template +PolymorphicAction MakePolymorphicAction(const Impl& impl); + +} // namespace testing +``` + +As an example, let's define an action that returns the second argument +in the mock function's argument list. The first step is to define an +implementation class: + +``` +class ReturnSecondArgumentAction { + public: + template + Result Perform(const ArgumentTuple& args) const { + // To get the i-th (0-based) argument, use ::testing::get(args). + return ::testing::get<1>(args); + } +}; +``` + +This implementation class does _not_ need to inherit from any +particular class. What matters is that it must have a `Perform()` +method template. This method template takes the mock function's +arguments as a tuple in a **single** argument, and returns the result of +the action. It can be either `const` or not, but must be invokable +with exactly one template argument, which is the result type. In other +words, you must be able to call `Perform(args)` where `R` is the +mock function's return type and `args` is its arguments in a tuple. + +Next, we use `MakePolymorphicAction()` to turn an instance of the +implementation class into the polymorphic action we need. It will be +convenient to have a wrapper for this: + +``` +using ::testing::MakePolymorphicAction; +using ::testing::PolymorphicAction; + +PolymorphicAction ReturnSecondArgument() { + return MakePolymorphicAction(ReturnSecondArgumentAction()); +} +``` + +Now, you can use this polymorphic action the same way you use the +built-in ones: + +``` +using ::testing::_; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, int(bool flag, int n)); + MOCK_METHOD3(DoThat, string(int x, const char* str1, const char* str2)); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(ReturnSecondArgument()); + EXPECT_CALL(foo, DoThat(_, _, _)) + .WillOnce(ReturnSecondArgument()); + ... + foo.DoThis(true, 5); // Will return 5. + foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". +``` + +## Teaching Google Mock How to Print Your Values ## + +When an uninteresting or unexpected call occurs, Google Mock prints the +argument values and the stack trace to help you debug. Assertion +macros like `EXPECT_THAT` and `EXPECT_EQ` also print the values in +question when the assertion fails. Google Mock and Google Test do this using +Google Test's user-extensible value printer. + +This printer knows how to print built-in C++ types, native arrays, STL +containers, and any type that supports the `<<` operator. For other +types, it prints the raw bytes in the value and hopes that you the +user can figure it out. +[Google Test's advanced guide](../../googletest/docs/AdvancedGuide.md#teaching-google-test-how-to-print-your-values) +explains how to extend the printer to do a better job at +printing your particular type than to dump the bytes. diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/DesignDoc.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/DesignDoc.md new file mode 100644 index 00000000..3f515c3b --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/DesignDoc.md @@ -0,0 +1,280 @@ +This page discusses the design of new Google Mock features. + + + +# Macros for Defining Actions # + +## Problem ## + +Due to the lack of closures in C++, it currently requires some +non-trivial effort to define a custom action in Google Mock. For +example, suppose you want to "increment the value pointed to by the +second argument of the mock function and return it", you could write: + +``` +int IncrementArg1(Unused, int* p, Unused) { + return ++(*p); +} + +... WillOnce(Invoke(IncrementArg1)); +``` + +There are several things unsatisfactory about this approach: + + * Even though the action only cares about the second argument of the mock function, its definition needs to list other arguments as dummies. This is tedious. + * The defined action is usable only in mock functions that takes exactly 3 arguments - an unnecessary restriction. + * To use the action, one has to say `Invoke(IncrementArg1)`, which isn't as nice as `IncrementArg1()`. + +The latter two problems can be overcome using `MakePolymorphicAction()`, +but it requires much more boilerplate code: + +``` +class IncrementArg1Action { + public: + template + Result Perform(const ArgumentTuple& args) const { + return ++(*tr1::get<1>(args)); + } +}; + +PolymorphicAction IncrementArg1() { + return MakePolymorphicAction(IncrementArg1Action()); +} + +... WillOnce(IncrementArg1()); +``` + +Our goal is to allow defining custom actions with the least amount of +boiler-plate C++ requires. + +## Solution ## + +We propose to introduce a new macro: +``` +ACTION(name) { statements; } +``` + +Using this in a namespace scope will define an action with the given +name that executes the statements. Inside the statements, you can +refer to the K-th (0-based) argument of the mock function as `argK`. +For example: +``` +ACTION(IncrementArg1) { return ++(*arg1); } +``` +allows you to write +``` +... WillOnce(IncrementArg1()); +``` + +Note that you don't need to specify the types of the mock function +arguments, as brevity is a top design goal here. Rest assured that +your code is still type-safe though: you'll get a compiler error if +`*arg1` doesn't support the `++` operator, or if the type of +`++(*arg1)` isn't compatible with the mock function's return type. + +Another example: +``` +ACTION(Foo) { + (*arg2)(5); + Blah(); + *arg1 = 0; + return arg0; +} +``` +defines an action `Foo()` that invokes argument #2 (a function pointer) +with 5, calls function `Blah()`, sets the value pointed to by argument +#1 to 0, and returns argument #0. + +For more convenience and flexibility, you can also use the following +pre-defined symbols in the body of `ACTION`: + +| `argK_type` | The type of the K-th (0-based) argument of the mock function | +|:------------|:-------------------------------------------------------------| +| `args` | All arguments of the mock function as a tuple | +| `args_type` | The type of all arguments of the mock function as a tuple | +| `return_type` | The return type of the mock function | +| `function_type` | The type of the mock function | + +For example, when using an `ACTION` as a stub action for mock function: +``` +int DoSomething(bool flag, int* ptr); +``` +we have: +| **Pre-defined Symbol** | **Is Bound To** | +|:-----------------------|:----------------| +| `arg0` | the value of `flag` | +| `arg0_type` | the type `bool` | +| `arg1` | the value of `ptr` | +| `arg1_type` | the type `int*` | +| `args` | the tuple `(flag, ptr)` | +| `args_type` | the type `std::tr1::tuple` | +| `return_type` | the type `int` | +| `function_type` | the type `int(bool, int*)` | + +## Parameterized actions ## + +Sometimes you'll want to parameterize the action. For that we propose +another macro +``` +ACTION_P(name, param) { statements; } +``` + +For example, +``` +ACTION_P(Add, n) { return arg0 + n; } +``` +will allow you to write +``` +// Returns argument #0 + 5. +... WillOnce(Add(5)); +``` + +For convenience, we use the term _arguments_ for the values used to +invoke the mock function, and the term _parameters_ for the values +used to instantiate an action. + +Note that you don't need to provide the type of the parameter either. +Suppose the parameter is named `param`, you can also use the +Google-Mock-defined symbol `param_type` to refer to the type of the +parameter as inferred by the compiler. + +We will also provide `ACTION_P2`, `ACTION_P3`, and etc to support +multi-parameter actions. For example, +``` +ACTION_P2(ReturnDistanceTo, x, y) { + double dx = arg0 - x; + double dy = arg1 - y; + return sqrt(dx*dx + dy*dy); +} +``` +lets you write +``` +... WillOnce(ReturnDistanceTo(5.0, 26.5)); +``` + +You can view `ACTION` as a degenerated parameterized action where the +number of parameters is 0. + +## Advanced Usages ## + +### Overloading Actions ### + +You can easily define actions overloaded on the number of parameters: +``` +ACTION_P(Plus, a) { ... } +ACTION_P2(Plus, a, b) { ... } +``` + +### Restricting the Type of an Argument or Parameter ### + +For maximum brevity and reusability, the `ACTION*` macros don't let +you specify the types of the mock function arguments and the action +parameters. Instead, we let the compiler infer the types for us. + +Sometimes, however, we may want to be more explicit about the types. +There are several tricks to do that. For example: +``` +ACTION(Foo) { + // Makes sure arg0 can be converted to int. + int n = arg0; + ... use n instead of arg0 here ... +} + +ACTION_P(Bar, param) { + // Makes sure the type of arg1 is const char*. + ::testing::StaticAssertTypeEq(); + + // Makes sure param can be converted to bool. + bool flag = param; +} +``` +where `StaticAssertTypeEq` is a compile-time assertion we plan to add to +Google Test (the name is chosen to match `static_assert` in C++0x). + +### Using the ACTION Object's Type ### + +If you are writing a function that returns an `ACTION` object, you'll +need to know its type. The type depends on the macro used to define +the action and the parameter types. The rule is relatively simple: +| **Given Definition** | **Expression** | **Has Type** | +|:---------------------|:---------------|:-------------| +| `ACTION(Foo)` | `Foo()` | `FooAction` | +| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | +| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | +| ... | ... | ... | + +Note that we have to pick different suffixes (`Action`, `ActionP`, +`ActionP2`, and etc) for actions with different numbers of parameters, +or the action definitions cannot be overloaded on the number of +parameters. + +## When to Use ## + +While the new macros are very convenient, please also consider other +means of implementing actions (e.g. via `ActionInterface` or +`MakePolymorphicAction()`), especially if you need to use the defined +action a lot. While the other approaches require more work, they give +you more control on the types of the mock function arguments and the +action parameters, which in general leads to better compiler error +messages that pay off in the long run. They also allow overloading +actions based on parameter types, as opposed to just the number of +parameters. + +## Related Work ## + +As you may have realized, the `ACTION*` macros resemble closures (also +known as lambda expressions or anonymous functions). Indeed, both of +them seek to lower the syntactic overhead for defining a function. + +C++0x will support lambdas, but they are not part of C++ right now. +Some non-standard libraries (most notably BLL or Boost Lambda Library) +try to alleviate this problem. However, they are not a good choice +for defining actions as: + + * They are non-standard and not widely installed. Google Mock only depends on standard libraries and `tr1::tuple`, which is part of the new C++ standard and comes with gcc 4+. We want to keep it that way. + * They are not trivial to learn. + * They will become obsolete when C++0x's lambda feature is widely supported. We don't want to make our users use a dying library. + * Since they are based on operators, they are rather ad hoc: you cannot use statements, and you cannot pass the lambda arguments to a function, for example. + * They have subtle semantics that easily confuses new users. For example, in expression `_1++ + foo++`, `foo` will be incremented only once where the expression is evaluated, while `_1` will be incremented every time the unnamed function is invoked. This is far from intuitive. + +`ACTION*` avoid all these problems. + +## Future Improvements ## + +There may be a need for composing `ACTION*` definitions (i.e. invoking +another `ACTION` inside the definition of one `ACTION*`). We are not +sure we want it yet, as one can get a similar effect by putting +`ACTION` definitions in function templates and composing the function +templates. We'll revisit this based on user feedback. + +The reason we don't allow `ACTION*()` inside a function body is that +the current C++ standard doesn't allow function-local types to be used +to instantiate templates. The upcoming C++0x standard will lift this +restriction. Once this feature is widely supported by compilers, we +can revisit the implementation and add support for using `ACTION*()` +inside a function. + +C++0x will also support lambda expressions. When they become +available, we may want to support using lambdas as actions. + +# Macros for Defining Matchers # + +Once the macros for defining actions are implemented, we plan to do +the same for matchers: + +``` +MATCHER(name) { statements; } +``` + +where you can refer to the value being matched as `arg`. For example, +given: + +``` +MATCHER(IsPositive) { return arg > 0; } +``` + +you can use `IsPositive()` as a matcher that matches a value iff it is +greater than 0. + +We will also add `MATCHER_P`, `MATCHER_P2`, and etc for parameterized +matchers. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/DevGuide.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/DevGuide.md new file mode 100644 index 00000000..f4bab75c --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/DevGuide.md @@ -0,0 +1,132 @@ + + +If you are interested in understanding the internals of Google Mock, +building from source, or contributing ideas or modifications to the +project, then this document is for you. + +# Introduction # + +First, let's give you some background of the project. + +## Licensing ## + +All Google Mock source and pre-built packages are provided under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php). + +## The Google Mock Community ## + +The Google Mock community exists primarily through the [discussion group](http://groups.google.com/group/googlemock), the +[issue tracker](https://github.com/google/googletest/issues) and, to a lesser extent, the [source control repository](../). You are definitely encouraged to contribute to the +discussion and you can also help us to keep the effectiveness of the +group high by following and promoting the guidelines listed here. + +### Please Be Friendly ### + +Showing courtesy and respect to others is a vital part of the Google +culture, and we strongly encourage everyone participating in Google +Mock development to join us in accepting nothing less. Of course, +being courteous is not the same as failing to constructively disagree +with each other, but it does mean that we should be respectful of each +other when enumerating the 42 technical reasons that a particular +proposal may not be the best choice. There's never a reason to be +antagonistic or dismissive toward anyone who is sincerely trying to +contribute to a discussion. + +Sure, C++ testing is serious business and all that, but it's also +a lot of fun. Let's keep it that way. Let's strive to be one of the +friendliest communities in all of open source. + +### Where to Discuss Google Mock ### + +As always, discuss Google Mock in the official [Google C++ Mocking Framework discussion group](http://groups.google.com/group/googlemock). You don't have to actually submit +code in order to sign up. Your participation itself is a valuable +contribution. + +# Working with the Code # + +If you want to get your hands dirty with the code inside Google Mock, +this is the section for you. + +## Checking Out the Source from Subversion ## + +Checking out the Google Mock source is most useful if you plan to +tweak it yourself. You check out the source for Google Mock using a +[Subversion](http://subversion.tigris.org/) client as you would for any +other project hosted on Google Code. Please see the instruction on +the [source code access page](../) for how to do it. + +## Compiling from Source ## + +Once you check out the code, you can find instructions on how to +compile it in the [README](../README.md) file. + +## Testing ## + +A mocking framework is of no good if itself is not thoroughly tested. +Tests should be written for any new code, and changes should be +verified to not break existing tests before they are submitted for +review. To perform the tests, follow the instructions in [README](http://code.google.com/p/googlemock/source/browse/trunk/README) and +verify that there are no failures. + +# Contributing Code # + +We are excited that Google Mock is now open source, and hope to get +great patches from the community. Before you fire up your favorite IDE +and begin hammering away at that new feature, though, please take the +time to read this section and understand the process. While it seems +rigorous, we want to keep a high standard of quality in the code +base. + +## Contributor License Agreements ## + +You must sign a Contributor License Agreement (CLA) before we can +accept any code. The CLA protects you and us. + + * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html). + * If you work for a company that wants to allow you to contribute your work to Google Mock, then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. + +## Coding Style ## + +To keep the source consistent, readable, diffable and easy to merge, +we use a fairly rigid coding style, as defined by the [google-styleguide](https://github.com/google/styleguide) project. All patches will be expected +to conform to the style outlined [here](https://github.com/google/styleguide/blob/gh-pages/cppguide.xml). + +## Submitting Patches ## + +Please do submit code. Here's what you need to do: + + 1. Normally you should make your change against the SVN trunk instead of a branch or a tag, as the latter two are for release control and should be treated mostly as read-only. + 1. Decide which code you want to submit. A submission should be a set of changes that addresses one issue in the [Google Mock issue tracker](http://code.google.com/p/googlemock/issues/list). Please don't mix more than one logical change per submittal, because it makes the history hard to follow. If you want to make a change that doesn't have a corresponding issue in the issue tracker, please create one. + 1. Also, coordinate with team members that are listed on the issue in question. This ensures that work isn't being duplicated and communicating your plan early also generally leads to better patches. + 1. Ensure that your code adheres to the [Google Mock source code style](#Coding_Style.md). + 1. Ensure that there are unit tests for your code. + 1. Sign a Contributor License Agreement. + 1. Create a patch file using `svn diff`. + 1. We use [Rietveld](http://codereview.appspot.com/) to do web-based code reviews. You can read about the tool [here](https://github.com/rietveld-codereview/rietveld/wiki). When you are ready, upload your patch via Rietveld and notify `googlemock@googlegroups.com` to review it. There are several ways to upload the patch. We recommend using the [upload\_gmock.py](../scripts/upload_gmock.py) script, which you can find in the `scripts/` folder in the SVN trunk. + +## Google Mock Committers ## + +The current members of the Google Mock engineering team are the only +committers at present. In the great tradition of eating one's own +dogfood, we will be requiring each new Google Mock engineering team +member to earn the right to become a committer by following the +procedures in this document, writing consistently great code, and +demonstrating repeatedly that he or she truly gets the zen of Google +Mock. + +# Release Process # + +We follow the typical release process for Subversion-based projects: + + 1. A release branch named `release-X.Y` is created. + 1. Bugs are fixed and features are added in trunk; those individual patches are merged into the release branch until it's stable. + 1. An individual point release (the `Z` in `X.Y.Z`) is made by creating a tag from the branch. + 1. Repeat steps 2 and 3 throughout one release cycle (as determined by features or time). + 1. Go back to step 1 to create another release branch and so on. + + +--- + +This page is based on the [Making GWT Better](http://code.google.com/webtoolkit/makinggwtbetter.html) guide from the [Google Web Toolkit](http://code.google.com/webtoolkit/) project. Except as otherwise [noted](http://code.google.com/policies.html#restrictions), the content of this page is licensed under the [Creative Commons Attribution 2.5 License](http://creativecommons.org/licenses/by/2.5/). diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/Documentation.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/Documentation.md new file mode 100644 index 00000000..444151ee --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/Documentation.md @@ -0,0 +1,12 @@ +This page lists all documentation wiki pages for Google Mock **(the SVN trunk version)** +- **if you use a released version of Google Mock, please read the documentation for that specific version instead.** + + * [ForDummies](ForDummies.md) -- start here if you are new to Google Mock. + * [CheatSheet](CheatSheet.md) -- a quick reference. + * [CookBook](CookBook.md) -- recipes for doing various tasks using Google Mock. + * [FrequentlyAskedQuestions](FrequentlyAskedQuestions.md) -- check here before asking a question on the mailing list. + +To contribute code to Google Mock, read: + + * [DevGuide](DevGuide.md) -- read this _before_ writing your first patch. + * [Pump Manual](../googletest/docs/PumpManual.md) -- how we generate some of Google Mock's source files. diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/ForDummies.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/ForDummies.md new file mode 100644 index 00000000..e2f362a6 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/ForDummies.md @@ -0,0 +1,439 @@ + + +(**Note:** If you get compiler errors that you don't understand, be sure to consult [Google Mock Doctor](FrequentlyAskedQuestions.md#how-am-i-supposed-to-make-sense-of-these-horrible-template-errors).) + +# What Is Google C++ Mocking Framework? # +When you write a prototype or test, often it's not feasible or wise to rely on real objects entirely. A **mock object** implements the same interface as a real object (so it can be used as one), but lets you specify at run time how it will be used and what it should do (which methods will be called? in which order? how many times? with what arguments? what will they return? etc). + +**Note:** It is easy to confuse the term _fake objects_ with mock objects. Fakes and mocks actually mean very different things in the Test-Driven Development (TDD) community: + + * **Fake** objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake. + * **Mocks** are objects pre-programmed with _expectations_, which form a specification of the calls they are expected to receive. + +If all this seems too abstract for you, don't worry - the most important thing to remember is that a mock allows you to check the _interaction_ between itself and code that uses it. The difference between fakes and mocks will become much clearer once you start to use mocks. + +**Google C++ Mocking Framework** (or **Google Mock** for short) is a library (sometimes we also call it a "framework" to make it sound cool) for creating mock classes and using them. It does to C++ what [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/) do to Java. + +Using Google Mock involves three basic steps: + + 1. Use some simple macros to describe the interface you want to mock, and they will expand to the implementation of your mock class; + 1. Create some mock objects and specify its expectations and behavior using an intuitive syntax; + 1. Exercise code that uses the mock objects. Google Mock will catch any violation of the expectations as soon as it arises. + +# Why Google Mock? # +While mock objects help you remove unnecessary dependencies in tests and make them fast and reliable, using mocks manually in C++ is _hard_: + + * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distance to avoid it. + * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad hoc restrictions. + * The knowledge you gained from using one mock doesn't transfer to the next. + +In contrast, Java and Python programmers have some fine mock frameworks, which automate the creation of mocks. As a result, mocking is a proven effective technique and widely adopted practice in those communities. Having the right tool absolutely makes the difference. + +Google Mock was built to help C++ programmers. It was inspired by [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/), but designed with C++'s specifics in mind. It is your friend if any of the following problems is bothering you: + + * You are stuck with a sub-optimal design and wish you had done more prototyping before it was too late, but prototyping in C++ is by no means "rapid". + * Your tests are slow as they depend on too many libraries or use expensive resources (e.g. a database). + * Your tests are brittle as some resources they use are unreliable (e.g. the network). + * You want to test how your code handles a failure (e.g. a file checksum error), but it's not easy to cause one. + * You need to make sure that your module interacts with other modules in the right way, but it's hard to observe the interaction; therefore you resort to observing the side effects at the end of the action, which is awkward at best. + * You want to "mock out" your dependencies, except that they don't have mock implementations yet; and, frankly, you aren't thrilled by some of those hand-written mocks. + +We encourage you to use Google Mock as: + + * a _design_ tool, for it lets you experiment with your interface design early and often. More iterations lead to better designs! + * a _testing_ tool to cut your tests' outbound dependencies and probe the interaction between your module and its collaborators. + +# Getting Started # +Using Google Mock is easy! Inside your C++ source file, just #include `"gtest/gtest.h"` and `"gmock/gmock.h"`, and you are ready to go. + +# A Case for Mock Turtles # +Let's look at an example. Suppose you are developing a graphics program that relies on a LOGO-like API for drawing. How would you test that it does the right thing? Well, you can run it and compare the screen with a golden screen snapshot, but let's admit it: tests like this are expensive to run and fragile (What if you just upgraded to a shiny new graphics card that has better anti-aliasing? Suddenly you have to update all your golden images.). It would be too painful if all your tests are like this. Fortunately, you learned about Dependency Injection and know the right thing to do: instead of having your application talk to the drawing API directly, wrap the API in an interface (say, `Turtle`) and code to that interface: + +``` +class Turtle { + ... + virtual ~Turtle() {} + virtual void PenUp() = 0; + virtual void PenDown() = 0; + virtual void Forward(int distance) = 0; + virtual void Turn(int degrees) = 0; + virtual void GoTo(int x, int y) = 0; + virtual int GetX() const = 0; + virtual int GetY() const = 0; +}; +``` + +(Note that the destructor of `Turtle` **must** be virtual, as is the case for **all** classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you'll get corrupted program states like memory leaks.) + +You can control whether the turtle's movement will leave a trace using `PenUp()` and `PenDown()`, and control its movement using `Forward()`, `Turn()`, and `GoTo()`. Finally, `GetX()` and `GetY()` tell you the current position of the turtle. + +Your program will normally use a real implementation of this interface. In tests, you can use a mock implementation instead. This allows you to easily check what drawing primitives your program is calling, with what arguments, and in which order. Tests written this way are much more robust (they won't break because your new machine does anti-aliasing differently), easier to read and maintain (the intent of a test is expressed in the code, not in some binary images), and run _much, much faster_. + +# Writing the Mock Class # +If you are lucky, the mocks you need to use have already been implemented by some nice people. If, however, you find yourself in the position to write a mock class, relax - Google Mock turns this task into a fun game! (Well, almost.) + +## How to Define It ## +Using the `Turtle` interface as example, here are the simple steps you need to follow: + + 1. Derive a class `MockTurtle` from `Turtle`. + 1. Take a _virtual_ function of `Turtle` (while it's possible to [mock non-virtual methods using templates](CookBook.md#mocking-nonvirtual-methods), it's much more involved). Count how many arguments it has. + 1. In the `public:` section of the child class, write `MOCK_METHODn();` (or `MOCK_CONST_METHODn();` if you are mocking a `const` method), where `n` is the number of the arguments; if you counted wrong, shame on you, and a compiler error will tell you so. + 1. Now comes the fun part: you take the function signature, cut-and-paste the _function name_ as the _first_ argument to the macro, and leave what's left as the _second_ argument (in case you're curious, this is the _type of the function_). + 1. Repeat until all virtual functions you want to mock are done. + +After the process, you should have something like: + +``` +#include "gmock/gmock.h" // Brings in Google Mock. +class MockTurtle : public Turtle { + public: + ... + MOCK_METHOD0(PenUp, void()); + MOCK_METHOD0(PenDown, void()); + MOCK_METHOD1(Forward, void(int distance)); + MOCK_METHOD1(Turn, void(int degrees)); + MOCK_METHOD2(GoTo, void(int x, int y)); + MOCK_CONST_METHOD0(GetX, int()); + MOCK_CONST_METHOD0(GetY, int()); +}; +``` + +You don't need to define these mock methods somewhere else - the `MOCK_METHOD*` macros will generate the definitions for you. It's that simple! Once you get the hang of it, you can pump out mock classes faster than your source-control system can handle your check-ins. + +**Tip:** If even this is too much work for you, you'll find the +`gmock_gen.py` tool in Google Mock's `scripts/generator/` directory (courtesy of the [cppclean](http://code.google.com/p/cppclean/) project) useful. This command-line +tool requires that you have Python 2.4 installed. You give it a C++ file and the name of an abstract class defined in it, +and it will print the definition of the mock class for you. Due to the +complexity of the C++ language, this script may not always work, but +it can be quite handy when it does. For more details, read the [user documentation](../scripts/generator/README). + +## Where to Put It ## +When you define a mock class, you need to decide where to put its definition. Some people put it in a `*_test.cc`. This is fine when the interface being mocked (say, `Foo`) is owned by the same person or team. Otherwise, when the owner of `Foo` changes it, your test could break. (You can't really expect `Foo`'s maintainer to fix every test that uses `Foo`, can you?) + +So, the rule of thumb is: if you need to mock `Foo` and it's owned by others, define the mock class in `Foo`'s package (better, in a `testing` sub-package such that you can clearly separate production code and testing utilities), and put it in a `mock_foo.h`. Then everyone can reference `mock_foo.h` from their tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and only tests that depend on the changed methods need to be fixed. + +Another way to do it: you can introduce a thin layer `FooAdaptor` on top of `Foo` and code to this new interface. Since you own `FooAdaptor`, you can absorb changes in `Foo` much more easily. While this is more work initially, carefully choosing the adaptor interface can make your code easier to write and more readable (a net win in the long run), as you can choose `FooAdaptor` to fit your specific domain much better than `Foo` does. + +# Using Mocks in Tests # +Once you have a mock class, using it is easy. The typical work flow is: + + 1. Import the Google Mock names from the `testing` namespace such that you can use them unqualified (You only have to do it once per file. Remember that namespaces are a good idea and good for your health.). + 1. Create some mock objects. + 1. Specify your expectations on them (How many times will a method be called? With what arguments? What should it do? etc.). + 1. Exercise some code that uses the mocks; optionally, check the result using Google Test assertions. If a mock method is called more than expected or with wrong arguments, you'll get an error immediately. + 1. When a mock is destructed, Google Mock will automatically check whether all expectations on it have been satisfied. + +Here's an example: + +``` +#include "path/to/mock-turtle.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +using ::testing::AtLeast; // #1 + +TEST(PainterTest, CanDrawSomething) { + MockTurtle turtle; // #2 + EXPECT_CALL(turtle, PenDown()) // #3 + .Times(AtLeast(1)); + + Painter painter(&turtle); // #4 + + EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); +} // #5 + +int main(int argc, char** argv) { + // The following line must be executed to initialize Google Mock + // (and Google Test) before running the tests. + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} +``` + +As you might have guessed, this test checks that `PenDown()` is called at least once. If the `painter` object didn't call this method, your test will fail with a message like this: + +``` +path/to/my_test.cc:119: Failure +Actual function call count doesn't match this expectation: +Actually: never called; +Expected: called at least once. +``` + +**Tip 1:** If you run the test from an Emacs buffer, you can hit `` on the line number displayed in the error message to jump right to the failed expectation. + +**Tip 2:** If your mock objects are never deleted, the final verification won't happen. Therefore it's a good idea to use a heap leak checker in your tests when you allocate mocks on the heap. + +**Important note:** Google Mock requires expectations to be set **before** the mock functions are called, otherwise the behavior is **undefined**. In particular, you mustn't interleave `EXPECT_CALL()`s and calls to the mock functions. + +This means `EXPECT_CALL()` should be read as expecting that a call will occur _in the future_, not that a call has occurred. Why does Google Mock work like that? Well, specifying the expectation beforehand allows Google Mock to report a violation as soon as it arises, when the context (stack trace, etc) is still available. This makes debugging much easier. + +Admittedly, this test is contrived and doesn't do much. You can easily achieve the same effect without using Google Mock. However, as we shall reveal soon, Google Mock allows you to do _much more_ with the mocks. + +## Using Google Mock with Any Testing Framework ## +If you want to use something other than Google Test (e.g. [CppUnit](http://sourceforge.net/projects/cppunit/) or +[CxxTest](http://cxxtest.tigris.org/)) as your testing framework, just change the `main()` function in the previous section to: +``` +int main(int argc, char** argv) { + // The following line causes Google Mock to throw an exception on failure, + // which will be interpreted by your testing framework as a test failure. + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + ... whatever your testing framework requires ... +} +``` + +This approach has a catch: it makes Google Mock throw an exception +from a mock object's destructor sometimes. With some compilers, this +sometimes causes the test program to crash. You'll still be able to +notice that the test has failed, but it's not a graceful failure. + +A better solution is to use Google Test's +[event listener API](../../googletest/docs/AdvancedGuide.md#extending-google-test-by-handling-test-events) +to report a test failure to your testing framework properly. You'll need to +implement the `OnTestPartResult()` method of the event listener interface, but it +should be straightforward. + +If this turns out to be too much work, we suggest that you stick with +Google Test, which works with Google Mock seamlessly (in fact, it is +technically part of Google Mock.). If there is a reason that you +cannot use Google Test, please let us know. + +# Setting Expectations # +The key to using a mock object successfully is to set the _right expectations_ on it. If you set the expectations too strict, your test will fail as the result of unrelated changes. If you set them too loose, bugs can slip through. You want to do it just right such that your test can catch exactly the kind of bugs you intend it to catch. Google Mock provides the necessary means for you to do it "just right." + +## General Syntax ## +In Google Mock we use the `EXPECT_CALL()` macro to set an expectation on a mock method. The general syntax is: + +``` +EXPECT_CALL(mock_object, method(matchers)) + .Times(cardinality) + .WillOnce(action) + .WillRepeatedly(action); +``` + +The macro has two arguments: first the mock object, and then the method and its arguments. Note that the two are separated by a comma (`,`), not a period (`.`). (Why using a comma? The answer is that it was necessary for technical reasons.) + +The macro can be followed by some optional _clauses_ that provide more information about the expectation. We'll discuss how each clause works in the coming sections. + +This syntax is designed to make an expectation read like English. For example, you can probably guess that + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .Times(5) + .WillOnce(Return(100)) + .WillOnce(Return(150)) + .WillRepeatedly(Return(200)); +``` + +says that the `turtle` object's `GetX()` method will be called five times, it will return 100 the first time, 150 the second time, and then 200 every time. Some people like to call this style of syntax a Domain-Specific Language (DSL). + +**Note:** Why do we use a macro to do this? It serves two purposes: first it makes expectations easily identifiable (either by `grep` or by a human reader), and second it allows Google Mock to include the source file location of a failed expectation in messages, making debugging easier. + +## Matchers: What Arguments Do We Expect? ## +When a mock function takes arguments, we must specify what arguments we are expecting; for example: + +``` +// Expects the turtle to move forward by 100 units. +EXPECT_CALL(turtle, Forward(100)); +``` + +Sometimes you may not want to be too specific (Remember that talk about tests being too rigid? Over specification leads to brittle tests and obscures the intent of tests. Therefore we encourage you to specify only what's necessary - no more, no less.). If you care to check that `Forward()` will be called but aren't interested in its actual argument, write `_` as the argument, which means "anything goes": + +``` +using ::testing::_; +... +// Expects the turtle to move forward. +EXPECT_CALL(turtle, Forward(_)); +``` + +`_` is an instance of what we call **matchers**. A matcher is like a predicate and can test whether an argument is what we'd expect. You can use a matcher inside `EXPECT_CALL()` wherever a function argument is expected. + +A list of built-in matchers can be found in the [CheatSheet](CheatSheet.md). For example, here's the `Ge` (greater than or equal) matcher: + +``` +using ::testing::Ge;... +EXPECT_CALL(turtle, Forward(Ge(100))); +``` + +This checks that the turtle will be told to go forward by at least 100 units. + +## Cardinalities: How Many Times Will It Be Called? ## +The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We call its argument a **cardinality** as it tells _how many times_ the call should occur. It allows us to repeat an expectation many times without actually writing it as many times. More importantly, a cardinality can be "fuzzy", just like a matcher can be. This allows a user to express the intent of a test exactly. + +An interesting special case is when we say `Times(0)`. You may have guessed - it means that the function shouldn't be called with the given arguments at all, and Google Mock will report a Google Test failure whenever the function is (wrongfully) called. + +We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the list of built-in cardinalities you can use, see the [CheatSheet](CheatSheet.md). + +The `Times()` clause can be omitted. **If you omit `Times()`, Google Mock will infer the cardinality for you.** The rules are easy to remember: + + * If **neither** `WillOnce()` **nor** `WillRepeatedly()` is in the `EXPECT_CALL()`, the inferred cardinality is `Times(1)`. + * If there are `n WillOnce()`'s but **no** `WillRepeatedly()`, where `n` >= 1, the cardinality is `Times(n)`. + * If there are `n WillOnce()`'s and **one** `WillRepeatedly()`, where `n` >= 0, the cardinality is `Times(AtLeast(n))`. + +**Quick quiz:** what do you think will happen if a function is expected to be called twice but actually called four times? + +## Actions: What Should It Do? ## +Remember that a mock object doesn't really have a working implementation? We as users have to tell it what to do when a method is invoked. This is easy in Google Mock. + +First, if the return type of a mock function is a built-in type or a pointer, the function has a **default action** (a `void` function will just return, a `bool` function will return `false`, and other functions will return 0). In addition, in C++ 11 and above, a mock function whose return type is default-constructible (i.e. has a default constructor) has a default action of returning a default-constructed value. If you don't say anything, this behavior will be used. + +Second, if a mock function doesn't have a default action, or the default action doesn't suit you, you can specify the action to be taken each time the expectation matches using a series of `WillOnce()` clauses followed by an optional `WillRepeatedly()`. For example, + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillOnce(Return(300)); +``` + +This says that `turtle.GetX()` will be called _exactly three times_ (Google Mock inferred this from how many `WillOnce()` clauses we've written, since we didn't explicitly write `Times()`), and will return 100, 200, and 300 respectively. + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillRepeatedly(Return(300)); +``` + +says that `turtle.GetY()` will be called _at least twice_ (Google Mock knows this as we've written two `WillOnce()` clauses and a `WillRepeatedly()` while having no explicit `Times()`), will return 100 the first time, 200 the second time, and 300 from the third time on. + +Of course, if you explicitly write a `Times()`, Google Mock will not try to infer the cardinality itself. What if the number you specified is larger than there are `WillOnce()` clauses? Well, after all `WillOnce()`s are used up, Google Mock will do the _default_ action for the function every time (unless, of course, you have a `WillRepeatedly()`.). + +What can we do inside `WillOnce()` besides `Return()`? You can return a reference using `ReturnRef(variable)`, or invoke a pre-defined function, among [others](CheatSheet.md#actions). + +**Important note:** The `EXPECT_CALL()` statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want: + +``` +int n = 100; +EXPECT_CALL(turtle, GetX()) +.Times(4) +.WillRepeatedly(Return(n++)); +``` + +Instead of returning 100, 101, 102, ..., consecutively, this mock function will always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)` will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will return the same pointer every time. If you want the side effect to happen every time, you need to define a custom action, which we'll teach in the [CookBook](CookBook.md). + +Time for another quiz! What do you think the following means? + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) +.Times(4) +.WillOnce(Return(100)); +``` + +Obviously `turtle.GetY()` is expected to be called four times. But if you think it will return 100 every time, think twice! Remember that one `WillOnce()` clause will be consumed each time the function is invoked and the default action will be taken afterwards. So the right answer is that `turtle.GetY()` will return 100 the first time, but **return 0 from the second time on**, as returning 0 is the default action for `int` functions. + +## Using Multiple Expectations ## +So far we've only shown examples where you have a single expectation. More realistically, you're going to specify expectations on multiple mock methods, which may be from multiple mock objects. + +By default, when a mock method is invoked, Google Mock will search the expectations in the **reverse order** they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones."). If the matching expectation cannot take any more calls, you will get an upper-bound-violated failure. Here's an example: + +``` +using ::testing::_;... +EXPECT_CALL(turtle, Forward(_)); // #1 +EXPECT_CALL(turtle, Forward(10)) // #2 + .Times(2); +``` + +If `Forward(10)` is called three times in a row, the third time it will be an error, as the last matching expectation (#2) has been saturated. If, however, the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, as now #1 will be the matching expectation. + +**Side note:** Why does Google Mock search for a match in the _reverse_ order of the expectations? The reason is that this allows a user to set up the default expectations in a mock object's constructor or the test fixture's set-up phase and then customize the mock by writing more specific expectations in the test body. So, if you have two expectations on the same method, you want to put the one with more specific matchers **after** the other, or the more specific rule would be shadowed by the more general one that comes after it. + +## Ordered vs Unordered Calls ## +By default, an expectation can match a call even though an earlier expectation hasn't been satisfied. In other words, the calls don't have to occur in the order the expectations are specified. + +Sometimes, you may want all the expected calls to occur in a strict order. To say this in Google Mock is easy: + +``` +using ::testing::InSequence;... +TEST(FooTest, DrawsLineSegment) { + ... + { + InSequence dummy; + + EXPECT_CALL(turtle, PenDown()); + EXPECT_CALL(turtle, Forward(100)); + EXPECT_CALL(turtle, PenUp()); + } + Foo(); +} +``` + +By creating an object of type `InSequence`, all expectations in its scope are put into a _sequence_ and have to occur _sequentially_. Since we are just relying on the constructor and destructor of this object to do the actual work, its name is really irrelevant. + +In this example, we test that `Foo()` calls the three expected functions in the order as written. If a call is made out-of-order, it will be an error. + +(What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](CookBook#Expecting_Partially_Ordered_Calls.md).) + +## All Expectations Are Sticky (Unless Said Otherwise) ## +Now let's do a quick quiz to see how well you can use this mock stuff already. How would you test that the turtle is asked to go to the origin _exactly twice_ (you want to ignore any other instructions it receives)? + +After you've come up with your answer, take a look at ours and compare notes (solve it yourself first - don't cheat!): + +``` +using ::testing::_;... +EXPECT_CALL(turtle, GoTo(_, _)) // #1 + .Times(AnyNumber()); +EXPECT_CALL(turtle, GoTo(0, 0)) // #2 + .Times(2); +``` + +Suppose `turtle.GoTo(0, 0)` is called three times. In the third time, Google Mock will see that the arguments match expectation #2 (remember that we always pick the last matching expectation). Now, since we said that there should be only two such calls, Google Mock will report an error immediately. This is basically what we've told you in the "Using Multiple Expectations" section above. + +This example shows that **expectations in Google Mock are "sticky" by default**, in the sense that they remain active even after we have reached their invocation upper bounds. This is an important rule to remember, as it affects the meaning of the spec, and is **different** to how it's done in many other mocking frameworks (Why'd we do that? Because we think our rule makes the common cases easier to express and understand.). + +Simple? Let's see if you've really understood it: what does the following code say? + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)); +} +``` + +If you think it says that `turtle.GetX()` will be called `n` times and will return 10, 20, 30, ..., consecutively, think twice! The problem is that, as we said, expectations are sticky. So, the second time `turtle.GetX()` is called, the last (latest) `EXPECT_CALL()` statement will match, and will immediately lead to an "upper bound exceeded" error - this piece of code is not very useful! + +One correct way of saying that `turtle.GetX()` will return 10, 20, 30, ..., is to explicitly say that the expectations are _not_ sticky. In other words, they should _retire_ as soon as they are saturated: + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); +} +``` + +And, there's a better way to do it: in this case, we expect the calls to occur in a specific order, and we line up the actions to match the order. Since the order is important here, we should make it explicit using a sequence: + +``` +using ::testing::InSequence; +using ::testing::Return; +... +{ + InSequence s; + + for (int i = 1; i <= n; i++) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); + } +} +``` + +By the way, the other situation where an expectation may _not_ be sticky is when it's in a sequence - as soon as another expectation that comes after it in the sequence has been used, it automatically retires (and will never be used to match any call). + +## Uninteresting Calls ## +A mock object may have many methods, and not all of them are that interesting. For example, in some tests we may not care about how many times `GetX()` and `GetY()` get called. + +In Google Mock, if you are not interested in a method, just don't say anything about it. If a call to this method occurs, you'll see a warning in the test output, but it won't be a failure. + +# What Now? # +Congratulations! You've learned enough about Google Mock to start using it. Now, you might want to join the [googlemock](http://groups.google.com/group/googlemock) discussion group and actually write some tests using Google Mock - it will be fun. Hey, it may even be addictive - you've been warned. + +Then, if you feel like increasing your mock quotient, you should move on to the [CookBook](CookBook.md). You can learn many advanced features of Google Mock there -- and advance your level of enjoyment and testing bliss. diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/FrequentlyAskedQuestions.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/FrequentlyAskedQuestions.md new file mode 100644 index 00000000..5eac83f4 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/FrequentlyAskedQuestions.md @@ -0,0 +1,628 @@ + + +Please send your questions to the +[googlemock](http://groups.google.com/group/googlemock) discussion +group. If you need help with compiler errors, make sure you have +tried [Google Mock Doctor](#How_am_I_supposed_to_make_sense_of_these_horrible_template_error.md) first. + +## When I call a method on my mock object, the method for the real object is invoked instead. What's the problem? ## + +In order for a method to be mocked, it must be _virtual_, unless you use the [high-perf dependency injection technique](CookBook.md#mocking-nonvirtual-methods). + +## I wrote some matchers. After I upgraded to a new version of Google Mock, they no longer compile. What's going on? ## + +After version 1.4.0 of Google Mock was released, we had an idea on how +to make it easier to write matchers that can generate informative +messages efficiently. We experimented with this idea and liked what +we saw. Therefore we decided to implement it. + +Unfortunately, this means that if you have defined your own matchers +by implementing `MatcherInterface` or using `MakePolymorphicMatcher()`, +your definitions will no longer compile. Matchers defined using the +`MATCHER*` family of macros are not affected. + +Sorry for the hassle if your matchers are affected. We believe it's +in everyone's long-term interest to make this change sooner than +later. Fortunately, it's usually not hard to migrate an existing +matcher to the new API. Here's what you need to do: + +If you wrote your matcher like this: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` + +you'll need to change it to: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` +(i.e. rename `Matches()` to `MatchAndExplain()` and give it a second +argument of type `MatchResultListener*`.) + +If you were also using `ExplainMatchResultTo()` to improve the matcher +message: +``` +// Old matcher definition that doesn't work with the lastest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + + virtual void ExplainMatchResultTo(MyType value, + ::std::ostream* os) const { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Foo property is " << value.GetFoo(); + } + ... +}; +``` + +you should move the logic of `ExplainMatchResultTo()` into +`MatchAndExplain()`, using the `MatchResultListener` argument where +the `::std::ostream` was used: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Foo property is " << value.GetFoo(); + return value.GetFoo() > 5; + } + ... +}; +``` + +If your matcher is defined using `MakePolymorphicMatcher()`: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you should rename the `Matches()` method to `MatchAndExplain()` and +add a `MatchResultListener*` argument (the same as what you need to do +for matchers defined by implementing `MatcherInterface`): +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +If your polymorphic matcher uses `ExplainMatchResultTo()` for better +failure messages: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +void ExplainMatchResultTo(const MyGreatMatcher& matcher, + MyType value, + ::std::ostream* os) { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Bar property is " << value.GetBar(); +} +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you'll need to move the logic inside `ExplainMatchResultTo()` to +`MatchAndExplain()`: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Bar property is " << value.GetBar(); + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +For more information, you can read these +[two](CookBook.md#writing-new-monomorphic-matchers) +[recipes](CookBook.md#writing-new-polymorphic-matchers) +from the cookbook. As always, you +are welcome to post questions on `googlemock@googlegroups.com` if you +need any help. + +## When using Google Mock, do I have to use Google Test as the testing framework? I have my favorite testing framework and don't want to switch. ## + +Google Mock works out of the box with Google Test. However, it's easy +to configure it to work with any testing framework of your choice. +[Here](ForDummies.md#using-google-mock-with-any-testing-framework) is how. + +## How am I supposed to make sense of these horrible template errors? ## + +If you are confused by the compiler errors gcc threw at you, +try consulting the _Google Mock Doctor_ tool first. What it does is to +scan stdin for gcc error messages, and spit out diagnoses on the +problems (we call them diseases) your code has. + +To "install", run command: +``` +alias gmd='/scripts/gmock_doctor.py' +``` + +To use it, do: +``` + 2>&1 | gmd +``` + +For example: +``` +make my_test 2>&1 | gmd +``` + +Or you can run `gmd` and copy-n-paste gcc's error messages to it. + +## Can I mock a variadic function? ## + +You cannot mock a variadic function (i.e. a function taking ellipsis +(`...`) arguments) directly in Google Mock. + +The problem is that in general, there is _no way_ for a mock object to +know how many arguments are passed to the variadic method, and what +the arguments' types are. Only the _author of the base class_ knows +the protocol, and we cannot look into his head. + +Therefore, to mock such a function, the _user_ must teach the mock +object how to figure out the number of arguments and their types. One +way to do it is to provide overloaded versions of the function. + +Ellipsis arguments are inherited from C and not really a C++ feature. +They are unsafe to use and don't work with arguments that have +constructors or destructors. Therefore we recommend to avoid them in +C++ as much as possible. + +## MSVC gives me warning C4301 or C4373 when I define a mock method with a const parameter. Why? ## + +If you compile this using Microsoft Visual C++ 2005 SP1: +``` +class Foo { + ... + virtual void Bar(const int i) = 0; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Bar, void(const int i)); +}; +``` +You may get the following warning: +``` +warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier +``` + +This is a MSVC bug. The same code compiles fine with gcc ,for +example. If you use Visual C++ 2008 SP1, you would get the warning: +``` +warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers +``` + +In C++, if you _declare_ a function with a `const` parameter, the +`const` modifier is _ignored_. Therefore, the `Foo` base class above +is equivalent to: +``` +class Foo { + ... + virtual void Bar(int i) = 0; // int or const int? Makes no difference. +}; +``` + +In fact, you can _declare_ Bar() with an `int` parameter, and _define_ +it with a `const int` parameter. The compiler will still match them +up. + +Since making a parameter `const` is meaningless in the method +_declaration_, we recommend to remove it in both `Foo` and `MockFoo`. +That should workaround the VC bug. + +Note that we are talking about the _top-level_ `const` modifier here. +If the function parameter is passed by pointer or reference, declaring +the _pointee_ or _referee_ as `const` is still meaningful. For +example, the following two declarations are _not_ equivalent: +``` +void Bar(int* p); // Neither p nor *p is const. +void Bar(const int* p); // p is not const, but *p is. +``` + +## I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? ## + +We've noticed that when the `/clr` compiler flag is used, Visual C++ +uses 5~6 times as much memory when compiling a mock class. We suggest +to avoid `/clr` when compiling native C++ mocks. + +## I can't figure out why Google Mock thinks my expectations are not satisfied. What should I do? ## + +You might want to run your test with +`--gmock_verbose=info`. This flag lets Google Mock print a trace +of every mock function call it receives. By studying the trace, +you'll gain insights on why the expectations you set are not met. + +## How can I assert that a function is NEVER called? ## + +``` +EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +## I have a failed test where Google Mock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? ## + +When Google Mock detects a failure, it prints relevant information +(the mock function arguments, the state of relevant expectations, and +etc) to help the user debug. If another failure is detected, Google +Mock will do the same, including printing the state of relevant +expectations. + +Sometimes an expectation's state didn't change between two failures, +and you'll see the same description of the state twice. They are +however _not_ redundant, as they refer to _different points in time_. +The fact they are the same _is_ interesting information. + +## I get a heap check failure when using a mock object, but using a real object is fine. What can be wrong? ## + +Does the class (hopefully a pure interface) you are mocking have a +virtual destructor? + +Whenever you derive from a base class, make sure its destructor is +virtual. Otherwise Bad Things will happen. Consider the following +code: + +``` +class Base { + public: + // Not virtual, but should be. + ~Base() { ... } + ... +}; + +class Derived : public Base { + public: + ... + private: + std::string value_; +}; + +... + Base* p = new Derived; + ... + delete p; // Surprise! ~Base() will be called, but ~Derived() will not + // - value_ is leaked. +``` + +By changing `~Base()` to virtual, `~Derived()` will be correctly +called when `delete p` is executed, and the heap checker +will be happy. + +## The "newer expectations override older ones" rule makes writing expectations awkward. Why does Google Mock do that? ## + +When people complain about this, often they are referring to code like: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. However, I have to write the expectations in the +// reverse order. This sucks big time!!! +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); +``` + +The problem is that they didn't pick the **best** way to express the test's +intent. + +By default, expectations don't have to be matched in _any_ particular +order. If you want them to match in a certain order, you need to be +explicit. This is Google Mock's (and jMock's) fundamental philosophy: it's +easy to accidentally over-specify your tests, and we want to make it +harder to do so. + +There are two better ways to write the test spec. You could either +put the expectations in sequence: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. Using a sequence, we can write the expectations +// in their natural order. +{ + InSequence s; + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +} +``` + +or you can put the sequence of actions in the same expectation: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +``` + +Back to the original questions: why does Google Mock search the +expectations (and `ON_CALL`s) from back to front? Because this +allows a user to set up a mock's behavior for the common case early +(e.g. in the mock's constructor or the test fixture's set-up phase) +and customize it with more specific rules later. If Google Mock +searches from front to back, this very useful pattern won't be +possible. + +## Google Mock prints a warning when a function without EXPECT\_CALL is called, even if I have set its behavior using ON\_CALL. Would it be reasonable not to show the warning in this case? ## + +When choosing between being neat and being safe, we lean toward the +latter. So the answer is that we think it's better to show the +warning. + +Often people write `ON_CALL`s in the mock object's +constructor or `SetUp()`, as the default behavior rarely changes from +test to test. Then in the test body they set the expectations, which +are often different for each test. Having an `ON_CALL` in the set-up +part of a test doesn't mean that the calls are expected. If there's +no `EXPECT_CALL` and the method is called, it's possibly an error. If +we quietly let the call go through without notifying the user, bugs +may creep in unnoticed. + +If, however, you are sure that the calls are OK, you can write + +``` +EXPECT_CALL(foo, Bar(_)) + .WillRepeatedly(...); +``` + +instead of + +``` +ON_CALL(foo, Bar(_)) + .WillByDefault(...); +``` + +This tells Google Mock that you do expect the calls and no warning should be +printed. + +Also, you can control the verbosity using the `--gmock_verbose` flag. +If you find the output too noisy when debugging, just choose a less +verbose level. + +## How can I delete the mock function's argument in an action? ## + +If you find yourself needing to perform some action that's not +supported by Google Mock directly, remember that you can define your own +actions using +[MakeAction()](CookBook.md#writing-new-actions) or +[MakePolymorphicAction()](CookBook.md#writing_new_polymorphic_actions), +or you can write a stub function and invoke it using +[Invoke()](CookBook.md#using-functions_methods_functors). + +## MOCK\_METHODn()'s second argument looks funny. Why don't you use the MOCK\_METHODn(Method, return\_type, arg\_1, ..., arg\_n) syntax? ## + +What?! I think it's beautiful. :-) + +While which syntax looks more natural is a subjective matter to some +extent, Google Mock's syntax was chosen for several practical advantages it +has. + +Try to mock a function that takes a map as an argument: +``` +virtual int GetSize(const map& m); +``` + +Using the proposed syntax, it would be: +``` +MOCK_METHOD1(GetSize, int, const map& m); +``` + +Guess what? You'll get a compiler error as the compiler thinks that +`const map& m` are **two**, not one, arguments. To work +around this you can use `typedef` to give the map type a name, but +that gets in the way of your work. Google Mock's syntax avoids this +problem as the function's argument types are protected inside a pair +of parentheses: +``` +// This compiles fine. +MOCK_METHOD1(GetSize, int(const map& m)); +``` + +You still need a `typedef` if the return type contains an unprotected +comma, but that's much rarer. + +Other advantages include: + 1. `MOCK_METHOD1(Foo, int, bool)` can leave a reader wonder whether the method returns `int` or `bool`, while there won't be such confusion using Google Mock's syntax. + 1. The way Google Mock describes a function type is nothing new, although many people may not be familiar with it. The same syntax was used in C, and the `function` library in `tr1` uses this syntax extensively. Since `tr1` will become a part of the new version of STL, we feel very comfortable to be consistent with it. + 1. The function type syntax is also used in other parts of Google Mock's API (e.g. the action interface) in order to make the implementation tractable. A user needs to learn it anyway in order to utilize Google Mock's more advanced features. We'd as well stick to the same syntax in `MOCK_METHOD*`! + +## My code calls a static/global function. Can I mock it? ## + +You can, but you need to make some changes. + +In general, if you find yourself needing to mock a static function, +it's a sign that your modules are too tightly coupled (and less +flexible, less reusable, less testable, etc). You are probably better +off defining a small interface and call the function through that +interface, which then can be easily mocked. It's a bit of work +initially, but usually pays for itself quickly. + +This Google Testing Blog +[post](http://googletesting.blogspot.com/2008/06/defeat-static-cling.html) +says it excellently. Check it out. + +## My mock object needs to do complex stuff. It's a lot of pain to specify the actions. Google Mock sucks! ## + +I know it's not a question, but you get an answer for free any way. :-) + +With Google Mock, you can create mocks in C++ easily. And people might be +tempted to use them everywhere. Sometimes they work great, and +sometimes you may find them, well, a pain to use. So, what's wrong in +the latter case? + +When you write a test without using mocks, you exercise the code and +assert that it returns the correct value or that the system is in an +expected state. This is sometimes called "state-based testing". + +Mocks are great for what some call "interaction-based" testing: +instead of checking the system state at the very end, mock objects +verify that they are invoked the right way and report an error as soon +as it arises, giving you a handle on the precise context in which the +error was triggered. This is often more effective and economical to +do than state-based testing. + +If you are doing state-based testing and using a test double just to +simulate the real object, you are probably better off using a fake. +Using a mock in this case causes pain, as it's not a strong point for +mocks to perform complex actions. If you experience this and think +that mocks suck, you are just not using the right tool for your +problem. Or, you might be trying to solve the wrong problem. :-) + +## I got a warning "Uninteresting function call encountered - default action taken.." Should I panic? ## + +By all means, NO! It's just an FYI. + +What it means is that you have a mock function, you haven't set any +expectations on it (by Google Mock's rule this means that you are not +interested in calls to this function and therefore it can be called +any number of times), and it is called. That's OK - you didn't say +it's not OK to call the function! + +What if you actually meant to disallow this function to be called, but +forgot to write `EXPECT_CALL(foo, Bar()).Times(0)`? While +one can argue that it's the user's fault, Google Mock tries to be nice and +prints you a note. + +So, when you see the message and believe that there shouldn't be any +uninteresting calls, you should investigate what's going on. To make +your life easier, Google Mock prints the function name and arguments +when an uninteresting call is encountered. + +## I want to define a custom action. Should I use Invoke() or implement the action interface? ## + +Either way is fine - you want to choose the one that's more convenient +for your circumstance. + +Usually, if your action is for a particular function type, defining it +using `Invoke()` should be easier; if your action can be used in +functions of different types (e.g. if you are defining +`Return(value)`), `MakePolymorphicAction()` is +easiest. Sometimes you want precise control on what types of +functions the action can be used in, and implementing +`ActionInterface` is the way to go here. See the implementation of +`Return()` in `include/gmock/gmock-actions.h` for an example. + +## I'm using the set-argument-pointee action, and the compiler complains about "conflicting return type specified". What does it mean? ## + +You got this error as Google Mock has no idea what value it should return +when the mock method is called. `SetArgPointee()` says what the +side effect is, but doesn't say what the return value should be. You +need `DoAll()` to chain a `SetArgPointee()` with a `Return()`. + +See this [recipe](CookBook.md#mocking_side_effects) for more details and an example. + + +## My question is not in your FAQ! ## + +If you cannot find the answer to your question in this FAQ, there are +some other resources you can use: + + 1. read other [documentation](Documentation.md), + 1. search the mailing list [archive](http://groups.google.com/group/googlemock/topics), + 1. ask it on [googlemock@googlegroups.com](mailto:googlemock@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googlemock) before you can post.). + +Please note that creating an issue in the +[issue tracker](https://github.com/google/googletest/issues) is _not_ +a good way to get your answer, as it is monitored infrequently by a +very small number of people. + +When asking a question, it's helpful to provide as much of the +following information as possible (people cannot help you if there's +not enough information in your question): + + * the version (or the revision number if you check out from SVN directly) of Google Mock you use (Google Mock is under active development, so it's possible that your problem has been solved in a later version), + * your operating system, + * the name and version of your compiler, + * the complete command line flags you give to your compiler, + * the complete compiler error messages (if the question is about compilation), + * the _actual_ code (ideally, a minimal but complete program) that has the problem you encounter. diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/KnownIssues.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/KnownIssues.md new file mode 100644 index 00000000..adadf514 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/KnownIssues.md @@ -0,0 +1,19 @@ +As any non-trivial software system, Google Mock has some known limitations and problems. We are working on improving it, and welcome your help! The follow is a list of issues we know about. + + + +## README contains outdated information on Google Mock's compatibility with other testing frameworks ## + +The `README` file in release 1.1.0 still says that Google Mock only works with Google Test. Actually, you can configure Google Mock to work with any testing framework you choose. + +## Tests failing on machines using Power PC CPUs (e.g. some Macs) ## + +`gmock_output_test` and `gmock-printers_test` are known to fail with Power PC CPUs. This is due to portability issues with these tests, and is not an indication of problems in Google Mock itself. You can safely ignore them. + +## Failed to resolve libgtest.so.0 in tests when built against installed Google Test ## + +This only applies if you manually built and installed Google Test, and then built a Google Mock against it (either explicitly, or because gtest-config was in your path post-install). In this situation, Libtool has a known issue with certain systems' ldconfig setup: + +http://article.gmane.org/gmane.comp.sysutils.automake.general/9025 + +This requires a manual run of "sudo ldconfig" after the "sudo make install" for Google Test before any binaries which link against it can be executed. This isn't a bug in our install, but we should at least have documented it or hacked a work-around into our install. We should have one of these solutions in our next release. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/CheatSheet.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/CheatSheet.md new file mode 100644 index 00000000..3c7bed4c --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/CheatSheet.md @@ -0,0 +1,525 @@ + + +# Defining a Mock Class # + +## Mocking a Normal Class ## + +Given +``` +class Foo { + ... + virtual ~Foo(); + virtual int GetSize() const = 0; + virtual string Describe(const char* name) = 0; + virtual string Describe(int type) = 0; + virtual bool Process(Bar elem, int count) = 0; +}; +``` +(note that `~Foo()` **must** be virtual) we can define its mock as +``` +#include + +class MockFoo : public Foo { + MOCK_CONST_METHOD0(GetSize, int()); + MOCK_METHOD1(Describe, string(const char* name)); + MOCK_METHOD1(Describe, string(int type)); + MOCK_METHOD2(Process, bool(Bar elem, int count)); +}; +``` + +To create a "nice" mock object which ignores all uninteresting calls, +or a "strict" mock object, which treats them as failures: +``` +NiceMock nice_foo; // The type is a subclass of MockFoo. +StrictMock strict_foo; // The type is a subclass of MockFoo. +``` + +## Mocking a Class Template ## + +To mock +``` +template +class StackInterface { + public: + ... + virtual ~StackInterface(); + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; +``` +(note that `~StackInterface()` **must** be virtual) just append `_T` to the `MOCK_*` macros: +``` +template +class MockStack : public StackInterface { + public: + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Specifying Calling Conventions for Mock Functions ## + +If your mock function doesn't use the default calling convention, you +can specify it by appending `_WITH_CALLTYPE` to any of the macros +described in the previous two sections and supplying the calling +convention as the first argument to the macro. For example, +``` + MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n)); + MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Bar, int(double x, double y)); +``` +where `STDMETHODCALLTYPE` is defined by `` on Windows. + +# Using Mocks in Tests # + +The typical flow is: + 1. Import the Google Mock names you need to use. All Google Mock names are in the `testing` namespace unless they are macros or otherwise noted. + 1. Create the mock objects. + 1. Optionally, set the default actions of the mock objects. + 1. Set your expectations on the mock objects (How will they be called? What wil they do?). + 1. Exercise code that uses the mock objects; if necessary, check the result using [Google Test](http://code.google.com/p/googletest/) assertions. + 1. When a mock objects is destructed, Google Mock automatically verifies that all expectations on it have been satisfied. + +Here is an example: +``` +using ::testing::Return; // #1 + +TEST(BarTest, DoesThis) { + MockFoo foo; // #2 + + ON_CALL(foo, GetSize()) // #3 + .WillByDefault(Return(1)); + // ... other default actions ... + + EXPECT_CALL(foo, Describe(5)) // #4 + .Times(3) + .WillRepeatedly(Return("Category 5")); + // ... other expectations ... + + EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 +} // #6 +``` + +# Setting Default Actions # + +Google Mock has a **built-in default action** for any function that +returns `void`, `bool`, a numeric value, or a pointer. + +To customize the default action for functions with return type `T` globally: +``` +using ::testing::DefaultValue; + +DefaultValue::Set(value); // Sets the default value to be returned. +// ... use the mocks ... +DefaultValue::Clear(); // Resets the default value. +``` + +To customize the default action for a particular method, use `ON_CALL()`: +``` +ON_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .WillByDefault(action); +``` + +# Setting Expectations # + +`EXPECT_CALL()` sets **expectations** on a mock method (How will it be +called? What will it do?): +``` +EXPECT_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .Times(cardinality) ? + .InSequence(sequences) * + .After(expectations) * + .WillOnce(action) * + .WillRepeatedly(action) ? + .RetiresOnSaturation(); ? +``` + +If `Times()` is omitted, the cardinality is assumed to be: + + * `Times(1)` when there is neither `WillOnce()` nor `WillRepeatedly()`; + * `Times(n)` when there are `n WillOnce()`s but no `WillRepeatedly()`, where `n` >= 1; or + * `Times(AtLeast(n))` when there are `n WillOnce()`s and a `WillRepeatedly()`, where `n` >= 0. + +A method with no `EXPECT_CALL()` is free to be invoked _any number of times_, and the default action will be taken each time. + +# Matchers # + +A **matcher** matches a _single_ argument. You can use it inside +`ON_CALL()` or `EXPECT_CALL()`, or use it to validate a value +directly: + +| `EXPECT_THAT(value, matcher)` | Asserts that `value` matches `matcher`. | +|:------------------------------|:----------------------------------------| +| `ASSERT_THAT(value, matcher)` | The same as `EXPECT_THAT(value, matcher)`, except that it generates a **fatal** failure. | + +Built-in matchers (where `argument` is the function argument) are +divided into several categories: + +## Wildcard ## +|`_`|`argument` can be any value of the correct type.| +|:--|:-----------------------------------------------| +|`A()` or `An()`|`argument` can be any value of type `type`. | + +## Generic Comparison ## + +|`Eq(value)` or `value`|`argument == value`| +|:---------------------|:------------------| +|`Ge(value)` |`argument >= value`| +|`Gt(value)` |`argument > value` | +|`Le(value)` |`argument <= value`| +|`Lt(value)` |`argument < value` | +|`Ne(value)` |`argument != value`| +|`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| +|`NotNull()` |`argument` is a non-null pointer (raw or smart).| +|`Ref(variable)` |`argument` is a reference to `variable`.| +|`TypedEq(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| + +Except `Ref()`, these matchers make a _copy_ of `value` in case it's +modified or destructed later. If the compiler complains that `value` +doesn't have a public copy constructor, try wrap it in `ByRef()`, +e.g. `Eq(ByRef(non_copyable_value))`. If you do that, make sure +`non_copyable_value` is not changed afterwards, or the meaning of your +matcher will be changed. + +## Floating-Point Matchers ## + +|`DoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal.| +|:-------------------|:----------------------------------------------------------------------------------------------| +|`FloatEq(a_float)` |`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. | +|`NanSensitiveDoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. | +|`NanSensitiveFloatEq(a_float)`|`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. | + +The above matchers use ULP-based comparison (the same as used in +[Google Test](http://code.google.com/p/googletest/)). They +automatically pick a reasonable error bound based on the absolute +value of the expected value. `DoubleEq()` and `FloatEq()` conform to +the IEEE standard, which requires comparing two NaNs for equality to +return false. The `NanSensitive*` version instead treats two NaNs as +equal, which is often what a user wants. + +## String Matchers ## + +The `argument` can be either a C string or a C++ string object: + +|`ContainsRegex(string)`|`argument` matches the given regular expression.| +|:----------------------|:-----------------------------------------------| +|`EndsWith(suffix)` |`argument` ends with string `suffix`. | +|`HasSubstr(string)` |`argument` contains `string` as a sub-string. | +|`MatchesRegex(string)` |`argument` matches the given regular expression with the match starting at the first character and ending at the last character.| +|`StartsWith(prefix)` |`argument` starts with string `prefix`. | +|`StrCaseEq(string)` |`argument` is equal to `string`, ignoring case. | +|`StrCaseNe(string)` |`argument` is not equal to `string`, ignoring case.| +|`StrEq(string)` |`argument` is equal to `string`. | +|`StrNe(string)` |`argument` is not equal to `string`. | + +`StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide +strings as well. + +## Container Matchers ## + +Most STL-style containers support `==`, so you can use +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. If you want to write the elements in-line, +match them more flexibly, or get more informative messages, you can use: + +| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | +|:--------------|:-------------------------------------------------------------------------------------------| +|`ElementsAre(e0, e1, ..., en)`|`argument` has `n + 1` elements, where the i-th element matches `ei`, which can be a value or a matcher. 0 to 10 arguments are allowed.| +|`ElementsAreArray(array)` or `ElementsAreArray(array, count)`|The same as `ElementsAre()` except that the expected element values/matchers come from a C-style array.| +| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | + +These matchers can also match: + + 1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`), and + 1. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer, int len)` -- see [Multi-argument Matchers](#Multiargument_Matchers.md)). + +where the array may be multi-dimensional (i.e. its elements can be arrays). + +## Member Matchers ## + +|`Field(&class::field, m)`|`argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| +|:------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| +|`Key(e)` |`argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`.| +|`Pair(m1, m2)` |`argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. | +|`Property(&class::property, m)`|`argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| + +## Matching the Result of a Function or Functor ## + +|`ResultOf(f, m)`|`f(argument)` matches matcher `m`, where `f` is a function or functor.| +|:---------------|:---------------------------------------------------------------------| + +## Pointer Matchers ## + +|`Pointee(m)`|`argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`.| +|:-----------|:-----------------------------------------------------------------------------------------------| + +## Multiargument Matchers ## + +These are matchers on tuple types. They can be used in +`.With()`. The following can be used on functions with two
+arguments
`x` and `y`: + +|`Eq()`|`x == y`| +|:-----|:-------| +|`Ge()`|`x >= y`| +|`Gt()`|`x > y` | +|`Le()`|`x <= y`| +|`Lt()`|`x < y` | +|`Ne()`|`x != y`| + +You can use the following selectors to pick a subset of the arguments +(or reorder them) to participate in the matching: + +|`AllArgs(m)`|Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`.| +|:-----------|:-------------------------------------------------------------------| +|`Args(m)`|The `k` selected (using 0-based indices) arguments match `m`, e.g. `Args<1, 2>(Contains(5))`.| + +## Composite Matchers ## + +You can make a matcher from one or more other matchers: + +|`AllOf(m1, m2, ..., mn)`|`argument` matches all of the matchers `m1` to `mn`.| +|:-----------------------|:---------------------------------------------------| +|`AnyOf(m1, m2, ..., mn)`|`argument` matches at least one of the matchers `m1` to `mn`.| +|`Not(m)` |`argument` doesn't match matcher `m`. | + +## Adapters for Matchers ## + +|`MatcherCast(m)`|casts matcher `m` to type `Matcher`.| +|:------------------|:--------------------------------------| +|`SafeMatcherCast(m)`| [safely casts](V1_5_CookBook#Casting_Matchers.md) matcher `m` to type `Matcher`. | +|`Truly(predicate)` |`predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor.| + +## Matchers as Predicates ## + +|`Matches(m)`|a unary functor that returns `true` if the argument matches `m`.| +|:-----------|:---------------------------------------------------------------| +|`ExplainMatchResult(m, value, result_listener)`|returns `true` if `value` matches `m`, explaining the result to `result_listener`.| +|`Value(x, m)`|returns `true` if the value of `x` matches `m`. | + +## Defining Matchers ## + +| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | +|:-------------------------------------------------|:------------------------------------------------------| +| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a macher `IsDivisibleBy(n)` to match a number divisible by `n`. | +| `MATCHER_P2(IsBetween, a, b, "is between %(a)s and %(b)s") { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | + +**Notes:** + + 1. The `MATCHER*` macros cannot be used inside a function or class. + 1. The matcher body must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). + 1. You can use `PrintToString(x)` to convert a value `x` of any type to a string. + +## Matchers as Test Assertions ## + +|`ASSERT_THAT(expression, m)`|Generates a [fatal failure](http://code.google.com/p/googletest/wiki/GoogleTestPrimer#Assertions) if the value of `expression` doesn't match matcher `m`.| +|:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------| +|`EXPECT_THAT(expression, m)`|Generates a non-fatal failure if the value of `expression` doesn't match matcher `m`. | + +# Actions # + +**Actions** specify what a mock function should do when invoked. + +## Returning a Value ## + +|`Return()`|Return from a `void` mock function.| +|:---------|:----------------------------------| +|`Return(value)`|Return `value`. | +|`ReturnArg()`|Return the `N`-th (0-based) argument.| +|`ReturnNew(a1, ..., ak)`|Return `new T(a1, ..., ak)`; a different object is created each time.| +|`ReturnNull()`|Return a null pointer. | +|`ReturnRef(variable)`|Return a reference to `variable`. | + +## Side Effects ## + +|`Assign(&variable, value)`|Assign `value` to variable.| +|:-------------------------|:--------------------------| +| `DeleteArg()` | Delete the `N`-th (0-based) argument, which must be a pointer. | +| `SaveArg(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. | +| `SetArgReferee(value)` | Assign value to the variable referenced by the `N`-th (0-based) argument. | +|`SetArgumentPointee(value)`|Assign `value` to the variable pointed by the `N`-th (0-based) argument.| +|`SetArrayArgument(first, last)`|Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range.| +|`SetErrnoAndReturn(error, value)`|Set `errno` to `error` and return `value`.| +|`Throw(exception)` |Throws the given exception, which can be any copyable value. Available since v1.1.0.| + +## Using a Function or a Functor as an Action ## + +|`Invoke(f)`|Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor.| +|:----------|:-----------------------------------------------------------------------------------------------------------------| +|`Invoke(object_pointer, &class::method)`|Invoke the {method on the object with the arguments passed to the mock function. | +|`InvokeWithoutArgs(f)`|Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | +|`InvokeWithoutArgs(object_pointer, &class::method)`|Invoke the method on the object, which takes no arguments. | +|`InvokeArgument(arg1, arg2, ..., argk)`|Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments.| + +The return value of the invoked function is used as the return value +of the action. + +When defining a function or functor to be used with `Invoke*()`, you can declare any unused parameters as `Unused`: +``` + double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } + ... + EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance)); +``` + +In `InvokeArgument(...)`, if an argument needs to be passed by reference, wrap it inside `ByRef()`. For example, +``` + InvokeArgument<2>(5, string("Hi"), ByRef(foo)) +``` +calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by value, and `foo` by reference. + +## Default Action ## + +|`DoDefault()`|Do the default action (specified by `ON_CALL()` or the built-in one).| +|:------------|:--------------------------------------------------------------------| + +**Note:** due to technical reasons, `DoDefault()` cannot be used inside a composite action - trying to do so will result in a run-time error. + +## Composite Actions ## + +|`DoAll(a1, a2, ..., an)`|Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void. | +|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------| +|`IgnoreResult(a)` |Perform action `a` and ignore its result. `a` must not return void. | +|`WithArg(a)` |Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. | +|`WithArgs(a)`|Pass the selected (0-based) arguments of the mock function to action `a` and perform it. | +|`WithoutArgs(a)` |Perform action `a` without any arguments. | + +## Defining Actions ## + +| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | +|:--------------------------------------|:---------------------------------------------------------------------------------------| +| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | +| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. | + +The `ACTION*` macros cannot be used inside a function or class. + +# Cardinalities # + +These are used in `Times()` to specify how many times a mock function will be called: + +|`AnyNumber()`|The function can be called any number of times.| +|:------------|:----------------------------------------------| +|`AtLeast(n)` |The call is expected at least `n` times. | +|`AtMost(n)` |The call is expected at most `n` times. | +|`Between(m, n)`|The call is expected between `m` and `n` (inclusive) times.| +|`Exactly(n) or n`|The call is expected exactly `n` times. In particular, the call should never happen when `n` is 0.| + +# Expectation Order # + +By default, the expectations can be matched in _any_ order. If some +or all expectations must be matched in a given order, there are two +ways to specify it. They can be used either independently or +together. + +## The After Clause ## + +``` +using ::testing::Expectation; +... +Expectation init_x = EXPECT_CALL(foo, InitX()); +Expectation init_y = EXPECT_CALL(foo, InitY()); +EXPECT_CALL(foo, Bar()) + .After(init_x, init_y); +``` +says that `Bar()` can be called only after both `InitX()` and +`InitY()` have been called. + +If you don't know how many pre-requisites an expectation has when you +write it, you can use an `ExpectationSet` to collect them: + +``` +using ::testing::ExpectationSet; +... +ExpectationSet all_inits; +for (int i = 0; i < element_count; i++) { + all_inits += EXPECT_CALL(foo, InitElement(i)); +} +EXPECT_CALL(foo, Bar()) + .After(all_inits); +``` +says that `Bar()` can be called only after all elements have been +initialized (but we don't care about which elements get initialized +before the others). + +Modifying an `ExpectationSet` after using it in an `.After()` doesn't +affect the meaning of the `.After()`. + +## Sequences ## + +When you have a long chain of sequential expectations, it's easier to +specify the order using **sequences**, which don't require you to given +each expectation in the chain a different name. All expected
+calls
in the same sequence must occur in the order they are +specified. + +``` +using ::testing::Sequence; +Sequence s1, s2; +... +EXPECT_CALL(foo, Reset()) + .InSequence(s1, s2) + .WillOnce(Return(true)); +EXPECT_CALL(foo, GetSize()) + .InSequence(s1) + .WillOnce(Return(1)); +EXPECT_CALL(foo, Describe(A())) + .InSequence(s2) + .WillOnce(Return("dummy")); +``` +says that `Reset()` must be called before _both_ `GetSize()` _and_ +`Describe()`, and the latter two can occur in any order. + +To put many expectations in a sequence conveniently: +``` +using ::testing::InSequence; +{ + InSequence dummy; + + EXPECT_CALL(...)...; + EXPECT_CALL(...)...; + ... + EXPECT_CALL(...)...; +} +``` +says that all expected calls in the scope of `dummy` must occur in +strict order. The name `dummy` is irrelevant.) + +# Verifying and Resetting a Mock # + +Google Mock will verify the expectations on a mock object when it is destructed, or you can do it earlier: +``` +using ::testing::Mock; +... +// Verifies and removes the expectations on mock_obj; +// returns true iff successful. +Mock::VerifyAndClearExpectations(&mock_obj); +... +// Verifies and removes the expectations on mock_obj; +// also removes the default actions set by ON_CALL(); +// returns true iff successful. +Mock::VerifyAndClear(&mock_obj); +``` + +You can also tell Google Mock that a mock object can be leaked and doesn't +need to be verified: +``` +Mock::AllowLeak(&mock_obj); +``` + +# Mock Classes # + +Google Mock defines a convenient mock class template +``` +class MockFunction { + public: + MOCK_METHODn(Call, R(A1, ..., An)); +}; +``` +See this [recipe](V1_5_CookBook#Using_Check_Points.md) for one application of it. + +# Flags # + +| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. | +|:-------------------------------|:----------------------------------------------| +| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. | \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/CookBook.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/CookBook.md new file mode 100644 index 00000000..26e153c6 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/CookBook.md @@ -0,0 +1,3250 @@ + + +You can find recipes for using Google Mock here. If you haven't yet, +please read the [ForDummies](V1_5_ForDummies.md) document first to make sure you understand +the basics. + +**Note:** Google Mock lives in the `testing` name space. For +readability, it is recommended to write `using ::testing::Foo;` once in +your file before using the name `Foo` defined by Google Mock. We omit +such `using` statements in this page for brevity, but you should do it +in your own code. + +# Creating Mock Classes # + +## Mocking Private or Protected Methods ## + +You must always put a mock method definition (`MOCK_METHOD*`) in a +`public:` section of the mock class, regardless of the method being +mocked being `public`, `protected`, or `private` in the base class. +This allows `ON_CALL` and `EXPECT_CALL` to reference the mock function +from outside of the mock class. (Yes, C++ allows a subclass to change +the access level of a virtual function in the base class.) Example: + +``` +class Foo { + public: + ... + virtual bool Transform(Gadget* g) = 0; + + protected: + virtual void Resume(); + + private: + virtual int GetTimeOut(); +}; + +class MockFoo : public Foo { + public: + ... + MOCK_METHOD1(Transform, bool(Gadget* g)); + + // The following must be in the public section, even though the + // methods are protected or private in the base class. + MOCK_METHOD0(Resume, void()); + MOCK_METHOD0(GetTimeOut, int()); +}; +``` + +## Mocking Overloaded Methods ## + +You can mock overloaded functions as usual. No special attention is required: + +``` +class Foo { + ... + + // Must be virtual as we'll inherit from Foo. + virtual ~Foo(); + + // Overloaded on the types and/or numbers of arguments. + virtual int Add(Element x); + virtual int Add(int times, Element x); + + // Overloaded on the const-ness of this object. + virtual Bar& GetBar(); + virtual const Bar& GetBar() const; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Add, int(Element x)); + MOCK_METHOD2(Add, int(int times, Element x); + + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +``` + +**Note:** if you don't mock all versions of the overloaded method, the +compiler will give you a warning about some methods in the base class +being hidden. To fix that, use `using` to bring them in scope: + +``` +class MockFoo : public Foo { + ... + using Foo::Add; + MOCK_METHOD1(Add, int(Element x)); + // We don't want to mock int Add(int times, Element x); + ... +}; +``` + +## Mocking Class Templates ## + +To mock a class template, append `_T` to the `MOCK_*` macros: + +``` +template +class StackInterface { + ... + // Must be virtual as we'll inherit from StackInterface. + virtual ~StackInterface(); + + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; + +template +class MockStack : public StackInterface { + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Mocking Nonvirtual Methods ## + +Google Mock can mock non-virtual functions to be used in what we call _hi-perf +dependency injection_. + +In this case, instead of sharing a common base class with the real +class, your mock class will be _unrelated_ to the real class, but +contain methods with the same signatures. The syntax for mocking +non-virtual methods is the _same_ as mocking virtual methods: + +``` +// A simple packet stream class. None of its members is virtual. +class ConcretePacketStream { + public: + void AppendPacket(Packet* new_packet); + const Packet* GetPacket(size_t packet_number) const; + size_t NumberOfPackets() const; + ... +}; + +// A mock packet stream class. It inherits from no other, but defines +// GetPacket() and NumberOfPackets(). +class MockPacketStream { + public: + MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number)); + MOCK_CONST_METHOD0(NumberOfPackets, size_t()); + ... +}; +``` + +Note that the mock class doesn't define `AppendPacket()`, unlike the +real class. That's fine as long as the test doesn't need to call it. + +Next, you need a way to say that you want to use +`ConcretePacketStream` in production code, and use `MockPacketStream` +in tests. Since the functions are not virtual and the two classes are +unrelated, you must specify your choice at _compile time_ (as opposed +to run time). + +One way to do it is to templatize your code that needs to use a packet +stream. More specifically, you will give your code a template type +argument for the type of the packet stream. In production, you will +instantiate your template with `ConcretePacketStream` as the type +argument. In tests, you will instantiate the same template with +`MockPacketStream`. For example, you may write: + +``` +template +void CreateConnection(PacketStream* stream) { ... } + +template +class PacketReader { + public: + void ReadPackets(PacketStream* stream, size_t packet_num); +}; +``` + +Then you can use `CreateConnection()` and +`PacketReader` in production code, and use +`CreateConnection()` and +`PacketReader` in tests. + +``` + MockPacketStream mock_stream; + EXPECT_CALL(mock_stream, ...)...; + .. set more expectations on mock_stream ... + PacketReader reader(&mock_stream); + ... exercise reader ... +``` + +## Mocking Free Functions ## + +It's possible to use Google Mock to mock a free function (i.e. a +C-style function or a static method). You just need to rewrite your +code to use an interface (abstract class). + +Instead of calling a free function (say, `OpenFile`) directly, +introduce an interface for it and have a concrete subclass that calls +the free function: + +``` +class FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) = 0; +}; + +class File : public FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) { + return OpenFile(path, mode); + } +}; +``` + +Your code should talk to `FileInterface` to open a file. Now it's +easy to mock out the function. + +This may seem much hassle, but in practice you often have multiple +related functions that you can put in the same interface, so the +per-function syntactic overhead will be much lower. + +If you are concerned about the performance overhead incurred by +virtual functions, and profiling confirms your concern, you can +combine this with the recipe for [mocking non-virtual methods](#Mocking_Nonvirtual_Methods.md). + +## Nice Mocks and Strict Mocks ## + +If a mock method has no `EXPECT_CALL` spec but is called, Google Mock +will print a warning about the "uninteresting call". The rationale is: + + * New methods may be added to an interface after a test is written. We shouldn't fail a test just because a method it doesn't know about is called. + * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, he can add an `EXPECT_CALL()` to suppress the warning. + +However, sometimes you may want to suppress all "uninteresting call" +warnings, while sometimes you may want the opposite, i.e. to treat all +of them as errors. Google Mock lets you make the decision on a +per-mock-object basis. + +Suppose your test uses a mock class `MockFoo`: + +``` +TEST(...) { + MockFoo mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +If a method of `mock_foo` other than `DoThis()` is called, it will be +reported by Google Mock as a warning. However, if you rewrite your +test to use `NiceMock` instead, the warning will be gone, +resulting in a cleaner test output: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +`NiceMock` is a subclass of `MockFoo`, so it can be used +wherever `MockFoo` is accepted. + +It also works if `MockFoo`'s constructor takes some arguments, as +`NiceMock` "inherits" `MockFoo`'s constructors: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +The usage of `StrictMock` is similar, except that it makes all +uninteresting calls failures: + +``` +using ::testing::StrictMock; + +TEST(...) { + StrictMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... + + // The test will fail if a method of mock_foo other than DoThis() + // is called. +} +``` + +There are some caveats though (I don't like them just as much as the +next guy, but sadly they are side effects of C++'s limitations): + + 1. `NiceMock` and `StrictMock` only work for mock methods defined using the `MOCK_METHOD*` family of macros **directly** in the `MockFoo` class. If a mock method is defined in a **base class** of `MockFoo`, the "nice" or "strict" modifier may not affect it, depending on the compiler. In particular, nesting `NiceMock` and `StrictMock` (e.g. `NiceMock >`) is **not** supported. + 1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). + 1. During the constructor or destructor of `MockFoo`, the mock object is _not_ nice or strict. This may cause surprises if the constructor or destructor calls a mock method on `this` object. (This behavior, however, is consistent with C++'s general rule: if a constructor or destructor calls a virtual method of `this` object, that method is treated as non-virtual. In other words, to the base class's constructor or destructor, `this` object behaves like an instance of the base class, not the derived class. This rule is required for safety. Otherwise a base constructor may use members of a derived class before they are initialized, or a base destructor may use members of a derived class after they have been destroyed.) + +Finally, you should be **very cautious** when using this feature, as the +decision you make applies to **all** future changes to the mock +class. If an important change is made in the interface you are mocking +(and thus in the mock class), it could break your tests (if you use +`StrictMock`) or let bugs pass through without a warning (if you use +`NiceMock`). Therefore, try to specify the mock's behavior using +explicit `EXPECT_CALL` first, and only turn to `NiceMock` or +`StrictMock` as the last resort. + +## Simplifying the Interface without Breaking Existing Code ## + +Sometimes a method has a long list of arguments that is mostly +uninteresting. For example, + +``` +class LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) = 0; +}; +``` + +This method's argument list is lengthy and hard to work with (let's +say that the `message` argument is not even 0-terminated). If we mock +it as is, using the mock will be awkward. If, however, we try to +simplify this interface, we'll need to fix all clients depending on +it, which is often infeasible. + +The trick is to re-dispatch the method in the mock class: + +``` +class ScopedMockLog : public LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, const tm* tm_time, + const char* message, size_t message_len) { + // We are only interested in the log severity, full file name, and + // log message. + Log(severity, full_filename, std::string(message, message_len)); + } + + // Implements the mock method: + // + // void Log(LogSeverity severity, + // const string& file_path, + // const string& message); + MOCK_METHOD3(Log, void(LogSeverity severity, const string& file_path, + const string& message)); +}; +``` + +By defining a new mock method with a trimmed argument list, we make +the mock class much more user-friendly. + +## Alternative to Mocking Concrete Classes ## + +Often you may find yourself using classes that don't implement +interfaces. In order to test your code that uses such a class (let's +call it `Concrete`), you may be tempted to make the methods of +`Concrete` virtual and then mock it. + +Try not to do that. + +Making a non-virtual function virtual is a big decision. It creates an +extension point where subclasses can tweak your class' behavior. This +weakens your control on the class because now it's harder to maintain +the class' invariants. You should make a function virtual only when +there is a valid reason for a subclass to override it. + +Mocking concrete classes directly is problematic as it creates a tight +coupling between the class and the tests - any small change in the +class may invalidate your tests and make test maintenance a pain. + +To avoid such problems, many programmers have been practicing "coding +to interfaces": instead of talking to the `Concrete` class, your code +would define an interface and talk to it. Then you implement that +interface as an adaptor on top of `Concrete`. In tests, you can easily +mock that interface to observe how your code is doing. + +This technique incurs some overhead: + + * You pay the cost of virtual function calls (usually not a problem). + * There is more abstraction for the programmers to learn. + +However, it can also bring significant benefits in addition to better +testability: + + * `Concrete`'s API may not fit your problem domain very well, as you may not be the only client it tries to serve. By designing your own interface, you have a chance to tailor it to your need - you may add higher-level functionalities, rename stuff, etc instead of just trimming the class. This allows you to write your code (user of the interface) in a more natural way, which means it will be more readable, more maintainable, and you'll be more productive. + * If `Concrete`'s implementation ever has to change, you don't have to rewrite everywhere it is used. Instead, you can absorb the change in your implementation of the interface, and your other code and tests will be insulated from this change. + +Some people worry that if everyone is practicing this technique, they +will end up writing lots of redundant code. This concern is totally +understandable. However, there are two reasons why it may not be the +case: + + * Different projects may need to use `Concrete` in different ways, so the best interfaces for them will be different. Therefore, each of them will have its own domain-specific interface on top of `Concrete`, and they will not be the same code. + * If enough projects want to use the same interface, they can always share it, just like they have been sharing `Concrete`. You can check in the interface and the adaptor somewhere near `Concrete` (perhaps in a `contrib` sub-directory) and let many projects use it. + +You need to weigh the pros and cons carefully for your particular +problem, but I'd like to assure you that the Java community has been +practicing this for a long time and it's a proven effective technique +applicable in a wide variety of situations. :-) + +## Delegating Calls to a Fake ## + +Some times you have a non-trivial fake implementation of an +interface. For example: + +``` +class Foo { + public: + virtual ~Foo() {} + virtual char DoThis(int n) = 0; + virtual void DoThat(const char* s, int* p) = 0; +}; + +class FakeFoo : public Foo { + public: + virtual char DoThis(int n) { + return (n > 0) ? '+' : + (n < 0) ? '-' : '0'; + } + + virtual void DoThat(const char* s, int* p) { + *p = strlen(s); + } +}; +``` + +Now you want to mock this interface such that you can set expectations +on it. However, you also want to use `FakeFoo` for the default +behavior, as duplicating it in the mock object is, well, a lot of +work. + +When you define the mock class using Google Mock, you can have it +delegate its default action to a fake class you already have, using +this pattern: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + // Normal mock method definitions using Google Mock. + MOCK_METHOD1(DoThis, char(int n)); + MOCK_METHOD2(DoThat, void(const char* s, int* p)); + + // Delegates the default actions of the methods to a FakeFoo object. + // This must be called *before* the custom ON_CALL() statements. + void DelegateToFake() { + ON_CALL(*this, DoThis(_)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThis)); + ON_CALL(*this, DoThat(_, _)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThat)); + } + private: + FakeFoo fake_; // Keeps an instance of the fake in the mock. +}; +``` + +With that, you can use `MockFoo` in your tests as usual. Just remember +that if you don't explicitly set an action in an `ON_CALL()` or +`EXPECT_CALL()`, the fake will be called upon to do it: + +``` +using ::testing::_; + +TEST(AbcTest, Xyz) { + MockFoo foo; + foo.DelegateToFake(); // Enables the fake for delegation. + + // Put your ON_CALL(foo, ...)s here, if any. + + // No action specified, meaning to use the default action. + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(foo, DoThat(_, _)); + + int n = 0; + EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked. + foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. + EXPECT_EQ(2, n); +} +``` + +**Some tips:** + + * If you want, you can still override the default action by providing your own `ON_CALL()` or using `.WillOnce()` / `.WillRepeatedly()` in `EXPECT_CALL()`. + * In `DelegateToFake()`, you only need to delegate the methods whose fake implementation you intend to use. + * The general technique discussed here works for overloaded methods, but you'll need to tell the compiler which version you mean. To disambiguate a mock function (the one you specify inside the parentheses of `ON_CALL()`), see the "Selecting Between Overloaded Functions" section on this page; to disambiguate a fake function (the one you place inside `Invoke()`), use a `static_cast` to specify the function's type. + * Having to mix a mock and a fake is often a sign of something gone wrong. Perhaps you haven't got used to the interaction-based way of testing yet. Or perhaps your interface is taking on too many roles and should be split up. Therefore, **don't abuse this**. We would only recommend to do it as an intermediate step when you are refactoring your code. + +Regarding the tip on mixing a mock and a fake, here's an example on +why it may be a bad sign: Suppose you have a class `System` for +low-level system operations. In particular, it does file and I/O +operations. And suppose you want to test how your code uses `System` +to do I/O, and you just want the file operations to work normally. If +you mock out the entire `System` class, you'll have to provide a fake +implementation for the file operation part, which suggests that +`System` is taking on too many roles. + +Instead, you can define a `FileOps` interface and an `IOOps` interface +and split `System`'s functionalities into the two. Then you can mock +`IOOps` without mocking `FileOps`. + +## Delegating Calls to a Real Object ## + +When using testing doubles (mocks, fakes, stubs, and etc), sometimes +their behaviors will differ from those of the real objects. This +difference could be either intentional (as in simulating an error such +that you can test the error handling code) or unintentional. If your +mocks have different behaviors than the real objects by mistake, you +could end up with code that passes the tests but fails in production. + +You can use the _delegating-to-real_ technique to ensure that your +mock has the same behavior as the real object while retaining the +ability to validate calls. This technique is very similar to the +delegating-to-fake technique, the difference being that we use a real +object instead of a fake. Here's an example: + +``` +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MockFoo() { + // By default, all calls are delegated to the real object. + ON_CALL(*this, DoThis()) + .WillByDefault(Invoke(&real_, &Foo::DoThis)); + ON_CALL(*this, DoThat(_)) + .WillByDefault(Invoke(&real_, &Foo::DoThat)); + ... + } + MOCK_METHOD0(DoThis, ...); + MOCK_METHOD1(DoThat, ...); + ... + private: + Foo real_; +}; +... + + MockFoo mock; + + EXPECT_CALL(mock, DoThis()) + .Times(3); + EXPECT_CALL(mock, DoThat("Hi")) + .Times(AtLeast(1)); + ... use mock in test ... +``` + +With this, Google Mock will verify that your code made the right calls +(with the right arguments, in the right order, called the right number +of times, etc), and a real object will answer the calls (so the +behavior will be the same as in production). This gives you the best +of both worlds. + +## Delegating Calls to a Parent Class ## + +Ideally, you should code to interfaces, whose methods are all pure +virtual. In reality, sometimes you do need to mock a virtual method +that is not pure (i.e, it already has an implementation). For example: + +``` +class Foo { + public: + virtual ~Foo(); + + virtual void Pure(int n) = 0; + virtual int Concrete(const char* str) { ... } +}; + +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); +}; +``` + +Sometimes you may want to call `Foo::Concrete()` instead of +`MockFoo::Concrete()`. Perhaps you want to do it as part of a stub +action, or perhaps your test doesn't need to mock `Concrete()` at all +(but it would be oh-so painful to have to define a new mock class +whenever you don't need to mock one of its methods). + +The trick is to leave a back door in your mock class for accessing the +real methods in the base class: + +``` +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); + + // Use this to call Concrete() defined in Foo. + int FooConcrete(const char* str) { return Foo::Concrete(str); } +}; +``` + +Now, you can call `Foo::Concrete()` inside an action by: + +``` +using ::testing::_; +using ::testing::Invoke; +... + EXPECT_CALL(foo, Concrete(_)) + .WillOnce(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +or tell the mock object that you don't want to mock `Concrete()`: + +``` +using ::testing::Invoke; +... + ON_CALL(foo, Concrete(_)) + .WillByDefault(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +(Why don't we just write `Invoke(&foo, &Foo::Concrete)`? If you do +that, `MockFoo::Concrete()` will be called (and cause an infinite +recursion) since `Foo::Concrete()` is virtual. That's just how C++ +works.) + +# Using Matchers # + +## Matching Argument Values Exactly ## + +You can specify exactly which arguments a mock method is expecting: + +``` +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(5)) + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", bar)); +``` + +## Using Simple Matchers ## + +You can use matchers to match arguments that have a certain property: + +``` +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", NotNull())); + // The second argument must not be NULL. +``` + +A frequently used matcher is `_`, which matches anything: + +``` +using ::testing::_; +using ::testing::NotNull; +... + EXPECT_CALL(foo, DoThat(_, NotNull())); +``` + +## Combining Matchers ## + +You can build complex matchers from existing ones using `AllOf()`, +`AnyOf()`, and `Not()`: + +``` +using ::testing::AllOf; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +... + // The argument must be > 5 and != 10. + EXPECT_CALL(foo, DoThis(AllOf(Gt(5), + Ne(10)))); + + // The first argument must not contain sub-string "blah". + EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), + NULL)); +``` + +## Casting Matchers ## + +Google Mock matchers are statically typed, meaning that the compiler +can catch your mistake if you use a matcher of the wrong type (for +example, if you use `Eq(5)` to match a `string` argument). Good for +you! + +Sometimes, however, you know what you're doing and want the compiler +to give you some slack. One example is that you have a matcher for +`long` and the argument you want to match is `int`. While the two +types aren't exactly the same, there is nothing really wrong with +using a `Matcher` to match an `int` - after all, we can first +convert the `int` argument to a `long` before giving it to the +matcher. + +To support this need, Google Mock gives you the +`SafeMatcherCast(m)` function. It casts a matcher `m` to type +`Matcher`. To ensure safety, Google Mock checks that (let `U` be the +type `m` accepts): + + 1. Type `T` can be implicitly cast to type `U`; + 1. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and floating-point numbers), the conversion from `T` to `U` is not lossy (in other words, any value representable by `T` can also be represented by `U`); and + 1. When `U` is a reference, `T` must also be a reference (as the underlying matcher may be interested in the address of the `U` value). + +The code won't compile if any of these conditions isn't met. + +Here's one example: + +``` +using ::testing::SafeMatcherCast; + +// A base class and a child class. +class Base { ... }; +class Derived : public Base { ... }; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(DoThis, void(Derived* derived)); +}; +... + + MockFoo foo; + // m is a Matcher we got from somewhere. + EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); +``` + +If you find `SafeMatcherCast(m)` too limiting, you can use a similar +function `MatcherCast(m)`. The difference is that `MatcherCast` works +as long as you can `static_cast` type `T` to type `U`. + +`MatcherCast` essentially lets you bypass C++'s type system +(`static_cast` isn't always safe as it could throw away information, +for example), so be careful not to misuse/abuse it. + +## Selecting Between Overloaded Functions ## + +If you expect an overloaded function to be called, the compiler may +need some help on which overloaded version it is. + +To disambiguate functions overloaded on the const-ness of this object, +use the `Const()` argument wrapper. + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + ... + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +... + + MockFoo foo; + Bar bar1, bar2; + EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). + .WillOnce(ReturnRef(bar1)); + EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). + .WillOnce(ReturnRef(bar2)); +``` + +(`Const()` is defined by Google Mock and returns a `const` reference +to its argument.) + +To disambiguate overloaded functions with the same number of arguments +but different argument types, you may need to specify the exact type +of a matcher, either by wrapping your matcher in `Matcher()`, or +using a matcher whose type is fixed (`TypedEq`, `An()`, +etc): + +``` +using ::testing::An; +using ::testing::Lt; +using ::testing::Matcher; +using ::testing::TypedEq; + +class MockPrinter : public Printer { + public: + MOCK_METHOD1(Print, void(int n)); + MOCK_METHOD1(Print, void(char c)); +}; + +TEST(PrinterTest, Print) { + MockPrinter printer; + + EXPECT_CALL(printer, Print(An())); // void Print(int); + EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); + EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); + + printer.Print(3); + printer.Print(6); + printer.Print('a'); +} +``` + +## Performing Different Actions Based on the Arguments ## + +When a mock method is called, the _last_ matching expectation that's +still active will be selected (think "newer overrides older"). So, you +can make a method do different things depending on its argument values +like this: + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Return; +... + // The default case. + EXPECT_CALL(foo, DoThis(_)) + .WillRepeatedly(Return('b')); + + // The more specific case. + EXPECT_CALL(foo, DoThis(Lt(5))) + .WillRepeatedly(Return('a')); +``` + +Now, if `foo.DoThis()` is called with a value less than 5, `'a'` will +be returned; otherwise `'b'` will be returned. + +## Matching Multiple Arguments as a Whole ## + +Sometimes it's not enough to match the arguments individually. For +example, we may want to say that the first argument must be less than +the second argument. The `With()` clause allows us to match +all arguments of a mock function as a whole. For example, + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Ne; +... + EXPECT_CALL(foo, InRange(Ne(0), _)) + .With(Lt()); +``` + +says that the first argument of `InRange()` must not be 0, and must be +less than the second argument. + +The expression inside `With()` must be a matcher of type +`Matcher >`, where `A1`, ..., `An` are the +types of the function arguments. + +You can also write `AllArgs(m)` instead of `m` inside `.With()`. The +two forms are equivalent, but `.With(AllArgs(Lt()))` is more readable +than `.With(Lt())`. + +You can use `Args(m)` to match the `n` selected arguments +against `m`. For example, + +``` +using ::testing::_; +using ::testing::AllOf; +using ::testing::Args; +using ::testing::Lt; +... + EXPECT_CALL(foo, Blah(_, _, _)) + .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); +``` + +says that `Blah()` will be called with arguments `x`, `y`, and `z` where +`x < y < z`. + +As a convenience and example, Google Mock provides some matchers for +2-tuples, including the `Lt()` matcher above. See the [CheatSheet](V1_5_CheatSheet.md) for +the complete list. + +## Using Matchers as Predicates ## + +Have you noticed that a matcher is just a fancy predicate that also +knows how to describe itself? Many existing algorithms take predicates +as arguments (e.g. those defined in STL's `` header), and +it would be a shame if Google Mock matchers are not allowed to +participate. + +Luckily, you can use a matcher where a unary predicate functor is +expected by wrapping it inside the `Matches()` function. For example, + +``` +#include +#include + +std::vector v; +... +// How many elements in v are >= 10? +const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); +``` + +Since you can build complex matchers from simpler ones easily using +Google Mock, this gives you a way to conveniently construct composite +predicates (doing the same using STL's `` header is just +painful). For example, here's a predicate that's satisfied by any +number that is >= 0, <= 100, and != 50: + +``` +Matches(AllOf(Ge(0), Le(100), Ne(50))) +``` + +## Using Matchers in Google Test Assertions ## + +Since matchers are basically predicates that also know how to describe +themselves, there is a way to take advantage of them in +[Google Test](http://code.google.com/p/googletest/) assertions. It's +called `ASSERT_THAT` and `EXPECT_THAT`: + +``` + ASSERT_THAT(value, matcher); // Asserts that value matches matcher. + EXPECT_THAT(value, matcher); // The non-fatal version. +``` + +For example, in a Google Test test you can write: + +``` +#include + +using ::testing::AllOf; +using ::testing::Ge; +using ::testing::Le; +using ::testing::MatchesRegex; +using ::testing::StartsWith; +... + + EXPECT_THAT(Foo(), StartsWith("Hello")); + EXPECT_THAT(Bar(), MatchesRegex("Line \\d+")); + ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10))); +``` + +which (as you can probably guess) executes `Foo()`, `Bar()`, and +`Baz()`, and verifies that: + + * `Foo()` returns a string that starts with `"Hello"`. + * `Bar()` returns a string that matches regular expression `"Line \\d+"`. + * `Baz()` returns a number in the range [5, 10]. + +The nice thing about these macros is that _they read like +English_. They generate informative messages too. For example, if the +first `EXPECT_THAT()` above fails, the message will be something like: + +``` +Value of: Foo() + Actual: "Hi, world!" +Expected: starts with "Hello" +``` + +**Credit:** The idea of `(ASSERT|EXPECT)_THAT` was stolen from the +[Hamcrest](http://code.google.com/p/hamcrest/) project, which adds +`assertThat()` to JUnit. + +## Using Predicates as Matchers ## + +Google Mock provides a built-in set of matchers. In case you find them +lacking, you can use an arbitray unary predicate function or functor +as a matcher - as long as the predicate accepts a value of the type +you want. You do this by wrapping the predicate inside the `Truly()` +function, for example: + +``` +using ::testing::Truly; + +int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } +... + + // Bar() must be called with an even number. + EXPECT_CALL(foo, Bar(Truly(IsEven))); +``` + +Note that the predicate function / functor doesn't have to return +`bool`. It works as long as the return value can be used as the +condition in statement `if (condition) ...`. + +## Matching Arguments that Are Not Copyable ## + +When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, Google Mock saves +away a copy of `bar`. When `Foo()` is called later, Google Mock +compares the argument to `Foo()` with the saved copy of `bar`. This +way, you don't need to worry about `bar` being modified or destroyed +after the `EXPECT_CALL()` is executed. The same is true when you use +matchers like `Eq(bar)`, `Le(bar)`, and so on. + +But what if `bar` cannot be copied (i.e. has no copy constructor)? You +could define your own matcher function and use it with `Truly()`, as +the previous couple of recipes have shown. Or, you may be able to get +away from it if you can guarantee that `bar` won't be changed after +the `EXPECT_CALL()` is executed. Just tell Google Mock that it should +save a reference to `bar`, instead of a copy of it. Here's how: + +``` +using ::testing::Eq; +using ::testing::ByRef; +using ::testing::Lt; +... + // Expects that Foo()'s argument == bar. + EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar)))); + + // Expects that Foo()'s argument < bar. + EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar)))); +``` + +Remember: if you do this, don't change `bar` after the +`EXPECT_CALL()`, or the result is undefined. + +## Validating a Member of an Object ## + +Often a mock function takes a reference to object as an argument. When +matching the argument, you may not want to compare the entire object +against a fixed object, as that may be over-specification. Instead, +you may need to validate a certain member variable or the result of a +certain getter method of the object. You can do this with `Field()` +and `Property()`. More specifically, + +``` +Field(&Foo::bar, m) +``` + +is a matcher that matches a `Foo` object whose `bar` member variable +satisfies matcher `m`. + +``` +Property(&Foo::baz, m) +``` + +is a matcher that matches a `Foo` object whose `baz()` method returns +a value that satisfies matcher `m`. + +For example: + +> | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | +|:-----------------------------|:-----------------------------------| +> | `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | + +Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no +argument and be declared as `const`. + +BTW, `Field()` and `Property()` can also match plain pointers to +objects. For instance, + +``` +Field(&Foo::number, Ge(3)) +``` + +matches a plain pointer `p` where `p->number >= 3`. If `p` is `NULL`, +the match will always fail regardless of the inner matcher. + +What if you want to validate more than one members at the same time? +Remember that there is `AllOf()`. + +## Validating the Value Pointed to by a Pointer Argument ## + +C++ functions often take pointers as arguments. You can use matchers +like `NULL`, `NotNull()`, and other comparison matchers to match a +pointer, but what if you want to make sure the value _pointed to_ by +the pointer, instead of the pointer itself, has a certain property? +Well, you can use the `Pointee(m)` matcher. + +`Pointee(m)` matches a pointer iff `m` matches the value the pointer +points to. For example: + +``` +using ::testing::Ge; +using ::testing::Pointee; +... + EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); +``` + +expects `foo.Bar()` to be called with a pointer that points to a value +greater than or equal to 3. + +One nice thing about `Pointee()` is that it treats a `NULL` pointer as +a match failure, so you can write `Pointee(m)` instead of + +``` + AllOf(NotNull(), Pointee(m)) +``` + +without worrying that a `NULL` pointer will crash your test. + +Also, did we tell you that `Pointee()` works with both raw pointers +**and** smart pointers (`linked_ptr`, `shared_ptr`, `scoped_ptr`, and +etc)? + +What if you have a pointer to pointer? You guessed it - you can use +nested `Pointee()` to probe deeper inside the value. For example, +`Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer +that points to a number less than 3 (what a mouthful...). + +## Testing a Certain Property of an Object ## + +Sometimes you want to specify that an object argument has a certain +property, but there is no existing matcher that does this. If you want +good error messages, you should define a matcher. If you want to do it +quick and dirty, you could get away with writing an ordinary function. + +Let's say you have a mock function that takes an object of type `Foo`, +which has an `int bar()` method and an `int baz()` method, and you +want to constrain that the argument's `bar()` value plus its `baz()` +value is a given number. Here's how you can define a matcher to do it: + +``` +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class BarPlusBazEqMatcher : public MatcherInterface { + public: + explicit BarPlusBazEqMatcher(int expected_sum) + : expected_sum_(expected_sum) {} + + virtual bool MatchAndExplain(const Foo& foo, + MatchResultListener* listener) const { + return (foo.bar() + foo.baz()) == expected_sum_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "bar() + baz() equals " << expected_sum_; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "bar() + baz() does not equal " << expected_sum_; + } + private: + const int expected_sum_; +}; + +inline Matcher BarPlusBazEq(int expected_sum) { + return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); +} + +... + + EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; +``` + +## Matching Containers ## + +Sometimes an STL container (e.g. list, vector, map, ...) is passed to +a mock function and you may want to validate it. Since most STL +containers support the `==` operator, you can write +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. + +Sometimes, though, you may want to be more flexible (for example, the +first element must be an exact match, but the second element can be +any positive number, and so on). Also, containers used in tests often +have a small number of elements, and having to define the expected +container out-of-line is a bit of a hassle. + +You can use the `ElementsAre()` matcher in such cases: + +``` +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); +``` + +The above matcher says that the container must have 4 elements, which +must be 1, greater than 0, anything, and 5 respectively. + +`ElementsAre()` is overloaded to take 0 to 10 arguments. If more are +needed, you can place them in a C-style array and use +`ElementsAreArray()` instead: + +``` +using ::testing::ElementsAreArray; +... + + // ElementsAreArray accepts an array of element values. + const int expected_vector1[] = { 1, 5, 2, 4, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); + + // Or, an array of element matchers. + Matcher expected_vector2 = { 1, Gt(2), _, 3, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); +``` + +In case the array needs to be dynamically created (and therefore the +array size cannot be inferred by the compiler), you can give +`ElementsAreArray()` an additional argument to specify the array size: + +``` +using ::testing::ElementsAreArray; +... + int* const expected_vector3 = new int[count]; + ... fill expected_vector3 with values ... + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); +``` + +**Tips:** + + * `ElementAre*()` works with _any_ container that implements the STL iterator concept (i.e. it has a `const_iterator` type and supports `begin()/end()`) and supports `size()`, not just the ones defined in STL. It will even work with container types yet to be written - as long as they follows the above pattern. + * You can use nested `ElementAre*()` to match nested (multi-dimensional) containers. + * If the container is passed by pointer instead of by reference, just write `Pointee(ElementsAre*(...))`. + * The order of elements _matters_ for `ElementsAre*()`. Therefore don't use it with containers whose element order is undefined (e.g. `hash_map`). + +## Sharing Matchers ## + +Under the hood, a Google Mock matcher object consists of a pointer to +a ref-counted implementation object. Copying matchers is allowed and +very efficient, as only the pointer is copied. When the last matcher +that references the implementation object dies, the implementation +object will be deleted. + +Therefore, if you have some complex matcher that you want to use again +and again, there is no need to build it everytime. Just assign it to a +matcher variable and use that variable repeatedly! For example, + +``` + Matcher in_range = AllOf(Gt(5), Le(10)); + ... use in_range as a matcher in multiple EXPECT_CALLs ... +``` + +# Setting Expectations # + +## Ignoring Uninteresting Calls ## + +If you are not interested in how a mock method is called, just don't +say anything about it. In this case, if the method is ever called, +Google Mock will perform its default action to allow the test program +to continue. If you are not happy with the default action taken by +Google Mock, you can override it using `DefaultValue::Set()` +(described later in this document) or `ON_CALL()`. + +Please note that once you expressed interest in a particular mock +method (via `EXPECT_CALL()`), all invocations to it must match some +expectation. If this function is called but the arguments don't match +any `EXPECT_CALL()` statement, it will be an error. + +## Disallowing Unexpected Calls ## + +If a mock method shouldn't be called at all, explicitly say so: + +``` +using ::testing::_; +... + EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +If some calls to the method are allowed, but the rest are not, just +list all the expected calls: + +``` +using ::testing::AnyNumber; +using ::testing::Gt; +... + EXPECT_CALL(foo, Bar(5)); + EXPECT_CALL(foo, Bar(Gt(10))) + .Times(AnyNumber()); +``` + +A call to `foo.Bar()` that doesn't match any of the `EXPECT_CALL()` +statements will be an error. + +## Expecting Ordered Calls ## + +Although an `EXPECT_CALL()` statement defined earlier takes precedence +when Google Mock tries to match a function call with an expectation, +by default calls don't have to happen in the order `EXPECT_CALL()` +statements are written. For example, if the arguments match the +matchers in the third `EXPECT_CALL()`, but not those in the first two, +then the third expectation will be used. + +If you would rather have all calls occur in the order of the +expectations, put the `EXPECT_CALL()` statements in a block where you +define a variable of type `InSequence`: + +``` + using ::testing::_; + using ::testing::InSequence; + + { + InSequence s; + + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(bar, DoThat(_)) + .Times(2); + EXPECT_CALL(foo, DoThis(6)); + } +``` + +In this example, we expect a call to `foo.DoThis(5)`, followed by two +calls to `bar.DoThat()` where the argument can be anything, which are +in turn followed by a call to `foo.DoThis(6)`. If a call occurred +out-of-order, Google Mock will report an error. + +## Expecting Partially Ordered Calls ## + +Sometimes requiring everything to occur in a predetermined order can +lead to brittle tests. For example, we may care about `A` occurring +before both `B` and `C`, but aren't interested in the relative order +of `B` and `C`. In this case, the test should reflect our real intent, +instead of being overly constraining. + +Google Mock allows you to impose an arbitrary DAG (directed acyclic +graph) on the calls. One way to express the DAG is to use the +[After](V1_5_CheatSheet#The_After_Clause.md) clause of `EXPECT_CALL`. + +Another way is via the `InSequence()` clause (not the same as the +`InSequence` class), which we borrowed from jMock 2. It's less +flexible than `After()`, but more convenient when you have long chains +of sequential calls, as it doesn't require you to come up with +different names for the expectations in the chains. Here's how it +works: + +If we view `EXPECT_CALL()` statements as nodes in a graph, and add an +edge from node A to node B wherever A must occur before B, we can get +a DAG. We use the term "sequence" to mean a directed path in this +DAG. Now, if we decompose the DAG into sequences, we just need to know +which sequences each `EXPECT_CALL()` belongs to in order to be able to +reconstruct the orginal DAG. + +So, to specify the partial order on the expectations we need to do two +things: first to define some `Sequence` objects, and then for each +`EXPECT_CALL()` say which `Sequence` objects it is part +of. Expectations in the same sequence must occur in the order they are +written. For example, + +``` + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(foo, A()) + .InSequence(s1, s2); + EXPECT_CALL(bar, B()) + .InSequence(s1); + EXPECT_CALL(bar, C()) + .InSequence(s2); + EXPECT_CALL(foo, D()) + .InSequence(s2); +``` + +specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> +C -> D`): + +``` + +---> B + | + A ---| + | + +---> C ---> D +``` + +This means that A must occur before B and C, and C must occur before +D. There's no restriction about the order other than these. + +## Controlling When an Expectation Retires ## + +When a mock method is called, Google Mock only consider expectations +that are still active. An expectation is active when created, and +becomes inactive (aka _retires_) when a call that has to occur later +has occurred. For example, in + +``` + using ::testing::_; + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 + .Times(AnyNumber()) + .InSequence(s1, s2); + EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 + .InSequence(s1); + EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 + .InSequence(s2); +``` + +as soon as either #2 or #3 is matched, #1 will retire. If a warning +`"File too large."` is logged after this, it will be an error. + +Note that an expectation doesn't retire automatically when it's +saturated. For example, + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 +``` + +says that there will be exactly one warning with the message `"File +too large."`. If the second warning contains this message too, #2 will +match again and result in an upper-bound-violated error. + +If this is not what you want, you can ask an expectation to retire as +soon as it becomes saturated: + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 + .RetiresOnSaturation(); +``` + +Here #2 can be used only once, so if you have two warnings with the +message `"File too large."`, the first will match #2 and the second +will match #1 - there will be no error. + +# Using Actions # + +## Returning References from Mock Methods ## + +If a mock function's return type is a reference, you need to use +`ReturnRef()` instead of `Return()` to return a result: + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetBar, Bar&()); +}; +... + + MockFoo foo; + Bar bar; + EXPECT_CALL(foo, GetBar()) + .WillOnce(ReturnRef(bar)); +``` + +## Combining Actions ## + +Want to do more than one thing when a function is called? That's +fine. `DoAll()` allow you to do sequence of actions every time. Only +the return value of the last action in the sequence will be used. + +``` +using ::testing::DoAll; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Bar, bool(int n)); +}; +... + + EXPECT_CALL(foo, Bar(_)) + .WillOnce(DoAll(action_1, + action_2, + ... + action_n)); +``` + +## Mocking Side Effects ## + +Sometimes a method exhibits its effect not via returning a value but +via side effects. For example, it may change some global state or +modify an output argument. To mock side effects, in general you can +define your own action by implementing `::testing::ActionInterface`. + +If all you need to do is to change an output argument, the built-in +`SetArgumentPointee()` action is convenient: + +``` +using ::testing::SetArgumentPointee; + +class MockMutator : public Mutator { + public: + MOCK_METHOD2(Mutate, void(bool mutate, int* value)); + ... +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, Mutate(true, _)) + .WillOnce(SetArgumentPointee<1>(5)); +``` + +In this example, when `mutator.Mutate()` is called, we will assign 5 +to the `int` variable pointed to by argument #1 +(0-based). + +`SetArgumentPointee()` conveniently makes an internal copy of the +value you pass to it, removing the need to keep the value in scope and +alive. The implication however is that the value must have a copy +constructor and assignment operator. + +If the mock method also needs to return a value as well, you can chain +`SetArgumentPointee()` with `Return()` using `DoAll()`: + +``` +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgumentPointee; + +class MockMutator : public Mutator { + public: + ... + MOCK_METHOD1(MutateInt, bool(int* value)); +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, MutateInt(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(5), + Return(true))); +``` + +If the output argument is an array, use the +`SetArrayArgument(first, last)` action instead. It copies the +elements in source range `[first, last)` to the array pointed to by +the `N`-th (0-based) argument: + +``` +using ::testing::NotNull; +using ::testing::SetArrayArgument; + +class MockArrayMutator : public ArrayMutator { + public: + MOCK_METHOD2(Mutate, void(int* values, int num_values)); + ... +}; +... + + MockArrayMutator mutator; + int values[5] = { 1, 2, 3, 4, 5 }; + EXPECT_CALL(mutator, Mutate(NotNull(), 5)) + .WillOnce(SetArrayArgument<0>(values, values + 5)); +``` + +This also works when the argument is an output iterator: + +``` +using ::testing::_; +using ::testing::SeArrayArgument; + +class MockRolodex : public Rolodex { + public: + MOCK_METHOD1(GetNames, void(std::back_insert_iterator >)); + ... +}; +... + + MockRolodex rolodex; + vector names; + names.push_back("George"); + names.push_back("John"); + names.push_back("Thomas"); + EXPECT_CALL(rolodex, GetNames(_)) + .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); +``` + +## Changing a Mock Object's Behavior Based on the State ## + +If you expect a call to change the behavior of a mock object, you can use `::testing::InSequence` to specify different behaviors before and after the call: + +``` +using ::testing::InSequence; +using ::testing::Return; + +... + { + InSequence seq; + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(my_mock, Flush()); + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(false)); + } + my_mock.FlushIfDirty(); +``` + +This makes `my_mock.IsDirty()` return `true` before `my_mock.Flush()` is called and return `false` afterwards. + +If the behavior change is more complex, you can store the effects in a variable and make a mock method get its return value from that variable: + +``` +using ::testing::_; +using ::testing::SaveArg; +using ::testing::Return; + +ACTION_P(ReturnPointee, p) { return *p; } +... + int previous_value = 0; + EXPECT_CALL(my_mock, GetPrevValue()) + .WillRepeatedly(ReturnPointee(&previous_value)); + EXPECT_CALL(my_mock, UpdateValue(_)) + .WillRepeatedly(SaveArg<0>(&previous_value)); + my_mock.DoSomethingToUpdateValue(); +``` + +Here `my_mock.GetPrevValue()` will always return the argument of the last `UpdateValue()` call. + +## Setting the Default Value for a Return Type ## + +If a mock method's return type is a built-in C++ type or pointer, by +default it will return 0 when invoked. You only need to specify an +action if this default value doesn't work for you. + +Sometimes, you may want to change this default value, or you may want +to specify a default value for types Google Mock doesn't know +about. You can do this using the `::testing::DefaultValue` class +template: + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD0(CalculateBar, Bar()); +}; +... + + Bar default_bar; + // Sets the default return value for type Bar. + DefaultValue::Set(default_bar); + + MockFoo foo; + + // We don't need to specify an action here, as the default + // return value works for us. + EXPECT_CALL(foo, CalculateBar()); + + foo.CalculateBar(); // This should return default_bar. + + // Unsets the default return value. + DefaultValue::Clear(); +``` + +Please note that changing the default value for a type can make you +tests hard to understand. We recommend you to use this feature +judiciously. For example, you may want to make sure the `Set()` and +`Clear()` calls are right next to the code that uses your mock. + +## Setting the Default Actions for a Mock Method ## + +You've learned how to change the default value of a given +type. However, this may be too coarse for your purpose: perhaps you +have two mock methods with the same return type and you want them to +have different behaviors. The `ON_CALL()` macro allows you to +customize your mock's behavior at the method level: + +``` +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Gt; +using ::testing::Return; +... + ON_CALL(foo, Sign(_)) + .WillByDefault(Return(-1)); + ON_CALL(foo, Sign(0)) + .WillByDefault(Return(0)); + ON_CALL(foo, Sign(Gt(0))) + .WillByDefault(Return(1)); + + EXPECT_CALL(foo, Sign(_)) + .Times(AnyNumber()); + + foo.Sign(5); // This should return 1. + foo.Sign(-9); // This should return -1. + foo.Sign(0); // This should return 0. +``` + +As you may have guessed, when there are more than one `ON_CALL()` +statements, the news order take precedence over the older ones. In +other words, the **last** one that matches the function arguments will +be used. This matching order allows you to set up the common behavior +in a mock object's constructor or the test fixture's set-up phase and +specialize the mock's behavior later. + +## Using Functions/Methods/Functors as Actions ## + +If the built-in actions don't suit you, you can easily use an existing +function, method, or functor as an action: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(Sum, int(int x, int y)); + MOCK_METHOD1(ComplexJob, bool(int x)); +}; + +int CalculateSum(int x, int y) { return x + y; } + +class Helper { + public: + bool ComplexJob(int x); +}; +... + + MockFoo foo; + Helper helper; + EXPECT_CALL(foo, Sum(_, _)) + .WillOnce(Invoke(CalculateSum)); + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(Invoke(&helper, &Helper::ComplexJob)); + + foo.Sum(5, 6); // Invokes CalculateSum(5, 6). + foo.ComplexJob(10); // Invokes helper.ComplexJob(10); +``` + +The only requirement is that the type of the function, etc must be +_compatible_ with the signature of the mock function, meaning that the +latter's arguments can be implicitly converted to the corresponding +arguments of the former, and the former's return type can be +implicitly converted to that of the latter. So, you can invoke +something whose type is _not_ exactly the same as the mock function, +as long as it's safe to do so - nice, huh? + +## Invoking a Function/Method/Functor Without Arguments ## + +`Invoke()` is very useful for doing actions that are more complex. It +passes the mock function's arguments to the function or functor being +invoked such that the callee has the full context of the call to work +with. If the invoked function is not interested in some or all of the +arguments, it can simply ignore them. + +Yet, a common pattern is that a test author wants to invoke a function +without the arguments of the mock function. `Invoke()` allows her to +do that using a wrapper function that throws away the arguments before +invoking an underlining nullary function. Needless to say, this can be +tedious and obscures the intent of the test. + +`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except +that it doesn't pass the mock function's arguments to the +callee. Here's an example: + +``` +using ::testing::_; +using ::testing::InvokeWithoutArgs; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(ComplexJob, bool(int n)); +}; + +bool Job1() { ... } +... + + MockFoo foo; + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(InvokeWithoutArgs(Job1)); + + foo.ComplexJob(10); // Invokes Job1(). +``` + +## Invoking an Argument of the Mock Function ## + +Sometimes a mock function will receive a function pointer or a functor +(in other words, a "callable") as an argument, e.g. + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, bool(int n, bool (*fp)(int))); +}; +``` + +and you may want to invoke this callable argument: + +``` +using ::testing::_; +... + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(...); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +Arghh, you need to refer to a mock function argument but C++ has no +lambda (yet), so you have to define your own action. :-( Or do you +really? + +Well, Google Mock has an action to solve _exactly_ this problem: + +``` + InvokeArgument(arg_1, arg_2, ..., arg_m) +``` + +will invoke the `N`-th (0-based) argument the mock function receives, +with `arg_1`, `arg_2`, ..., and `arg_m`. No matter if the argument is +a function pointer or a functor, Google Mock handles them both. + +With that, you could write: + +``` +using ::testing::_; +using ::testing::InvokeArgument; +... + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(InvokeArgument<1>(5)); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +What if the callable takes an argument by reference? No problem - just +wrap it inside `ByRef()`: + +``` +... + MOCK_METHOD1(Bar, bool(bool (*fp)(int, const Helper&))); +... +using ::testing::_; +using ::testing::ByRef; +using ::testing::InvokeArgument; +... + + MockFoo foo; + Helper helper; + ... + EXPECT_CALL(foo, Bar(_)) + .WillOnce(InvokeArgument<0>(5, ByRef(helper))); + // ByRef(helper) guarantees that a reference to helper, not a copy of it, + // will be passed to the callable. +``` + +What if the callable takes an argument by reference and we do **not** +wrap the argument in `ByRef()`? Then `InvokeArgument()` will _make a +copy_ of the argument, and pass a _reference to the copy_, instead of +a reference to the original value, to the callable. This is especially +handy when the argument is a temporary value: + +``` +... + MOCK_METHOD1(DoThat, bool(bool (*f)(const double& x, const string& s))); +... +using ::testing::_; +using ::testing::InvokeArgument; +... + + MockFoo foo; + ... + EXPECT_CALL(foo, DoThat(_)) + .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); + // Will execute (*f)(5.0, string("Hi")), where f is the function pointer + // DoThat() receives. Note that the values 5.0 and string("Hi") are + // temporary and dead once the EXPECT_CALL() statement finishes. Yet + // it's fine to perform this action later, since a copy of the values + // are kept inside the InvokeArgument action. +``` + +## Ignoring an Action's Result ## + +Sometimes you have an action that returns _something_, but you need an +action that returns `void` (perhaps you want to use it in a mock +function that returns `void`, or perhaps it needs to be used in +`DoAll()` and it's not the last in the list). `IgnoreResult()` lets +you do that. For example: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; + +int Process(const MyData& data); +string DoSomething(); + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Abc, void(const MyData& data)); + MOCK_METHOD0(Xyz, bool()); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, Abc(_)) + // .WillOnce(Invoke(Process)); + // The above line won't compile as Process() returns int but Abc() needs + // to return void. + .WillOnce(IgnoreResult(Invoke(Process))); + + EXPECT_CALL(foo, Xyz()) + .WillOnce(DoAll(IgnoreResult(Invoke(DoSomething)), + // Ignores the string DoSomething() returns. + Return(true))); +``` + +Note that you **cannot** use `IgnoreResult()` on an action that already +returns `void`. Doing so will lead to ugly compiler errors. + +## Selecting an Action's Arguments ## + +Say you have a mock function `Foo()` that takes seven arguments, and +you have a custom action that you want to invoke when `Foo()` is +called. Trouble is, the custom action only wants three arguments: + +``` +using ::testing::_; +using ::testing::Invoke; +... + MOCK_METHOD7(Foo, bool(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight)); +... + +bool IsVisibleInQuadrant1(bool visible, int x, int y) { + return visible && x >= 0 && y >= 0; +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( +``` + +To please the compiler God, you can to define an "adaptor" that has +the same signature as `Foo()` and calls the custom action with the +right arguments: + +``` +using ::testing::_; +using ::testing::Invoke; + +bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight) { + return IsVisibleInQuadrant1(visible, x, y); +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. +``` + +But isn't this awkward? + +Google Mock provides a generic _action adaptor_, so you can spend your +time minding more important business than writing your own +adaptors. Here's the syntax: + +``` + WithArgs(action) +``` + +creates an action that passes the arguments of the mock function at +the given indices (0-based) to the inner `action` and performs +it. Using `WithArgs`, our original example can be written as: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::WithArgs; +... + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); + // No need to define your own adaptor. +``` + +For better readability, Google Mock also gives you: + + * `WithoutArgs(action)` when the inner `action` takes _no_ argument, and + * `WithArg(action)` (no `s` after `Arg`) when the inner `action` takes _one_ argument. + +As you may have realized, `InvokeWithoutArgs(...)` is just syntactic +sugar for `WithoutArgs(Inovke(...))`. + +Here are more tips: + + * The inner action used in `WithArgs` and friends does not have to be `Invoke()` -- it can be anything. + * You can repeat an argument in the argument list if necessary, e.g. `WithArgs<2, 3, 3, 5>(...)`. + * You can change the order of the arguments, e.g. `WithArgs<3, 2, 1>(...)`. + * The types of the selected arguments do _not_ have to match the signature of the inner action exactly. It works as long as they can be implicitly converted to the corresponding arguments of the inner action. For example, if the 4-th argument of the mock function is an `int` and `my_action` takes a `double`, `WithArg<4>(my_action)` will work. + +## Ignoring Arguments in Action Functions ## + +The selecting-an-action's-arguments recipe showed us one way to make a +mock function and an action with incompatible argument lists fit +together. The downside is that wrapping the action in +`WithArgs<...>()` can get tedious for people writing the tests. + +If you are defining a function, method, or functor to be used with +`Invoke*()`, and you are not interested in some of its arguments, an +alternative to `WithArgs` is to declare the uninteresting arguments as +`Unused`. This makes the definition less cluttered and less fragile in +case the types of the uninteresting arguments change. It could also +increase the chance the action function can be reused. For example, +given + +``` + MOCK_METHOD3(Foo, double(const string& label, double x, double y)); + MOCK_METHOD3(Bar, double(int index, double x, double y)); +``` + +instead of + +``` +using ::testing::_; +using ::testing::Invoke; + +double DistanceToOriginWithLabel(const string& label, double x, double y) { + return sqrt(x*x + y*y); +} + +double DistanceToOriginWithIndex(int index, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOriginWithLabel)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOriginWithIndex)); +``` + +you could write + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Unused; + +double DistanceToOrigin(Unused, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOrigin)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOrigin)); +``` + +## Sharing Actions ## + +Just like matchers, a Google Mock action object consists of a pointer +to a ref-counted implementation object. Therefore copying actions is +also allowed and very efficient. When the last action that references +the implementation object dies, the implementation object will be +deleted. + +If you have some complex action that you want to use again and again, +you may not have to build it from scratch everytime. If the action +doesn't have an internal state (i.e. if it always does the same thing +no matter how many times it has been called), you can assign it to an +action variable and use that variable repeatedly. For example: + +``` + Action set_flag = DoAll(SetArgumentPointee<0>(5), + Return(true)); + ... use set_flag in .WillOnce() and .WillRepeatedly() ... +``` + +However, if the action has its own state, you may be surprised if you +share the action object. Suppose you have an action factory +`IncrementCounter(init)` which creates an action that increments and +returns a counter whose initial value is `init`, using two actions +created from the same expression and using a shared action will +exihibit different behaviors. Example: + +``` + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(IncrementCounter(0)); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(IncrementCounter(0)); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 1 - Blah() uses a different + // counter than Bar()'s. +``` + +versus + +``` + Action increment = IncrementCounter(0); + + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(increment); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(increment); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 3 - the counter is shared. +``` + +# Misc Recipes on Using Google Mock # + +## Forcing a Verification ## + +When it's being destoyed, your friendly mock object will automatically +verify that all expectations on it have been satisfied, and will +generate [Google Test](http://code.google.com/p/googletest/) failures +if not. This is convenient as it leaves you with one less thing to +worry about. That is, unless you are not sure if your mock object will +be destoyed. + +How could it be that your mock object won't eventually be destroyed? +Well, it might be created on the heap and owned by the code you are +testing. Suppose there's a bug in that code and it doesn't delete the +mock object properly - you could end up with a passing test when +there's actually a bug. + +Using a heap checker is a good idea and can alleviate the concern, but +its implementation may not be 100% reliable. So, sometimes you do want +to _force_ Google Mock to verify a mock object before it is +(hopefully) destructed. You can do this with +`Mock::VerifyAndClearExpectations(&mock_object)`: + +``` +TEST(MyServerTest, ProcessesRequest) { + using ::testing::Mock; + + MockFoo* const foo = new MockFoo; + EXPECT_CALL(*foo, ...)...; + // ... other expectations ... + + // server now owns foo. + MyServer server(foo); + server.ProcessRequest(...); + + // In case that server's destructor will forget to delete foo, + // this will verify the expectations anyway. + Mock::VerifyAndClearExpectations(foo); +} // server is destroyed when it goes out of scope here. +``` + +**Tip:** The `Mock::VerifyAndClearExpectations()` function returns a +`bool` to indicate whether the verification was successful (`true` for +yes), so you can wrap that function call inside a `ASSERT_TRUE()` if +there is no point going further when the verification has failed. + +## Using Check Points ## + +Sometimes you may want to "reset" a mock object at various check +points in your test: at each check point, you verify that all existing +expectations on the mock object have been satisfied, and then you set +some new expectations on it as if it's newly created. This allows you +to work with a mock object in "phases" whose sizes are each +manageable. + +One such scenario is that in your test's `SetUp()` function, you may +want to put the object you are testing into a certain state, with the +help from a mock object. Once in the desired state, you want to clear +all expectations on the mock, such that in the `TEST_F` body you can +set fresh expectations on it. + +As you may have figured out, the `Mock::VerifyAndClearExpectations()` +function we saw in the previous recipe can help you here. Or, if you +are using `ON_CALL()` to set default actions on the mock object and +want to clear the default actions as well, use +`Mock::VerifyAndClear(&mock_object)` instead. This function does what +`Mock::VerifyAndClearExpectations(&mock_object)` does and returns the +same `bool`, **plus** it clears the `ON_CALL()` statements on +`mock_object` too. + +Another trick you can use to achieve the same effect is to put the +expectations in sequences and insert calls to a dummy "check-point" +function at specific places. Then you can verify that the mock +function calls do happen at the right time. For example, if you are +exercising code: + +``` +Foo(1); +Foo(2); +Foo(3); +``` + +and want to verify that `Foo(1)` and `Foo(3)` both invoke +`mock.Bar("a")`, but `Foo(2)` doesn't invoke anything. You can write: + +``` +using ::testing::MockFunction; + +TEST(FooTest, InvokesBarCorrectly) { + MyMock mock; + // Class MockFunction has exactly one mock method. It is named + // Call() and has type F. + MockFunction check; + { + InSequence s; + + EXPECT_CALL(mock, Bar("a")); + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(mock, Bar("a")); + } + Foo(1); + check.Call("1"); + Foo(2); + check.Call("2"); + Foo(3); +} +``` + +The expectation spec says that the first `Bar("a")` must happen before +check point "1", the second `Bar("a")` must happen after check point "2", +and nothing should happen between the two check points. The explicit +check points make it easy to tell which `Bar("a")` is called by which +call to `Foo()`. + +## Mocking Destructors ## + +Sometimes you want to make sure a mock object is destructed at the +right time, e.g. after `bar->A()` is called but before `bar->B()` is +called. We already know that you can specify constraints on the order +of mock function calls, so all we need to do is to mock the destructor +of the mock function. + +This sounds simple, except for one problem: a destructor is a special +function with special syntax and special semantics, and the +`MOCK_METHOD0` macro doesn't work for it: + +``` + MOCK_METHOD0(~MockFoo, void()); // Won't compile! +``` + +The good news is that you can use a simple pattern to achieve the same +effect. First, add a mock function `Die()` to your mock class and call +it in the destructor, like this: + +``` +class MockFoo : public Foo { + ... + // Add the following two lines to the mock class. + MOCK_METHOD0(Die, void()); + virtual ~MockFoo() { Die(); } +}; +``` + +(If the name `Die()` clashes with an existing symbol, choose another +name.) Now, we have translated the problem of testing when a `MockFoo` +object dies to testing when its `Die()` method is called: + +``` + MockFoo* foo = new MockFoo; + MockBar* bar = new MockBar; + ... + { + InSequence s; + + // Expects *foo to die after bar->A() and before bar->B(). + EXPECT_CALL(*bar, A()); + EXPECT_CALL(*foo, Die()); + EXPECT_CALL(*bar, B()); + } +``` + +And that's that. + +## Using Google Mock and Threads ## + +**IMPORTANT NOTE:** What we describe in this recipe is **NOT** true yet, +as Google Mock is not currently thread-safe. However, all we need to +make it thread-safe is to implement some synchronization operations in +`` - and then the information below will +become true. + +In a **unit** test, it's best if you could isolate and test a piece of +code in a single-threaded context. That avoids race conditions and +dead locks, and makes debugging your test much easier. + +Yet many programs are multi-threaded, and sometimes to test something +we need to pound on it from more than one thread. Google Mock works +for this purpose too. + +Remember the steps for using a mock: + + 1. Create a mock object `foo`. + 1. Set its default actions and expectations using `ON_CALL()` and `EXPECT_CALL()`. + 1. The code under test calls methods of `foo`. + 1. Optionally, verify and reset the mock. + 1. Destroy the mock yourself, or let the code under test destroy it. The destructor will automatically verify it. + +If you follow the following simple rules, your mocks and threads can +live happily togeter: + + * Execute your _test code_ (as opposed to the code being tested) in _one_ thread. This makes your test easy to follow. + * Obviously, you can do step #1 without locking. + * When doing step #2 and #5, make sure no other thread is accessing `foo`. Obvious too, huh? + * #3 and #4 can be done either in one thread or in multiple threads - anyway you want. Google Mock takes care of the locking, so you don't have to do any - unless required by your test logic. + +If you violate the rules (for example, if you set expectations on a +mock while another thread is calling its methods), you get undefined +behavior. That's not fun, so don't do it. + +Google Mock guarantees that the action for a mock function is done in +the same thread that called the mock function. For example, in + +``` + EXPECT_CALL(mock, Foo(1)) + .WillOnce(action1); + EXPECT_CALL(mock, Foo(2)) + .WillOnce(action2); +``` + +if `Foo(1)` is called in thread 1 and `Foo(2)` is called in thread 2, +Google Mock will execute `action1` in thread 1 and `action2` in thread +2. + +Google Mock does _not_ impose a sequence on actions performed in +different threads (doing so may create deadlocks as the actions may +need to cooperate). This means that the execution of `action1` and +`action2` in the above example _may_ interleave. If this is a problem, +you should add proper synchronization logic to `action1` and `action2` +to make the test thread-safe. + + +Also, remember that `DefaultValue` is a global resource that +potentially affects _all_ living mock objects in your +program. Naturally, you won't want to mess with it from multiple +threads or when there still are mocks in action. + +## Controlling How Much Information Google Mock Prints ## + +When Google Mock sees something that has the potential of being an +error (e.g. a mock function with no expectation is called, a.k.a. an +uninteresting call, which is allowed but perhaps you forgot to +explicitly ban the call), it prints some warning messages, including +the arguments of the function and the return value. Hopefully this +will remind you to take a look and see if there is indeed a problem. + +Sometimes you are confident that your tests are correct and may not +appreciate such friendly messages. Some other times, you are debugging +your tests or learning about the behavior of the code you are testing, +and wish you could observe every mock call that happens (including +argument values and the return value). Clearly, one size doesn't fit +all. + +You can control how much Google Mock tells you using the +`--gmock_verbose=LEVEL` command-line flag, where `LEVEL` is a string +with three possible values: + + * `info`: Google Mock will print all informational messages, warnings, and errors (most verbose). At this setting, Google Mock will also log any calls to the `ON_CALL/EXPECT_CALL` macros. + * `warning`: Google Mock will print both warnings and errors (less verbose). This is the default. + * `error`: Google Mock will print errors only (least verbose). + +Alternatively, you can adjust the value of that flag from within your +tests like so: + +``` + ::testing::FLAGS_gmock_verbose = "error"; +``` + +Now, judiciously use the right flag to enable Google Mock serve you better! + +## Running Tests in Emacs ## + +If you build and run your tests in Emacs, the source file locations of +Google Mock and [Google Test](http://code.google.com/p/googletest/) +errors will be highlighted. Just press `` on one of them and +you'll be taken to the offending line. Or, you can just type `C-x `` +to jump to the next error. + +To make it even easier, you can add the following lines to your +`~/.emacs` file: + +``` +(global-set-key "\M-m" 'compile) ; m is for make +(global-set-key [M-down] 'next-error) +(global-set-key [M-up] '(lambda () (interactive) (next-error -1))) +``` + +Then you can type `M-m` to start a build, or `M-up`/`M-down` to move +back and forth between errors. + +## Fusing Google Mock Source Files ## + +Google Mock's implementation consists of dozens of files (excluding +its own tests). Sometimes you may want them to be packaged up in +fewer files instead, such that you can easily copy them to a new +machine and start hacking there. For this we provide an experimental +Python script `fuse_gmock_files.py` in the `scripts/` directory +(starting with release 1.2.0). Assuming you have Python 2.4 or above +installed on your machine, just go to that directory and run +``` +python fuse_gmock_files.py OUTPUT_DIR +``` + +and you should see an `OUTPUT_DIR` directory being created with files +`gtest/gtest.h`, `gmock/gmock.h`, and `gmock-gtest-all.cc` in it. +These three files contain everything you need to use Google Mock (and +Google Test). Just copy them to anywhere you want and you are ready +to write tests and use mocks. You can use the +[scrpts/test/Makefile](http://code.google.com/p/googlemock/source/browse/trunk/scripts/test/Makefile) file as an example on how to compile your tests +against them. + +# Extending Google Mock # + +## Writing New Matchers Quickly ## + +The `MATCHER*` family of macros can be used to define custom matchers +easily. The syntax: + +``` +MATCHER(name, "description string") { statements; } +``` + +will define a matcher with the given name that executes the +statements, which must return a `bool` to indicate if the match +succeeds. Inside the statements, you can refer to the value being +matched by `arg`, and refer to its type by `arg_type`. + +The description string documents what the matcher does, and is used to +generate the failure message when the match fails. Since a +`MATCHER()` is usually defined in a header file shared by multiple C++ +source files, we require the description to be a C-string _literal_ to +avoid possible side effects. It can be empty (`""`), in which case +Google Mock will use the sequence of words in the matcher name as the +description. + +For example: +``` +MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } +``` +allows you to write +``` + // Expects mock_foo.Bar(n) to be called where n is divisible by 7. + EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); +``` +or, +``` + // Verifies that the value of some_expression is divisible by 7. + EXPECT_THAT(some_expression, IsDivisibleBy7()); +``` +If the above assertion fails, it will print something like: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 +``` +where the description `"is divisible by 7"` is automatically calculated from the +matcher name `IsDivisibleBy7`. + +Optionally, you can stream additional information to a hidden argument +named `result_listener` to explain the match result. For example, a +better definition of `IsDivisibleBy7` is: +``` +MATCHER(IsDivisibleBy7, "") { + if ((arg % 7) == 0) + return true; + + *result_listener << "the remainder is " << (arg % 7); + return false; +} +``` + +With this definition, the above assertion will give a better message: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 (the remainder is 6) +``` + +You should let `MatchAndExplain()` print _any additional information_ +that can help a user understand the match result. Note that it should +explain why the match succeeds in case of a success (unless it's +obvious) - this is useful when the matcher is used inside +`Not()`. There is no need to print the argument value itself, as +Google Mock already prints it for you. + +**Notes:** + + 1. The type of the value being matched (`arg_type`) is determined by the context in which you use the matcher and is supplied to you by the compiler, so you don't need to worry about declaring it (nor can you). This allows the matcher to be polymorphic. For example, `IsDivisibleBy7()` can be used to match any type where the value of `(arg % 7) == 0` can be implicitly converted to a `bool`. In the `Bar(IsDivisibleBy7())` example above, if method `Bar()` takes an `int`, `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be `unsigned long`; and so on. + 1. Google Mock doesn't guarantee when or how many times a matcher will be invoked. Therefore the matcher logic must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). This requirement must be satisfied no matter how you define the matcher (e.g. using one of the methods described in the following recipes). In particular, a matcher can never call a mock function, as that will affect the state of the mock object and Google Mock. + +## Writing New Parameterized Matchers Quickly ## + +Sometimes you'll want to define a matcher that has parameters. For that you +can use the macro: +``` +MATCHER_P(name, param_name, "description string") { statements; } +``` + +For example: +``` +MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +``` +will allow you to write: +``` + EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +``` +which may lead to this message (assuming `n` is 10): +``` + Value of: Blah("a") + Expected: has absolute value 10 + Actual: -9 +``` + +Note that both the matcher description and its parameter are +printed, making the message human-friendly. + +In the matcher definition body, you can write `foo_type` to +reference the type of a parameter named `foo`. For example, in the +body of `MATCHER_P(HasAbsoluteValue, value)` above, you can write +`value_type` to refer to the type of `value`. + +Google Mock also provides `MATCHER_P2`, `MATCHER_P3`, ..., up to +`MATCHER_P10` to support multi-parameter matchers: +``` +MATCHER_Pk(name, param_1, ..., param_k, "description string") { statements; } +``` + +Please note that the custom description string is for a particular +**instance** of the matcher, where the parameters have been bound to +actual values. Therefore usually you'll want the parameter values to +be part of the description. Google Mock lets you do that using +Python-style interpolations. The following syntaxes are supported +currently: + +| `%%` | a single `%` character | +|:-----|:-----------------------| +| `%(*)s` | all parameters of the matcher printed as a tuple | +| `%(foo)s` | value of the matcher parameter named `foo` | + +For example, +``` + MATCHER_P2(InClosedRange, low, hi, "is in range [%(low)s, %(hi)s]") { + return low <= arg && arg <= hi; + } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the message: +``` + Expected: is in range [4, 6] +``` + +If you specify `""` as the description, the failure message will +contain the sequence of words in the matcher name followed by the +parameter values printed as a tuple. For example, +``` + MATCHER_P2(InClosedRange, low, hi, "") { ... } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the text: +``` + Expected: in closed range (4, 6) +``` + +For the purpose of typing, you can view +``` +MATCHER_Pk(Foo, p1, ..., pk, "description string") { ... } +``` +as shorthand for +``` +template +FooMatcherPk +Foo(p1_type p1, ..., pk_type pk) { ... } +``` + +When you write `Foo(v1, ..., vk)`, the compiler infers the types of +the parameters `v1`, ..., and `vk` for you. If you are not happy with +the result of the type inference, you can specify the types by +explicitly instantiating the template, as in `Foo(5, false)`. +As said earlier, you don't get to (or need to) specify +`arg_type` as that's determined by the context in which the matcher +is used. + +You can assign the result of expression `Foo(p1, ..., pk)` to a +variable of type `FooMatcherPk`. This can be +useful when composing matchers. Matchers that don't have a parameter +or have only one parameter have special types: you can assign `Foo()` +to a `FooMatcher`-typed variable, and assign `Foo(p)` to a +`FooMatcherP`-typed variable. + +While you can instantiate a matcher template with reference types, +passing the parameters by pointer usually makes your code more +readable. If, however, you still want to pass a parameter by +reference, be aware that in the failure message generated by the +matcher you will see the value of the referenced object but not its +address. + +You can overload matchers with different numbers of parameters: +``` +MATCHER_P(Blah, a, "description string 1") { ... } +MATCHER_P2(Blah, a, b, "description string 2") { ... } +``` + +While it's tempting to always use the `MATCHER*` macros when defining +a new matcher, you should also consider implementing +`MatcherInterface` or using `MakePolymorphicMatcher()` instead (see +the recipes that follow), especially if you need to use the matcher a +lot. While these approaches require more work, they give you more +control on the types of the value being matched and the matcher +parameters, which in general leads to better compiler error messages +that pay off in the long run. They also allow overloading matchers +based on parameter types (as opposed to just based on the number of +parameters). + +## Writing New Monomorphic Matchers ## + +A matcher of argument type `T` implements +`::testing::MatcherInterface` and does two things: it tests whether a +value of type `T` matches the matcher, and can describe what kind of +values it matches. The latter ability is used for generating readable +error messages when expectations are violated. + +The interface looks like this: + +``` +class MatchResultListener { + public: + ... + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x); + + // Returns the underlying ostream. + ::std::ostream* stream(); +}; + +template +class MatcherInterface { + public: + virtual ~MatcherInterface(); + + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Describes this matcher to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. + virtual void DescribeNegationTo(::std::ostream* os) const; +}; +``` + +If you need a custom matcher but `Truly()` is not a good option (for +example, you may not be happy with the way `Truly(predicate)` +describes itself, or you may want your matcher to be polymorphic as +`Eq(value)` is), you can define a matcher to do whatever you want in +two steps: first implement the matcher interface, and then define a +factory function to create a matcher instance. The second step is not +strictly needed but it makes the syntax of using the matcher nicer. + +For example, you can define a matcher to test whether an `int` is +divisible by 7 and then use it like this: +``` +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, MatchResultListener* listener) const { + return (n % 7) == 0; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "is divisible by 7"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "is not divisible by 7"; + } +}; + +inline Matcher DivisibleBy7() { + return MakeMatcher(new DivisibleBy7Matcher); +} +... + + EXPECT_CALL(foo, Bar(DivisibleBy7())); +``` + +You may improve the matcher message by streaming additional +information to the `listener` argument in `MatchAndExplain()`: + +``` +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, + MatchResultListener* listener) const { + const int remainder = n % 7; + if (remainder != 0) { + *listener << "the remainder is " << remainder; + } + return remainder == 0; + } + ... +}; +``` + +Then, `EXPECT_THAT(x, DivisibleBy7());` may general a message like this: +``` +Value of: x +Expected: is divisible by 7 + Actual: 23 (the remainder is 2) +``` + +## Writing New Polymorphic Matchers ## + +You've learned how to write your own matchers in the previous +recipe. Just one problem: a matcher created using `MakeMatcher()` only +works for one particular type of arguments. If you want a +_polymorphic_ matcher that works with arguments of several types (for +instance, `Eq(x)` can be used to match a `value` as long as `value` == +`x` compiles -- `value` and `x` don't have to share the same type), +you can learn the trick from `` but it's a bit +involved. + +Fortunately, most of the time you can define a polymorphic matcher +easily with the help of `MakePolymorphicMatcher()`. Here's how you can +define `NotNull()` as an example: + +``` +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +using ::testing::NotNull; +using ::testing::PolymorphicMatcher; + +class NotNullMatcher { + public: + // To implement a polymorphic matcher, first define a COPYABLE class + // that has three members MatchAndExplain(), DescribeTo(), and + // DescribeNegationTo(), like the following. + + // In this example, we want to use NotNull() with any pointer, so + // MatchAndExplain() accepts a pointer of any type as its first argument. + // In general, you can define MatchAndExplain() as an ordinary method or + // a method template, or even overload it. + template + bool MatchAndExplain(T* p, + MatchResultListener* /* listener */) const { + return p != NULL; + } + + // Describes the property of a value matching this matcher. + void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } + + // Describes the property of a value NOT matching this matcher. + void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } +}; + +// To construct a polymorphic matcher, pass an instance of the class +// to MakePolymorphicMatcher(). Note the return type. +inline PolymorphicMatcher NotNull() { + return MakePolymorphicMatcher(NotNullMatcher()); +} +... + + EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. +``` + +**Note:** Your polymorphic matcher class does **not** need to inherit from +`MatcherInterface` or any other class, and its methods do **not** need +to be virtual. + +Like in a monomorphic matcher, you may explain the match result by +streaming additional information to the `listener` argument in +`MatchAndExplain()`. + +## Writing New Cardinalities ## + +A cardinality is used in `Times()` to tell Google Mock how many times +you expect a call to occur. It doesn't have to be exact. For example, +you can say `AtLeast(5)` or `Between(2, 4)`. + +If the built-in set of cardinalities doesn't suit you, you are free to +define your own by implementing the following interface (in namespace +`testing`): + +``` +class CardinalityInterface { + public: + virtual ~CardinalityInterface(); + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; +``` + +For example, to specify that a call must occur even number of times, +you can write + +``` +using ::testing::Cardinality; +using ::testing::CardinalityInterface; +using ::testing::MakeCardinality; + +class EvenNumberCardinality : public CardinalityInterface { + public: + virtual bool IsSatisfiedByCallCount(int call_count) const { + return (call_count % 2) == 0; + } + + virtual bool IsSaturatedByCallCount(int call_count) const { + return false; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "called even number of times"; + } +}; + +Cardinality EvenNumber() { + return MakeCardinality(new EvenNumberCardinality); +} +... + + EXPECT_CALL(foo, Bar(3)) + .Times(EvenNumber()); +``` + +## Writing New Actions Quickly ## + +If the built-in actions don't work for you, and you find it +inconvenient to use `Invoke()`, you can use a macro from the `ACTION*` +family to quickly define a new action that can be used in your code as +if it's a built-in action. + +By writing +``` +ACTION(name) { statements; } +``` +in a namespace scope (i.e. not inside a class or function), you will +define an action with the given name that executes the statements. +The value returned by `statements` will be used as the return value of +the action. Inside the statements, you can refer to the K-th +(0-based) argument of the mock function as `argK`. For example: +``` +ACTION(IncrementArg1) { return ++(*arg1); } +``` +allows you to write +``` +... WillOnce(IncrementArg1()); +``` + +Note that you don't need to specify the types of the mock function +arguments. Rest assured that your code is type-safe though: +you'll get a compiler error if `*arg1` doesn't support the `++` +operator, or if the type of `++(*arg1)` isn't compatible with the mock +function's return type. + +Another example: +``` +ACTION(Foo) { + (*arg2)(5); + Blah(); + *arg1 = 0; + return arg0; +} +``` +defines an action `Foo()` that invokes argument #2 (a function pointer) +with 5, calls function `Blah()`, sets the value pointed to by argument +#1 to 0, and returns argument #0. + +For more convenience and flexibility, you can also use the following +pre-defined symbols in the body of `ACTION`: + +| `argK_type` | The type of the K-th (0-based) argument of the mock function | +|:------------|:-------------------------------------------------------------| +| `args` | All arguments of the mock function as a tuple | +| `args_type` | The type of all arguments of the mock function as a tuple | +| `return_type` | The return type of the mock function | +| `function_type` | The type of the mock function | + +For example, when using an `ACTION` as a stub action for mock function: +``` +int DoSomething(bool flag, int* ptr); +``` +we have: +| **Pre-defined Symbol** | **Is Bound To** | +|:-----------------------|:----------------| +| `arg0` | the value of `flag` | +| `arg0_type` | the type `bool` | +| `arg1` | the value of `ptr` | +| `arg1_type` | the type `int*` | +| `args` | the tuple `(flag, ptr)` | +| `args_type` | the type `std::tr1::tuple` | +| `return_type` | the type `int` | +| `function_type` | the type `int(bool, int*)` | + +## Writing New Parameterized Actions Quickly ## + +Sometimes you'll want to parameterize an action you define. For that +we have another macro +``` +ACTION_P(name, param) { statements; } +``` + +For example, +``` +ACTION_P(Add, n) { return arg0 + n; } +``` +will allow you to write +``` +// Returns argument #0 + 5. +... WillOnce(Add(5)); +``` + +For convenience, we use the term _arguments_ for the values used to +invoke the mock function, and the term _parameters_ for the values +used to instantiate an action. + +Note that you don't need to provide the type of the parameter either. +Suppose the parameter is named `param`, you can also use the +Google-Mock-defined symbol `param_type` to refer to the type of the +parameter as inferred by the compiler. For example, in the body of +`ACTION_P(Add, n)` above, you can write `n_type` for the type of `n`. + +Google Mock also provides `ACTION_P2`, `ACTION_P3`, and etc to support +multi-parameter actions. For example, +``` +ACTION_P2(ReturnDistanceTo, x, y) { + double dx = arg0 - x; + double dy = arg1 - y; + return sqrt(dx*dx + dy*dy); +} +``` +lets you write +``` +... WillOnce(ReturnDistanceTo(5.0, 26.5)); +``` + +You can view `ACTION` as a degenerated parameterized action where the +number of parameters is 0. + +You can also easily define actions overloaded on the number of parameters: +``` +ACTION_P(Plus, a) { ... } +ACTION_P2(Plus, a, b) { ... } +``` + +## Restricting the Type of an Argument or Parameter in an ACTION ## + +For maximum brevity and reusability, the `ACTION*` macros don't ask +you to provide the types of the mock function arguments and the action +parameters. Instead, we let the compiler infer the types for us. + +Sometimes, however, we may want to be more explicit about the types. +There are several tricks to do that. For example: +``` +ACTION(Foo) { + // Makes sure arg0 can be converted to int. + int n = arg0; + ... use n instead of arg0 here ... +} + +ACTION_P(Bar, param) { + // Makes sure the type of arg1 is const char*. + ::testing::StaticAssertTypeEq(); + + // Makes sure param can be converted to bool. + bool flag = param; +} +``` +where `StaticAssertTypeEq` is a compile-time assertion in Google Test +that verifies two types are the same. + +## Writing New Action Templates Quickly ## + +Sometimes you want to give an action explicit template parameters that +cannot be inferred from its value parameters. `ACTION_TEMPLATE()` +supports that and can be viewed as an extension to `ACTION()` and +`ACTION_P*()`. + +The syntax: +``` +ACTION_TEMPLATE(ActionName, + HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), + AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +``` + +defines an action template that takes _m_ explicit template parameters +and _n_ value parameters, where _m_ is between 1 and 10, and _n_ is +between 0 and 10. `name_i` is the name of the i-th template +parameter, and `kind_i` specifies whether it's a `typename`, an +integral constant, or a template. `p_i` is the name of the i-th value +parameter. + +Example: +``` +// DuplicateArg(output) converts the k-th argument of the mock +// function to type T and copies it to *output. +ACTION_TEMPLATE(DuplicateArg, + // Note the comma between int and k: + HAS_2_TEMPLATE_PARAMS(int, k, typename, T), + AND_1_VALUE_PARAMS(output)) { + *output = T(std::tr1::get(args)); +} +``` + +To create an instance of an action template, write: +``` + ActionName(v1, ..., v_n) +``` +where the `t`s are the template arguments and the +`v`s are the value arguments. The value argument +types are inferred by the compiler. For example: +``` +using ::testing::_; +... + int n; + EXPECT_CALL(mock, Foo(_, _)) + .WillOnce(DuplicateArg<1, unsigned char>(&n)); +``` + +If you want to explicitly specify the value argument types, you can +provide additional template arguments: +``` + ActionName(v1, ..., v_n) +``` +where `u_i` is the desired type of `v_i`. + +`ACTION_TEMPLATE` and `ACTION`/`ACTION_P*` can be overloaded on the +number of value parameters, but not on the number of template +parameters. Without the restriction, the meaning of the following is +unclear: + +``` + OverloadedAction(x); +``` + +Are we using a single-template-parameter action where `bool` refers to +the type of `x`, or a two-template-parameter action where the compiler +is asked to infer the type of `x`? + +## Using the ACTION Object's Type ## + +If you are writing a function that returns an `ACTION` object, you'll +need to know its type. The type depends on the macro used to define +the action and the parameter types. The rule is relatively simple: +| **Given Definition** | **Expression** | **Has Type** | +|:---------------------|:---------------|:-------------| +| `ACTION(Foo)` | `Foo()` | `FooAction` | +| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | +| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | +| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `FooActionP` | +| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | +| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz(bool_value, int_value)` | `FooActionP2` | +| ... | ... | ... | + +Note that we have to pick different suffixes (`Action`, `ActionP`, +`ActionP2`, and etc) for actions with different numbers of value +parameters, or the action definitions cannot be overloaded on the +number of them. + +## Writing New Monomorphic Actions ## + +While the `ACTION*` macros are very convenient, sometimes they are +inappropriate. For example, despite the tricks shown in the previous +recipes, they don't let you directly specify the types of the mock +function arguments and the action parameters, which in general leads +to unoptimized compiler error messages that can baffle unfamiliar +users. They also don't allow overloading actions based on parameter +types without jumping through some hoops. + +An alternative to the `ACTION*` macros is to implement +`::testing::ActionInterface`, where `F` is the type of the mock +function in which the action will be used. For example: + +``` +template class ActionInterface { + public: + virtual ~ActionInterface(); + + // Performs the action. Result is the return type of function type + // F, and ArgumentTuple is the tuple of arguments of F. + // + // For example, if F is int(bool, const string&), then Result would + // be int, and ArgumentTuple would be tr1::tuple. + virtual Result Perform(const ArgumentTuple& args) = 0; +}; + +using ::testing::_; +using ::testing::Action; +using ::testing::ActionInterface; +using ::testing::MakeAction; + +typedef int IncrementMethod(int*); + +class IncrementArgumentAction : public ActionInterface { + public: + virtual int Perform(const tr1::tuple& args) { + int* p = tr1::get<0>(args); // Grabs the first argument. + return *p++; + } +}; + +Action IncrementArgument() { + return MakeAction(new IncrementArgumentAction); +} +... + + EXPECT_CALL(foo, Baz(_)) + .WillOnce(IncrementArgument()); + + int n = 5; + foo.Baz(&n); // Should return 5 and change n to 6. +``` + +## Writing New Polymorphic Actions ## + +The previous recipe showed you how to define your own action. This is +all good, except that you need to know the type of the function in +which the action will be used. Sometimes that can be a problem. For +example, if you want to use the action in functions with _different_ +types (e.g. like `Return()` and `SetArgumentPointee()`). + +If an action can be used in several types of mock functions, we say +it's _polymorphic_. The `MakePolymorphicAction()` function template +makes it easy to define such an action: + +``` +namespace testing { + +template +PolymorphicAction MakePolymorphicAction(const Impl& impl); + +} // namespace testing +``` + +As an example, let's define an action that returns the second argument +in the mock function's argument list. The first step is to define an +implementation class: + +``` +class ReturnSecondArgumentAction { + public: + template + Result Perform(const ArgumentTuple& args) const { + // To get the i-th (0-based) argument, use tr1::get(args). + return tr1::get<1>(args); + } +}; +``` + +This implementation class does _not_ need to inherit from any +particular class. What matters is that it must have a `Perform()` +method template. This method template takes the mock function's +arguments as a tuple in a **single** argument, and returns the result of +the action. It can be either `const` or not, but must be invokable +with exactly one template argument, which is the result type. In other +words, you must be able to call `Perform(args)` where `R` is the +mock function's return type and `args` is its arguments in a tuple. + +Next, we use `MakePolymorphicAction()` to turn an instance of the +implementation class into the polymorphic action we need. It will be +convenient to have a wrapper for this: + +``` +using ::testing::MakePolymorphicAction; +using ::testing::PolymorphicAction; + +PolymorphicAction ReturnSecondArgument() { + return MakePolymorphicAction(ReturnSecondArgumentAction()); +} +``` + +Now, you can use this polymorphic action the same way you use the +built-in ones: + +``` +using ::testing::_; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, int(bool flag, int n)); + MOCK_METHOD3(DoThat, string(int x, const char* str1, const char* str2)); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(ReturnSecondArgument()); + EXPECT_CALL(foo, DoThat(_, _, _)) + .WillOnce(ReturnSecondArgument()); + ... + foo.DoThis(true, 5); // Will return 5. + foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". +``` + +## Teaching Google Mock How to Print Your Values ## + +When an uninteresting or unexpected call occurs, Google Mock prints +the argument values to help you debug. The `EXPECT_THAT` and +`ASSERT_THAT` assertions also print the value being validated when the +test fails. Google Mock does this using the user-extensible value +printer defined in ``. + +This printer knows how to print the built-in C++ types, native arrays, +STL containers, and any type that supports the `<<` operator. For +other types, it prints the raw bytes in the value and hope you the +user can figure it out. + +Did I say that the printer is `extensible`? That means you can teach +it to do a better job at printing your particular type than to dump +the bytes. To do that, you just need to define `<<` for your type: + +``` +#include + +namespace foo { + +class Foo { ... }; + +// It's important that the << operator is defined in the SAME +// namespace that defines Foo. C++'s look-up rules rely on that. +::std::ostream& operator<<(::std::ostream& os, const Foo& foo) { + return os << foo.DebugString(); // Whatever needed to print foo to os. +} + +} // namespace foo +``` + +Sometimes, this might not be an option. For example, your team may +consider it dangerous or bad style to have a `<<` operator for `Foo`, +or `Foo` may already have a `<<` operator that doesn't do what you +want (and you cannot change it). Don't despair though - Google Mock +gives you a second chance to get it right. Namely, you can define a +`PrintTo()` function like this: + +``` +#include + +namespace foo { + +class Foo { ... }; + +// It's important that PrintTo() is defined in the SAME +// namespace that defines Foo. C++'s look-up rules rely on that. +void PrintTo(const Foo& foo, ::std::ostream* os) { + *os << foo.DebugString(); // Whatever needed to print foo to os. +} + +} // namespace foo +``` + +What if you have both `<<` and `PrintTo()`? In this case, the latter +will override the former when Google Mock is concerned. This allows +you to customize how the value should appear in Google Mock's output +without affecting code that relies on the behavior of its `<<` +operator. + +**Note:** When printing a pointer of type `T*`, Google Mock calls +`PrintTo(T*, std::ostream* os)` instead of `operator<<(std::ostream&, T*)`. +Therefore the only way to affect how a pointer is printed by Google +Mock is to define `PrintTo()` for it. Also note that `T*` and `const T*` +are different types, so you may need to define `PrintTo()` for both. + +Why does Google Mock treat pointers specially? There are several reasons: + + * We cannot use `operator<<` to print a `signed char*` or `unsigned char*`, since it will print the pointer as a NUL-terminated C string, which likely will cause an access violation. + * We want `NULL` pointers to be printed as `"NULL"`, but `operator<<` prints it as `"0"`, `"nullptr"`, or something else, depending on the compiler. + * With some compilers, printing a `NULL` `char*` using `operator<<` will segfault. + * `operator<<` prints a function pointer as a `bool` (hence it always prints `"1"`), which is not very useful. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/Documentation.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/Documentation.md new file mode 100644 index 00000000..315b0a29 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/Documentation.md @@ -0,0 +1,11 @@ +This page lists all documentation wiki pages for Google Mock **version 1.5.0** -- **if you use a different version of Google Mock, please read the documentation for that specific version instead.** + + * [ForDummies](V1_5_ForDummies.md) -- start here if you are new to Google Mock. + * [CheatSheet](V1_5_CheatSheet.md) -- a quick reference. + * [CookBook](V1_5_CookBook.md) -- recipes for doing various tasks using Google Mock. + * [FrequentlyAskedQuestions](V1_5_FrequentlyAskedQuestions.md) -- check here before asking a question on the mailing list. + +To contribute code to Google Mock, read: + + * DevGuide -- read this _before_ writing your first patch. + * [Pump Manual](http://code.google.com/p/googletest/wiki/PumpManual) -- how we generate some of Google Mock's source files. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/ForDummies.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/ForDummies.md new file mode 100644 index 00000000..f389606c --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/ForDummies.md @@ -0,0 +1,439 @@ + + +(**Note:** If you get compiler errors that you don't understand, be sure to consult [Google Mock Doctor](V1_5_FrequentlyAskedQuestions#How_am_I_supposed_to_make_sense_of_these_horrible_template_error.md).) + +# What Is Google C++ Mocking Framework? # +When you write a prototype or test, often it's not feasible or wise to rely on real objects entirely. A **mock object** implements the same interface as a real object (so it can be used as one), but lets you specify at run time how it will be used and what it should do (which methods will be called? in which order? how many times? with what arguments? what will they return? etc). + +**Note:** It is easy to confuse the term _fake objects_ with mock objects. Fakes and mocks actually mean very different things in the Test-Driven Development (TDD) community: + + * **Fake** objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake. + * **Mocks** are objects pre-programmed with _expectations_, which form a specification of the calls they are expected to receive. + +If all this seems too abstract for you, don't worry - the most important thing to remember is that a mock allows you to check the _interaction_ between itself and code that uses it. The difference between fakes and mocks will become much clearer once you start to use mocks. + +**Google C++ Mocking Framework** (or **Google Mock** for short) is a library (sometimes we also call it a "framework" to make it sound cool) for creating mock classes and using them. It does to C++ what [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/) do to Java. + +Using Google Mock involves three basic steps: + + 1. Use some simple macros to describe the interface you want to mock, and they will expand to the implementation of your mock class; + 1. Create some mock objects and specify its expectations and behavior using an intuitive syntax; + 1. Exercise code that uses the mock objects. Google Mock will catch any violation of the expectations as soon as it arises. + +# Why Google Mock? # +While mock objects help you remove unnecessary dependencies in tests and make them fast and reliable, using mocks manually in C++ is _hard_: + + * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distance to avoid it. + * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad hoc restrictions. + * The knowledge you gained from using one mock doesn't transfer to the next. + +In contrast, Java and Python programmers have some fine mock frameworks, which automate the creation of mocks. As a result, mocking is a proven effective technique and widely adopted practice in those communities. Having the right tool absolutely makes the difference. + +Google Mock was built to help C++ programmers. It was inspired by [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/), but designed with C++'s specifics in mind. It is your friend if any of the following problems is bothering you: + + * You are stuck with a sub-optimal design and wish you had done more prototyping before it was too late, but prototyping in C++ is by no means "rapid". + * Your tests are slow as they depend on too many libraries or use expensive resources (e.g. a database). + * Your tests are brittle as some resources they use are unreliable (e.g. the network). + * You want to test how your code handles a failure (e.g. a file checksum error), but it's not easy to cause one. + * You need to make sure that your module interacts with other modules in the right way, but it's hard to observe the interaction; therefore you resort to observing the side effects at the end of the action, which is awkward at best. + * You want to "mock out" your dependencies, except that they don't have mock implementations yet; and, frankly, you aren't thrilled by some of those hand-written mocks. + +We encourage you to use Google Mock as: + + * a _design_ tool, for it lets you experiment with your interface design early and often. More iterations lead to better designs! + * a _testing_ tool to cut your tests' outbound dependencies and probe the interaction between your module and its collaborators. + +# Getting Started # +Using Google Mock is easy! Inside your C++ source file, just #include `` and ``, and you are ready to go. + +# A Case for Mock Turtles # +Let's look at an example. Suppose you are developing a graphics program that relies on a LOGO-like API for drawing. How would you test that it does the right thing? Well, you can run it and compare the screen with a golden screen snapshot, but let's admit it: tests like this are expensive to run and fragile (What if you just upgraded to a shiny new graphics card that has better anti-aliasing? Suddenly you have to update all your golden images.). It would be too painful if all your tests are like this. Fortunately, you learned about Dependency Injection and know the right thing to do: instead of having your application talk to the drawing API directly, wrap the API in an interface (say, `Turtle`) and code to that interface: + +``` +class Turtle { + ... + virtual ~Turtle() {} + virtual void PenUp() = 0; + virtual void PenDown() = 0; + virtual void Forward(int distance) = 0; + virtual void Turn(int degrees) = 0; + virtual void GoTo(int x, int y) = 0; + virtual int GetX() const = 0; + virtual int GetY() const = 0; +}; +``` + +(Note that the destructor of `Turtle` **must** be virtual, as is the case for **all** classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you'll get corrupted program states like memory leaks.) + +You can control whether the turtle's movement will leave a trace using `PenUp()` and `PenDown()`, and control its movement using `Forward()`, `Turn()`, and `GoTo()`. Finally, `GetX()` and `GetY()` tell you the current position of the turtle. + +Your program will normally use a real implementation of this interface. In tests, you can use a mock implementation instead. This allows you to easily check what drawing primitives your program is calling, with what arguments, and in which order. Tests written this way are much more robust (they won't break because your new machine does anti-aliasing differently), easier to read and maintain (the intent of a test is expressed in the code, not in some binary images), and run _much, much faster_. + +# Writing the Mock Class # +If you are lucky, the mocks you need to use have already been implemented by some nice people. If, however, you find yourself in the position to write a mock class, relax - Google Mock turns this task into a fun game! (Well, almost.) + +## How to Define It ## +Using the `Turtle` interface as example, here are the simple steps you need to follow: + + 1. Derive a class `MockTurtle` from `Turtle`. + 1. Take a virtual function of `Turtle`. Count how many arguments it has. + 1. In the `public:` section of the child class, write `MOCK_METHODn();` (or `MOCK_CONST_METHODn();` if you are mocking a `const` method), where `n` is the number of the arguments; if you counted wrong, shame on you, and a compiler error will tell you so. + 1. Now comes the fun part: you take the function signature, cut-and-paste the _function name_ as the _first_ argument to the macro, and leave what's left as the _second_ argument (in case you're curious, this is the _type of the function_). + 1. Repeat until all virtual functions you want to mock are done. + +After the process, you should have something like: + +``` +#include // Brings in Google Mock. +class MockTurtle : public Turtle { + public: + ... + MOCK_METHOD0(PenUp, void()); + MOCK_METHOD0(PenDown, void()); + MOCK_METHOD1(Forward, void(int distance)); + MOCK_METHOD1(Turn, void(int degrees)); + MOCK_METHOD2(GoTo, void(int x, int y)); + MOCK_CONST_METHOD0(GetX, int()); + MOCK_CONST_METHOD0(GetY, int()); +}; +``` + +You don't need to define these mock methods somewhere else - the `MOCK_METHOD*` macros will generate the definitions for you. It's that simple! Once you get the hang of it, you can pump out mock classes faster than your source-control system can handle your check-ins. + +**Tip:** If even this is too much work for you, you'll find the +`gmock_gen.py` tool in Google Mock's `scripts/generator/` directory (courtesy of the [cppclean](http://code.google.com/p/cppclean/) project) useful. This command-line +tool requires that you have Python 2.4 installed. You give it a C++ file and the name of an abstract class defined in it, +and it will print the definition of the mock class for you. Due to the +complexity of the C++ language, this script may not always work, but +it can be quite handy when it does. For more details, read the [user documentation](http://code.google.com/p/googlemock/source/browse/trunk/scripts/generator/README). + +## Where to Put It ## +When you define a mock class, you need to decide where to put its definition. Some people put it in a `*_test.cc`. This is fine when the interface being mocked (say, `Foo`) is owned by the same person or team. Otherwise, when the owner of `Foo` changes it, your test could break. (You can't really expect `Foo`'s maintainer to fix every test that uses `Foo`, can you?) + +So, the rule of thumb is: if you need to mock `Foo` and it's owned by others, define the mock class in `Foo`'s package (better, in a `testing` sub-package such that you can clearly separate production code and testing utilities), and put it in a `mock_foo.h`. Then everyone can reference `mock_foo.h` from their tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and only tests that depend on the changed methods need to be fixed. + +Another way to do it: you can introduce a thin layer `FooAdaptor` on top of `Foo` and code to this new interface. Since you own `FooAdaptor`, you can absorb changes in `Foo` much more easily. While this is more work initially, carefully choosing the adaptor interface can make your code easier to write and more readable (a net win in the long run), as you can choose `FooAdaptor` to fit your specific domain much better than `Foo` does. + +# Using Mocks in Tests # +Once you have a mock class, using it is easy. The typical work flow is: + + 1. Import the Google Mock names from the `testing` namespace such that you can use them unqualified (You only have to do it once per file. Remember that namespaces are a good idea and good for your health.). + 1. Create some mock objects. + 1. Specify your expectations on them (How many times will a method be called? With what arguments? What should it do? etc.). + 1. Exercise some code that uses the mocks; optionally, check the result using Google Test assertions. If a mock method is called more than expected or with wrong arguments, you'll get an error immediately. + 1. When a mock is destructed, Google Mock will automatically check whether all expectations on it have been satisfied. + +Here's an example: + +``` +#include "path/to/mock-turtle.h" +#include +#include +using ::testing::AtLeast; // #1 + +TEST(PainterTest, CanDrawSomething) { + MockTurtle turtle; // #2 + EXPECT_CALL(turtle, PenDown()) // #3 + .Times(AtLeast(1)); + + Painter painter(&turtle); // #4 + + EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); +} // #5 + +int main(int argc, char** argv) { + // The following line must be executed to initialize Google Mock + // (and Google Test) before running the tests. + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} +``` + +As you might have guessed, this test checks that `PenDown()` is called at least once. If the `painter` object didn't call this method, your test will fail with a message like this: + +``` +path/to/my_test.cc:119: Failure +Actual function call count doesn't match this expectation: +Actually: never called; +Expected: called at least once. +``` + +**Tip 1:** If you run the test from an Emacs buffer, you can hit `` on the line number displayed in the error message to jump right to the failed expectation. + +**Tip 2:** If your mock objects are never deleted, the final verification won't happen. Therefore it's a good idea to use a heap leak checker in your tests when you allocate mocks on the heap. + +**Important note:** Google Mock requires expectations to be set **before** the mock functions are called, otherwise the behavior is **undefined**. In particular, you mustn't interleave `EXPECT_CALL()`s and calls to the mock functions. + +This means `EXPECT_CALL()` should be read as expecting that a call will occur _in the future_, not that a call has occurred. Why does Google Mock work like that? Well, specifying the expectation beforehand allows Google Mock to report a violation as soon as it arises, when the context (stack trace, etc) is still available. This makes debugging much easier. + +Admittedly, this test is contrived and doesn't do much. You can easily achieve the same effect without using Google Mock. However, as we shall reveal soon, Google Mock allows you to do _much more_ with the mocks. + +## Using Google Mock with Any Testing Framework ## +If you want to use something other than Google Test (e.g. [CppUnit](http://apps.sourceforge.net/mediawiki/cppunit/index.php?title=Main_Page) or +[CxxTest](http://cxxtest.tigris.org/)) as your testing framework, just change the `main()` function in the previous section to: +``` +int main(int argc, char** argv) { + // The following line causes Google Mock to throw an exception on failure, + // which will be interpreted by your testing framework as a test failure. + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + ... whatever your testing framework requires ... +} +``` + +This approach has a catch: it makes Google Mock throw an exception +from a mock object's destructor sometimes. With some compilers, this +sometimes causes the test program to crash. You'll still be able to +notice that the test has failed, but it's not a graceful failure. + +A better solution is to use Google Test's +[event listener API](http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Extending_Google_Test_by_Handling_Test_Events) +to report a test failure to your testing framework properly. You'll need to +implement the `OnTestPartResult()` method of the event listener interface, but it +should be straightforward. + +If this turns out to be too much work, we suggest that you stick with +Google Test, which works with Google Mock seamlessly (in fact, it is +technically part of Google Mock.). If there is a reason that you +cannot use Google Test, please let us know. + +# Setting Expectations # +The key to using a mock object successfully is to set the _right expectations_ on it. If you set the expectations too strict, your test will fail as the result of unrelated changes. If you set them too loose, bugs can slip through. You want to do it just right such that your test can catch exactly the kind of bugs you intend it to catch. Google Mock provides the necessary means for you to do it "just right." + +## General Syntax ## +In Google Mock we use the `EXPECT_CALL()` macro to set an expectation on a mock method. The general syntax is: + +``` +EXPECT_CALL(mock_object, method(matchers)) + .Times(cardinality) + .WillOnce(action) + .WillRepeatedly(action); +``` + +The macro has two arguments: first the mock object, and then the method and its arguments. Note that the two are separated by a comma (`,`), not a period (`.`). (Why using a comma? The answer is that it was necessary for technical reasons.) + +The macro can be followed by some optional _clauses_ that provide more information about the expectation. We'll discuss how each clause works in the coming sections. + +This syntax is designed to make an expectation read like English. For example, you can probably guess that + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .Times(5) + .WillOnce(Return(100)) + .WillOnce(Return(150)) + .WillRepeatedly(Return(200)); +``` + +says that the `turtle` object's `GetX()` method will be called five times, it will return 100 the first time, 150 the second time, and then 200 every time. Some people like to call this style of syntax a Domain-Specific Language (DSL). + +**Note:** Why do we use a macro to do this? It serves two purposes: first it makes expectations easily identifiable (either by `grep` or by a human reader), and second it allows Google Mock to include the source file location of a failed expectation in messages, making debugging easier. + +## Matchers: What Arguments Do We Expect? ## +When a mock function takes arguments, we must specify what arguments we are expecting; for example: + +``` +// Expects the turtle to move forward by 100 units. +EXPECT_CALL(turtle, Forward(100)); +``` + +Sometimes you may not want to be too specific (Remember that talk about tests being too rigid? Over specification leads to brittle tests and obscures the intent of tests. Therefore we encourage you to specify only what's necessary - no more, no less.). If you care to check that `Forward()` will be called but aren't interested in its actual argument, write `_` as the argument, which means "anything goes": + +``` +using ::testing::_; +... +// Expects the turtle to move forward. +EXPECT_CALL(turtle, Forward(_)); +``` + +`_` is an instance of what we call **matchers**. A matcher is like a predicate and can test whether an argument is what we'd expect. You can use a matcher inside `EXPECT_CALL()` wherever a function argument is expected. + +A list of built-in matchers can be found in the [CheatSheet](V1_5_CheatSheet.md). For example, here's the `Ge` (greater than or equal) matcher: + +``` +using ::testing::Ge;... +EXPECT_CALL(turtle, Forward(Ge(100))); +``` + +This checks that the turtle will be told to go forward by at least 100 units. + +## Cardinalities: How Many Times Will It Be Called? ## +The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We call its argument a **cardinality** as it tells _how many times_ the call should occur. It allows us to repeat an expectation many times without actually writing it as many times. More importantly, a cardinality can be "fuzzy", just like a matcher can be. This allows a user to express the intent of a test exactly. + +An interesting special case is when we say `Times(0)`. You may have guessed - it means that the function shouldn't be called with the given arguments at all, and Google Mock will report a Google Test failure whenever the function is (wrongfully) called. + +We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the list of built-in cardinalities you can use, see the [CheatSheet](V1_5_CheatSheet.md). + +The `Times()` clause can be omitted. **If you omit `Times()`, Google Mock will infer the cardinality for you.** The rules are easy to remember: + + * If **neither** `WillOnce()` **nor** `WillRepeatedly()` is in the `EXPECT_CALL()`, the inferred cardinality is `Times(1)`. + * If there are `n WillOnce()`'s but **no** `WillRepeatedly()`, where `n` >= 1, the cardinality is `Times(n)`. + * If there are `n WillOnce()`'s and **one** `WillRepeatedly()`, where `n` >= 0, the cardinality is `Times(AtLeast(n))`. + +**Quick quiz:** what do you think will happen if a function is expected to be called twice but actually called four times? + +## Actions: What Should It Do? ## +Remember that a mock object doesn't really have a working implementation? We as users have to tell it what to do when a method is invoked. This is easy in Google Mock. + +First, if the return type of a mock function is a built-in type or a pointer, the function has a **default action** (a `void` function will just return, a `bool` function will return `false`, and other functions will return 0). If you don't say anything, this behavior will be used. + +Second, if a mock function doesn't have a default action, or the default action doesn't suit you, you can specify the action to be taken each time the expectation matches using a series of `WillOnce()` clauses followed by an optional `WillRepeatedly()`. For example, + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillOnce(Return(300)); +``` + +This says that `turtle.GetX()` will be called _exactly three times_ (Google Mock inferred this from how many `WillOnce()` clauses we've written, since we didn't explicitly write `Times()`), and will return 100, 200, and 300 respectively. + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillRepeatedly(Return(300)); +``` + +says that `turtle.GetY()` will be called _at least twice_ (Google Mock knows this as we've written two `WillOnce()` clauses and a `WillRepeatedly()` while having no explicit `Times()`), will return 100 the first time, 200 the second time, and 300 from the third time on. + +Of course, if you explicitly write a `Times()`, Google Mock will not try to infer the cardinality itself. What if the number you specified is larger than there are `WillOnce()` clauses? Well, after all `WillOnce()`s are used up, Google Mock will do the _default_ action for the function every time (unless, of course, you have a `WillRepeatedly()`.). + +What can we do inside `WillOnce()` besides `Return()`? You can return a reference using `ReturnRef(variable)`, or invoke a pre-defined function, among [others](V1_5_CheatSheet#Actions.md). + +**Important note:** The `EXPECT_CALL()` statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want: + +``` +int n = 100; +EXPECT_CALL(turtle, GetX()) +.Times(4) +.WillOnce(Return(n++)); +``` + +Instead of returning 100, 101, 102, ..., consecutively, this mock function will always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)` will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will return the same pointer every time. If you want the side effect to happen every time, you need to define a custom action, which we'll teach in the [CookBook](V1_5_CookBook.md). + +Time for another quiz! What do you think the following means? + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) +.Times(4) +.WillOnce(Return(100)); +``` + +Obviously `turtle.GetY()` is expected to be called four times. But if you think it will return 100 every time, think twice! Remember that one `WillOnce()` clause will be consumed each time the function is invoked and the default action will be taken afterwards. So the right answer is that `turtle.GetY()` will return 100 the first time, but **return 0 from the second time on**, as returning 0 is the default action for `int` functions. + +## Using Multiple Expectations ## +So far we've only shown examples where you have a single expectation. More realistically, you're going to specify expectations on multiple mock methods, which may be from multiple mock objects. + +By default, when a mock method is invoked, Google Mock will search the expectations in the **reverse order** they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones."). If the matching expectation cannot take any more calls, you will get an upper-bound-violated failure. Here's an example: + +``` +using ::testing::_;... +EXPECT_CALL(turtle, Forward(_)); // #1 +EXPECT_CALL(turtle, Forward(10)) // #2 + .Times(2); +``` + +If `Forward(10)` is called three times in a row, the third time it will be an error, as the last matching expectation (#2) has been saturated. If, however, the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, as now #1 will be the matching expectation. + +**Side note:** Why does Google Mock search for a match in the _reverse_ order of the expectations? The reason is that this allows a user to set up the default expectations in a mock object's constructor or the test fixture's set-up phase and then customize the mock by writing more specific expectations in the test body. So, if you have two expectations on the same method, you want to put the one with more specific matchers **after** the other, or the more specific rule would be shadowed by the more general one that comes after it. + +## Ordered vs Unordered Calls ## +By default, an expectation can match a call even though an earlier expectation hasn't been satisfied. In other words, the calls don't have to occur in the order the expectations are specified. + +Sometimes, you may want all the expected calls to occur in a strict order. To say this in Google Mock is easy: + +``` +using ::testing::InSequence;... +TEST(FooTest, DrawsLineSegment) { + ... + { + InSequence dummy; + + EXPECT_CALL(turtle, PenDown()); + EXPECT_CALL(turtle, Forward(100)); + EXPECT_CALL(turtle, PenUp()); + } + Foo(); +} +``` + +By creating an object of type `InSequence`, all expectations in its scope are put into a _sequence_ and have to occur _sequentially_. Since we are just relying on the constructor and destructor of this object to do the actual work, its name is really irrelevant. + +In this example, we test that `Foo()` calls the three expected functions in the order as written. If a call is made out-of-order, it will be an error. + +(What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](V1_5_CookBook.md).) + +## All Expectations Are Sticky (Unless Said Otherwise) ## +Now let's do a quick quiz to see how well you can use this mock stuff already. How would you test that the turtle is asked to go to the origin _exactly twice_ (you want to ignore any other instructions it receives)? + +After you've come up with your answer, take a look at ours and compare notes (solve it yourself first - don't cheat!): + +``` +using ::testing::_;... +EXPECT_CALL(turtle, GoTo(_, _)) // #1 + .Times(AnyNumber()); +EXPECT_CALL(turtle, GoTo(0, 0)) // #2 + .Times(2); +``` + +Suppose `turtle.GoTo(0, 0)` is called three times. In the third time, Google Mock will see that the arguments match expectation #2 (remember that we always pick the last matching expectation). Now, since we said that there should be only two such calls, Google Mock will report an error immediately. This is basically what we've told you in the "Using Multiple Expectations" section above. + +This example shows that **expectations in Google Mock are "sticky" by default**, in the sense that they remain active even after we have reached their invocation upper bounds. This is an important rule to remember, as it affects the meaning of the spec, and is **different** to how it's done in many other mocking frameworks (Why'd we do that? Because we think our rule makes the common cases easier to express and understand.). + +Simple? Let's see if you've really understood it: what does the following code say? + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)); +} +``` + +If you think it says that `turtle.GetX()` will be called `n` times and will return 10, 20, 30, ..., consecutively, think twice! The problem is that, as we said, expectations are sticky. So, the second time `turtle.GetX()` is called, the last (latest) `EXPECT_CALL()` statement will match, and will immediately lead to an "upper bound exceeded" error - this piece of code is not very useful! + +One correct way of saying that `turtle.GetX()` will return 10, 20, 30, ..., is to explicitly say that the expectations are _not_ sticky. In other words, they should _retire_ as soon as they are saturated: + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); +} +``` + +And, there's a better way to do it: in this case, we expect the calls to occur in a specific order, and we line up the actions to match the order. Since the order is important here, we should make it explicit using a sequence: + +``` +using ::testing::InSequence; +using ::testing::Return; +... +{ + InSequence s; + + for (int i = 1; i <= n; i++) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); + } +} +``` + +By the way, the other situation where an expectation may _not_ be sticky is when it's in a sequence - as soon as another expectation that comes after it in the sequence has been used, it automatically retires (and will never be used to match any call). + +## Uninteresting Calls ## +A mock object may have many methods, and not all of them are that interesting. For example, in some tests we may not care about how many times `GetX()` and `GetY()` get called. + +In Google Mock, if you are not interested in a method, just don't say anything about it. If a call to this method occurs, you'll see a warning in the test output, but it won't be a failure. + +# What Now? # +Congratulations! You've learned enough about Google Mock to start using it. Now, you might want to join the [googlemock](http://groups.google.com/group/googlemock) discussion group and actually write some tests using Google Mock - it will be fun. Hey, it may even be addictive - you've been warned. + +Then, if you feel like increasing your mock quotient, you should move on to the [CookBook](V1_5_CookBook.md). You can learn many advanced features of Google Mock there -- and advance your level of enjoyment and testing bliss. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/FrequentlyAskedQuestions.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/FrequentlyAskedQuestions.md new file mode 100644 index 00000000..7593243c --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_5/FrequentlyAskedQuestions.md @@ -0,0 +1,624 @@ + + +Please send your questions to the +[googlemock](http://groups.google.com/group/googlemock) discussion +group. If you need help with compiler errors, make sure you have +tried [Google Mock Doctor](#How_am_I_supposed_to_make_sense_of_these_horrible_template_error.md) first. + +## I wrote some matchers. After I upgraded to a new version of Google Mock, they no longer compile. What's going on? ## + +After version 1.4.0 of Google Mock was released, we had an idea on how +to make it easier to write matchers that can generate informative +messages efficiently. We experimented with this idea and liked what +we saw. Therefore we decided to implement it. + +Unfortunately, this means that if you have defined your own matchers +by implementing `MatcherInterface` or using `MakePolymorphicMatcher()`, +your definitions will no longer compile. Matchers defined using the +`MATCHER*` family of macros are not affected. + +Sorry for the hassle if your matchers are affected. We believe it's +in everyone's long-term interest to make this change sooner than +later. Fortunately, it's usually not hard to migrate an existing +matcher to the new API. Here's what you need to do: + +If you wrote your matcher like this: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` + +you'll need to change it to: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` +(i.e. rename `Matches()` to `MatchAndExplain()` and give it a second +argument of type `MatchResultListener*`.) + +If you were also using `ExplainMatchResultTo()` to improve the matcher +message: +``` +// Old matcher definition that doesn't work with the lastest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + + virtual void ExplainMatchResultTo(MyType value, + ::std::ostream* os) const { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Foo property is " << value.GetFoo(); + } + ... +}; +``` + +you should move the logic of `ExplainMatchResultTo()` into +`MatchAndExplain()`, using the `MatchResultListener` argument where +the `::std::ostream` was used: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Foo property is " << value.GetFoo(); + return value.GetFoo() > 5; + } + ... +}; +``` + +If your matcher is defined using `MakePolymorphicMatcher()`: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you should rename the `Matches()` method to `MatchAndExplain()` and +add a `MatchResultListener*` argument (the same as what you need to do +for matchers defined by implementing `MatcherInterface`): +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +If your polymorphic matcher uses `ExplainMatchResultTo()` for better +failure messages: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +void ExplainMatchResultTo(const MyGreatMatcher& matcher, + MyType value, + ::std::ostream* os) { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Bar property is " << value.GetBar(); +} +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you'll need to move the logic inside `ExplainMatchResultTo()` to +`MatchAndExplain()`: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Bar property is " << value.GetBar(); + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +For more information, you can read these +[two](V1_5_CookBook#Writing_New_Monomorphic_Matchers.md) +[recipes](V1_5_CookBook#Writing_New_Polymorphic_Matchers.md) +from the cookbook. As always, you +are welcome to post questions on `googlemock@googlegroups.com` if you +need any help. + +## When using Google Mock, do I have to use Google Test as the testing framework? I have my favorite testing framework and don't want to switch. ## + +Google Mock works out of the box with Google Test. However, it's easy +to configure it to work with any testing framework of your choice. +[Here](V1_5_ForDummies#Using_Google_Mock_with_Any_Testing_Framework.md) is how. + +## How am I supposed to make sense of these horrible template errors? ## + +If you are confused by the compiler errors gcc threw at you, +try consulting the _Google Mock Doctor_ tool first. What it does is to +scan stdin for gcc error messages, and spit out diagnoses on the +problems (we call them diseases) your code has. + +To "install", run command: +``` +alias gmd='/scripts/gmock_doctor.py' +``` + +To use it, do: +``` + 2>&1 | gmd +``` + +For example: +``` +make my_test 2>&1 | gmd +``` + +Or you can run `gmd` and copy-n-paste gcc's error messages to it. + +## Can I mock a variadic function? ## + +You cannot mock a variadic function (i.e. a function taking ellipsis +(`...`) arguments) directly in Google Mock. + +The problem is that in general, there is _no way_ for a mock object to +know how many arguments are passed to the variadic method, and what +the arguments' types are. Only the _author of the base class_ knows +the protocol, and we cannot look into his head. + +Therefore, to mock such a function, the _user_ must teach the mock +object how to figure out the number of arguments and their types. One +way to do it is to provide overloaded versions of the function. + +Ellipsis arguments are inherited from C and not really a C++ feature. +They are unsafe to use and don't work with arguments that have +constructors or destructors. Therefore we recommend to avoid them in +C++ as much as possible. + +## MSVC gives me warning C4301 or C4373 when I define a mock method with a const parameter. Why? ## + +If you compile this using Microsoft Visual C++ 2005 SP1: +``` +class Foo { + ... + virtual void Bar(const int i) = 0; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Bar, void(const int i)); +}; +``` +You may get the following warning: +``` +warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier +``` + +This is a MSVC bug. The same code compiles fine with gcc ,for +example. If you use Visual C++ 2008 SP1, you would get the warning: +``` +warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers +``` + +In C++, if you _declare_ a function with a `const` parameter, the +`const` modifier is _ignored_. Therefore, the `Foo` base class above +is equivalent to: +``` +class Foo { + ... + virtual void Bar(int i) = 0; // int or const int? Makes no difference. +}; +``` + +In fact, you can _declare_ Bar() with an `int` parameter, and _define_ +it with a `const int` parameter. The compiler will still match them +up. + +Since making a parameter `const` is meaningless in the method +_declaration_, we recommend to remove it in both `Foo` and `MockFoo`. +That should workaround the VC bug. + +Note that we are talking about the _top-level_ `const` modifier here. +If the function parameter is passed by pointer or reference, declaring +the _pointee_ or _referee_ as `const` is still meaningful. For +example, the following two declarations are _not_ equivalent: +``` +void Bar(int* p); // Neither p nor *p is const. +void Bar(const int* p); // p is not const, but *p is. +``` + +## I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? ## + +We've noticed that when the `/clr` compiler flag is used, Visual C++ +uses 5~6 times as much memory when compiling a mock class. We suggest +to avoid `/clr` when compiling native C++ mocks. + +## I can't figure out why Google Mock thinks my expectations are not satisfied. What should I do? ## + +You might want to run your test with +`--gmock_verbose=info`. This flag lets Google Mock print a trace +of every mock function call it receives. By studying the trace, +you'll gain insights on why the expectations you set are not met. + +## How can I assert that a function is NEVER called? ## + +``` +EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +## I have a failed test where Google Mock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? ## + +When Google Mock detects a failure, it prints relevant information +(the mock function arguments, the state of relevant expectations, and +etc) to help the user debug. If another failure is detected, Google +Mock will do the same, including printing the state of relevant +expectations. + +Sometimes an expectation's state didn't change between two failures, +and you'll see the same description of the state twice. They are +however _not_ redundant, as they refer to _different points in time_. +The fact they are the same _is_ interesting information. + +## I get a heap check failure when using a mock object, but using a real object is fine. What can be wrong? ## + +Does the class (hopefully a pure interface) you are mocking have a +virtual destructor? + +Whenever you derive from a base class, make sure its destructor is +virtual. Otherwise Bad Things will happen. Consider the following +code: + +``` +class Base { + public: + // Not virtual, but should be. + ~Base() { ... } + ... +}; + +class Derived : public Base { + public: + ... + private: + std::string value_; +}; + +... + Base* p = new Derived; + ... + delete p; // Surprise! ~Base() will be called, but ~Derived() will not + // - value_ is leaked. +``` + +By changing `~Base()` to virtual, `~Derived()` will be correctly +called when `delete p` is executed, and the heap checker +will be happy. + +## The "newer expectations override older ones" rule makes writing expectations awkward. Why does Google Mock do that? ## + +When people complain about this, often they are referring to code like: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. However, I have to write the expectations in the +// reverse order. This sucks big time!!! +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); +``` + +The problem is that they didn't pick the **best** way to express the test's +intent. + +By default, expectations don't have to be matched in _any_ particular +order. If you want them to match in a certain order, you need to be +explicit. This is Google Mock's (and jMock's) fundamental philosophy: it's +easy to accidentally over-specify your tests, and we want to make it +harder to do so. + +There are two better ways to write the test spec. You could either +put the expectations in sequence: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. Using a sequence, we can write the expectations +// in their natural order. +{ + InSequence s; + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +} +``` + +or you can put the sequence of actions in the same expectation: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +``` + +Back to the original questions: why does Google Mock search the +expectations (and `ON_CALL`s) from back to front? Because this +allows a user to set up a mock's behavior for the common case early +(e.g. in the mock's constructor or the test fixture's set-up phase) +and customize it with more specific rules later. If Google Mock +searches from front to back, this very useful pattern won't be +possible. + +## Google Mock prints a warning when a function without EXPECT\_CALL is called, even if I have set its behavior using ON\_CALL. Would it be reasonable not to show the warning in this case? ## + +When choosing between being neat and being safe, we lean toward the +latter. So the answer is that we think it's better to show the +warning. + +Often people write `ON_CALL`s in the mock object's +constructor or `SetUp()`, as the default behavior rarely changes from +test to test. Then in the test body they set the expectations, which +are often different for each test. Having an `ON_CALL` in the set-up +part of a test doesn't mean that the calls are expected. If there's +no `EXPECT_CALL` and the method is called, it's possibly an error. If +we quietly let the call go through without notifying the user, bugs +may creep in unnoticed. + +If, however, you are sure that the calls are OK, you can write + +``` +EXPECT_CALL(foo, Bar(_)) + .WillRepeatedly(...); +``` + +instead of + +``` +ON_CALL(foo, Bar(_)) + .WillByDefault(...); +``` + +This tells Google Mock that you do expect the calls and no warning should be +printed. + +Also, you can control the verbosity using the `--gmock_verbose` flag. +If you find the output too noisy when debugging, just choose a less +verbose level. + +## How can I delete the mock function's argument in an action? ## + +If you find yourself needing to perform some action that's not +supported by Google Mock directly, remember that you can define your own +actions using +[MakeAction()](V1_5_CookBook#Writing_New_Actions.md) or +[MakePolymorphicAction()](V1_5_CookBook#Writing_New_Polymorphic_Actions.md), +or you can write a stub function and invoke it using +[Invoke()](V1_5_CookBook#Using_Functions_Methods_Functors.md). + +## MOCK\_METHODn()'s second argument looks funny. Why don't you use the MOCK\_METHODn(Method, return\_type, arg\_1, ..., arg\_n) syntax? ## + +What?! I think it's beautiful. :-) + +While which syntax looks more natural is a subjective matter to some +extent, Google Mock's syntax was chosen for several practical advantages it +has. + +Try to mock a function that takes a map as an argument: +``` +virtual int GetSize(const map& m); +``` + +Using the proposed syntax, it would be: +``` +MOCK_METHOD1(GetSize, int, const map& m); +``` + +Guess what? You'll get a compiler error as the compiler thinks that +`const map& m` are **two**, not one, arguments. To work +around this you can use `typedef` to give the map type a name, but +that gets in the way of your work. Google Mock's syntax avoids this +problem as the function's argument types are protected inside a pair +of parentheses: +``` +// This compiles fine. +MOCK_METHOD1(GetSize, int(const map& m)); +``` + +You still need a `typedef` if the return type contains an unprotected +comma, but that's much rarer. + +Other advantages include: + 1. `MOCK_METHOD1(Foo, int, bool)` can leave a reader wonder whether the method returns `int` or `bool`, while there won't be such confusion using Google Mock's syntax. + 1. The way Google Mock describes a function type is nothing new, although many people may not be familiar with it. The same syntax was used in C, and the `function` library in `tr1` uses this syntax extensively. Since `tr1` will become a part of the new version of STL, we feel very comfortable to be consistent with it. + 1. The function type syntax is also used in other parts of Google Mock's API (e.g. the action interface) in order to make the implementation tractable. A user needs to learn it anyway in order to utilize Google Mock's more advanced features. We'd as well stick to the same syntax in `MOCK_METHOD*`! + +## My code calls a static/global function. Can I mock it? ## + +You can, but you need to make some changes. + +In general, if you find yourself needing to mock a static function, +it's a sign that your modules are too tightly coupled (and less +flexible, less reusable, less testable, etc). You are probably better +off defining a small interface and call the function through that +interface, which then can be easily mocked. It's a bit of work +initially, but usually pays for itself quickly. + +This Google Testing Blog +[post](http://googletesting.blogspot.com/2008/06/defeat-static-cling.html) +says it excellently. Check it out. + +## My mock object needs to do complex stuff. It's a lot of pain to specify the actions. Google Mock sucks! ## + +I know it's not a question, but you get an answer for free any way. :-) + +With Google Mock, you can create mocks in C++ easily. And people might be +tempted to use them everywhere. Sometimes they work great, and +sometimes you may find them, well, a pain to use. So, what's wrong in +the latter case? + +When you write a test without using mocks, you exercise the code and +assert that it returns the correct value or that the system is in an +expected state. This is sometimes called "state-based testing". + +Mocks are great for what some call "interaction-based" testing: +instead of checking the system state at the very end, mock objects +verify that they are invoked the right way and report an error as soon +as it arises, giving you a handle on the precise context in which the +error was triggered. This is often more effective and economical to +do than state-based testing. + +If you are doing state-based testing and using a test double just to +simulate the real object, you are probably better off using a fake. +Using a mock in this case causes pain, as it's not a strong point for +mocks to perform complex actions. If you experience this and think +that mocks suck, you are just not using the right tool for your +problem. Or, you might be trying to solve the wrong problem. :-) + +## I got a warning "Uninteresting function call encountered - default action taken.." Should I panic? ## + +By all means, NO! It's just an FYI. + +What it means is that you have a mock function, you haven't set any +expectations on it (by Google Mock's rule this means that you are not +interested in calls to this function and therefore it can be called +any number of times), and it is called. That's OK - you didn't say +it's not OK to call the function! + +What if you actually meant to disallow this function to be called, but +forgot to write `EXPECT_CALL(foo, Bar()).Times(0)`? While +one can argue that it's the user's fault, Google Mock tries to be nice and +prints you a note. + +So, when you see the message and believe that there shouldn't be any +uninteresting calls, you should investigate what's going on. To make +your life easier, Google Mock prints the function name and arguments +when an uninteresting call is encountered. + +## I want to define a custom action. Should I use Invoke() or implement the action interface? ## + +Either way is fine - you want to choose the one that's more convenient +for your circumstance. + +Usually, if your action is for a particular function type, defining it +using `Invoke()` should be easier; if your action can be used in +functions of different types (e.g. if you are defining +`Return(value)`), `MakePolymorphicAction()` is +easiest. Sometimes you want precise control on what types of +functions the action can be used in, and implementing +`ActionInterface` is the way to go here. See the implementation of +`Return()` in `include/gmock/gmock-actions.h` for an example. + +## I'm using the set-argument-pointee action, and the compiler complains about "conflicting return type specified". What does it mean? ## + +You got this error as Google Mock has no idea what value it should return +when the mock method is called. `SetArgumentPointee()` says what the +side effect is, but doesn't say what the return value should be. You +need `DoAll()` to chain a `SetArgumentPointee()` with a `Return()`. + +See this [recipe](V1_5_CookBook#Mocking_Side_Effects.md) for more details and an example. + + +## My question is not in your FAQ! ## + +If you cannot find the answer to your question in this FAQ, there are +some other resources you can use: + + 1. read other [wiki pages](http://code.google.com/p/googlemock/w/list), + 1. search the mailing list [archive](http://groups.google.com/group/googlemock/topics), + 1. ask it on [googlemock@googlegroups.com](mailto:googlemock@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googlemock) before you can post.). + +Please note that creating an issue in the +[issue tracker](http://code.google.com/p/googlemock/issues/list) is _not_ +a good way to get your answer, as it is monitored infrequently by a +very small number of people. + +When asking a question, it's helpful to provide as much of the +following information as possible (people cannot help you if there's +not enough information in your question): + + * the version (or the revision number if you check out from SVN directly) of Google Mock you use (Google Mock is under active development, so it's possible that your problem has been solved in a later version), + * your operating system, + * the name and version of your compiler, + * the complete command line flags you give to your compiler, + * the complete compiler error messages (if the question is about compilation), + * the _actual_ code (ideally, a minimal but complete program) that has the problem you encounter. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/CheatSheet.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/CheatSheet.md new file mode 100644 index 00000000..91de1d21 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/CheatSheet.md @@ -0,0 +1,534 @@ + + +# Defining a Mock Class # + +## Mocking a Normal Class ## + +Given +``` +class Foo { + ... + virtual ~Foo(); + virtual int GetSize() const = 0; + virtual string Describe(const char* name) = 0; + virtual string Describe(int type) = 0; + virtual bool Process(Bar elem, int count) = 0; +}; +``` +(note that `~Foo()` **must** be virtual) we can define its mock as +``` +#include "gmock/gmock.h" + +class MockFoo : public Foo { + MOCK_CONST_METHOD0(GetSize, int()); + MOCK_METHOD1(Describe, string(const char* name)); + MOCK_METHOD1(Describe, string(int type)); + MOCK_METHOD2(Process, bool(Bar elem, int count)); +}; +``` + +To create a "nice" mock object which ignores all uninteresting calls, +or a "strict" mock object, which treats them as failures: +``` +NiceMock nice_foo; // The type is a subclass of MockFoo. +StrictMock strict_foo; // The type is a subclass of MockFoo. +``` + +## Mocking a Class Template ## + +To mock +``` +template +class StackInterface { + public: + ... + virtual ~StackInterface(); + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; +``` +(note that `~StackInterface()` **must** be virtual) just append `_T` to the `MOCK_*` macros: +``` +template +class MockStack : public StackInterface { + public: + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Specifying Calling Conventions for Mock Functions ## + +If your mock function doesn't use the default calling convention, you +can specify it by appending `_WITH_CALLTYPE` to any of the macros +described in the previous two sections and supplying the calling +convention as the first argument to the macro. For example, +``` + MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n)); + MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Bar, int(double x, double y)); +``` +where `STDMETHODCALLTYPE` is defined by `` on Windows. + +# Using Mocks in Tests # + +The typical flow is: + 1. Import the Google Mock names you need to use. All Google Mock names are in the `testing` namespace unless they are macros or otherwise noted. + 1. Create the mock objects. + 1. Optionally, set the default actions of the mock objects. + 1. Set your expectations on the mock objects (How will they be called? What wil they do?). + 1. Exercise code that uses the mock objects; if necessary, check the result using [Google Test](http://code.google.com/p/googletest/) assertions. + 1. When a mock objects is destructed, Google Mock automatically verifies that all expectations on it have been satisfied. + +Here is an example: +``` +using ::testing::Return; // #1 + +TEST(BarTest, DoesThis) { + MockFoo foo; // #2 + + ON_CALL(foo, GetSize()) // #3 + .WillByDefault(Return(1)); + // ... other default actions ... + + EXPECT_CALL(foo, Describe(5)) // #4 + .Times(3) + .WillRepeatedly(Return("Category 5")); + // ... other expectations ... + + EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 +} // #6 +``` + +# Setting Default Actions # + +Google Mock has a **built-in default action** for any function that +returns `void`, `bool`, a numeric value, or a pointer. + +To customize the default action for functions with return type `T` globally: +``` +using ::testing::DefaultValue; + +DefaultValue::Set(value); // Sets the default value to be returned. +// ... use the mocks ... +DefaultValue::Clear(); // Resets the default value. +``` + +To customize the default action for a particular method, use `ON_CALL()`: +``` +ON_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .WillByDefault(action); +``` + +# Setting Expectations # + +`EXPECT_CALL()` sets **expectations** on a mock method (How will it be +called? What will it do?): +``` +EXPECT_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .Times(cardinality) ? + .InSequence(sequences) * + .After(expectations) * + .WillOnce(action) * + .WillRepeatedly(action) ? + .RetiresOnSaturation(); ? +``` + +If `Times()` is omitted, the cardinality is assumed to be: + + * `Times(1)` when there is neither `WillOnce()` nor `WillRepeatedly()`; + * `Times(n)` when there are `n WillOnce()`s but no `WillRepeatedly()`, where `n` >= 1; or + * `Times(AtLeast(n))` when there are `n WillOnce()`s and a `WillRepeatedly()`, where `n` >= 0. + +A method with no `EXPECT_CALL()` is free to be invoked _any number of times_, and the default action will be taken each time. + +# Matchers # + +A **matcher** matches a _single_ argument. You can use it inside +`ON_CALL()` or `EXPECT_CALL()`, or use it to validate a value +directly: + +| `EXPECT_THAT(value, matcher)` | Asserts that `value` matches `matcher`. | +|:------------------------------|:----------------------------------------| +| `ASSERT_THAT(value, matcher)` | The same as `EXPECT_THAT(value, matcher)`, except that it generates a **fatal** failure. | + +Built-in matchers (where `argument` is the function argument) are +divided into several categories: + +## Wildcard ## +|`_`|`argument` can be any value of the correct type.| +|:--|:-----------------------------------------------| +|`A()` or `An()`|`argument` can be any value of type `type`. | + +## Generic Comparison ## + +|`Eq(value)` or `value`|`argument == value`| +|:---------------------|:------------------| +|`Ge(value)` |`argument >= value`| +|`Gt(value)` |`argument > value` | +|`Le(value)` |`argument <= value`| +|`Lt(value)` |`argument < value` | +|`Ne(value)` |`argument != value`| +|`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| +|`NotNull()` |`argument` is a non-null pointer (raw or smart).| +|`Ref(variable)` |`argument` is a reference to `variable`.| +|`TypedEq(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| + +Except `Ref()`, these matchers make a _copy_ of `value` in case it's +modified or destructed later. If the compiler complains that `value` +doesn't have a public copy constructor, try wrap it in `ByRef()`, +e.g. `Eq(ByRef(non_copyable_value))`. If you do that, make sure +`non_copyable_value` is not changed afterwards, or the meaning of your +matcher will be changed. + +## Floating-Point Matchers ## + +|`DoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal.| +|:-------------------|:----------------------------------------------------------------------------------------------| +|`FloatEq(a_float)` |`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. | +|`NanSensitiveDoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. | +|`NanSensitiveFloatEq(a_float)`|`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. | + +These matchers use ULP-based comparison (the same as used in +[Google Test](http://code.google.com/p/googletest/)). They +automatically pick a reasonable error bound based on the absolute +value of the expected value. `DoubleEq()` and `FloatEq()` conform to +the IEEE standard, which requires comparing two NaNs for equality to +return false. The `NanSensitive*` version instead treats two NaNs as +equal, which is often what a user wants. + +## String Matchers ## + +The `argument` can be either a C string or a C++ string object: + +|`ContainsRegex(string)`|`argument` matches the given regular expression.| +|:----------------------|:-----------------------------------------------| +|`EndsWith(suffix)` |`argument` ends with string `suffix`. | +|`HasSubstr(string)` |`argument` contains `string` as a sub-string. | +|`MatchesRegex(string)` |`argument` matches the given regular expression with the match starting at the first character and ending at the last character.| +|`StartsWith(prefix)` |`argument` starts with string `prefix`. | +|`StrCaseEq(string)` |`argument` is equal to `string`, ignoring case. | +|`StrCaseNe(string)` |`argument` is not equal to `string`, ignoring case.| +|`StrEq(string)` |`argument` is equal to `string`. | +|`StrNe(string)` |`argument` is not equal to `string`. | + +`ContainsRegex()` and `MatchesRegex()` use the regular expression +syntax defined +[here](http://code.google.com/p/googletest/wiki/V1_6_AdvancedGuide#Regular_Expression_Syntax). +`StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide +strings as well. + +## Container Matchers ## + +Most STL-style containers support `==`, so you can use +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. If you want to write the elements in-line, +match them more flexibly, or get more informative messages, you can use: + +| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | +|:--------------|:-------------------------------------------------------------------------------------------| +| `Each(e)` | `argument` is a container where _every_ element matches `e`, which can be either a value or a matcher. | +| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the i-th element matches `ei`, which can be a value or a matcher. 0 to 10 arguments are allowed. | +| `ElementsAreArray(array)` or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from a C-style array. | +| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | +| `Pointwise(m, container)` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. | + +These matchers can also match: + + 1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`), and + 1. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer, int len)` -- see [Multi-argument Matchers](#Multiargument_Matchers.md)). + +where the array may be multi-dimensional (i.e. its elements can be arrays). + +## Member Matchers ## + +|`Field(&class::field, m)`|`argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| +|:------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| +|`Key(e)` |`argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`.| +|`Pair(m1, m2)` |`argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. | +|`Property(&class::property, m)`|`argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| + +## Matching the Result of a Function or Functor ## + +|`ResultOf(f, m)`|`f(argument)` matches matcher `m`, where `f` is a function or functor.| +|:---------------|:---------------------------------------------------------------------| + +## Pointer Matchers ## + +|`Pointee(m)`|`argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`.| +|:-----------|:-----------------------------------------------------------------------------------------------| + +## Multiargument Matchers ## + +Technically, all matchers match a _single_ value. A "multi-argument" +matcher is just one that matches a _tuple_. The following matchers can +be used to match a tuple `(x, y)`: + +|`Eq()`|`x == y`| +|:-----|:-------| +|`Ge()`|`x >= y`| +|`Gt()`|`x > y` | +|`Le()`|`x <= y`| +|`Lt()`|`x < y` | +|`Ne()`|`x != y`| + +You can use the following selectors to pick a subset of the arguments +(or reorder them) to participate in the matching: + +|`AllArgs(m)`|Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`.| +|:-----------|:-------------------------------------------------------------------| +|`Args(m)`|The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`.| + +## Composite Matchers ## + +You can make a matcher from one or more other matchers: + +|`AllOf(m1, m2, ..., mn)`|`argument` matches all of the matchers `m1` to `mn`.| +|:-----------------------|:---------------------------------------------------| +|`AnyOf(m1, m2, ..., mn)`|`argument` matches at least one of the matchers `m1` to `mn`.| +|`Not(m)` |`argument` doesn't match matcher `m`. | + +## Adapters for Matchers ## + +|`MatcherCast(m)`|casts matcher `m` to type `Matcher`.| +|:------------------|:--------------------------------------| +|`SafeMatcherCast(m)`| [safely casts](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Casting_Matchers) matcher `m` to type `Matcher`. | +|`Truly(predicate)` |`predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor.| + +## Matchers as Predicates ## + +|`Matches(m)(value)`|evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor.| +|:------------------|:---------------------------------------------------------------------------------------------| +|`ExplainMatchResult(m, value, result_listener)`|evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. | +|`Value(value, m)` |evaluates to `true` if `value` matches `m`. | + +## Defining Matchers ## + +| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | +|:-------------------------------------------------|:------------------------------------------------------| +| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a macher `IsDivisibleBy(n)` to match a number divisible by `n`. | +| `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | + +**Notes:** + + 1. The `MATCHER*` macros cannot be used inside a function or class. + 1. The matcher body must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). + 1. You can use `PrintToString(x)` to convert a value `x` of any type to a string. + +## Matchers as Test Assertions ## + +|`ASSERT_THAT(expression, m)`|Generates a [fatal failure](http://code.google.com/p/googletest/wiki/V1_6_Primer#Assertions) if the value of `expression` doesn't match matcher `m`.| +|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------| +|`EXPECT_THAT(expression, m)`|Generates a non-fatal failure if the value of `expression` doesn't match matcher `m`. | + +# Actions # + +**Actions** specify what a mock function should do when invoked. + +## Returning a Value ## + +|`Return()`|Return from a `void` mock function.| +|:---------|:----------------------------------| +|`Return(value)`|Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type at the time the expectation is set, not when the action is executed.| +|`ReturnArg()`|Return the `N`-th (0-based) argument.| +|`ReturnNew(a1, ..., ak)`|Return `new T(a1, ..., ak)`; a different object is created each time.| +|`ReturnNull()`|Return a null pointer. | +|`ReturnPointee(ptr)`|Return the value pointed to by `ptr`.| +|`ReturnRef(variable)`|Return a reference to `variable`. | +|`ReturnRefOfCopy(value)`|Return a reference to a copy of `value`; the copy lives as long as the action.| + +## Side Effects ## + +|`Assign(&variable, value)`|Assign `value` to variable.| +|:-------------------------|:--------------------------| +| `DeleteArg()` | Delete the `N`-th (0-based) argument, which must be a pointer. | +| `SaveArg(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. | +| `SaveArgPointee(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. | +| `SetArgReferee(value)` | Assign value to the variable referenced by the `N`-th (0-based) argument. | +|`SetArgPointee(value)` |Assign `value` to the variable pointed by the `N`-th (0-based) argument.| +|`SetArgumentPointee(value)`|Same as `SetArgPointee(value)`. Deprecated. Will be removed in v1.7.0.| +|`SetArrayArgument(first, last)`|Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range.| +|`SetErrnoAndReturn(error, value)`|Set `errno` to `error` and return `value`.| +|`Throw(exception)` |Throws the given exception, which can be any copyable value. Available since v1.1.0.| + +## Using a Function or a Functor as an Action ## + +|`Invoke(f)`|Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor.| +|:----------|:-----------------------------------------------------------------------------------------------------------------| +|`Invoke(object_pointer, &class::method)`|Invoke the {method on the object with the arguments passed to the mock function. | +|`InvokeWithoutArgs(f)`|Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | +|`InvokeWithoutArgs(object_pointer, &class::method)`|Invoke the method on the object, which takes no arguments. | +|`InvokeArgument(arg1, arg2, ..., argk)`|Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments.| + +The return value of the invoked function is used as the return value +of the action. + +When defining a function or functor to be used with `Invoke*()`, you can declare any unused parameters as `Unused`: +``` + double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } + ... + EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance)); +``` + +In `InvokeArgument(...)`, if an argument needs to be passed by reference, wrap it inside `ByRef()`. For example, +``` + InvokeArgument<2>(5, string("Hi"), ByRef(foo)) +``` +calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by value, and `foo` by reference. + +## Default Action ## + +|`DoDefault()`|Do the default action (specified by `ON_CALL()` or the built-in one).| +|:------------|:--------------------------------------------------------------------| + +**Note:** due to technical reasons, `DoDefault()` cannot be used inside a composite action - trying to do so will result in a run-time error. + +## Composite Actions ## + +|`DoAll(a1, a2, ..., an)`|Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void. | +|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------| +|`IgnoreResult(a)` |Perform action `a` and ignore its result. `a` must not return void. | +|`WithArg(a)` |Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. | +|`WithArgs(a)`|Pass the selected (0-based) arguments of the mock function to action `a` and perform it. | +|`WithoutArgs(a)` |Perform action `a` without any arguments. | + +## Defining Actions ## + +| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | +|:--------------------------------------|:---------------------------------------------------------------------------------------| +| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | +| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. | + +The `ACTION*` macros cannot be used inside a function or class. + +# Cardinalities # + +These are used in `Times()` to specify how many times a mock function will be called: + +|`AnyNumber()`|The function can be called any number of times.| +|:------------|:----------------------------------------------| +|`AtLeast(n)` |The call is expected at least `n` times. | +|`AtMost(n)` |The call is expected at most `n` times. | +|`Between(m, n)`|The call is expected between `m` and `n` (inclusive) times.| +|`Exactly(n) or n`|The call is expected exactly `n` times. In particular, the call should never happen when `n` is 0.| + +# Expectation Order # + +By default, the expectations can be matched in _any_ order. If some +or all expectations must be matched in a given order, there are two +ways to specify it. They can be used either independently or +together. + +## The After Clause ## + +``` +using ::testing::Expectation; +... +Expectation init_x = EXPECT_CALL(foo, InitX()); +Expectation init_y = EXPECT_CALL(foo, InitY()); +EXPECT_CALL(foo, Bar()) + .After(init_x, init_y); +``` +says that `Bar()` can be called only after both `InitX()` and +`InitY()` have been called. + +If you don't know how many pre-requisites an expectation has when you +write it, you can use an `ExpectationSet` to collect them: + +``` +using ::testing::ExpectationSet; +... +ExpectationSet all_inits; +for (int i = 0; i < element_count; i++) { + all_inits += EXPECT_CALL(foo, InitElement(i)); +} +EXPECT_CALL(foo, Bar()) + .After(all_inits); +``` +says that `Bar()` can be called only after all elements have been +initialized (but we don't care about which elements get initialized +before the others). + +Modifying an `ExpectationSet` after using it in an `.After()` doesn't +affect the meaning of the `.After()`. + +## Sequences ## + +When you have a long chain of sequential expectations, it's easier to +specify the order using **sequences**, which don't require you to given +each expectation in the chain a different name. All expected
+calls
in the same sequence must occur in the order they are +specified. + +``` +using ::testing::Sequence; +Sequence s1, s2; +... +EXPECT_CALL(foo, Reset()) + .InSequence(s1, s2) + .WillOnce(Return(true)); +EXPECT_CALL(foo, GetSize()) + .InSequence(s1) + .WillOnce(Return(1)); +EXPECT_CALL(foo, Describe(A())) + .InSequence(s2) + .WillOnce(Return("dummy")); +``` +says that `Reset()` must be called before _both_ `GetSize()` _and_ +`Describe()`, and the latter two can occur in any order. + +To put many expectations in a sequence conveniently: +``` +using ::testing::InSequence; +{ + InSequence dummy; + + EXPECT_CALL(...)...; + EXPECT_CALL(...)...; + ... + EXPECT_CALL(...)...; +} +``` +says that all expected calls in the scope of `dummy` must occur in +strict order. The name `dummy` is irrelevant.) + +# Verifying and Resetting a Mock # + +Google Mock will verify the expectations on a mock object when it is destructed, or you can do it earlier: +``` +using ::testing::Mock; +... +// Verifies and removes the expectations on mock_obj; +// returns true iff successful. +Mock::VerifyAndClearExpectations(&mock_obj); +... +// Verifies and removes the expectations on mock_obj; +// also removes the default actions set by ON_CALL(); +// returns true iff successful. +Mock::VerifyAndClear(&mock_obj); +``` + +You can also tell Google Mock that a mock object can be leaked and doesn't +need to be verified: +``` +Mock::AllowLeak(&mock_obj); +``` + +# Mock Classes # + +Google Mock defines a convenient mock class template +``` +class MockFunction { + public: + MOCK_METHODn(Call, R(A1, ..., An)); +}; +``` +See this [recipe](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Using_Check_Points) for one application of it. + +# Flags # + +| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. | +|:-------------------------------|:----------------------------------------------| +| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. | \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/CookBook.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/CookBook.md new file mode 100644 index 00000000..f5975a00 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/CookBook.md @@ -0,0 +1,3342 @@ + + +You can find recipes for using Google Mock here. If you haven't yet, +please read the [ForDummies](V1_6_ForDummies.md) document first to make sure you understand +the basics. + +**Note:** Google Mock lives in the `testing` name space. For +readability, it is recommended to write `using ::testing::Foo;` once in +your file before using the name `Foo` defined by Google Mock. We omit +such `using` statements in this page for brevity, but you should do it +in your own code. + +# Creating Mock Classes # + +## Mocking Private or Protected Methods ## + +You must always put a mock method definition (`MOCK_METHOD*`) in a +`public:` section of the mock class, regardless of the method being +mocked being `public`, `protected`, or `private` in the base class. +This allows `ON_CALL` and `EXPECT_CALL` to reference the mock function +from outside of the mock class. (Yes, C++ allows a subclass to change +the access level of a virtual function in the base class.) Example: + +``` +class Foo { + public: + ... + virtual bool Transform(Gadget* g) = 0; + + protected: + virtual void Resume(); + + private: + virtual int GetTimeOut(); +}; + +class MockFoo : public Foo { + public: + ... + MOCK_METHOD1(Transform, bool(Gadget* g)); + + // The following must be in the public section, even though the + // methods are protected or private in the base class. + MOCK_METHOD0(Resume, void()); + MOCK_METHOD0(GetTimeOut, int()); +}; +``` + +## Mocking Overloaded Methods ## + +You can mock overloaded functions as usual. No special attention is required: + +``` +class Foo { + ... + + // Must be virtual as we'll inherit from Foo. + virtual ~Foo(); + + // Overloaded on the types and/or numbers of arguments. + virtual int Add(Element x); + virtual int Add(int times, Element x); + + // Overloaded on the const-ness of this object. + virtual Bar& GetBar(); + virtual const Bar& GetBar() const; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Add, int(Element x)); + MOCK_METHOD2(Add, int(int times, Element x); + + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +``` + +**Note:** if you don't mock all versions of the overloaded method, the +compiler will give you a warning about some methods in the base class +being hidden. To fix that, use `using` to bring them in scope: + +``` +class MockFoo : public Foo { + ... + using Foo::Add; + MOCK_METHOD1(Add, int(Element x)); + // We don't want to mock int Add(int times, Element x); + ... +}; +``` + +## Mocking Class Templates ## + +To mock a class template, append `_T` to the `MOCK_*` macros: + +``` +template +class StackInterface { + ... + // Must be virtual as we'll inherit from StackInterface. + virtual ~StackInterface(); + + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; + +template +class MockStack : public StackInterface { + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Mocking Nonvirtual Methods ## + +Google Mock can mock non-virtual functions to be used in what we call _hi-perf +dependency injection_. + +In this case, instead of sharing a common base class with the real +class, your mock class will be _unrelated_ to the real class, but +contain methods with the same signatures. The syntax for mocking +non-virtual methods is the _same_ as mocking virtual methods: + +``` +// A simple packet stream class. None of its members is virtual. +class ConcretePacketStream { + public: + void AppendPacket(Packet* new_packet); + const Packet* GetPacket(size_t packet_number) const; + size_t NumberOfPackets() const; + ... +}; + +// A mock packet stream class. It inherits from no other, but defines +// GetPacket() and NumberOfPackets(). +class MockPacketStream { + public: + MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number)); + MOCK_CONST_METHOD0(NumberOfPackets, size_t()); + ... +}; +``` + +Note that the mock class doesn't define `AppendPacket()`, unlike the +real class. That's fine as long as the test doesn't need to call it. + +Next, you need a way to say that you want to use +`ConcretePacketStream` in production code, and use `MockPacketStream` +in tests. Since the functions are not virtual and the two classes are +unrelated, you must specify your choice at _compile time_ (as opposed +to run time). + +One way to do it is to templatize your code that needs to use a packet +stream. More specifically, you will give your code a template type +argument for the type of the packet stream. In production, you will +instantiate your template with `ConcretePacketStream` as the type +argument. In tests, you will instantiate the same template with +`MockPacketStream`. For example, you may write: + +``` +template +void CreateConnection(PacketStream* stream) { ... } + +template +class PacketReader { + public: + void ReadPackets(PacketStream* stream, size_t packet_num); +}; +``` + +Then you can use `CreateConnection()` and +`PacketReader` in production code, and use +`CreateConnection()` and +`PacketReader` in tests. + +``` + MockPacketStream mock_stream; + EXPECT_CALL(mock_stream, ...)...; + .. set more expectations on mock_stream ... + PacketReader reader(&mock_stream); + ... exercise reader ... +``` + +## Mocking Free Functions ## + +It's possible to use Google Mock to mock a free function (i.e. a +C-style function or a static method). You just need to rewrite your +code to use an interface (abstract class). + +Instead of calling a free function (say, `OpenFile`) directly, +introduce an interface for it and have a concrete subclass that calls +the free function: + +``` +class FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) = 0; +}; + +class File : public FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) { + return OpenFile(path, mode); + } +}; +``` + +Your code should talk to `FileInterface` to open a file. Now it's +easy to mock out the function. + +This may seem much hassle, but in practice you often have multiple +related functions that you can put in the same interface, so the +per-function syntactic overhead will be much lower. + +If you are concerned about the performance overhead incurred by +virtual functions, and profiling confirms your concern, you can +combine this with the recipe for [mocking non-virtual methods](#Mocking_Nonvirtual_Methods.md). + +## Nice Mocks and Strict Mocks ## + +If a mock method has no `EXPECT_CALL` spec but is called, Google Mock +will print a warning about the "uninteresting call". The rationale is: + + * New methods may be added to an interface after a test is written. We shouldn't fail a test just because a method it doesn't know about is called. + * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, he can add an `EXPECT_CALL()` to suppress the warning. + +However, sometimes you may want to suppress all "uninteresting call" +warnings, while sometimes you may want the opposite, i.e. to treat all +of them as errors. Google Mock lets you make the decision on a +per-mock-object basis. + +Suppose your test uses a mock class `MockFoo`: + +``` +TEST(...) { + MockFoo mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +If a method of `mock_foo` other than `DoThis()` is called, it will be +reported by Google Mock as a warning. However, if you rewrite your +test to use `NiceMock` instead, the warning will be gone, +resulting in a cleaner test output: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +`NiceMock` is a subclass of `MockFoo`, so it can be used +wherever `MockFoo` is accepted. + +It also works if `MockFoo`'s constructor takes some arguments, as +`NiceMock` "inherits" `MockFoo`'s constructors: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +The usage of `StrictMock` is similar, except that it makes all +uninteresting calls failures: + +``` +using ::testing::StrictMock; + +TEST(...) { + StrictMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... + + // The test will fail if a method of mock_foo other than DoThis() + // is called. +} +``` + +There are some caveats though (I don't like them just as much as the +next guy, but sadly they are side effects of C++'s limitations): + + 1. `NiceMock` and `StrictMock` only work for mock methods defined using the `MOCK_METHOD*` family of macros **directly** in the `MockFoo` class. If a mock method is defined in a **base class** of `MockFoo`, the "nice" or "strict" modifier may not affect it, depending on the compiler. In particular, nesting `NiceMock` and `StrictMock` (e.g. `NiceMock >`) is **not** supported. + 1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). + 1. During the constructor or destructor of `MockFoo`, the mock object is _not_ nice or strict. This may cause surprises if the constructor or destructor calls a mock method on `this` object. (This behavior, however, is consistent with C++'s general rule: if a constructor or destructor calls a virtual method of `this` object, that method is treated as non-virtual. In other words, to the base class's constructor or destructor, `this` object behaves like an instance of the base class, not the derived class. This rule is required for safety. Otherwise a base constructor may use members of a derived class before they are initialized, or a base destructor may use members of a derived class after they have been destroyed.) + +Finally, you should be **very cautious** when using this feature, as the +decision you make applies to **all** future changes to the mock +class. If an important change is made in the interface you are mocking +(and thus in the mock class), it could break your tests (if you use +`StrictMock`) or let bugs pass through without a warning (if you use +`NiceMock`). Therefore, try to specify the mock's behavior using +explicit `EXPECT_CALL` first, and only turn to `NiceMock` or +`StrictMock` as the last resort. + +## Simplifying the Interface without Breaking Existing Code ## + +Sometimes a method has a long list of arguments that is mostly +uninteresting. For example, + +``` +class LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) = 0; +}; +``` + +This method's argument list is lengthy and hard to work with (let's +say that the `message` argument is not even 0-terminated). If we mock +it as is, using the mock will be awkward. If, however, we try to +simplify this interface, we'll need to fix all clients depending on +it, which is often infeasible. + +The trick is to re-dispatch the method in the mock class: + +``` +class ScopedMockLog : public LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, const tm* tm_time, + const char* message, size_t message_len) { + // We are only interested in the log severity, full file name, and + // log message. + Log(severity, full_filename, std::string(message, message_len)); + } + + // Implements the mock method: + // + // void Log(LogSeverity severity, + // const string& file_path, + // const string& message); + MOCK_METHOD3(Log, void(LogSeverity severity, const string& file_path, + const string& message)); +}; +``` + +By defining a new mock method with a trimmed argument list, we make +the mock class much more user-friendly. + +## Alternative to Mocking Concrete Classes ## + +Often you may find yourself using classes that don't implement +interfaces. In order to test your code that uses such a class (let's +call it `Concrete`), you may be tempted to make the methods of +`Concrete` virtual and then mock it. + +Try not to do that. + +Making a non-virtual function virtual is a big decision. It creates an +extension point where subclasses can tweak your class' behavior. This +weakens your control on the class because now it's harder to maintain +the class' invariants. You should make a function virtual only when +there is a valid reason for a subclass to override it. + +Mocking concrete classes directly is problematic as it creates a tight +coupling between the class and the tests - any small change in the +class may invalidate your tests and make test maintenance a pain. + +To avoid such problems, many programmers have been practicing "coding +to interfaces": instead of talking to the `Concrete` class, your code +would define an interface and talk to it. Then you implement that +interface as an adaptor on top of `Concrete`. In tests, you can easily +mock that interface to observe how your code is doing. + +This technique incurs some overhead: + + * You pay the cost of virtual function calls (usually not a problem). + * There is more abstraction for the programmers to learn. + +However, it can also bring significant benefits in addition to better +testability: + + * `Concrete`'s API may not fit your problem domain very well, as you may not be the only client it tries to serve. By designing your own interface, you have a chance to tailor it to your need - you may add higher-level functionalities, rename stuff, etc instead of just trimming the class. This allows you to write your code (user of the interface) in a more natural way, which means it will be more readable, more maintainable, and you'll be more productive. + * If `Concrete`'s implementation ever has to change, you don't have to rewrite everywhere it is used. Instead, you can absorb the change in your implementation of the interface, and your other code and tests will be insulated from this change. + +Some people worry that if everyone is practicing this technique, they +will end up writing lots of redundant code. This concern is totally +understandable. However, there are two reasons why it may not be the +case: + + * Different projects may need to use `Concrete` in different ways, so the best interfaces for them will be different. Therefore, each of them will have its own domain-specific interface on top of `Concrete`, and they will not be the same code. + * If enough projects want to use the same interface, they can always share it, just like they have been sharing `Concrete`. You can check in the interface and the adaptor somewhere near `Concrete` (perhaps in a `contrib` sub-directory) and let many projects use it. + +You need to weigh the pros and cons carefully for your particular +problem, but I'd like to assure you that the Java community has been +practicing this for a long time and it's a proven effective technique +applicable in a wide variety of situations. :-) + +## Delegating Calls to a Fake ## + +Some times you have a non-trivial fake implementation of an +interface. For example: + +``` +class Foo { + public: + virtual ~Foo() {} + virtual char DoThis(int n) = 0; + virtual void DoThat(const char* s, int* p) = 0; +}; + +class FakeFoo : public Foo { + public: + virtual char DoThis(int n) { + return (n > 0) ? '+' : + (n < 0) ? '-' : '0'; + } + + virtual void DoThat(const char* s, int* p) { + *p = strlen(s); + } +}; +``` + +Now you want to mock this interface such that you can set expectations +on it. However, you also want to use `FakeFoo` for the default +behavior, as duplicating it in the mock object is, well, a lot of +work. + +When you define the mock class using Google Mock, you can have it +delegate its default action to a fake class you already have, using +this pattern: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + // Normal mock method definitions using Google Mock. + MOCK_METHOD1(DoThis, char(int n)); + MOCK_METHOD2(DoThat, void(const char* s, int* p)); + + // Delegates the default actions of the methods to a FakeFoo object. + // This must be called *before* the custom ON_CALL() statements. + void DelegateToFake() { + ON_CALL(*this, DoThis(_)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThis)); + ON_CALL(*this, DoThat(_, _)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThat)); + } + private: + FakeFoo fake_; // Keeps an instance of the fake in the mock. +}; +``` + +With that, you can use `MockFoo` in your tests as usual. Just remember +that if you don't explicitly set an action in an `ON_CALL()` or +`EXPECT_CALL()`, the fake will be called upon to do it: + +``` +using ::testing::_; + +TEST(AbcTest, Xyz) { + MockFoo foo; + foo.DelegateToFake(); // Enables the fake for delegation. + + // Put your ON_CALL(foo, ...)s here, if any. + + // No action specified, meaning to use the default action. + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(foo, DoThat(_, _)); + + int n = 0; + EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked. + foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. + EXPECT_EQ(2, n); +} +``` + +**Some tips:** + + * If you want, you can still override the default action by providing your own `ON_CALL()` or using `.WillOnce()` / `.WillRepeatedly()` in `EXPECT_CALL()`. + * In `DelegateToFake()`, you only need to delegate the methods whose fake implementation you intend to use. + * The general technique discussed here works for overloaded methods, but you'll need to tell the compiler which version you mean. To disambiguate a mock function (the one you specify inside the parentheses of `ON_CALL()`), see the "Selecting Between Overloaded Functions" section on this page; to disambiguate a fake function (the one you place inside `Invoke()`), use a `static_cast` to specify the function's type. + * Having to mix a mock and a fake is often a sign of something gone wrong. Perhaps you haven't got used to the interaction-based way of testing yet. Or perhaps your interface is taking on too many roles and should be split up. Therefore, **don't abuse this**. We would only recommend to do it as an intermediate step when you are refactoring your code. + +Regarding the tip on mixing a mock and a fake, here's an example on +why it may be a bad sign: Suppose you have a class `System` for +low-level system operations. In particular, it does file and I/O +operations. And suppose you want to test how your code uses `System` +to do I/O, and you just want the file operations to work normally. If +you mock out the entire `System` class, you'll have to provide a fake +implementation for the file operation part, which suggests that +`System` is taking on too many roles. + +Instead, you can define a `FileOps` interface and an `IOOps` interface +and split `System`'s functionalities into the two. Then you can mock +`IOOps` without mocking `FileOps`. + +## Delegating Calls to a Real Object ## + +When using testing doubles (mocks, fakes, stubs, and etc), sometimes +their behaviors will differ from those of the real objects. This +difference could be either intentional (as in simulating an error such +that you can test the error handling code) or unintentional. If your +mocks have different behaviors than the real objects by mistake, you +could end up with code that passes the tests but fails in production. + +You can use the _delegating-to-real_ technique to ensure that your +mock has the same behavior as the real object while retaining the +ability to validate calls. This technique is very similar to the +delegating-to-fake technique, the difference being that we use a real +object instead of a fake. Here's an example: + +``` +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MockFoo() { + // By default, all calls are delegated to the real object. + ON_CALL(*this, DoThis()) + .WillByDefault(Invoke(&real_, &Foo::DoThis)); + ON_CALL(*this, DoThat(_)) + .WillByDefault(Invoke(&real_, &Foo::DoThat)); + ... + } + MOCK_METHOD0(DoThis, ...); + MOCK_METHOD1(DoThat, ...); + ... + private: + Foo real_; +}; +... + + MockFoo mock; + + EXPECT_CALL(mock, DoThis()) + .Times(3); + EXPECT_CALL(mock, DoThat("Hi")) + .Times(AtLeast(1)); + ... use mock in test ... +``` + +With this, Google Mock will verify that your code made the right calls +(with the right arguments, in the right order, called the right number +of times, etc), and a real object will answer the calls (so the +behavior will be the same as in production). This gives you the best +of both worlds. + +## Delegating Calls to a Parent Class ## + +Ideally, you should code to interfaces, whose methods are all pure +virtual. In reality, sometimes you do need to mock a virtual method +that is not pure (i.e, it already has an implementation). For example: + +``` +class Foo { + public: + virtual ~Foo(); + + virtual void Pure(int n) = 0; + virtual int Concrete(const char* str) { ... } +}; + +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); +}; +``` + +Sometimes you may want to call `Foo::Concrete()` instead of +`MockFoo::Concrete()`. Perhaps you want to do it as part of a stub +action, or perhaps your test doesn't need to mock `Concrete()` at all +(but it would be oh-so painful to have to define a new mock class +whenever you don't need to mock one of its methods). + +The trick is to leave a back door in your mock class for accessing the +real methods in the base class: + +``` +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); + + // Use this to call Concrete() defined in Foo. + int FooConcrete(const char* str) { return Foo::Concrete(str); } +}; +``` + +Now, you can call `Foo::Concrete()` inside an action by: + +``` +using ::testing::_; +using ::testing::Invoke; +... + EXPECT_CALL(foo, Concrete(_)) + .WillOnce(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +or tell the mock object that you don't want to mock `Concrete()`: + +``` +using ::testing::Invoke; +... + ON_CALL(foo, Concrete(_)) + .WillByDefault(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +(Why don't we just write `Invoke(&foo, &Foo::Concrete)`? If you do +that, `MockFoo::Concrete()` will be called (and cause an infinite +recursion) since `Foo::Concrete()` is virtual. That's just how C++ +works.) + +# Using Matchers # + +## Matching Argument Values Exactly ## + +You can specify exactly which arguments a mock method is expecting: + +``` +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(5)) + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", bar)); +``` + +## Using Simple Matchers ## + +You can use matchers to match arguments that have a certain property: + +``` +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", NotNull())); + // The second argument must not be NULL. +``` + +A frequently used matcher is `_`, which matches anything: + +``` +using ::testing::_; +using ::testing::NotNull; +... + EXPECT_CALL(foo, DoThat(_, NotNull())); +``` + +## Combining Matchers ## + +You can build complex matchers from existing ones using `AllOf()`, +`AnyOf()`, and `Not()`: + +``` +using ::testing::AllOf; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +... + // The argument must be > 5 and != 10. + EXPECT_CALL(foo, DoThis(AllOf(Gt(5), + Ne(10)))); + + // The first argument must not contain sub-string "blah". + EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), + NULL)); +``` + +## Casting Matchers ## + +Google Mock matchers are statically typed, meaning that the compiler +can catch your mistake if you use a matcher of the wrong type (for +example, if you use `Eq(5)` to match a `string` argument). Good for +you! + +Sometimes, however, you know what you're doing and want the compiler +to give you some slack. One example is that you have a matcher for +`long` and the argument you want to match is `int`. While the two +types aren't exactly the same, there is nothing really wrong with +using a `Matcher` to match an `int` - after all, we can first +convert the `int` argument to a `long` before giving it to the +matcher. + +To support this need, Google Mock gives you the +`SafeMatcherCast(m)` function. It casts a matcher `m` to type +`Matcher`. To ensure safety, Google Mock checks that (let `U` be the +type `m` accepts): + + 1. Type `T` can be implicitly cast to type `U`; + 1. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and floating-point numbers), the conversion from `T` to `U` is not lossy (in other words, any value representable by `T` can also be represented by `U`); and + 1. When `U` is a reference, `T` must also be a reference (as the underlying matcher may be interested in the address of the `U` value). + +The code won't compile if any of these conditions isn't met. + +Here's one example: + +``` +using ::testing::SafeMatcherCast; + +// A base class and a child class. +class Base { ... }; +class Derived : public Base { ... }; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(DoThis, void(Derived* derived)); +}; +... + + MockFoo foo; + // m is a Matcher we got from somewhere. + EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); +``` + +If you find `SafeMatcherCast(m)` too limiting, you can use a similar +function `MatcherCast(m)`. The difference is that `MatcherCast` works +as long as you can `static_cast` type `T` to type `U`. + +`MatcherCast` essentially lets you bypass C++'s type system +(`static_cast` isn't always safe as it could throw away information, +for example), so be careful not to misuse/abuse it. + +## Selecting Between Overloaded Functions ## + +If you expect an overloaded function to be called, the compiler may +need some help on which overloaded version it is. + +To disambiguate functions overloaded on the const-ness of this object, +use the `Const()` argument wrapper. + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + ... + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +... + + MockFoo foo; + Bar bar1, bar2; + EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). + .WillOnce(ReturnRef(bar1)); + EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). + .WillOnce(ReturnRef(bar2)); +``` + +(`Const()` is defined by Google Mock and returns a `const` reference +to its argument.) + +To disambiguate overloaded functions with the same number of arguments +but different argument types, you may need to specify the exact type +of a matcher, either by wrapping your matcher in `Matcher()`, or +using a matcher whose type is fixed (`TypedEq`, `An()`, +etc): + +``` +using ::testing::An; +using ::testing::Lt; +using ::testing::Matcher; +using ::testing::TypedEq; + +class MockPrinter : public Printer { + public: + MOCK_METHOD1(Print, void(int n)); + MOCK_METHOD1(Print, void(char c)); +}; + +TEST(PrinterTest, Print) { + MockPrinter printer; + + EXPECT_CALL(printer, Print(An())); // void Print(int); + EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); + EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); + + printer.Print(3); + printer.Print(6); + printer.Print('a'); +} +``` + +## Performing Different Actions Based on the Arguments ## + +When a mock method is called, the _last_ matching expectation that's +still active will be selected (think "newer overrides older"). So, you +can make a method do different things depending on its argument values +like this: + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Return; +... + // The default case. + EXPECT_CALL(foo, DoThis(_)) + .WillRepeatedly(Return('b')); + + // The more specific case. + EXPECT_CALL(foo, DoThis(Lt(5))) + .WillRepeatedly(Return('a')); +``` + +Now, if `foo.DoThis()` is called with a value less than 5, `'a'` will +be returned; otherwise `'b'` will be returned. + +## Matching Multiple Arguments as a Whole ## + +Sometimes it's not enough to match the arguments individually. For +example, we may want to say that the first argument must be less than +the second argument. The `With()` clause allows us to match +all arguments of a mock function as a whole. For example, + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Ne; +... + EXPECT_CALL(foo, InRange(Ne(0), _)) + .With(Lt()); +``` + +says that the first argument of `InRange()` must not be 0, and must be +less than the second argument. + +The expression inside `With()` must be a matcher of type +`Matcher >`, where `A1`, ..., `An` are the +types of the function arguments. + +You can also write `AllArgs(m)` instead of `m` inside `.With()`. The +two forms are equivalent, but `.With(AllArgs(Lt()))` is more readable +than `.With(Lt())`. + +You can use `Args(m)` to match the `n` selected arguments +(as a tuple) against `m`. For example, + +``` +using ::testing::_; +using ::testing::AllOf; +using ::testing::Args; +using ::testing::Lt; +... + EXPECT_CALL(foo, Blah(_, _, _)) + .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); +``` + +says that `Blah()` will be called with arguments `x`, `y`, and `z` where +`x < y < z`. + +As a convenience and example, Google Mock provides some matchers for +2-tuples, including the `Lt()` matcher above. See the [CheatSheet](V1_6_CheatSheet.md) for +the complete list. + +Note that if you want to pass the arguments to a predicate of your own +(e.g. `.With(Args<0, 1>(Truly(&MyPredicate)))`), that predicate MUST be +written to take a `tr1::tuple` as its argument; Google Mock will pass the `n` +selected arguments as _one_ single tuple to the predicate. + +## Using Matchers as Predicates ## + +Have you noticed that a matcher is just a fancy predicate that also +knows how to describe itself? Many existing algorithms take predicates +as arguments (e.g. those defined in STL's `` header), and +it would be a shame if Google Mock matchers are not allowed to +participate. + +Luckily, you can use a matcher where a unary predicate functor is +expected by wrapping it inside the `Matches()` function. For example, + +``` +#include +#include + +std::vector v; +... +// How many elements in v are >= 10? +const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); +``` + +Since you can build complex matchers from simpler ones easily using +Google Mock, this gives you a way to conveniently construct composite +predicates (doing the same using STL's `` header is just +painful). For example, here's a predicate that's satisfied by any +number that is >= 0, <= 100, and != 50: + +``` +Matches(AllOf(Ge(0), Le(100), Ne(50))) +``` + +## Using Matchers in Google Test Assertions ## + +Since matchers are basically predicates that also know how to describe +themselves, there is a way to take advantage of them in +[Google Test](http://code.google.com/p/googletest/) assertions. It's +called `ASSERT_THAT` and `EXPECT_THAT`: + +``` + ASSERT_THAT(value, matcher); // Asserts that value matches matcher. + EXPECT_THAT(value, matcher); // The non-fatal version. +``` + +For example, in a Google Test test you can write: + +``` +#include "gmock/gmock.h" + +using ::testing::AllOf; +using ::testing::Ge; +using ::testing::Le; +using ::testing::MatchesRegex; +using ::testing::StartsWith; +... + + EXPECT_THAT(Foo(), StartsWith("Hello")); + EXPECT_THAT(Bar(), MatchesRegex("Line \\d+")); + ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10))); +``` + +which (as you can probably guess) executes `Foo()`, `Bar()`, and +`Baz()`, and verifies that: + + * `Foo()` returns a string that starts with `"Hello"`. + * `Bar()` returns a string that matches regular expression `"Line \\d+"`. + * `Baz()` returns a number in the range [5, 10]. + +The nice thing about these macros is that _they read like +English_. They generate informative messages too. For example, if the +first `EXPECT_THAT()` above fails, the message will be something like: + +``` +Value of: Foo() + Actual: "Hi, world!" +Expected: starts with "Hello" +``` + +**Credit:** The idea of `(ASSERT|EXPECT)_THAT` was stolen from the +[Hamcrest](http://code.google.com/p/hamcrest/) project, which adds +`assertThat()` to JUnit. + +## Using Predicates as Matchers ## + +Google Mock provides a built-in set of matchers. In case you find them +lacking, you can use an arbitray unary predicate function or functor +as a matcher - as long as the predicate accepts a value of the type +you want. You do this by wrapping the predicate inside the `Truly()` +function, for example: + +``` +using ::testing::Truly; + +int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } +... + + // Bar() must be called with an even number. + EXPECT_CALL(foo, Bar(Truly(IsEven))); +``` + +Note that the predicate function / functor doesn't have to return +`bool`. It works as long as the return value can be used as the +condition in statement `if (condition) ...`. + +## Matching Arguments that Are Not Copyable ## + +When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, Google Mock saves +away a copy of `bar`. When `Foo()` is called later, Google Mock +compares the argument to `Foo()` with the saved copy of `bar`. This +way, you don't need to worry about `bar` being modified or destroyed +after the `EXPECT_CALL()` is executed. The same is true when you use +matchers like `Eq(bar)`, `Le(bar)`, and so on. + +But what if `bar` cannot be copied (i.e. has no copy constructor)? You +could define your own matcher function and use it with `Truly()`, as +the previous couple of recipes have shown. Or, you may be able to get +away from it if you can guarantee that `bar` won't be changed after +the `EXPECT_CALL()` is executed. Just tell Google Mock that it should +save a reference to `bar`, instead of a copy of it. Here's how: + +``` +using ::testing::Eq; +using ::testing::ByRef; +using ::testing::Lt; +... + // Expects that Foo()'s argument == bar. + EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar)))); + + // Expects that Foo()'s argument < bar. + EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar)))); +``` + +Remember: if you do this, don't change `bar` after the +`EXPECT_CALL()`, or the result is undefined. + +## Validating a Member of an Object ## + +Often a mock function takes a reference to object as an argument. When +matching the argument, you may not want to compare the entire object +against a fixed object, as that may be over-specification. Instead, +you may need to validate a certain member variable or the result of a +certain getter method of the object. You can do this with `Field()` +and `Property()`. More specifically, + +``` +Field(&Foo::bar, m) +``` + +is a matcher that matches a `Foo` object whose `bar` member variable +satisfies matcher `m`. + +``` +Property(&Foo::baz, m) +``` + +is a matcher that matches a `Foo` object whose `baz()` method returns +a value that satisfies matcher `m`. + +For example: + +> | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | +|:-----------------------------|:-----------------------------------| +> | `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | + +Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no +argument and be declared as `const`. + +BTW, `Field()` and `Property()` can also match plain pointers to +objects. For instance, + +``` +Field(&Foo::number, Ge(3)) +``` + +matches a plain pointer `p` where `p->number >= 3`. If `p` is `NULL`, +the match will always fail regardless of the inner matcher. + +What if you want to validate more than one members at the same time? +Remember that there is `AllOf()`. + +## Validating the Value Pointed to by a Pointer Argument ## + +C++ functions often take pointers as arguments. You can use matchers +like `NULL`, `NotNull()`, and other comparison matchers to match a +pointer, but what if you want to make sure the value _pointed to_ by +the pointer, instead of the pointer itself, has a certain property? +Well, you can use the `Pointee(m)` matcher. + +`Pointee(m)` matches a pointer iff `m` matches the value the pointer +points to. For example: + +``` +using ::testing::Ge; +using ::testing::Pointee; +... + EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); +``` + +expects `foo.Bar()` to be called with a pointer that points to a value +greater than or equal to 3. + +One nice thing about `Pointee()` is that it treats a `NULL` pointer as +a match failure, so you can write `Pointee(m)` instead of + +``` + AllOf(NotNull(), Pointee(m)) +``` + +without worrying that a `NULL` pointer will crash your test. + +Also, did we tell you that `Pointee()` works with both raw pointers +**and** smart pointers (`linked_ptr`, `shared_ptr`, `scoped_ptr`, and +etc)? + +What if you have a pointer to pointer? You guessed it - you can use +nested `Pointee()` to probe deeper inside the value. For example, +`Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer +that points to a number less than 3 (what a mouthful...). + +## Testing a Certain Property of an Object ## + +Sometimes you want to specify that an object argument has a certain +property, but there is no existing matcher that does this. If you want +good error messages, you should define a matcher. If you want to do it +quick and dirty, you could get away with writing an ordinary function. + +Let's say you have a mock function that takes an object of type `Foo`, +which has an `int bar()` method and an `int baz()` method, and you +want to constrain that the argument's `bar()` value plus its `baz()` +value is a given number. Here's how you can define a matcher to do it: + +``` +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class BarPlusBazEqMatcher : public MatcherInterface { + public: + explicit BarPlusBazEqMatcher(int expected_sum) + : expected_sum_(expected_sum) {} + + virtual bool MatchAndExplain(const Foo& foo, + MatchResultListener* listener) const { + return (foo.bar() + foo.baz()) == expected_sum_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "bar() + baz() equals " << expected_sum_; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "bar() + baz() does not equal " << expected_sum_; + } + private: + const int expected_sum_; +}; + +inline Matcher BarPlusBazEq(int expected_sum) { + return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); +} + +... + + EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; +``` + +## Matching Containers ## + +Sometimes an STL container (e.g. list, vector, map, ...) is passed to +a mock function and you may want to validate it. Since most STL +containers support the `==` operator, you can write +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. + +Sometimes, though, you may want to be more flexible (for example, the +first element must be an exact match, but the second element can be +any positive number, and so on). Also, containers used in tests often +have a small number of elements, and having to define the expected +container out-of-line is a bit of a hassle. + +You can use the `ElementsAre()` matcher in such cases: + +``` +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); +``` + +The above matcher says that the container must have 4 elements, which +must be 1, greater than 0, anything, and 5 respectively. + +`ElementsAre()` is overloaded to take 0 to 10 arguments. If more are +needed, you can place them in a C-style array and use +`ElementsAreArray()` instead: + +``` +using ::testing::ElementsAreArray; +... + + // ElementsAreArray accepts an array of element values. + const int expected_vector1[] = { 1, 5, 2, 4, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); + + // Or, an array of element matchers. + Matcher expected_vector2 = { 1, Gt(2), _, 3, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); +``` + +In case the array needs to be dynamically created (and therefore the +array size cannot be inferred by the compiler), you can give +`ElementsAreArray()` an additional argument to specify the array size: + +``` +using ::testing::ElementsAreArray; +... + int* const expected_vector3 = new int[count]; + ... fill expected_vector3 with values ... + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); +``` + +**Tips:** + + * `ElementAre*()` works with _any_ container that implements the STL iterator concept (i.e. it has a `const_iterator` type and supports `begin()/end()`) and supports `size()`, not just the ones defined in STL. It will even work with container types yet to be written - as long as they follows the above pattern. + * You can use nested `ElementAre*()` to match nested (multi-dimensional) containers. + * If the container is passed by pointer instead of by reference, just write `Pointee(ElementsAre*(...))`. + * The order of elements _matters_ for `ElementsAre*()`. Therefore don't use it with containers whose element order is undefined (e.g. `hash_map`). + +## Sharing Matchers ## + +Under the hood, a Google Mock matcher object consists of a pointer to +a ref-counted implementation object. Copying matchers is allowed and +very efficient, as only the pointer is copied. When the last matcher +that references the implementation object dies, the implementation +object will be deleted. + +Therefore, if you have some complex matcher that you want to use again +and again, there is no need to build it everytime. Just assign it to a +matcher variable and use that variable repeatedly! For example, + +``` + Matcher in_range = AllOf(Gt(5), Le(10)); + ... use in_range as a matcher in multiple EXPECT_CALLs ... +``` + +# Setting Expectations # + +## Ignoring Uninteresting Calls ## + +If you are not interested in how a mock method is called, just don't +say anything about it. In this case, if the method is ever called, +Google Mock will perform its default action to allow the test program +to continue. If you are not happy with the default action taken by +Google Mock, you can override it using `DefaultValue::Set()` +(described later in this document) or `ON_CALL()`. + +Please note that once you expressed interest in a particular mock +method (via `EXPECT_CALL()`), all invocations to it must match some +expectation. If this function is called but the arguments don't match +any `EXPECT_CALL()` statement, it will be an error. + +## Disallowing Unexpected Calls ## + +If a mock method shouldn't be called at all, explicitly say so: + +``` +using ::testing::_; +... + EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +If some calls to the method are allowed, but the rest are not, just +list all the expected calls: + +``` +using ::testing::AnyNumber; +using ::testing::Gt; +... + EXPECT_CALL(foo, Bar(5)); + EXPECT_CALL(foo, Bar(Gt(10))) + .Times(AnyNumber()); +``` + +A call to `foo.Bar()` that doesn't match any of the `EXPECT_CALL()` +statements will be an error. + +## Expecting Ordered Calls ## + +Although an `EXPECT_CALL()` statement defined earlier takes precedence +when Google Mock tries to match a function call with an expectation, +by default calls don't have to happen in the order `EXPECT_CALL()` +statements are written. For example, if the arguments match the +matchers in the third `EXPECT_CALL()`, but not those in the first two, +then the third expectation will be used. + +If you would rather have all calls occur in the order of the +expectations, put the `EXPECT_CALL()` statements in a block where you +define a variable of type `InSequence`: + +``` + using ::testing::_; + using ::testing::InSequence; + + { + InSequence s; + + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(bar, DoThat(_)) + .Times(2); + EXPECT_CALL(foo, DoThis(6)); + } +``` + +In this example, we expect a call to `foo.DoThis(5)`, followed by two +calls to `bar.DoThat()` where the argument can be anything, which are +in turn followed by a call to `foo.DoThis(6)`. If a call occurred +out-of-order, Google Mock will report an error. + +## Expecting Partially Ordered Calls ## + +Sometimes requiring everything to occur in a predetermined order can +lead to brittle tests. For example, we may care about `A` occurring +before both `B` and `C`, but aren't interested in the relative order +of `B` and `C`. In this case, the test should reflect our real intent, +instead of being overly constraining. + +Google Mock allows you to impose an arbitrary DAG (directed acyclic +graph) on the calls. One way to express the DAG is to use the +[After](http://code.google.com/p/googlemock/wiki/V1_6_CheatSheet#The_After_Clause) clause of `EXPECT_CALL`. + +Another way is via the `InSequence()` clause (not the same as the +`InSequence` class), which we borrowed from jMock 2. It's less +flexible than `After()`, but more convenient when you have long chains +of sequential calls, as it doesn't require you to come up with +different names for the expectations in the chains. Here's how it +works: + +If we view `EXPECT_CALL()` statements as nodes in a graph, and add an +edge from node A to node B wherever A must occur before B, we can get +a DAG. We use the term "sequence" to mean a directed path in this +DAG. Now, if we decompose the DAG into sequences, we just need to know +which sequences each `EXPECT_CALL()` belongs to in order to be able to +reconstruct the orginal DAG. + +So, to specify the partial order on the expectations we need to do two +things: first to define some `Sequence` objects, and then for each +`EXPECT_CALL()` say which `Sequence` objects it is part +of. Expectations in the same sequence must occur in the order they are +written. For example, + +``` + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(foo, A()) + .InSequence(s1, s2); + EXPECT_CALL(bar, B()) + .InSequence(s1); + EXPECT_CALL(bar, C()) + .InSequence(s2); + EXPECT_CALL(foo, D()) + .InSequence(s2); +``` + +specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> +C -> D`): + +``` + +---> B + | + A ---| + | + +---> C ---> D +``` + +This means that A must occur before B and C, and C must occur before +D. There's no restriction about the order other than these. + +## Controlling When an Expectation Retires ## + +When a mock method is called, Google Mock only consider expectations +that are still active. An expectation is active when created, and +becomes inactive (aka _retires_) when a call that has to occur later +has occurred. For example, in + +``` + using ::testing::_; + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 + .Times(AnyNumber()) + .InSequence(s1, s2); + EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 + .InSequence(s1); + EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 + .InSequence(s2); +``` + +as soon as either #2 or #3 is matched, #1 will retire. If a warning +`"File too large."` is logged after this, it will be an error. + +Note that an expectation doesn't retire automatically when it's +saturated. For example, + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 +``` + +says that there will be exactly one warning with the message `"File +too large."`. If the second warning contains this message too, #2 will +match again and result in an upper-bound-violated error. + +If this is not what you want, you can ask an expectation to retire as +soon as it becomes saturated: + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 + .RetiresOnSaturation(); +``` + +Here #2 can be used only once, so if you have two warnings with the +message `"File too large."`, the first will match #2 and the second +will match #1 - there will be no error. + +# Using Actions # + +## Returning References from Mock Methods ## + +If a mock function's return type is a reference, you need to use +`ReturnRef()` instead of `Return()` to return a result: + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetBar, Bar&()); +}; +... + + MockFoo foo; + Bar bar; + EXPECT_CALL(foo, GetBar()) + .WillOnce(ReturnRef(bar)); +``` + +## Returning Live Values from Mock Methods ## + +The `Return(x)` action saves a copy of `x` when the action is +_created_, and always returns the same value whenever it's +executed. Sometimes you may want to instead return the _live_ value of +`x` (i.e. its value at the time when the action is _executed_.). + +If the mock function's return type is a reference, you can do it using +`ReturnRef(x)`, as shown in the previous recipe ("Returning References +from Mock Methods"). However, Google Mock doesn't let you use +`ReturnRef()` in a mock function whose return type is not a reference, +as doing that usually indicates a user error. So, what shall you do? + +You may be tempted to try `ByRef()`: + +``` +using testing::ByRef; +using testing::Return; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetValue, int()); +}; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(Return(ByRef(x))); + x = 42; + EXPECT_EQ(42, foo.GetValue()); +``` + +Unfortunately, it doesn't work here. The above code will fail with error: + +``` +Value of: foo.GetValue() + Actual: 0 +Expected: 42 +``` + +The reason is that `Return(value)` converts `value` to the actual +return type of the mock function at the time when the action is +_created_, not when it is _executed_. (This behavior was chosen for +the action to be safe when `value` is a proxy object that references +some temporary objects.) As a result, `ByRef(x)` is converted to an +`int` value (instead of a `const int&`) when the expectation is set, +and `Return(ByRef(x))` will always return 0. + +`ReturnPointee(pointer)` was provided to solve this problem +specifically. It returns the value pointed to by `pointer` at the time +the action is _executed_: + +``` +using testing::ReturnPointee; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(ReturnPointee(&x)); // Note the & here. + x = 42; + EXPECT_EQ(42, foo.GetValue()); // This will succeed now. +``` + +## Combining Actions ## + +Want to do more than one thing when a function is called? That's +fine. `DoAll()` allow you to do sequence of actions every time. Only +the return value of the last action in the sequence will be used. + +``` +using ::testing::DoAll; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Bar, bool(int n)); +}; +... + + EXPECT_CALL(foo, Bar(_)) + .WillOnce(DoAll(action_1, + action_2, + ... + action_n)); +``` + +## Mocking Side Effects ## + +Sometimes a method exhibits its effect not via returning a value but +via side effects. For example, it may change some global state or +modify an output argument. To mock side effects, in general you can +define your own action by implementing `::testing::ActionInterface`. + +If all you need to do is to change an output argument, the built-in +`SetArgPointee()` action is convenient: + +``` +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + MOCK_METHOD2(Mutate, void(bool mutate, int* value)); + ... +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, Mutate(true, _)) + .WillOnce(SetArgPointee<1>(5)); +``` + +In this example, when `mutator.Mutate()` is called, we will assign 5 +to the `int` variable pointed to by argument #1 +(0-based). + +`SetArgPointee()` conveniently makes an internal copy of the +value you pass to it, removing the need to keep the value in scope and +alive. The implication however is that the value must have a copy +constructor and assignment operator. + +If the mock method also needs to return a value as well, you can chain +`SetArgPointee()` with `Return()` using `DoAll()`: + +``` +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + ... + MOCK_METHOD1(MutateInt, bool(int* value)); +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, MutateInt(_)) + .WillOnce(DoAll(SetArgPointee<0>(5), + Return(true))); +``` + +If the output argument is an array, use the +`SetArrayArgument(first, last)` action instead. It copies the +elements in source range `[first, last)` to the array pointed to by +the `N`-th (0-based) argument: + +``` +using ::testing::NotNull; +using ::testing::SetArrayArgument; + +class MockArrayMutator : public ArrayMutator { + public: + MOCK_METHOD2(Mutate, void(int* values, int num_values)); + ... +}; +... + + MockArrayMutator mutator; + int values[5] = { 1, 2, 3, 4, 5 }; + EXPECT_CALL(mutator, Mutate(NotNull(), 5)) + .WillOnce(SetArrayArgument<0>(values, values + 5)); +``` + +This also works when the argument is an output iterator: + +``` +using ::testing::_; +using ::testing::SeArrayArgument; + +class MockRolodex : public Rolodex { + public: + MOCK_METHOD1(GetNames, void(std::back_insert_iterator >)); + ... +}; +... + + MockRolodex rolodex; + vector names; + names.push_back("George"); + names.push_back("John"); + names.push_back("Thomas"); + EXPECT_CALL(rolodex, GetNames(_)) + .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); +``` + +## Changing a Mock Object's Behavior Based on the State ## + +If you expect a call to change the behavior of a mock object, you can use `::testing::InSequence` to specify different behaviors before and after the call: + +``` +using ::testing::InSequence; +using ::testing::Return; + +... + { + InSequence seq; + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(my_mock, Flush()); + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(false)); + } + my_mock.FlushIfDirty(); +``` + +This makes `my_mock.IsDirty()` return `true` before `my_mock.Flush()` is called and return `false` afterwards. + +If the behavior change is more complex, you can store the effects in a variable and make a mock method get its return value from that variable: + +``` +using ::testing::_; +using ::testing::SaveArg; +using ::testing::Return; + +ACTION_P(ReturnPointee, p) { return *p; } +... + int previous_value = 0; + EXPECT_CALL(my_mock, GetPrevValue()) + .WillRepeatedly(ReturnPointee(&previous_value)); + EXPECT_CALL(my_mock, UpdateValue(_)) + .WillRepeatedly(SaveArg<0>(&previous_value)); + my_mock.DoSomethingToUpdateValue(); +``` + +Here `my_mock.GetPrevValue()` will always return the argument of the last `UpdateValue()` call. + +## Setting the Default Value for a Return Type ## + +If a mock method's return type is a built-in C++ type or pointer, by +default it will return 0 when invoked. You only need to specify an +action if this default value doesn't work for you. + +Sometimes, you may want to change this default value, or you may want +to specify a default value for types Google Mock doesn't know +about. You can do this using the `::testing::DefaultValue` class +template: + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD0(CalculateBar, Bar()); +}; +... + + Bar default_bar; + // Sets the default return value for type Bar. + DefaultValue::Set(default_bar); + + MockFoo foo; + + // We don't need to specify an action here, as the default + // return value works for us. + EXPECT_CALL(foo, CalculateBar()); + + foo.CalculateBar(); // This should return default_bar. + + // Unsets the default return value. + DefaultValue::Clear(); +``` + +Please note that changing the default value for a type can make you +tests hard to understand. We recommend you to use this feature +judiciously. For example, you may want to make sure the `Set()` and +`Clear()` calls are right next to the code that uses your mock. + +## Setting the Default Actions for a Mock Method ## + +You've learned how to change the default value of a given +type. However, this may be too coarse for your purpose: perhaps you +have two mock methods with the same return type and you want them to +have different behaviors. The `ON_CALL()` macro allows you to +customize your mock's behavior at the method level: + +``` +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Gt; +using ::testing::Return; +... + ON_CALL(foo, Sign(_)) + .WillByDefault(Return(-1)); + ON_CALL(foo, Sign(0)) + .WillByDefault(Return(0)); + ON_CALL(foo, Sign(Gt(0))) + .WillByDefault(Return(1)); + + EXPECT_CALL(foo, Sign(_)) + .Times(AnyNumber()); + + foo.Sign(5); // This should return 1. + foo.Sign(-9); // This should return -1. + foo.Sign(0); // This should return 0. +``` + +As you may have guessed, when there are more than one `ON_CALL()` +statements, the news order take precedence over the older ones. In +other words, the **last** one that matches the function arguments will +be used. This matching order allows you to set up the common behavior +in a mock object's constructor or the test fixture's set-up phase and +specialize the mock's behavior later. + +## Using Functions/Methods/Functors as Actions ## + +If the built-in actions don't suit you, you can easily use an existing +function, method, or functor as an action: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(Sum, int(int x, int y)); + MOCK_METHOD1(ComplexJob, bool(int x)); +}; + +int CalculateSum(int x, int y) { return x + y; } + +class Helper { + public: + bool ComplexJob(int x); +}; +... + + MockFoo foo; + Helper helper; + EXPECT_CALL(foo, Sum(_, _)) + .WillOnce(Invoke(CalculateSum)); + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(Invoke(&helper, &Helper::ComplexJob)); + + foo.Sum(5, 6); // Invokes CalculateSum(5, 6). + foo.ComplexJob(10); // Invokes helper.ComplexJob(10); +``` + +The only requirement is that the type of the function, etc must be +_compatible_ with the signature of the mock function, meaning that the +latter's arguments can be implicitly converted to the corresponding +arguments of the former, and the former's return type can be +implicitly converted to that of the latter. So, you can invoke +something whose type is _not_ exactly the same as the mock function, +as long as it's safe to do so - nice, huh? + +## Invoking a Function/Method/Functor Without Arguments ## + +`Invoke()` is very useful for doing actions that are more complex. It +passes the mock function's arguments to the function or functor being +invoked such that the callee has the full context of the call to work +with. If the invoked function is not interested in some or all of the +arguments, it can simply ignore them. + +Yet, a common pattern is that a test author wants to invoke a function +without the arguments of the mock function. `Invoke()` allows her to +do that using a wrapper function that throws away the arguments before +invoking an underlining nullary function. Needless to say, this can be +tedious and obscures the intent of the test. + +`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except +that it doesn't pass the mock function's arguments to the +callee. Here's an example: + +``` +using ::testing::_; +using ::testing::InvokeWithoutArgs; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(ComplexJob, bool(int n)); +}; + +bool Job1() { ... } +... + + MockFoo foo; + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(InvokeWithoutArgs(Job1)); + + foo.ComplexJob(10); // Invokes Job1(). +``` + +## Invoking an Argument of the Mock Function ## + +Sometimes a mock function will receive a function pointer or a functor +(in other words, a "callable") as an argument, e.g. + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, bool(int n, bool (*fp)(int))); +}; +``` + +and you may want to invoke this callable argument: + +``` +using ::testing::_; +... + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(...); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +Arghh, you need to refer to a mock function argument but C++ has no +lambda (yet), so you have to define your own action. :-( Or do you +really? + +Well, Google Mock has an action to solve _exactly_ this problem: + +``` + InvokeArgument(arg_1, arg_2, ..., arg_m) +``` + +will invoke the `N`-th (0-based) argument the mock function receives, +with `arg_1`, `arg_2`, ..., and `arg_m`. No matter if the argument is +a function pointer or a functor, Google Mock handles them both. + +With that, you could write: + +``` +using ::testing::_; +using ::testing::InvokeArgument; +... + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(InvokeArgument<1>(5)); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +What if the callable takes an argument by reference? No problem - just +wrap it inside `ByRef()`: + +``` +... + MOCK_METHOD1(Bar, bool(bool (*fp)(int, const Helper&))); +... +using ::testing::_; +using ::testing::ByRef; +using ::testing::InvokeArgument; +... + + MockFoo foo; + Helper helper; + ... + EXPECT_CALL(foo, Bar(_)) + .WillOnce(InvokeArgument<0>(5, ByRef(helper))); + // ByRef(helper) guarantees that a reference to helper, not a copy of it, + // will be passed to the callable. +``` + +What if the callable takes an argument by reference and we do **not** +wrap the argument in `ByRef()`? Then `InvokeArgument()` will _make a +copy_ of the argument, and pass a _reference to the copy_, instead of +a reference to the original value, to the callable. This is especially +handy when the argument is a temporary value: + +``` +... + MOCK_METHOD1(DoThat, bool(bool (*f)(const double& x, const string& s))); +... +using ::testing::_; +using ::testing::InvokeArgument; +... + + MockFoo foo; + ... + EXPECT_CALL(foo, DoThat(_)) + .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); + // Will execute (*f)(5.0, string("Hi")), where f is the function pointer + // DoThat() receives. Note that the values 5.0 and string("Hi") are + // temporary and dead once the EXPECT_CALL() statement finishes. Yet + // it's fine to perform this action later, since a copy of the values + // are kept inside the InvokeArgument action. +``` + +## Ignoring an Action's Result ## + +Sometimes you have an action that returns _something_, but you need an +action that returns `void` (perhaps you want to use it in a mock +function that returns `void`, or perhaps it needs to be used in +`DoAll()` and it's not the last in the list). `IgnoreResult()` lets +you do that. For example: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; + +int Process(const MyData& data); +string DoSomething(); + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Abc, void(const MyData& data)); + MOCK_METHOD0(Xyz, bool()); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, Abc(_)) + // .WillOnce(Invoke(Process)); + // The above line won't compile as Process() returns int but Abc() needs + // to return void. + .WillOnce(IgnoreResult(Invoke(Process))); + + EXPECT_CALL(foo, Xyz()) + .WillOnce(DoAll(IgnoreResult(Invoke(DoSomething)), + // Ignores the string DoSomething() returns. + Return(true))); +``` + +Note that you **cannot** use `IgnoreResult()` on an action that already +returns `void`. Doing so will lead to ugly compiler errors. + +## Selecting an Action's Arguments ## + +Say you have a mock function `Foo()` that takes seven arguments, and +you have a custom action that you want to invoke when `Foo()` is +called. Trouble is, the custom action only wants three arguments: + +``` +using ::testing::_; +using ::testing::Invoke; +... + MOCK_METHOD7(Foo, bool(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight)); +... + +bool IsVisibleInQuadrant1(bool visible, int x, int y) { + return visible && x >= 0 && y >= 0; +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( +``` + +To please the compiler God, you can to define an "adaptor" that has +the same signature as `Foo()` and calls the custom action with the +right arguments: + +``` +using ::testing::_; +using ::testing::Invoke; + +bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight) { + return IsVisibleInQuadrant1(visible, x, y); +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. +``` + +But isn't this awkward? + +Google Mock provides a generic _action adaptor_, so you can spend your +time minding more important business than writing your own +adaptors. Here's the syntax: + +``` + WithArgs(action) +``` + +creates an action that passes the arguments of the mock function at +the given indices (0-based) to the inner `action` and performs +it. Using `WithArgs`, our original example can be written as: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::WithArgs; +... + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); + // No need to define your own adaptor. +``` + +For better readability, Google Mock also gives you: + + * `WithoutArgs(action)` when the inner `action` takes _no_ argument, and + * `WithArg(action)` (no `s` after `Arg`) when the inner `action` takes _one_ argument. + +As you may have realized, `InvokeWithoutArgs(...)` is just syntactic +sugar for `WithoutArgs(Inovke(...))`. + +Here are more tips: + + * The inner action used in `WithArgs` and friends does not have to be `Invoke()` -- it can be anything. + * You can repeat an argument in the argument list if necessary, e.g. `WithArgs<2, 3, 3, 5>(...)`. + * You can change the order of the arguments, e.g. `WithArgs<3, 2, 1>(...)`. + * The types of the selected arguments do _not_ have to match the signature of the inner action exactly. It works as long as they can be implicitly converted to the corresponding arguments of the inner action. For example, if the 4-th argument of the mock function is an `int` and `my_action` takes a `double`, `WithArg<4>(my_action)` will work. + +## Ignoring Arguments in Action Functions ## + +The selecting-an-action's-arguments recipe showed us one way to make a +mock function and an action with incompatible argument lists fit +together. The downside is that wrapping the action in +`WithArgs<...>()` can get tedious for people writing the tests. + +If you are defining a function, method, or functor to be used with +`Invoke*()`, and you are not interested in some of its arguments, an +alternative to `WithArgs` is to declare the uninteresting arguments as +`Unused`. This makes the definition less cluttered and less fragile in +case the types of the uninteresting arguments change. It could also +increase the chance the action function can be reused. For example, +given + +``` + MOCK_METHOD3(Foo, double(const string& label, double x, double y)); + MOCK_METHOD3(Bar, double(int index, double x, double y)); +``` + +instead of + +``` +using ::testing::_; +using ::testing::Invoke; + +double DistanceToOriginWithLabel(const string& label, double x, double y) { + return sqrt(x*x + y*y); +} + +double DistanceToOriginWithIndex(int index, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOriginWithLabel)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOriginWithIndex)); +``` + +you could write + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Unused; + +double DistanceToOrigin(Unused, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOrigin)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOrigin)); +``` + +## Sharing Actions ## + +Just like matchers, a Google Mock action object consists of a pointer +to a ref-counted implementation object. Therefore copying actions is +also allowed and very efficient. When the last action that references +the implementation object dies, the implementation object will be +deleted. + +If you have some complex action that you want to use again and again, +you may not have to build it from scratch everytime. If the action +doesn't have an internal state (i.e. if it always does the same thing +no matter how many times it has been called), you can assign it to an +action variable and use that variable repeatedly. For example: + +``` + Action set_flag = DoAll(SetArgPointee<0>(5), + Return(true)); + ... use set_flag in .WillOnce() and .WillRepeatedly() ... +``` + +However, if the action has its own state, you may be surprised if you +share the action object. Suppose you have an action factory +`IncrementCounter(init)` which creates an action that increments and +returns a counter whose initial value is `init`, using two actions +created from the same expression and using a shared action will +exihibit different behaviors. Example: + +``` + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(IncrementCounter(0)); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(IncrementCounter(0)); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 1 - Blah() uses a different + // counter than Bar()'s. +``` + +versus + +``` + Action increment = IncrementCounter(0); + + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(increment); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(increment); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 3 - the counter is shared. +``` + +# Misc Recipes on Using Google Mock # + +## Making the Compilation Faster ## + +Believe it or not, the _vast majority_ of the time spent on compiling +a mock class is in generating its constructor and destructor, as they +perform non-trivial tasks (e.g. verification of the +expectations). What's more, mock methods with different signatures +have different types and thus their constructors/destructors need to +be generated by the compiler separately. As a result, if you mock many +different types of methods, compiling your mock class can get really +slow. + +If you are experiencing slow compilation, you can move the definition +of your mock class' constructor and destructor out of the class body +and into a `.cpp` file. This way, even if you `#include` your mock +class in N files, the compiler only needs to generate its constructor +and destructor once, resulting in a much faster compilation. + +Let's illustrate the idea using an example. Here's the definition of a +mock class before applying this recipe: + +``` +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // Since we don't declare the constructor or the destructor, + // the compiler will generate them in every translation unit + // where this mock class is used. + + MOCK_METHOD0(DoThis, int()); + MOCK_METHOD1(DoThat, bool(const char* str)); + ... more mock methods ... +}; +``` + +After the change, it would look like: + +``` +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // The constructor and destructor are declared, but not defined, here. + MockFoo(); + virtual ~MockFoo(); + + MOCK_METHOD0(DoThis, int()); + MOCK_METHOD1(DoThat, bool(const char* str)); + ... more mock methods ... +}; +``` +and +``` +// File mock_foo.cpp. +#include "path/to/mock_foo.h" + +// The definitions may appear trivial, but the functions actually do a +// lot of things through the constructors/destructors of the member +// variables used to implement the mock methods. +MockFoo::MockFoo() {} +MockFoo::~MockFoo() {} +``` + +## Forcing a Verification ## + +When it's being destoyed, your friendly mock object will automatically +verify that all expectations on it have been satisfied, and will +generate [Google Test](http://code.google.com/p/googletest/) failures +if not. This is convenient as it leaves you with one less thing to +worry about. That is, unless you are not sure if your mock object will +be destoyed. + +How could it be that your mock object won't eventually be destroyed? +Well, it might be created on the heap and owned by the code you are +testing. Suppose there's a bug in that code and it doesn't delete the +mock object properly - you could end up with a passing test when +there's actually a bug. + +Using a heap checker is a good idea and can alleviate the concern, but +its implementation may not be 100% reliable. So, sometimes you do want +to _force_ Google Mock to verify a mock object before it is +(hopefully) destructed. You can do this with +`Mock::VerifyAndClearExpectations(&mock_object)`: + +``` +TEST(MyServerTest, ProcessesRequest) { + using ::testing::Mock; + + MockFoo* const foo = new MockFoo; + EXPECT_CALL(*foo, ...)...; + // ... other expectations ... + + // server now owns foo. + MyServer server(foo); + server.ProcessRequest(...); + + // In case that server's destructor will forget to delete foo, + // this will verify the expectations anyway. + Mock::VerifyAndClearExpectations(foo); +} // server is destroyed when it goes out of scope here. +``` + +**Tip:** The `Mock::VerifyAndClearExpectations()` function returns a +`bool` to indicate whether the verification was successful (`true` for +yes), so you can wrap that function call inside a `ASSERT_TRUE()` if +there is no point going further when the verification has failed. + +## Using Check Points ## + +Sometimes you may want to "reset" a mock object at various check +points in your test: at each check point, you verify that all existing +expectations on the mock object have been satisfied, and then you set +some new expectations on it as if it's newly created. This allows you +to work with a mock object in "phases" whose sizes are each +manageable. + +One such scenario is that in your test's `SetUp()` function, you may +want to put the object you are testing into a certain state, with the +help from a mock object. Once in the desired state, you want to clear +all expectations on the mock, such that in the `TEST_F` body you can +set fresh expectations on it. + +As you may have figured out, the `Mock::VerifyAndClearExpectations()` +function we saw in the previous recipe can help you here. Or, if you +are using `ON_CALL()` to set default actions on the mock object and +want to clear the default actions as well, use +`Mock::VerifyAndClear(&mock_object)` instead. This function does what +`Mock::VerifyAndClearExpectations(&mock_object)` does and returns the +same `bool`, **plus** it clears the `ON_CALL()` statements on +`mock_object` too. + +Another trick you can use to achieve the same effect is to put the +expectations in sequences and insert calls to a dummy "check-point" +function at specific places. Then you can verify that the mock +function calls do happen at the right time. For example, if you are +exercising code: + +``` +Foo(1); +Foo(2); +Foo(3); +``` + +and want to verify that `Foo(1)` and `Foo(3)` both invoke +`mock.Bar("a")`, but `Foo(2)` doesn't invoke anything. You can write: + +``` +using ::testing::MockFunction; + +TEST(FooTest, InvokesBarCorrectly) { + MyMock mock; + // Class MockFunction has exactly one mock method. It is named + // Call() and has type F. + MockFunction check; + { + InSequence s; + + EXPECT_CALL(mock, Bar("a")); + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(mock, Bar("a")); + } + Foo(1); + check.Call("1"); + Foo(2); + check.Call("2"); + Foo(3); +} +``` + +The expectation spec says that the first `Bar("a")` must happen before +check point "1", the second `Bar("a")` must happen after check point "2", +and nothing should happen between the two check points. The explicit +check points make it easy to tell which `Bar("a")` is called by which +call to `Foo()`. + +## Mocking Destructors ## + +Sometimes you want to make sure a mock object is destructed at the +right time, e.g. after `bar->A()` is called but before `bar->B()` is +called. We already know that you can specify constraints on the order +of mock function calls, so all we need to do is to mock the destructor +of the mock function. + +This sounds simple, except for one problem: a destructor is a special +function with special syntax and special semantics, and the +`MOCK_METHOD0` macro doesn't work for it: + +``` + MOCK_METHOD0(~MockFoo, void()); // Won't compile! +``` + +The good news is that you can use a simple pattern to achieve the same +effect. First, add a mock function `Die()` to your mock class and call +it in the destructor, like this: + +``` +class MockFoo : public Foo { + ... + // Add the following two lines to the mock class. + MOCK_METHOD0(Die, void()); + virtual ~MockFoo() { Die(); } +}; +``` + +(If the name `Die()` clashes with an existing symbol, choose another +name.) Now, we have translated the problem of testing when a `MockFoo` +object dies to testing when its `Die()` method is called: + +``` + MockFoo* foo = new MockFoo; + MockBar* bar = new MockBar; + ... + { + InSequence s; + + // Expects *foo to die after bar->A() and before bar->B(). + EXPECT_CALL(*bar, A()); + EXPECT_CALL(*foo, Die()); + EXPECT_CALL(*bar, B()); + } +``` + +And that's that. + +## Using Google Mock and Threads ## + +**IMPORTANT NOTE:** What we describe in this recipe is **ONLY** true on +platforms where Google Mock is thread-safe. Currently these are only +platforms that support the pthreads library (this includes Linux and Mac). +To make it thread-safe on other platforms we only need to implement +some synchronization operations in `"gtest/internal/gtest-port.h"`. + +In a **unit** test, it's best if you could isolate and test a piece of +code in a single-threaded context. That avoids race conditions and +dead locks, and makes debugging your test much easier. + +Yet many programs are multi-threaded, and sometimes to test something +we need to pound on it from more than one thread. Google Mock works +for this purpose too. + +Remember the steps for using a mock: + + 1. Create a mock object `foo`. + 1. Set its default actions and expectations using `ON_CALL()` and `EXPECT_CALL()`. + 1. The code under test calls methods of `foo`. + 1. Optionally, verify and reset the mock. + 1. Destroy the mock yourself, or let the code under test destroy it. The destructor will automatically verify it. + +If you follow the following simple rules, your mocks and threads can +live happily togeter: + + * Execute your _test code_ (as opposed to the code being tested) in _one_ thread. This makes your test easy to follow. + * Obviously, you can do step #1 without locking. + * When doing step #2 and #5, make sure no other thread is accessing `foo`. Obvious too, huh? + * #3 and #4 can be done either in one thread or in multiple threads - anyway you want. Google Mock takes care of the locking, so you don't have to do any - unless required by your test logic. + +If you violate the rules (for example, if you set expectations on a +mock while another thread is calling its methods), you get undefined +behavior. That's not fun, so don't do it. + +Google Mock guarantees that the action for a mock function is done in +the same thread that called the mock function. For example, in + +``` + EXPECT_CALL(mock, Foo(1)) + .WillOnce(action1); + EXPECT_CALL(mock, Foo(2)) + .WillOnce(action2); +``` + +if `Foo(1)` is called in thread 1 and `Foo(2)` is called in thread 2, +Google Mock will execute `action1` in thread 1 and `action2` in thread +2. + +Google Mock does _not_ impose a sequence on actions performed in +different threads (doing so may create deadlocks as the actions may +need to cooperate). This means that the execution of `action1` and +`action2` in the above example _may_ interleave. If this is a problem, +you should add proper synchronization logic to `action1` and `action2` +to make the test thread-safe. + + +Also, remember that `DefaultValue` is a global resource that +potentially affects _all_ living mock objects in your +program. Naturally, you won't want to mess with it from multiple +threads or when there still are mocks in action. + +## Controlling How Much Information Google Mock Prints ## + +When Google Mock sees something that has the potential of being an +error (e.g. a mock function with no expectation is called, a.k.a. an +uninteresting call, which is allowed but perhaps you forgot to +explicitly ban the call), it prints some warning messages, including +the arguments of the function and the return value. Hopefully this +will remind you to take a look and see if there is indeed a problem. + +Sometimes you are confident that your tests are correct and may not +appreciate such friendly messages. Some other times, you are debugging +your tests or learning about the behavior of the code you are testing, +and wish you could observe every mock call that happens (including +argument values and the return value). Clearly, one size doesn't fit +all. + +You can control how much Google Mock tells you using the +`--gmock_verbose=LEVEL` command-line flag, where `LEVEL` is a string +with three possible values: + + * `info`: Google Mock will print all informational messages, warnings, and errors (most verbose). At this setting, Google Mock will also log any calls to the `ON_CALL/EXPECT_CALL` macros. + * `warning`: Google Mock will print both warnings and errors (less verbose). This is the default. + * `error`: Google Mock will print errors only (least verbose). + +Alternatively, you can adjust the value of that flag from within your +tests like so: + +``` + ::testing::FLAGS_gmock_verbose = "error"; +``` + +Now, judiciously use the right flag to enable Google Mock serve you better! + +## Running Tests in Emacs ## + +If you build and run your tests in Emacs, the source file locations of +Google Mock and [Google Test](http://code.google.com/p/googletest/) +errors will be highlighted. Just press `` on one of them and +you'll be taken to the offending line. Or, you can just type `C-x `` +to jump to the next error. + +To make it even easier, you can add the following lines to your +`~/.emacs` file: + +``` +(global-set-key "\M-m" 'compile) ; m is for make +(global-set-key [M-down] 'next-error) +(global-set-key [M-up] '(lambda () (interactive) (next-error -1))) +``` + +Then you can type `M-m` to start a build, or `M-up`/`M-down` to move +back and forth between errors. + +## Fusing Google Mock Source Files ## + +Google Mock's implementation consists of dozens of files (excluding +its own tests). Sometimes you may want them to be packaged up in +fewer files instead, such that you can easily copy them to a new +machine and start hacking there. For this we provide an experimental +Python script `fuse_gmock_files.py` in the `scripts/` directory +(starting with release 1.2.0). Assuming you have Python 2.4 or above +installed on your machine, just go to that directory and run +``` +python fuse_gmock_files.py OUTPUT_DIR +``` + +and you should see an `OUTPUT_DIR` directory being created with files +`gtest/gtest.h`, `gmock/gmock.h`, and `gmock-gtest-all.cc` in it. +These three files contain everything you need to use Google Mock (and +Google Test). Just copy them to anywhere you want and you are ready +to write tests and use mocks. You can use the +[scrpts/test/Makefile](http://code.google.com/p/googlemock/source/browse/trunk/scripts/test/Makefile) file as an example on how to compile your tests +against them. + +# Extending Google Mock # + +## Writing New Matchers Quickly ## + +The `MATCHER*` family of macros can be used to define custom matchers +easily. The syntax: + +``` +MATCHER(name, description_string_expression) { statements; } +``` + +will define a matcher with the given name that executes the +statements, which must return a `bool` to indicate if the match +succeeds. Inside the statements, you can refer to the value being +matched by `arg`, and refer to its type by `arg_type`. + +The description string is a `string`-typed expression that documents +what the matcher does, and is used to generate the failure message +when the match fails. It can (and should) reference the special +`bool` variable `negation`, and should evaluate to the description of +the matcher when `negation` is `false`, or that of the matcher's +negation when `negation` is `true`. + +For convenience, we allow the description string to be empty (`""`), +in which case Google Mock will use the sequence of words in the +matcher name as the description. + +For example: +``` +MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } +``` +allows you to write +``` + // Expects mock_foo.Bar(n) to be called where n is divisible by 7. + EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); +``` +or, +``` +using ::testing::Not; +... + EXPECT_THAT(some_expression, IsDivisibleBy7()); + EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7())); +``` +If the above assertions fail, they will print something like: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 +... + Value of: some_other_expression + Expected: not (is divisible by 7) + Actual: 21 +``` +where the descriptions `"is divisible by 7"` and `"not (is divisible +by 7)"` are automatically calculated from the matcher name +`IsDivisibleBy7`. + +As you may have noticed, the auto-generated descriptions (especially +those for the negation) may not be so great. You can always override +them with a string expression of your own: +``` +MATCHER(IsDivisibleBy7, std::string(negation ? "isn't" : "is") + + " divisible by 7") { + return (arg % 7) == 0; +} +``` + +Optionally, you can stream additional information to a hidden argument +named `result_listener` to explain the match result. For example, a +better definition of `IsDivisibleBy7` is: +``` +MATCHER(IsDivisibleBy7, "") { + if ((arg % 7) == 0) + return true; + + *result_listener << "the remainder is " << (arg % 7); + return false; +} +``` + +With this definition, the above assertion will give a better message: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 (the remainder is 6) +``` + +You should let `MatchAndExplain()` print _any additional information_ +that can help a user understand the match result. Note that it should +explain why the match succeeds in case of a success (unless it's +obvious) - this is useful when the matcher is used inside +`Not()`. There is no need to print the argument value itself, as +Google Mock already prints it for you. + +**Notes:** + + 1. The type of the value being matched (`arg_type`) is determined by the context in which you use the matcher and is supplied to you by the compiler, so you don't need to worry about declaring it (nor can you). This allows the matcher to be polymorphic. For example, `IsDivisibleBy7()` can be used to match any type where the value of `(arg % 7) == 0` can be implicitly converted to a `bool`. In the `Bar(IsDivisibleBy7())` example above, if method `Bar()` takes an `int`, `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be `unsigned long`; and so on. + 1. Google Mock doesn't guarantee when or how many times a matcher will be invoked. Therefore the matcher logic must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). This requirement must be satisfied no matter how you define the matcher (e.g. using one of the methods described in the following recipes). In particular, a matcher can never call a mock function, as that will affect the state of the mock object and Google Mock. + +## Writing New Parameterized Matchers Quickly ## + +Sometimes you'll want to define a matcher that has parameters. For that you +can use the macro: +``` +MATCHER_P(name, param_name, description_string) { statements; } +``` +where the description string can be either `""` or a string expression +that references `negation` and `param_name`. + +For example: +``` +MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +``` +will allow you to write: +``` + EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +``` +which may lead to this message (assuming `n` is 10): +``` + Value of: Blah("a") + Expected: has absolute value 10 + Actual: -9 +``` + +Note that both the matcher description and its parameter are +printed, making the message human-friendly. + +In the matcher definition body, you can write `foo_type` to +reference the type of a parameter named `foo`. For example, in the +body of `MATCHER_P(HasAbsoluteValue, value)` above, you can write +`value_type` to refer to the type of `value`. + +Google Mock also provides `MATCHER_P2`, `MATCHER_P3`, ..., up to +`MATCHER_P10` to support multi-parameter matchers: +``` +MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; } +``` + +Please note that the custom description string is for a particular +**instance** of the matcher, where the parameters have been bound to +actual values. Therefore usually you'll want the parameter values to +be part of the description. Google Mock lets you do that by +referencing the matcher parameters in the description string +expression. + +For example, +``` + using ::testing::PrintToString; + MATCHER_P2(InClosedRange, low, hi, + std::string(negation ? "isn't" : "is") + " in range [" + + PrintToString(low) + ", " + PrintToString(hi) + "]") { + return low <= arg && arg <= hi; + } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the message: +``` + Expected: is in range [4, 6] +``` + +If you specify `""` as the description, the failure message will +contain the sequence of words in the matcher name followed by the +parameter values printed as a tuple. For example, +``` + MATCHER_P2(InClosedRange, low, hi, "") { ... } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the text: +``` + Expected: in closed range (4, 6) +``` + +For the purpose of typing, you can view +``` +MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +``` +as shorthand for +``` +template +FooMatcherPk +Foo(p1_type p1, ..., pk_type pk) { ... } +``` + +When you write `Foo(v1, ..., vk)`, the compiler infers the types of +the parameters `v1`, ..., and `vk` for you. If you are not happy with +the result of the type inference, you can specify the types by +explicitly instantiating the template, as in `Foo(5, false)`. +As said earlier, you don't get to (or need to) specify +`arg_type` as that's determined by the context in which the matcher +is used. + +You can assign the result of expression `Foo(p1, ..., pk)` to a +variable of type `FooMatcherPk`. This can be +useful when composing matchers. Matchers that don't have a parameter +or have only one parameter have special types: you can assign `Foo()` +to a `FooMatcher`-typed variable, and assign `Foo(p)` to a +`FooMatcherP`-typed variable. + +While you can instantiate a matcher template with reference types, +passing the parameters by pointer usually makes your code more +readable. If, however, you still want to pass a parameter by +reference, be aware that in the failure message generated by the +matcher you will see the value of the referenced object but not its +address. + +You can overload matchers with different numbers of parameters: +``` +MATCHER_P(Blah, a, description_string_1) { ... } +MATCHER_P2(Blah, a, b, description_string_2) { ... } +``` + +While it's tempting to always use the `MATCHER*` macros when defining +a new matcher, you should also consider implementing +`MatcherInterface` or using `MakePolymorphicMatcher()` instead (see +the recipes that follow), especially if you need to use the matcher a +lot. While these approaches require more work, they give you more +control on the types of the value being matched and the matcher +parameters, which in general leads to better compiler error messages +that pay off in the long run. They also allow overloading matchers +based on parameter types (as opposed to just based on the number of +parameters). + +## Writing New Monomorphic Matchers ## + +A matcher of argument type `T` implements +`::testing::MatcherInterface` and does two things: it tests whether a +value of type `T` matches the matcher, and can describe what kind of +values it matches. The latter ability is used for generating readable +error messages when expectations are violated. + +The interface looks like this: + +``` +class MatchResultListener { + public: + ... + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x); + + // Returns the underlying ostream. + ::std::ostream* stream(); +}; + +template +class MatcherInterface { + public: + virtual ~MatcherInterface(); + + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Describes this matcher to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. + virtual void DescribeNegationTo(::std::ostream* os) const; +}; +``` + +If you need a custom matcher but `Truly()` is not a good option (for +example, you may not be happy with the way `Truly(predicate)` +describes itself, or you may want your matcher to be polymorphic as +`Eq(value)` is), you can define a matcher to do whatever you want in +two steps: first implement the matcher interface, and then define a +factory function to create a matcher instance. The second step is not +strictly needed but it makes the syntax of using the matcher nicer. + +For example, you can define a matcher to test whether an `int` is +divisible by 7 and then use it like this: +``` +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, MatchResultListener* listener) const { + return (n % 7) == 0; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "is divisible by 7"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "is not divisible by 7"; + } +}; + +inline Matcher DivisibleBy7() { + return MakeMatcher(new DivisibleBy7Matcher); +} +... + + EXPECT_CALL(foo, Bar(DivisibleBy7())); +``` + +You may improve the matcher message by streaming additional +information to the `listener` argument in `MatchAndExplain()`: + +``` +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, + MatchResultListener* listener) const { + const int remainder = n % 7; + if (remainder != 0) { + *listener << "the remainder is " << remainder; + } + return remainder == 0; + } + ... +}; +``` + +Then, `EXPECT_THAT(x, DivisibleBy7());` may general a message like this: +``` +Value of: x +Expected: is divisible by 7 + Actual: 23 (the remainder is 2) +``` + +## Writing New Polymorphic Matchers ## + +You've learned how to write your own matchers in the previous +recipe. Just one problem: a matcher created using `MakeMatcher()` only +works for one particular type of arguments. If you want a +_polymorphic_ matcher that works with arguments of several types (for +instance, `Eq(x)` can be used to match a `value` as long as `value` == +`x` compiles -- `value` and `x` don't have to share the same type), +you can learn the trick from `"gmock/gmock-matchers.h"` but it's a bit +involved. + +Fortunately, most of the time you can define a polymorphic matcher +easily with the help of `MakePolymorphicMatcher()`. Here's how you can +define `NotNull()` as an example: + +``` +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +using ::testing::NotNull; +using ::testing::PolymorphicMatcher; + +class NotNullMatcher { + public: + // To implement a polymorphic matcher, first define a COPYABLE class + // that has three members MatchAndExplain(), DescribeTo(), and + // DescribeNegationTo(), like the following. + + // In this example, we want to use NotNull() with any pointer, so + // MatchAndExplain() accepts a pointer of any type as its first argument. + // In general, you can define MatchAndExplain() as an ordinary method or + // a method template, or even overload it. + template + bool MatchAndExplain(T* p, + MatchResultListener* /* listener */) const { + return p != NULL; + } + + // Describes the property of a value matching this matcher. + void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } + + // Describes the property of a value NOT matching this matcher. + void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } +}; + +// To construct a polymorphic matcher, pass an instance of the class +// to MakePolymorphicMatcher(). Note the return type. +inline PolymorphicMatcher NotNull() { + return MakePolymorphicMatcher(NotNullMatcher()); +} +... + + EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. +``` + +**Note:** Your polymorphic matcher class does **not** need to inherit from +`MatcherInterface` or any other class, and its methods do **not** need +to be virtual. + +Like in a monomorphic matcher, you may explain the match result by +streaming additional information to the `listener` argument in +`MatchAndExplain()`. + +## Writing New Cardinalities ## + +A cardinality is used in `Times()` to tell Google Mock how many times +you expect a call to occur. It doesn't have to be exact. For example, +you can say `AtLeast(5)` or `Between(2, 4)`. + +If the built-in set of cardinalities doesn't suit you, you are free to +define your own by implementing the following interface (in namespace +`testing`): + +``` +class CardinalityInterface { + public: + virtual ~CardinalityInterface(); + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; +``` + +For example, to specify that a call must occur even number of times, +you can write + +``` +using ::testing::Cardinality; +using ::testing::CardinalityInterface; +using ::testing::MakeCardinality; + +class EvenNumberCardinality : public CardinalityInterface { + public: + virtual bool IsSatisfiedByCallCount(int call_count) const { + return (call_count % 2) == 0; + } + + virtual bool IsSaturatedByCallCount(int call_count) const { + return false; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "called even number of times"; + } +}; + +Cardinality EvenNumber() { + return MakeCardinality(new EvenNumberCardinality); +} +... + + EXPECT_CALL(foo, Bar(3)) + .Times(EvenNumber()); +``` + +## Writing New Actions Quickly ## + +If the built-in actions don't work for you, and you find it +inconvenient to use `Invoke()`, you can use a macro from the `ACTION*` +family to quickly define a new action that can be used in your code as +if it's a built-in action. + +By writing +``` +ACTION(name) { statements; } +``` +in a namespace scope (i.e. not inside a class or function), you will +define an action with the given name that executes the statements. +The value returned by `statements` will be used as the return value of +the action. Inside the statements, you can refer to the K-th +(0-based) argument of the mock function as `argK`. For example: +``` +ACTION(IncrementArg1) { return ++(*arg1); } +``` +allows you to write +``` +... WillOnce(IncrementArg1()); +``` + +Note that you don't need to specify the types of the mock function +arguments. Rest assured that your code is type-safe though: +you'll get a compiler error if `*arg1` doesn't support the `++` +operator, or if the type of `++(*arg1)` isn't compatible with the mock +function's return type. + +Another example: +``` +ACTION(Foo) { + (*arg2)(5); + Blah(); + *arg1 = 0; + return arg0; +} +``` +defines an action `Foo()` that invokes argument #2 (a function pointer) +with 5, calls function `Blah()`, sets the value pointed to by argument +#1 to 0, and returns argument #0. + +For more convenience and flexibility, you can also use the following +pre-defined symbols in the body of `ACTION`: + +| `argK_type` | The type of the K-th (0-based) argument of the mock function | +|:------------|:-------------------------------------------------------------| +| `args` | All arguments of the mock function as a tuple | +| `args_type` | The type of all arguments of the mock function as a tuple | +| `return_type` | The return type of the mock function | +| `function_type` | The type of the mock function | + +For example, when using an `ACTION` as a stub action for mock function: +``` +int DoSomething(bool flag, int* ptr); +``` +we have: +| **Pre-defined Symbol** | **Is Bound To** | +|:-----------------------|:----------------| +| `arg0` | the value of `flag` | +| `arg0_type` | the type `bool` | +| `arg1` | the value of `ptr` | +| `arg1_type` | the type `int*` | +| `args` | the tuple `(flag, ptr)` | +| `args_type` | the type `std::tr1::tuple` | +| `return_type` | the type `int` | +| `function_type` | the type `int(bool, int*)` | + +## Writing New Parameterized Actions Quickly ## + +Sometimes you'll want to parameterize an action you define. For that +we have another macro +``` +ACTION_P(name, param) { statements; } +``` + +For example, +``` +ACTION_P(Add, n) { return arg0 + n; } +``` +will allow you to write +``` +// Returns argument #0 + 5. +... WillOnce(Add(5)); +``` + +For convenience, we use the term _arguments_ for the values used to +invoke the mock function, and the term _parameters_ for the values +used to instantiate an action. + +Note that you don't need to provide the type of the parameter either. +Suppose the parameter is named `param`, you can also use the +Google-Mock-defined symbol `param_type` to refer to the type of the +parameter as inferred by the compiler. For example, in the body of +`ACTION_P(Add, n)` above, you can write `n_type` for the type of `n`. + +Google Mock also provides `ACTION_P2`, `ACTION_P3`, and etc to support +multi-parameter actions. For example, +``` +ACTION_P2(ReturnDistanceTo, x, y) { + double dx = arg0 - x; + double dy = arg1 - y; + return sqrt(dx*dx + dy*dy); +} +``` +lets you write +``` +... WillOnce(ReturnDistanceTo(5.0, 26.5)); +``` + +You can view `ACTION` as a degenerated parameterized action where the +number of parameters is 0. + +You can also easily define actions overloaded on the number of parameters: +``` +ACTION_P(Plus, a) { ... } +ACTION_P2(Plus, a, b) { ... } +``` + +## Restricting the Type of an Argument or Parameter in an ACTION ## + +For maximum brevity and reusability, the `ACTION*` macros don't ask +you to provide the types of the mock function arguments and the action +parameters. Instead, we let the compiler infer the types for us. + +Sometimes, however, we may want to be more explicit about the types. +There are several tricks to do that. For example: +``` +ACTION(Foo) { + // Makes sure arg0 can be converted to int. + int n = arg0; + ... use n instead of arg0 here ... +} + +ACTION_P(Bar, param) { + // Makes sure the type of arg1 is const char*. + ::testing::StaticAssertTypeEq(); + + // Makes sure param can be converted to bool. + bool flag = param; +} +``` +where `StaticAssertTypeEq` is a compile-time assertion in Google Test +that verifies two types are the same. + +## Writing New Action Templates Quickly ## + +Sometimes you want to give an action explicit template parameters that +cannot be inferred from its value parameters. `ACTION_TEMPLATE()` +supports that and can be viewed as an extension to `ACTION()` and +`ACTION_P*()`. + +The syntax: +``` +ACTION_TEMPLATE(ActionName, + HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), + AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +``` + +defines an action template that takes _m_ explicit template parameters +and _n_ value parameters, where _m_ is between 1 and 10, and _n_ is +between 0 and 10. `name_i` is the name of the i-th template +parameter, and `kind_i` specifies whether it's a `typename`, an +integral constant, or a template. `p_i` is the name of the i-th value +parameter. + +Example: +``` +// DuplicateArg(output) converts the k-th argument of the mock +// function to type T and copies it to *output. +ACTION_TEMPLATE(DuplicateArg, + // Note the comma between int and k: + HAS_2_TEMPLATE_PARAMS(int, k, typename, T), + AND_1_VALUE_PARAMS(output)) { + *output = T(std::tr1::get(args)); +} +``` + +To create an instance of an action template, write: +``` + ActionName(v1, ..., v_n) +``` +where the `t`s are the template arguments and the +`v`s are the value arguments. The value argument +types are inferred by the compiler. For example: +``` +using ::testing::_; +... + int n; + EXPECT_CALL(mock, Foo(_, _)) + .WillOnce(DuplicateArg<1, unsigned char>(&n)); +``` + +If you want to explicitly specify the value argument types, you can +provide additional template arguments: +``` + ActionName(v1, ..., v_n) +``` +where `u_i` is the desired type of `v_i`. + +`ACTION_TEMPLATE` and `ACTION`/`ACTION_P*` can be overloaded on the +number of value parameters, but not on the number of template +parameters. Without the restriction, the meaning of the following is +unclear: + +``` + OverloadedAction(x); +``` + +Are we using a single-template-parameter action where `bool` refers to +the type of `x`, or a two-template-parameter action where the compiler +is asked to infer the type of `x`? + +## Using the ACTION Object's Type ## + +If you are writing a function that returns an `ACTION` object, you'll +need to know its type. The type depends on the macro used to define +the action and the parameter types. The rule is relatively simple: +| **Given Definition** | **Expression** | **Has Type** | +|:---------------------|:---------------|:-------------| +| `ACTION(Foo)` | `Foo()` | `FooAction` | +| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | +| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | +| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `FooActionP` | +| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | +| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz(bool_value, int_value)` | `FooActionP2` | +| ... | ... | ... | + +Note that we have to pick different suffixes (`Action`, `ActionP`, +`ActionP2`, and etc) for actions with different numbers of value +parameters, or the action definitions cannot be overloaded on the +number of them. + +## Writing New Monomorphic Actions ## + +While the `ACTION*` macros are very convenient, sometimes they are +inappropriate. For example, despite the tricks shown in the previous +recipes, they don't let you directly specify the types of the mock +function arguments and the action parameters, which in general leads +to unoptimized compiler error messages that can baffle unfamiliar +users. They also don't allow overloading actions based on parameter +types without jumping through some hoops. + +An alternative to the `ACTION*` macros is to implement +`::testing::ActionInterface`, where `F` is the type of the mock +function in which the action will be used. For example: + +``` +template class ActionInterface { + public: + virtual ~ActionInterface(); + + // Performs the action. Result is the return type of function type + // F, and ArgumentTuple is the tuple of arguments of F. + // + // For example, if F is int(bool, const string&), then Result would + // be int, and ArgumentTuple would be tr1::tuple. + virtual Result Perform(const ArgumentTuple& args) = 0; +}; + +using ::testing::_; +using ::testing::Action; +using ::testing::ActionInterface; +using ::testing::MakeAction; + +typedef int IncrementMethod(int*); + +class IncrementArgumentAction : public ActionInterface { + public: + virtual int Perform(const tr1::tuple& args) { + int* p = tr1::get<0>(args); // Grabs the first argument. + return *p++; + } +}; + +Action IncrementArgument() { + return MakeAction(new IncrementArgumentAction); +} +... + + EXPECT_CALL(foo, Baz(_)) + .WillOnce(IncrementArgument()); + + int n = 5; + foo.Baz(&n); // Should return 5 and change n to 6. +``` + +## Writing New Polymorphic Actions ## + +The previous recipe showed you how to define your own action. This is +all good, except that you need to know the type of the function in +which the action will be used. Sometimes that can be a problem. For +example, if you want to use the action in functions with _different_ +types (e.g. like `Return()` and `SetArgPointee()`). + +If an action can be used in several types of mock functions, we say +it's _polymorphic_. The `MakePolymorphicAction()` function template +makes it easy to define such an action: + +``` +namespace testing { + +template +PolymorphicAction MakePolymorphicAction(const Impl& impl); + +} // namespace testing +``` + +As an example, let's define an action that returns the second argument +in the mock function's argument list. The first step is to define an +implementation class: + +``` +class ReturnSecondArgumentAction { + public: + template + Result Perform(const ArgumentTuple& args) const { + // To get the i-th (0-based) argument, use tr1::get(args). + return tr1::get<1>(args); + } +}; +``` + +This implementation class does _not_ need to inherit from any +particular class. What matters is that it must have a `Perform()` +method template. This method template takes the mock function's +arguments as a tuple in a **single** argument, and returns the result of +the action. It can be either `const` or not, but must be invokable +with exactly one template argument, which is the result type. In other +words, you must be able to call `Perform(args)` where `R` is the +mock function's return type and `args` is its arguments in a tuple. + +Next, we use `MakePolymorphicAction()` to turn an instance of the +implementation class into the polymorphic action we need. It will be +convenient to have a wrapper for this: + +``` +using ::testing::MakePolymorphicAction; +using ::testing::PolymorphicAction; + +PolymorphicAction ReturnSecondArgument() { + return MakePolymorphicAction(ReturnSecondArgumentAction()); +} +``` + +Now, you can use this polymorphic action the same way you use the +built-in ones: + +``` +using ::testing::_; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, int(bool flag, int n)); + MOCK_METHOD3(DoThat, string(int x, const char* str1, const char* str2)); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(ReturnSecondArgument()); + EXPECT_CALL(foo, DoThat(_, _, _)) + .WillOnce(ReturnSecondArgument()); + ... + foo.DoThis(true, 5); // Will return 5. + foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". +``` + +## Teaching Google Mock How to Print Your Values ## + +When an uninteresting or unexpected call occurs, Google Mock prints the +argument values and the stack trace to help you debug. Assertion +macros like `EXPECT_THAT` and `EXPECT_EQ` also print the values in +question when the assertion fails. Google Mock and Google Test do this using +Google Test's user-extensible value printer. + +This printer knows how to print built-in C++ types, native arrays, STL +containers, and any type that supports the `<<` operator. For other +types, it prints the raw bytes in the value and hopes that you the +user can figure it out. +[Google Test's advanced guide](http://code.google.com/p/googletest/wiki/V1_6_AdvancedGuide#Teaching_Google_Test_How_to_Print_Your_Values) +explains how to extend the printer to do a better job at +printing your particular type than to dump the bytes. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/Documentation.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/Documentation.md new file mode 100644 index 00000000..dcc9156c --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/Documentation.md @@ -0,0 +1,12 @@ +This page lists all documentation wiki pages for Google Mock **1.6** +- **if you use a released version of Google Mock, please read the documentation for that specific version instead.** + + * [ForDummies](V1_6_ForDummies.md) -- start here if you are new to Google Mock. + * [CheatSheet](V1_6_CheatSheet.md) -- a quick reference. + * [CookBook](V1_6_CookBook.md) -- recipes for doing various tasks using Google Mock. + * [FrequentlyAskedQuestions](V1_6_FrequentlyAskedQuestions.md) -- check here before asking a question on the mailing list. + +To contribute code to Google Mock, read: + + * [DevGuide](DevGuide.md) -- read this _before_ writing your first patch. + * [Pump Manual](http://code.google.com/p/googletest/wiki/V1_6_PumpManual) -- how we generate some of Google Mock's source files. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/ForDummies.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/ForDummies.md new file mode 100644 index 00000000..0891b8c4 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/ForDummies.md @@ -0,0 +1,439 @@ + + +(**Note:** If you get compiler errors that you don't understand, be sure to consult [Google Mock Doctor](http://code.google.com/p/googlemock/wiki/V1_6_FrequentlyAskedQuestions#How_am_I_supposed_to_make_sense_of_these_horrible_template_error).) + +# What Is Google C++ Mocking Framework? # +When you write a prototype or test, often it's not feasible or wise to rely on real objects entirely. A **mock object** implements the same interface as a real object (so it can be used as one), but lets you specify at run time how it will be used and what it should do (which methods will be called? in which order? how many times? with what arguments? what will they return? etc). + +**Note:** It is easy to confuse the term _fake objects_ with mock objects. Fakes and mocks actually mean very different things in the Test-Driven Development (TDD) community: + + * **Fake** objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake. + * **Mocks** are objects pre-programmed with _expectations_, which form a specification of the calls they are expected to receive. + +If all this seems too abstract for you, don't worry - the most important thing to remember is that a mock allows you to check the _interaction_ between itself and code that uses it. The difference between fakes and mocks will become much clearer once you start to use mocks. + +**Google C++ Mocking Framework** (or **Google Mock** for short) is a library (sometimes we also call it a "framework" to make it sound cool) for creating mock classes and using them. It does to C++ what [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/) do to Java. + +Using Google Mock involves three basic steps: + + 1. Use some simple macros to describe the interface you want to mock, and they will expand to the implementation of your mock class; + 1. Create some mock objects and specify its expectations and behavior using an intuitive syntax; + 1. Exercise code that uses the mock objects. Google Mock will catch any violation of the expectations as soon as it arises. + +# Why Google Mock? # +While mock objects help you remove unnecessary dependencies in tests and make them fast and reliable, using mocks manually in C++ is _hard_: + + * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distance to avoid it. + * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad hoc restrictions. + * The knowledge you gained from using one mock doesn't transfer to the next. + +In contrast, Java and Python programmers have some fine mock frameworks, which automate the creation of mocks. As a result, mocking is a proven effective technique and widely adopted practice in those communities. Having the right tool absolutely makes the difference. + +Google Mock was built to help C++ programmers. It was inspired by [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/), but designed with C++'s specifics in mind. It is your friend if any of the following problems is bothering you: + + * You are stuck with a sub-optimal design and wish you had done more prototyping before it was too late, but prototyping in C++ is by no means "rapid". + * Your tests are slow as they depend on too many libraries or use expensive resources (e.g. a database). + * Your tests are brittle as some resources they use are unreliable (e.g. the network). + * You want to test how your code handles a failure (e.g. a file checksum error), but it's not easy to cause one. + * You need to make sure that your module interacts with other modules in the right way, but it's hard to observe the interaction; therefore you resort to observing the side effects at the end of the action, which is awkward at best. + * You want to "mock out" your dependencies, except that they don't have mock implementations yet; and, frankly, you aren't thrilled by some of those hand-written mocks. + +We encourage you to use Google Mock as: + + * a _design_ tool, for it lets you experiment with your interface design early and often. More iterations lead to better designs! + * a _testing_ tool to cut your tests' outbound dependencies and probe the interaction between your module and its collaborators. + +# Getting Started # +Using Google Mock is easy! Inside your C++ source file, just #include `"gtest/gtest.h"` and `"gmock/gmock.h"`, and you are ready to go. + +# A Case for Mock Turtles # +Let's look at an example. Suppose you are developing a graphics program that relies on a LOGO-like API for drawing. How would you test that it does the right thing? Well, you can run it and compare the screen with a golden screen snapshot, but let's admit it: tests like this are expensive to run and fragile (What if you just upgraded to a shiny new graphics card that has better anti-aliasing? Suddenly you have to update all your golden images.). It would be too painful if all your tests are like this. Fortunately, you learned about Dependency Injection and know the right thing to do: instead of having your application talk to the drawing API directly, wrap the API in an interface (say, `Turtle`) and code to that interface: + +``` +class Turtle { + ... + virtual ~Turtle() {} + virtual void PenUp() = 0; + virtual void PenDown() = 0; + virtual void Forward(int distance) = 0; + virtual void Turn(int degrees) = 0; + virtual void GoTo(int x, int y) = 0; + virtual int GetX() const = 0; + virtual int GetY() const = 0; +}; +``` + +(Note that the destructor of `Turtle` **must** be virtual, as is the case for **all** classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you'll get corrupted program states like memory leaks.) + +You can control whether the turtle's movement will leave a trace using `PenUp()` and `PenDown()`, and control its movement using `Forward()`, `Turn()`, and `GoTo()`. Finally, `GetX()` and `GetY()` tell you the current position of the turtle. + +Your program will normally use a real implementation of this interface. In tests, you can use a mock implementation instead. This allows you to easily check what drawing primitives your program is calling, with what arguments, and in which order. Tests written this way are much more robust (they won't break because your new machine does anti-aliasing differently), easier to read and maintain (the intent of a test is expressed in the code, not in some binary images), and run _much, much faster_. + +# Writing the Mock Class # +If you are lucky, the mocks you need to use have already been implemented by some nice people. If, however, you find yourself in the position to write a mock class, relax - Google Mock turns this task into a fun game! (Well, almost.) + +## How to Define It ## +Using the `Turtle` interface as example, here are the simple steps you need to follow: + + 1. Derive a class `MockTurtle` from `Turtle`. + 1. Take a _virtual_ function of `Turtle` (while it's possible to [mock non-virtual methods using templates](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Mocking_Nonvirtual_Methods), it's much more involved). Count how many arguments it has. + 1. In the `public:` section of the child class, write `MOCK_METHODn();` (or `MOCK_CONST_METHODn();` if you are mocking a `const` method), where `n` is the number of the arguments; if you counted wrong, shame on you, and a compiler error will tell you so. + 1. Now comes the fun part: you take the function signature, cut-and-paste the _function name_ as the _first_ argument to the macro, and leave what's left as the _second_ argument (in case you're curious, this is the _type of the function_). + 1. Repeat until all virtual functions you want to mock are done. + +After the process, you should have something like: + +``` +#include "gmock/gmock.h" // Brings in Google Mock. +class MockTurtle : public Turtle { + public: + ... + MOCK_METHOD0(PenUp, void()); + MOCK_METHOD0(PenDown, void()); + MOCK_METHOD1(Forward, void(int distance)); + MOCK_METHOD1(Turn, void(int degrees)); + MOCK_METHOD2(GoTo, void(int x, int y)); + MOCK_CONST_METHOD0(GetX, int()); + MOCK_CONST_METHOD0(GetY, int()); +}; +``` + +You don't need to define these mock methods somewhere else - the `MOCK_METHOD*` macros will generate the definitions for you. It's that simple! Once you get the hang of it, you can pump out mock classes faster than your source-control system can handle your check-ins. + +**Tip:** If even this is too much work for you, you'll find the +`gmock_gen.py` tool in Google Mock's `scripts/generator/` directory (courtesy of the [cppclean](http://code.google.com/p/cppclean/) project) useful. This command-line +tool requires that you have Python 2.4 installed. You give it a C++ file and the name of an abstract class defined in it, +and it will print the definition of the mock class for you. Due to the +complexity of the C++ language, this script may not always work, but +it can be quite handy when it does. For more details, read the [user documentation](http://code.google.com/p/googlemock/source/browse/trunk/scripts/generator/README). + +## Where to Put It ## +When you define a mock class, you need to decide where to put its definition. Some people put it in a `*_test.cc`. This is fine when the interface being mocked (say, `Foo`) is owned by the same person or team. Otherwise, when the owner of `Foo` changes it, your test could break. (You can't really expect `Foo`'s maintainer to fix every test that uses `Foo`, can you?) + +So, the rule of thumb is: if you need to mock `Foo` and it's owned by others, define the mock class in `Foo`'s package (better, in a `testing` sub-package such that you can clearly separate production code and testing utilities), and put it in a `mock_foo.h`. Then everyone can reference `mock_foo.h` from their tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and only tests that depend on the changed methods need to be fixed. + +Another way to do it: you can introduce a thin layer `FooAdaptor` on top of `Foo` and code to this new interface. Since you own `FooAdaptor`, you can absorb changes in `Foo` much more easily. While this is more work initially, carefully choosing the adaptor interface can make your code easier to write and more readable (a net win in the long run), as you can choose `FooAdaptor` to fit your specific domain much better than `Foo` does. + +# Using Mocks in Tests # +Once you have a mock class, using it is easy. The typical work flow is: + + 1. Import the Google Mock names from the `testing` namespace such that you can use them unqualified (You only have to do it once per file. Remember that namespaces are a good idea and good for your health.). + 1. Create some mock objects. + 1. Specify your expectations on them (How many times will a method be called? With what arguments? What should it do? etc.). + 1. Exercise some code that uses the mocks; optionally, check the result using Google Test assertions. If a mock method is called more than expected or with wrong arguments, you'll get an error immediately. + 1. When a mock is destructed, Google Mock will automatically check whether all expectations on it have been satisfied. + +Here's an example: + +``` +#include "path/to/mock-turtle.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +using ::testing::AtLeast; // #1 + +TEST(PainterTest, CanDrawSomething) { + MockTurtle turtle; // #2 + EXPECT_CALL(turtle, PenDown()) // #3 + .Times(AtLeast(1)); + + Painter painter(&turtle); // #4 + + EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); +} // #5 + +int main(int argc, char** argv) { + // The following line must be executed to initialize Google Mock + // (and Google Test) before running the tests. + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} +``` + +As you might have guessed, this test checks that `PenDown()` is called at least once. If the `painter` object didn't call this method, your test will fail with a message like this: + +``` +path/to/my_test.cc:119: Failure +Actual function call count doesn't match this expectation: +Actually: never called; +Expected: called at least once. +``` + +**Tip 1:** If you run the test from an Emacs buffer, you can hit `` on the line number displayed in the error message to jump right to the failed expectation. + +**Tip 2:** If your mock objects are never deleted, the final verification won't happen. Therefore it's a good idea to use a heap leak checker in your tests when you allocate mocks on the heap. + +**Important note:** Google Mock requires expectations to be set **before** the mock functions are called, otherwise the behavior is **undefined**. In particular, you mustn't interleave `EXPECT_CALL()`s and calls to the mock functions. + +This means `EXPECT_CALL()` should be read as expecting that a call will occur _in the future_, not that a call has occurred. Why does Google Mock work like that? Well, specifying the expectation beforehand allows Google Mock to report a violation as soon as it arises, when the context (stack trace, etc) is still available. This makes debugging much easier. + +Admittedly, this test is contrived and doesn't do much. You can easily achieve the same effect without using Google Mock. However, as we shall reveal soon, Google Mock allows you to do _much more_ with the mocks. + +## Using Google Mock with Any Testing Framework ## +If you want to use something other than Google Test (e.g. [CppUnit](http://apps.sourceforge.net/mediawiki/cppunit/index.php?title=Main_Page) or +[CxxTest](http://cxxtest.tigris.org/)) as your testing framework, just change the `main()` function in the previous section to: +``` +int main(int argc, char** argv) { + // The following line causes Google Mock to throw an exception on failure, + // which will be interpreted by your testing framework as a test failure. + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + ... whatever your testing framework requires ... +} +``` + +This approach has a catch: it makes Google Mock throw an exception +from a mock object's destructor sometimes. With some compilers, this +sometimes causes the test program to crash. You'll still be able to +notice that the test has failed, but it's not a graceful failure. + +A better solution is to use Google Test's +[event listener API](http://code.google.com/p/googletest/wiki/V1_6_AdvancedGuide#Extending_Google_Test_by_Handling_Test_Events) +to report a test failure to your testing framework properly. You'll need to +implement the `OnTestPartResult()` method of the event listener interface, but it +should be straightforward. + +If this turns out to be too much work, we suggest that you stick with +Google Test, which works with Google Mock seamlessly (in fact, it is +technically part of Google Mock.). If there is a reason that you +cannot use Google Test, please let us know. + +# Setting Expectations # +The key to using a mock object successfully is to set the _right expectations_ on it. If you set the expectations too strict, your test will fail as the result of unrelated changes. If you set them too loose, bugs can slip through. You want to do it just right such that your test can catch exactly the kind of bugs you intend it to catch. Google Mock provides the necessary means for you to do it "just right." + +## General Syntax ## +In Google Mock we use the `EXPECT_CALL()` macro to set an expectation on a mock method. The general syntax is: + +``` +EXPECT_CALL(mock_object, method(matchers)) + .Times(cardinality) + .WillOnce(action) + .WillRepeatedly(action); +``` + +The macro has two arguments: first the mock object, and then the method and its arguments. Note that the two are separated by a comma (`,`), not a period (`.`). (Why using a comma? The answer is that it was necessary for technical reasons.) + +The macro can be followed by some optional _clauses_ that provide more information about the expectation. We'll discuss how each clause works in the coming sections. + +This syntax is designed to make an expectation read like English. For example, you can probably guess that + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .Times(5) + .WillOnce(Return(100)) + .WillOnce(Return(150)) + .WillRepeatedly(Return(200)); +``` + +says that the `turtle` object's `GetX()` method will be called five times, it will return 100 the first time, 150 the second time, and then 200 every time. Some people like to call this style of syntax a Domain-Specific Language (DSL). + +**Note:** Why do we use a macro to do this? It serves two purposes: first it makes expectations easily identifiable (either by `grep` or by a human reader), and second it allows Google Mock to include the source file location of a failed expectation in messages, making debugging easier. + +## Matchers: What Arguments Do We Expect? ## +When a mock function takes arguments, we must specify what arguments we are expecting; for example: + +``` +// Expects the turtle to move forward by 100 units. +EXPECT_CALL(turtle, Forward(100)); +``` + +Sometimes you may not want to be too specific (Remember that talk about tests being too rigid? Over specification leads to brittle tests and obscures the intent of tests. Therefore we encourage you to specify only what's necessary - no more, no less.). If you care to check that `Forward()` will be called but aren't interested in its actual argument, write `_` as the argument, which means "anything goes": + +``` +using ::testing::_; +... +// Expects the turtle to move forward. +EXPECT_CALL(turtle, Forward(_)); +``` + +`_` is an instance of what we call **matchers**. A matcher is like a predicate and can test whether an argument is what we'd expect. You can use a matcher inside `EXPECT_CALL()` wherever a function argument is expected. + +A list of built-in matchers can be found in the [CheatSheet](V1_6_CheatSheet.md). For example, here's the `Ge` (greater than or equal) matcher: + +``` +using ::testing::Ge;... +EXPECT_CALL(turtle, Forward(Ge(100))); +``` + +This checks that the turtle will be told to go forward by at least 100 units. + +## Cardinalities: How Many Times Will It Be Called? ## +The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We call its argument a **cardinality** as it tells _how many times_ the call should occur. It allows us to repeat an expectation many times without actually writing it as many times. More importantly, a cardinality can be "fuzzy", just like a matcher can be. This allows a user to express the intent of a test exactly. + +An interesting special case is when we say `Times(0)`. You may have guessed - it means that the function shouldn't be called with the given arguments at all, and Google Mock will report a Google Test failure whenever the function is (wrongfully) called. + +We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the list of built-in cardinalities you can use, see the [CheatSheet](V1_6_CheatSheet.md). + +The `Times()` clause can be omitted. **If you omit `Times()`, Google Mock will infer the cardinality for you.** The rules are easy to remember: + + * If **neither** `WillOnce()` **nor** `WillRepeatedly()` is in the `EXPECT_CALL()`, the inferred cardinality is `Times(1)`. + * If there are `n WillOnce()`'s but **no** `WillRepeatedly()`, where `n` >= 1, the cardinality is `Times(n)`. + * If there are `n WillOnce()`'s and **one** `WillRepeatedly()`, where `n` >= 0, the cardinality is `Times(AtLeast(n))`. + +**Quick quiz:** what do you think will happen if a function is expected to be called twice but actually called four times? + +## Actions: What Should It Do? ## +Remember that a mock object doesn't really have a working implementation? We as users have to tell it what to do when a method is invoked. This is easy in Google Mock. + +First, if the return type of a mock function is a built-in type or a pointer, the function has a **default action** (a `void` function will just return, a `bool` function will return `false`, and other functions will return 0). If you don't say anything, this behavior will be used. + +Second, if a mock function doesn't have a default action, or the default action doesn't suit you, you can specify the action to be taken each time the expectation matches using a series of `WillOnce()` clauses followed by an optional `WillRepeatedly()`. For example, + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillOnce(Return(300)); +``` + +This says that `turtle.GetX()` will be called _exactly three times_ (Google Mock inferred this from how many `WillOnce()` clauses we've written, since we didn't explicitly write `Times()`), and will return 100, 200, and 300 respectively. + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillRepeatedly(Return(300)); +``` + +says that `turtle.GetY()` will be called _at least twice_ (Google Mock knows this as we've written two `WillOnce()` clauses and a `WillRepeatedly()` while having no explicit `Times()`), will return 100 the first time, 200 the second time, and 300 from the third time on. + +Of course, if you explicitly write a `Times()`, Google Mock will not try to infer the cardinality itself. What if the number you specified is larger than there are `WillOnce()` clauses? Well, after all `WillOnce()`s are used up, Google Mock will do the _default_ action for the function every time (unless, of course, you have a `WillRepeatedly()`.). + +What can we do inside `WillOnce()` besides `Return()`? You can return a reference using `ReturnRef(variable)`, or invoke a pre-defined function, among [others](http://code.google.com/p/googlemock/wiki/V1_6_CheatSheet#Actions). + +**Important note:** The `EXPECT_CALL()` statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want: + +``` +int n = 100; +EXPECT_CALL(turtle, GetX()) +.Times(4) +.WillRepeatedly(Return(n++)); +``` + +Instead of returning 100, 101, 102, ..., consecutively, this mock function will always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)` will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will return the same pointer every time. If you want the side effect to happen every time, you need to define a custom action, which we'll teach in the [CookBook](V1_6_CookBook.md). + +Time for another quiz! What do you think the following means? + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) +.Times(4) +.WillOnce(Return(100)); +``` + +Obviously `turtle.GetY()` is expected to be called four times. But if you think it will return 100 every time, think twice! Remember that one `WillOnce()` clause will be consumed each time the function is invoked and the default action will be taken afterwards. So the right answer is that `turtle.GetY()` will return 100 the first time, but **return 0 from the second time on**, as returning 0 is the default action for `int` functions. + +## Using Multiple Expectations ## +So far we've only shown examples where you have a single expectation. More realistically, you're going to specify expectations on multiple mock methods, which may be from multiple mock objects. + +By default, when a mock method is invoked, Google Mock will search the expectations in the **reverse order** they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones."). If the matching expectation cannot take any more calls, you will get an upper-bound-violated failure. Here's an example: + +``` +using ::testing::_;... +EXPECT_CALL(turtle, Forward(_)); // #1 +EXPECT_CALL(turtle, Forward(10)) // #2 + .Times(2); +``` + +If `Forward(10)` is called three times in a row, the third time it will be an error, as the last matching expectation (#2) has been saturated. If, however, the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, as now #1 will be the matching expectation. + +**Side note:** Why does Google Mock search for a match in the _reverse_ order of the expectations? The reason is that this allows a user to set up the default expectations in a mock object's constructor or the test fixture's set-up phase and then customize the mock by writing more specific expectations in the test body. So, if you have two expectations on the same method, you want to put the one with more specific matchers **after** the other, or the more specific rule would be shadowed by the more general one that comes after it. + +## Ordered vs Unordered Calls ## +By default, an expectation can match a call even though an earlier expectation hasn't been satisfied. In other words, the calls don't have to occur in the order the expectations are specified. + +Sometimes, you may want all the expected calls to occur in a strict order. To say this in Google Mock is easy: + +``` +using ::testing::InSequence;... +TEST(FooTest, DrawsLineSegment) { + ... + { + InSequence dummy; + + EXPECT_CALL(turtle, PenDown()); + EXPECT_CALL(turtle, Forward(100)); + EXPECT_CALL(turtle, PenUp()); + } + Foo(); +} +``` + +By creating an object of type `InSequence`, all expectations in its scope are put into a _sequence_ and have to occur _sequentially_. Since we are just relying on the constructor and destructor of this object to do the actual work, its name is really irrelevant. + +In this example, we test that `Foo()` calls the three expected functions in the order as written. If a call is made out-of-order, it will be an error. + +(What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](V1_6_CookBook.md).) + +## All Expectations Are Sticky (Unless Said Otherwise) ## +Now let's do a quick quiz to see how well you can use this mock stuff already. How would you test that the turtle is asked to go to the origin _exactly twice_ (you want to ignore any other instructions it receives)? + +After you've come up with your answer, take a look at ours and compare notes (solve it yourself first - don't cheat!): + +``` +using ::testing::_;... +EXPECT_CALL(turtle, GoTo(_, _)) // #1 + .Times(AnyNumber()); +EXPECT_CALL(turtle, GoTo(0, 0)) // #2 + .Times(2); +``` + +Suppose `turtle.GoTo(0, 0)` is called three times. In the third time, Google Mock will see that the arguments match expectation #2 (remember that we always pick the last matching expectation). Now, since we said that there should be only two such calls, Google Mock will report an error immediately. This is basically what we've told you in the "Using Multiple Expectations" section above. + +This example shows that **expectations in Google Mock are "sticky" by default**, in the sense that they remain active even after we have reached their invocation upper bounds. This is an important rule to remember, as it affects the meaning of the spec, and is **different** to how it's done in many other mocking frameworks (Why'd we do that? Because we think our rule makes the common cases easier to express and understand.). + +Simple? Let's see if you've really understood it: what does the following code say? + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)); +} +``` + +If you think it says that `turtle.GetX()` will be called `n` times and will return 10, 20, 30, ..., consecutively, think twice! The problem is that, as we said, expectations are sticky. So, the second time `turtle.GetX()` is called, the last (latest) `EXPECT_CALL()` statement will match, and will immediately lead to an "upper bound exceeded" error - this piece of code is not very useful! + +One correct way of saying that `turtle.GetX()` will return 10, 20, 30, ..., is to explicitly say that the expectations are _not_ sticky. In other words, they should _retire_ as soon as they are saturated: + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); +} +``` + +And, there's a better way to do it: in this case, we expect the calls to occur in a specific order, and we line up the actions to match the order. Since the order is important here, we should make it explicit using a sequence: + +``` +using ::testing::InSequence; +using ::testing::Return; +... +{ + InSequence s; + + for (int i = 1; i <= n; i++) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); + } +} +``` + +By the way, the other situation where an expectation may _not_ be sticky is when it's in a sequence - as soon as another expectation that comes after it in the sequence has been used, it automatically retires (and will never be used to match any call). + +## Uninteresting Calls ## +A mock object may have many methods, and not all of them are that interesting. For example, in some tests we may not care about how many times `GetX()` and `GetY()` get called. + +In Google Mock, if you are not interested in a method, just don't say anything about it. If a call to this method occurs, you'll see a warning in the test output, but it won't be a failure. + +# What Now? # +Congratulations! You've learned enough about Google Mock to start using it. Now, you might want to join the [googlemock](http://groups.google.com/group/googlemock) discussion group and actually write some tests using Google Mock - it will be fun. Hey, it may even be addictive - you've been warned. + +Then, if you feel like increasing your mock quotient, you should move on to the [CookBook](V1_6_CookBook.md). You can learn many advanced features of Google Mock there -- and advance your level of enjoyment and testing bliss. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/FrequentlyAskedQuestions.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/FrequentlyAskedQuestions.md new file mode 100644 index 00000000..f74715d2 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_6/FrequentlyAskedQuestions.md @@ -0,0 +1,628 @@ + + +Please send your questions to the +[googlemock](http://groups.google.com/group/googlemock) discussion +group. If you need help with compiler errors, make sure you have +tried [Google Mock Doctor](#How_am_I_supposed_to_make_sense_of_these_horrible_template_error.md) first. + +## When I call a method on my mock object, the method for the real object is invoked instead. What's the problem? ## + +In order for a method to be mocked, it must be _virtual_, unless you use the [high-perf dependency injection technique](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Mocking_Nonvirtual_Methods). + +## I wrote some matchers. After I upgraded to a new version of Google Mock, they no longer compile. What's going on? ## + +After version 1.4.0 of Google Mock was released, we had an idea on how +to make it easier to write matchers that can generate informative +messages efficiently. We experimented with this idea and liked what +we saw. Therefore we decided to implement it. + +Unfortunately, this means that if you have defined your own matchers +by implementing `MatcherInterface` or using `MakePolymorphicMatcher()`, +your definitions will no longer compile. Matchers defined using the +`MATCHER*` family of macros are not affected. + +Sorry for the hassle if your matchers are affected. We believe it's +in everyone's long-term interest to make this change sooner than +later. Fortunately, it's usually not hard to migrate an existing +matcher to the new API. Here's what you need to do: + +If you wrote your matcher like this: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` + +you'll need to change it to: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` +(i.e. rename `Matches()` to `MatchAndExplain()` and give it a second +argument of type `MatchResultListener*`.) + +If you were also using `ExplainMatchResultTo()` to improve the matcher +message: +``` +// Old matcher definition that doesn't work with the lastest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + + virtual void ExplainMatchResultTo(MyType value, + ::std::ostream* os) const { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Foo property is " << value.GetFoo(); + } + ... +}; +``` + +you should move the logic of `ExplainMatchResultTo()` into +`MatchAndExplain()`, using the `MatchResultListener` argument where +the `::std::ostream` was used: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Foo property is " << value.GetFoo(); + return value.GetFoo() > 5; + } + ... +}; +``` + +If your matcher is defined using `MakePolymorphicMatcher()`: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you should rename the `Matches()` method to `MatchAndExplain()` and +add a `MatchResultListener*` argument (the same as what you need to do +for matchers defined by implementing `MatcherInterface`): +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +If your polymorphic matcher uses `ExplainMatchResultTo()` for better +failure messages: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +void ExplainMatchResultTo(const MyGreatMatcher& matcher, + MyType value, + ::std::ostream* os) { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Bar property is " << value.GetBar(); +} +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you'll need to move the logic inside `ExplainMatchResultTo()` to +`MatchAndExplain()`: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Bar property is " << value.GetBar(); + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +For more information, you can read these +[two](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Writing_New_Monomorphic_Matchers) +[recipes](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Writing_New_Polymorphic_Matchers) +from the cookbook. As always, you +are welcome to post questions on `googlemock@googlegroups.com` if you +need any help. + +## When using Google Mock, do I have to use Google Test as the testing framework? I have my favorite testing framework and don't want to switch. ## + +Google Mock works out of the box with Google Test. However, it's easy +to configure it to work with any testing framework of your choice. +[Here](http://code.google.com/p/googlemock/wiki/V1_6_ForDummies#Using_Google_Mock_with_Any_Testing_Framework) is how. + +## How am I supposed to make sense of these horrible template errors? ## + +If you are confused by the compiler errors gcc threw at you, +try consulting the _Google Mock Doctor_ tool first. What it does is to +scan stdin for gcc error messages, and spit out diagnoses on the +problems (we call them diseases) your code has. + +To "install", run command: +``` +alias gmd='/scripts/gmock_doctor.py' +``` + +To use it, do: +``` + 2>&1 | gmd +``` + +For example: +``` +make my_test 2>&1 | gmd +``` + +Or you can run `gmd` and copy-n-paste gcc's error messages to it. + +## Can I mock a variadic function? ## + +You cannot mock a variadic function (i.e. a function taking ellipsis +(`...`) arguments) directly in Google Mock. + +The problem is that in general, there is _no way_ for a mock object to +know how many arguments are passed to the variadic method, and what +the arguments' types are. Only the _author of the base class_ knows +the protocol, and we cannot look into his head. + +Therefore, to mock such a function, the _user_ must teach the mock +object how to figure out the number of arguments and their types. One +way to do it is to provide overloaded versions of the function. + +Ellipsis arguments are inherited from C and not really a C++ feature. +They are unsafe to use and don't work with arguments that have +constructors or destructors. Therefore we recommend to avoid them in +C++ as much as possible. + +## MSVC gives me warning C4301 or C4373 when I define a mock method with a const parameter. Why? ## + +If you compile this using Microsoft Visual C++ 2005 SP1: +``` +class Foo { + ... + virtual void Bar(const int i) = 0; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Bar, void(const int i)); +}; +``` +You may get the following warning: +``` +warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier +``` + +This is a MSVC bug. The same code compiles fine with gcc ,for +example. If you use Visual C++ 2008 SP1, you would get the warning: +``` +warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers +``` + +In C++, if you _declare_ a function with a `const` parameter, the +`const` modifier is _ignored_. Therefore, the `Foo` base class above +is equivalent to: +``` +class Foo { + ... + virtual void Bar(int i) = 0; // int or const int? Makes no difference. +}; +``` + +In fact, you can _declare_ Bar() with an `int` parameter, and _define_ +it with a `const int` parameter. The compiler will still match them +up. + +Since making a parameter `const` is meaningless in the method +_declaration_, we recommend to remove it in both `Foo` and `MockFoo`. +That should workaround the VC bug. + +Note that we are talking about the _top-level_ `const` modifier here. +If the function parameter is passed by pointer or reference, declaring +the _pointee_ or _referee_ as `const` is still meaningful. For +example, the following two declarations are _not_ equivalent: +``` +void Bar(int* p); // Neither p nor *p is const. +void Bar(const int* p); // p is not const, but *p is. +``` + +## I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? ## + +We've noticed that when the `/clr` compiler flag is used, Visual C++ +uses 5~6 times as much memory when compiling a mock class. We suggest +to avoid `/clr` when compiling native C++ mocks. + +## I can't figure out why Google Mock thinks my expectations are not satisfied. What should I do? ## + +You might want to run your test with +`--gmock_verbose=info`. This flag lets Google Mock print a trace +of every mock function call it receives. By studying the trace, +you'll gain insights on why the expectations you set are not met. + +## How can I assert that a function is NEVER called? ## + +``` +EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +## I have a failed test where Google Mock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? ## + +When Google Mock detects a failure, it prints relevant information +(the mock function arguments, the state of relevant expectations, and +etc) to help the user debug. If another failure is detected, Google +Mock will do the same, including printing the state of relevant +expectations. + +Sometimes an expectation's state didn't change between two failures, +and you'll see the same description of the state twice. They are +however _not_ redundant, as they refer to _different points in time_. +The fact they are the same _is_ interesting information. + +## I get a heap check failure when using a mock object, but using a real object is fine. What can be wrong? ## + +Does the class (hopefully a pure interface) you are mocking have a +virtual destructor? + +Whenever you derive from a base class, make sure its destructor is +virtual. Otherwise Bad Things will happen. Consider the following +code: + +``` +class Base { + public: + // Not virtual, but should be. + ~Base() { ... } + ... +}; + +class Derived : public Base { + public: + ... + private: + std::string value_; +}; + +... + Base* p = new Derived; + ... + delete p; // Surprise! ~Base() will be called, but ~Derived() will not + // - value_ is leaked. +``` + +By changing `~Base()` to virtual, `~Derived()` will be correctly +called when `delete p` is executed, and the heap checker +will be happy. + +## The "newer expectations override older ones" rule makes writing expectations awkward. Why does Google Mock do that? ## + +When people complain about this, often they are referring to code like: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. However, I have to write the expectations in the +// reverse order. This sucks big time!!! +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); +``` + +The problem is that they didn't pick the **best** way to express the test's +intent. + +By default, expectations don't have to be matched in _any_ particular +order. If you want them to match in a certain order, you need to be +explicit. This is Google Mock's (and jMock's) fundamental philosophy: it's +easy to accidentally over-specify your tests, and we want to make it +harder to do so. + +There are two better ways to write the test spec. You could either +put the expectations in sequence: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. Using a sequence, we can write the expectations +// in their natural order. +{ + InSequence s; + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +} +``` + +or you can put the sequence of actions in the same expectation: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +``` + +Back to the original questions: why does Google Mock search the +expectations (and `ON_CALL`s) from back to front? Because this +allows a user to set up a mock's behavior for the common case early +(e.g. in the mock's constructor or the test fixture's set-up phase) +and customize it with more specific rules later. If Google Mock +searches from front to back, this very useful pattern won't be +possible. + +## Google Mock prints a warning when a function without EXPECT\_CALL is called, even if I have set its behavior using ON\_CALL. Would it be reasonable not to show the warning in this case? ## + +When choosing between being neat and being safe, we lean toward the +latter. So the answer is that we think it's better to show the +warning. + +Often people write `ON_CALL`s in the mock object's +constructor or `SetUp()`, as the default behavior rarely changes from +test to test. Then in the test body they set the expectations, which +are often different for each test. Having an `ON_CALL` in the set-up +part of a test doesn't mean that the calls are expected. If there's +no `EXPECT_CALL` and the method is called, it's possibly an error. If +we quietly let the call go through without notifying the user, bugs +may creep in unnoticed. + +If, however, you are sure that the calls are OK, you can write + +``` +EXPECT_CALL(foo, Bar(_)) + .WillRepeatedly(...); +``` + +instead of + +``` +ON_CALL(foo, Bar(_)) + .WillByDefault(...); +``` + +This tells Google Mock that you do expect the calls and no warning should be +printed. + +Also, you can control the verbosity using the `--gmock_verbose` flag. +If you find the output too noisy when debugging, just choose a less +verbose level. + +## How can I delete the mock function's argument in an action? ## + +If you find yourself needing to perform some action that's not +supported by Google Mock directly, remember that you can define your own +actions using +[MakeAction()](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Writing_New_Actions) or +[MakePolymorphicAction()](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Writing_New_Polymorphic_Actions), +or you can write a stub function and invoke it using +[Invoke()](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Using_Functions_Methods_Functors). + +## MOCK\_METHODn()'s second argument looks funny. Why don't you use the MOCK\_METHODn(Method, return\_type, arg\_1, ..., arg\_n) syntax? ## + +What?! I think it's beautiful. :-) + +While which syntax looks more natural is a subjective matter to some +extent, Google Mock's syntax was chosen for several practical advantages it +has. + +Try to mock a function that takes a map as an argument: +``` +virtual int GetSize(const map& m); +``` + +Using the proposed syntax, it would be: +``` +MOCK_METHOD1(GetSize, int, const map& m); +``` + +Guess what? You'll get a compiler error as the compiler thinks that +`const map& m` are **two**, not one, arguments. To work +around this you can use `typedef` to give the map type a name, but +that gets in the way of your work. Google Mock's syntax avoids this +problem as the function's argument types are protected inside a pair +of parentheses: +``` +// This compiles fine. +MOCK_METHOD1(GetSize, int(const map& m)); +``` + +You still need a `typedef` if the return type contains an unprotected +comma, but that's much rarer. + +Other advantages include: + 1. `MOCK_METHOD1(Foo, int, bool)` can leave a reader wonder whether the method returns `int` or `bool`, while there won't be such confusion using Google Mock's syntax. + 1. The way Google Mock describes a function type is nothing new, although many people may not be familiar with it. The same syntax was used in C, and the `function` library in `tr1` uses this syntax extensively. Since `tr1` will become a part of the new version of STL, we feel very comfortable to be consistent with it. + 1. The function type syntax is also used in other parts of Google Mock's API (e.g. the action interface) in order to make the implementation tractable. A user needs to learn it anyway in order to utilize Google Mock's more advanced features. We'd as well stick to the same syntax in `MOCK_METHOD*`! + +## My code calls a static/global function. Can I mock it? ## + +You can, but you need to make some changes. + +In general, if you find yourself needing to mock a static function, +it's a sign that your modules are too tightly coupled (and less +flexible, less reusable, less testable, etc). You are probably better +off defining a small interface and call the function through that +interface, which then can be easily mocked. It's a bit of work +initially, but usually pays for itself quickly. + +This Google Testing Blog +[post](http://googletesting.blogspot.com/2008/06/defeat-static-cling.html) +says it excellently. Check it out. + +## My mock object needs to do complex stuff. It's a lot of pain to specify the actions. Google Mock sucks! ## + +I know it's not a question, but you get an answer for free any way. :-) + +With Google Mock, you can create mocks in C++ easily. And people might be +tempted to use them everywhere. Sometimes they work great, and +sometimes you may find them, well, a pain to use. So, what's wrong in +the latter case? + +When you write a test without using mocks, you exercise the code and +assert that it returns the correct value or that the system is in an +expected state. This is sometimes called "state-based testing". + +Mocks are great for what some call "interaction-based" testing: +instead of checking the system state at the very end, mock objects +verify that they are invoked the right way and report an error as soon +as it arises, giving you a handle on the precise context in which the +error was triggered. This is often more effective and economical to +do than state-based testing. + +If you are doing state-based testing and using a test double just to +simulate the real object, you are probably better off using a fake. +Using a mock in this case causes pain, as it's not a strong point for +mocks to perform complex actions. If you experience this and think +that mocks suck, you are just not using the right tool for your +problem. Or, you might be trying to solve the wrong problem. :-) + +## I got a warning "Uninteresting function call encountered - default action taken.." Should I panic? ## + +By all means, NO! It's just an FYI. + +What it means is that you have a mock function, you haven't set any +expectations on it (by Google Mock's rule this means that you are not +interested in calls to this function and therefore it can be called +any number of times), and it is called. That's OK - you didn't say +it's not OK to call the function! + +What if you actually meant to disallow this function to be called, but +forgot to write `EXPECT_CALL(foo, Bar()).Times(0)`? While +one can argue that it's the user's fault, Google Mock tries to be nice and +prints you a note. + +So, when you see the message and believe that there shouldn't be any +uninteresting calls, you should investigate what's going on. To make +your life easier, Google Mock prints the function name and arguments +when an uninteresting call is encountered. + +## I want to define a custom action. Should I use Invoke() or implement the action interface? ## + +Either way is fine - you want to choose the one that's more convenient +for your circumstance. + +Usually, if your action is for a particular function type, defining it +using `Invoke()` should be easier; if your action can be used in +functions of different types (e.g. if you are defining +`Return(value)`), `MakePolymorphicAction()` is +easiest. Sometimes you want precise control on what types of +functions the action can be used in, and implementing +`ActionInterface` is the way to go here. See the implementation of +`Return()` in `include/gmock/gmock-actions.h` for an example. + +## I'm using the set-argument-pointee action, and the compiler complains about "conflicting return type specified". What does it mean? ## + +You got this error as Google Mock has no idea what value it should return +when the mock method is called. `SetArgPointee()` says what the +side effect is, but doesn't say what the return value should be. You +need `DoAll()` to chain a `SetArgPointee()` with a `Return()`. + +See this [recipe](http://code.google.com/p/googlemock/wiki/V1_6_CookBook#Mocking_Side_Effects) for more details and an example. + + +## My question is not in your FAQ! ## + +If you cannot find the answer to your question in this FAQ, there are +some other resources you can use: + + 1. read other [wiki pages](http://code.google.com/p/googlemock/w/list), + 1. search the mailing list [archive](http://groups.google.com/group/googlemock/topics), + 1. ask it on [googlemock@googlegroups.com](mailto:googlemock@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googlemock) before you can post.). + +Please note that creating an issue in the +[issue tracker](http://code.google.com/p/googlemock/issues/list) is _not_ +a good way to get your answer, as it is monitored infrequently by a +very small number of people. + +When asking a question, it's helpful to provide as much of the +following information as possible (people cannot help you if there's +not enough information in your question): + + * the version (or the revision number if you check out from SVN directly) of Google Mock you use (Google Mock is under active development, so it's possible that your problem has been solved in a later version), + * your operating system, + * the name and version of your compiler, + * the complete command line flags you give to your compiler, + * the complete compiler error messages (if the question is about compilation), + * the _actual_ code (ideally, a minimal but complete program) that has the problem you encounter. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/CheatSheet.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/CheatSheet.md new file mode 100644 index 00000000..db421e51 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/CheatSheet.md @@ -0,0 +1,556 @@ + + +# Defining a Mock Class # + +## Mocking a Normal Class ## + +Given +``` +class Foo { + ... + virtual ~Foo(); + virtual int GetSize() const = 0; + virtual string Describe(const char* name) = 0; + virtual string Describe(int type) = 0; + virtual bool Process(Bar elem, int count) = 0; +}; +``` +(note that `~Foo()` **must** be virtual) we can define its mock as +``` +#include "gmock/gmock.h" + +class MockFoo : public Foo { + MOCK_CONST_METHOD0(GetSize, int()); + MOCK_METHOD1(Describe, string(const char* name)); + MOCK_METHOD1(Describe, string(int type)); + MOCK_METHOD2(Process, bool(Bar elem, int count)); +}; +``` + +To create a "nice" mock object which ignores all uninteresting calls, +or a "strict" mock object, which treats them as failures: +``` +NiceMock nice_foo; // The type is a subclass of MockFoo. +StrictMock strict_foo; // The type is a subclass of MockFoo. +``` + +## Mocking a Class Template ## + +To mock +``` +template +class StackInterface { + public: + ... + virtual ~StackInterface(); + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; +``` +(note that `~StackInterface()` **must** be virtual) just append `_T` to the `MOCK_*` macros: +``` +template +class MockStack : public StackInterface { + public: + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Specifying Calling Conventions for Mock Functions ## + +If your mock function doesn't use the default calling convention, you +can specify it by appending `_WITH_CALLTYPE` to any of the macros +described in the previous two sections and supplying the calling +convention as the first argument to the macro. For example, +``` + MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n)); + MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Bar, int(double x, double y)); +``` +where `STDMETHODCALLTYPE` is defined by `` on Windows. + +# Using Mocks in Tests # + +The typical flow is: + 1. Import the Google Mock names you need to use. All Google Mock names are in the `testing` namespace unless they are macros or otherwise noted. + 1. Create the mock objects. + 1. Optionally, set the default actions of the mock objects. + 1. Set your expectations on the mock objects (How will they be called? What wil they do?). + 1. Exercise code that uses the mock objects; if necessary, check the result using [Google Test](http://code.google.com/p/googletest/) assertions. + 1. When a mock objects is destructed, Google Mock automatically verifies that all expectations on it have been satisfied. + +Here is an example: +``` +using ::testing::Return; // #1 + +TEST(BarTest, DoesThis) { + MockFoo foo; // #2 + + ON_CALL(foo, GetSize()) // #3 + .WillByDefault(Return(1)); + // ... other default actions ... + + EXPECT_CALL(foo, Describe(5)) // #4 + .Times(3) + .WillRepeatedly(Return("Category 5")); + // ... other expectations ... + + EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 +} // #6 +``` + +# Setting Default Actions # + +Google Mock has a **built-in default action** for any function that +returns `void`, `bool`, a numeric value, or a pointer. + +To customize the default action for functions with return type `T` globally: +``` +using ::testing::DefaultValue; + +DefaultValue::Set(value); // Sets the default value to be returned. +// ... use the mocks ... +DefaultValue::Clear(); // Resets the default value. +``` + +To customize the default action for a particular method, use `ON_CALL()`: +``` +ON_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .WillByDefault(action); +``` + +# Setting Expectations # + +`EXPECT_CALL()` sets **expectations** on a mock method (How will it be +called? What will it do?): +``` +EXPECT_CALL(mock_object, method(matchers)) + .With(multi_argument_matcher) ? + .Times(cardinality) ? + .InSequence(sequences) * + .After(expectations) * + .WillOnce(action) * + .WillRepeatedly(action) ? + .RetiresOnSaturation(); ? +``` + +If `Times()` is omitted, the cardinality is assumed to be: + + * `Times(1)` when there is neither `WillOnce()` nor `WillRepeatedly()`; + * `Times(n)` when there are `n WillOnce()`s but no `WillRepeatedly()`, where `n` >= 1; or + * `Times(AtLeast(n))` when there are `n WillOnce()`s and a `WillRepeatedly()`, where `n` >= 0. + +A method with no `EXPECT_CALL()` is free to be invoked _any number of times_, and the default action will be taken each time. + +# Matchers # + +A **matcher** matches a _single_ argument. You can use it inside +`ON_CALL()` or `EXPECT_CALL()`, or use it to validate a value +directly: + +| `EXPECT_THAT(value, matcher)` | Asserts that `value` matches `matcher`. | +|:------------------------------|:----------------------------------------| +| `ASSERT_THAT(value, matcher)` | The same as `EXPECT_THAT(value, matcher)`, except that it generates a **fatal** failure. | + +Built-in matchers (where `argument` is the function argument) are +divided into several categories: + +## Wildcard ## +|`_`|`argument` can be any value of the correct type.| +|:--|:-----------------------------------------------| +|`A()` or `An()`|`argument` can be any value of type `type`. | + +## Generic Comparison ## + +|`Eq(value)` or `value`|`argument == value`| +|:---------------------|:------------------| +|`Ge(value)` |`argument >= value`| +|`Gt(value)` |`argument > value` | +|`Le(value)` |`argument <= value`| +|`Lt(value)` |`argument < value` | +|`Ne(value)` |`argument != value`| +|`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| +|`NotNull()` |`argument` is a non-null pointer (raw or smart).| +|`Ref(variable)` |`argument` is a reference to `variable`.| +|`TypedEq(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| + +Except `Ref()`, these matchers make a _copy_ of `value` in case it's +modified or destructed later. If the compiler complains that `value` +doesn't have a public copy constructor, try wrap it in `ByRef()`, +e.g. `Eq(ByRef(non_copyable_value))`. If you do that, make sure +`non_copyable_value` is not changed afterwards, or the meaning of your +matcher will be changed. + +## Floating-Point Matchers ## + +|`DoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal.| +|:-------------------|:----------------------------------------------------------------------------------------------| +|`FloatEq(a_float)` |`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. | +|`NanSensitiveDoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. | +|`NanSensitiveFloatEq(a_float)`|`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. | + +The above matchers use ULP-based comparison (the same as used in +[Google Test](http://code.google.com/p/googletest/)). They +automatically pick a reasonable error bound based on the absolute +value of the expected value. `DoubleEq()` and `FloatEq()` conform to +the IEEE standard, which requires comparing two NaNs for equality to +return false. The `NanSensitive*` version instead treats two NaNs as +equal, which is often what a user wants. + +|`DoubleNear(a_double, max_abs_error)`|`argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal.| +|:------------------------------------|:--------------------------------------------------------------------------------------------------------------------| +|`FloatNear(a_float, max_abs_error)` |`argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. | +|`NanSensitiveDoubleNear(a_double, max_abs_error)`|`argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. | +|`NanSensitiveFloatNear(a_float, max_abs_error)`|`argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. | + +## String Matchers ## + +The `argument` can be either a C string or a C++ string object: + +|`ContainsRegex(string)`|`argument` matches the given regular expression.| +|:----------------------|:-----------------------------------------------| +|`EndsWith(suffix)` |`argument` ends with string `suffix`. | +|`HasSubstr(string)` |`argument` contains `string` as a sub-string. | +|`MatchesRegex(string)` |`argument` matches the given regular expression with the match starting at the first character and ending at the last character.| +|`StartsWith(prefix)` |`argument` starts with string `prefix`. | +|`StrCaseEq(string)` |`argument` is equal to `string`, ignoring case. | +|`StrCaseNe(string)` |`argument` is not equal to `string`, ignoring case.| +|`StrEq(string)` |`argument` is equal to `string`. | +|`StrNe(string)` |`argument` is not equal to `string`. | + +`ContainsRegex()` and `MatchesRegex()` use the regular expression +syntax defined +[here](http://code.google.com/p/googletest/wiki/AdvancedGuide#Regular_Expression_Syntax). +`StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide +strings as well. + +## Container Matchers ## + +Most STL-style containers support `==`, so you can use +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. If you want to write the elements in-line, +match them more flexibly, or get more informative messages, you can use: + +| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | +|:-------------------------|:---------------------------------------------------------------------------------------------------------------------------------| +| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | +| `Each(e)` | `argument` is a container where _every_ element matches `e`, which can be either a value or a matcher. | +| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the i-th element matches `ei`, which can be a value or a matcher. 0 to 10 arguments are allowed. | +| `ElementsAreArray({ e0, e1, ..., en })`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, vector, or C-style array. | +| `IsEmpty()` | `argument` is an empty container (`container.empty()`). | +| `Pointwise(m, container)` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. | +| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. | +| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under some permutation each element matches an `ei` (for a different `i`), which can be a value or a matcher. 0 to 10 arguments are allowed. | +| `UnorderedElementsAreArray({ e0, e1, ..., en })`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, vector, or C-style array. | +| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(UnorderedElementsAre(1, 2, 3))` verifies that `argument` contains elements `1`, `2`, and `3`, ignoring order. | +| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. | + +Notes: + + * These matchers can also match: + 1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`), and + 1. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer, int len)` -- see [Multi-argument Matchers](#Multiargument_Matchers.md)). + * The array being matched may be multi-dimensional (i.e. its elements can be arrays). + * `m` in `Pointwise(m, ...)` should be a matcher for `std::tr1::tuple` where `T` and `U` are the element type of the actual container and the expected container, respectively. For example, to compare two `Foo` containers where `Foo` doesn't support `operator==` but has an `Equals()` method, one might write: + +``` +using ::std::tr1::get; +MATCHER(FooEq, "") { + return get<0>(arg).Equals(get<1>(arg)); +} +... +EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos)); +``` + +## Member Matchers ## + +|`Field(&class::field, m)`|`argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| +|:------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| +|`Key(e)` |`argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`.| +|`Pair(m1, m2)` |`argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. | +|`Property(&class::property, m)`|`argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| + +## Matching the Result of a Function or Functor ## + +|`ResultOf(f, m)`|`f(argument)` matches matcher `m`, where `f` is a function or functor.| +|:---------------|:---------------------------------------------------------------------| + +## Pointer Matchers ## + +|`Pointee(m)`|`argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`.| +|:-----------|:-----------------------------------------------------------------------------------------------| + +## Multiargument Matchers ## + +Technically, all matchers match a _single_ value. A "multi-argument" +matcher is just one that matches a _tuple_. The following matchers can +be used to match a tuple `(x, y)`: + +|`Eq()`|`x == y`| +|:-----|:-------| +|`Ge()`|`x >= y`| +|`Gt()`|`x > y` | +|`Le()`|`x <= y`| +|`Lt()`|`x < y` | +|`Ne()`|`x != y`| + +You can use the following selectors to pick a subset of the arguments +(or reorder them) to participate in the matching: + +|`AllArgs(m)`|Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`.| +|:-----------|:-------------------------------------------------------------------| +|`Args(m)`|The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`.| + +## Composite Matchers ## + +You can make a matcher from one or more other matchers: + +|`AllOf(m1, m2, ..., mn)`|`argument` matches all of the matchers `m1` to `mn`.| +|:-----------------------|:---------------------------------------------------| +|`AnyOf(m1, m2, ..., mn)`|`argument` matches at least one of the matchers `m1` to `mn`.| +|`Not(m)` |`argument` doesn't match matcher `m`. | + +## Adapters for Matchers ## + +|`MatcherCast(m)`|casts matcher `m` to type `Matcher`.| +|:------------------|:--------------------------------------| +|`SafeMatcherCast(m)`| [safely casts](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Casting_Matchers) matcher `m` to type `Matcher`. | +|`Truly(predicate)` |`predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor.| + +## Matchers as Predicates ## + +|`Matches(m)(value)`|evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor.| +|:------------------|:---------------------------------------------------------------------------------------------| +|`ExplainMatchResult(m, value, result_listener)`|evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. | +|`Value(value, m)` |evaluates to `true` if `value` matches `m`. | + +## Defining Matchers ## + +| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | +|:-------------------------------------------------|:------------------------------------------------------| +| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a macher `IsDivisibleBy(n)` to match a number divisible by `n`. | +| `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | + +**Notes:** + + 1. The `MATCHER*` macros cannot be used inside a function or class. + 1. The matcher body must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). + 1. You can use `PrintToString(x)` to convert a value `x` of any type to a string. + +## Matchers as Test Assertions ## + +|`ASSERT_THAT(expression, m)`|Generates a [fatal failure](http://code.google.com/p/googletest/wiki/Primer#Assertions) if the value of `expression` doesn't match matcher `m`.| +|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------| +|`EXPECT_THAT(expression, m)`|Generates a non-fatal failure if the value of `expression` doesn't match matcher `m`. | + +# Actions # + +**Actions** specify what a mock function should do when invoked. + +## Returning a Value ## + +|`Return()`|Return from a `void` mock function.| +|:---------|:----------------------------------| +|`Return(value)`|Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type at the time the expectation is set, not when the action is executed.| +|`ReturnArg()`|Return the `N`-th (0-based) argument.| +|`ReturnNew(a1, ..., ak)`|Return `new T(a1, ..., ak)`; a different object is created each time.| +|`ReturnNull()`|Return a null pointer. | +|`ReturnPointee(ptr)`|Return the value pointed to by `ptr`.| +|`ReturnRef(variable)`|Return a reference to `variable`. | +|`ReturnRefOfCopy(value)`|Return a reference to a copy of `value`; the copy lives as long as the action.| + +## Side Effects ## + +|`Assign(&variable, value)`|Assign `value` to variable.| +|:-------------------------|:--------------------------| +| `DeleteArg()` | Delete the `N`-th (0-based) argument, which must be a pointer. | +| `SaveArg(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. | +| `SaveArgPointee(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. | +| `SetArgReferee(value)` | Assign value to the variable referenced by the `N`-th (0-based) argument. | +|`SetArgPointee(value)` |Assign `value` to the variable pointed by the `N`-th (0-based) argument.| +|`SetArgumentPointee(value)`|Same as `SetArgPointee(value)`. Deprecated. Will be removed in v1.7.0.| +|`SetArrayArgument(first, last)`|Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range.| +|`SetErrnoAndReturn(error, value)`|Set `errno` to `error` and return `value`.| +|`Throw(exception)` |Throws the given exception, which can be any copyable value. Available since v1.1.0.| + +## Using a Function or a Functor as an Action ## + +|`Invoke(f)`|Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor.| +|:----------|:-----------------------------------------------------------------------------------------------------------------| +|`Invoke(object_pointer, &class::method)`|Invoke the {method on the object with the arguments passed to the mock function. | +|`InvokeWithoutArgs(f)`|Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | +|`InvokeWithoutArgs(object_pointer, &class::method)`|Invoke the method on the object, which takes no arguments. | +|`InvokeArgument(arg1, arg2, ..., argk)`|Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments.| + +The return value of the invoked function is used as the return value +of the action. + +When defining a function or functor to be used with `Invoke*()`, you can declare any unused parameters as `Unused`: +``` + double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } + ... + EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance)); +``` + +In `InvokeArgument(...)`, if an argument needs to be passed by reference, wrap it inside `ByRef()`. For example, +``` + InvokeArgument<2>(5, string("Hi"), ByRef(foo)) +``` +calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by value, and `foo` by reference. + +## Default Action ## + +|`DoDefault()`|Do the default action (specified by `ON_CALL()` or the built-in one).| +|:------------|:--------------------------------------------------------------------| + +**Note:** due to technical reasons, `DoDefault()` cannot be used inside a composite action - trying to do so will result in a run-time error. + +## Composite Actions ## + +|`DoAll(a1, a2, ..., an)`|Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void. | +|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------| +|`IgnoreResult(a)` |Perform action `a` and ignore its result. `a` must not return void. | +|`WithArg(a)` |Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. | +|`WithArgs(a)`|Pass the selected (0-based) arguments of the mock function to action `a` and perform it. | +|`WithoutArgs(a)` |Perform action `a` without any arguments. | + +## Defining Actions ## + +| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | +|:--------------------------------------|:---------------------------------------------------------------------------------------| +| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | +| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. | + +The `ACTION*` macros cannot be used inside a function or class. + +# Cardinalities # + +These are used in `Times()` to specify how many times a mock function will be called: + +|`AnyNumber()`|The function can be called any number of times.| +|:------------|:----------------------------------------------| +|`AtLeast(n)` |The call is expected at least `n` times. | +|`AtMost(n)` |The call is expected at most `n` times. | +|`Between(m, n)`|The call is expected between `m` and `n` (inclusive) times.| +|`Exactly(n) or n`|The call is expected exactly `n` times. In particular, the call should never happen when `n` is 0.| + +# Expectation Order # + +By default, the expectations can be matched in _any_ order. If some +or all expectations must be matched in a given order, there are two +ways to specify it. They can be used either independently or +together. + +## The After Clause ## + +``` +using ::testing::Expectation; +... +Expectation init_x = EXPECT_CALL(foo, InitX()); +Expectation init_y = EXPECT_CALL(foo, InitY()); +EXPECT_CALL(foo, Bar()) + .After(init_x, init_y); +``` +says that `Bar()` can be called only after both `InitX()` and +`InitY()` have been called. + +If you don't know how many pre-requisites an expectation has when you +write it, you can use an `ExpectationSet` to collect them: + +``` +using ::testing::ExpectationSet; +... +ExpectationSet all_inits; +for (int i = 0; i < element_count; i++) { + all_inits += EXPECT_CALL(foo, InitElement(i)); +} +EXPECT_CALL(foo, Bar()) + .After(all_inits); +``` +says that `Bar()` can be called only after all elements have been +initialized (but we don't care about which elements get initialized +before the others). + +Modifying an `ExpectationSet` after using it in an `.After()` doesn't +affect the meaning of the `.After()`. + +## Sequences ## + +When you have a long chain of sequential expectations, it's easier to +specify the order using **sequences**, which don't require you to given +each expectation in the chain a different name. All expected
+calls
in the same sequence must occur in the order they are +specified. + +``` +using ::testing::Sequence; +Sequence s1, s2; +... +EXPECT_CALL(foo, Reset()) + .InSequence(s1, s2) + .WillOnce(Return(true)); +EXPECT_CALL(foo, GetSize()) + .InSequence(s1) + .WillOnce(Return(1)); +EXPECT_CALL(foo, Describe(A())) + .InSequence(s2) + .WillOnce(Return("dummy")); +``` +says that `Reset()` must be called before _both_ `GetSize()` _and_ +`Describe()`, and the latter two can occur in any order. + +To put many expectations in a sequence conveniently: +``` +using ::testing::InSequence; +{ + InSequence dummy; + + EXPECT_CALL(...)...; + EXPECT_CALL(...)...; + ... + EXPECT_CALL(...)...; +} +``` +says that all expected calls in the scope of `dummy` must occur in +strict order. The name `dummy` is irrelevant.) + +# Verifying and Resetting a Mock # + +Google Mock will verify the expectations on a mock object when it is destructed, or you can do it earlier: +``` +using ::testing::Mock; +... +// Verifies and removes the expectations on mock_obj; +// returns true iff successful. +Mock::VerifyAndClearExpectations(&mock_obj); +... +// Verifies and removes the expectations on mock_obj; +// also removes the default actions set by ON_CALL(); +// returns true iff successful. +Mock::VerifyAndClear(&mock_obj); +``` + +You can also tell Google Mock that a mock object can be leaked and doesn't +need to be verified: +``` +Mock::AllowLeak(&mock_obj); +``` + +# Mock Classes # + +Google Mock defines a convenient mock class template +``` +class MockFunction { + public: + MOCK_METHODn(Call, R(A1, ..., An)); +}; +``` +See this [recipe](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Using_Check_Points) for one application of it. + +# Flags # + +| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. | +|:-------------------------------|:----------------------------------------------| +| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. | \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/CookBook.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/CookBook.md new file mode 100644 index 00000000..419a0010 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/CookBook.md @@ -0,0 +1,3432 @@ + + +You can find recipes for using Google Mock here. If you haven't yet, +please read the [ForDummies](V1_7_ForDummies.md) document first to make sure you understand +the basics. + +**Note:** Google Mock lives in the `testing` name space. For +readability, it is recommended to write `using ::testing::Foo;` once in +your file before using the name `Foo` defined by Google Mock. We omit +such `using` statements in this page for brevity, but you should do it +in your own code. + +# Creating Mock Classes # + +## Mocking Private or Protected Methods ## + +You must always put a mock method definition (`MOCK_METHOD*`) in a +`public:` section of the mock class, regardless of the method being +mocked being `public`, `protected`, or `private` in the base class. +This allows `ON_CALL` and `EXPECT_CALL` to reference the mock function +from outside of the mock class. (Yes, C++ allows a subclass to change +the access level of a virtual function in the base class.) Example: + +``` +class Foo { + public: + ... + virtual bool Transform(Gadget* g) = 0; + + protected: + virtual void Resume(); + + private: + virtual int GetTimeOut(); +}; + +class MockFoo : public Foo { + public: + ... + MOCK_METHOD1(Transform, bool(Gadget* g)); + + // The following must be in the public section, even though the + // methods are protected or private in the base class. + MOCK_METHOD0(Resume, void()); + MOCK_METHOD0(GetTimeOut, int()); +}; +``` + +## Mocking Overloaded Methods ## + +You can mock overloaded functions as usual. No special attention is required: + +``` +class Foo { + ... + + // Must be virtual as we'll inherit from Foo. + virtual ~Foo(); + + // Overloaded on the types and/or numbers of arguments. + virtual int Add(Element x); + virtual int Add(int times, Element x); + + // Overloaded on the const-ness of this object. + virtual Bar& GetBar(); + virtual const Bar& GetBar() const; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Add, int(Element x)); + MOCK_METHOD2(Add, int(int times, Element x); + + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +``` + +**Note:** if you don't mock all versions of the overloaded method, the +compiler will give you a warning about some methods in the base class +being hidden. To fix that, use `using` to bring them in scope: + +``` +class MockFoo : public Foo { + ... + using Foo::Add; + MOCK_METHOD1(Add, int(Element x)); + // We don't want to mock int Add(int times, Element x); + ... +}; +``` + +## Mocking Class Templates ## + +To mock a class template, append `_T` to the `MOCK_*` macros: + +``` +template +class StackInterface { + ... + // Must be virtual as we'll inherit from StackInterface. + virtual ~StackInterface(); + + virtual int GetSize() const = 0; + virtual void Push(const Elem& x) = 0; +}; + +template +class MockStack : public StackInterface { + ... + MOCK_CONST_METHOD0_T(GetSize, int()); + MOCK_METHOD1_T(Push, void(const Elem& x)); +}; +``` + +## Mocking Nonvirtual Methods ## + +Google Mock can mock non-virtual functions to be used in what we call _hi-perf +dependency injection_. + +In this case, instead of sharing a common base class with the real +class, your mock class will be _unrelated_ to the real class, but +contain methods with the same signatures. The syntax for mocking +non-virtual methods is the _same_ as mocking virtual methods: + +``` +// A simple packet stream class. None of its members is virtual. +class ConcretePacketStream { + public: + void AppendPacket(Packet* new_packet); + const Packet* GetPacket(size_t packet_number) const; + size_t NumberOfPackets() const; + ... +}; + +// A mock packet stream class. It inherits from no other, but defines +// GetPacket() and NumberOfPackets(). +class MockPacketStream { + public: + MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number)); + MOCK_CONST_METHOD0(NumberOfPackets, size_t()); + ... +}; +``` + +Note that the mock class doesn't define `AppendPacket()`, unlike the +real class. That's fine as long as the test doesn't need to call it. + +Next, you need a way to say that you want to use +`ConcretePacketStream` in production code, and use `MockPacketStream` +in tests. Since the functions are not virtual and the two classes are +unrelated, you must specify your choice at _compile time_ (as opposed +to run time). + +One way to do it is to templatize your code that needs to use a packet +stream. More specifically, you will give your code a template type +argument for the type of the packet stream. In production, you will +instantiate your template with `ConcretePacketStream` as the type +argument. In tests, you will instantiate the same template with +`MockPacketStream`. For example, you may write: + +``` +template +void CreateConnection(PacketStream* stream) { ... } + +template +class PacketReader { + public: + void ReadPackets(PacketStream* stream, size_t packet_num); +}; +``` + +Then you can use `CreateConnection()` and +`PacketReader` in production code, and use +`CreateConnection()` and +`PacketReader` in tests. + +``` + MockPacketStream mock_stream; + EXPECT_CALL(mock_stream, ...)...; + .. set more expectations on mock_stream ... + PacketReader reader(&mock_stream); + ... exercise reader ... +``` + +## Mocking Free Functions ## + +It's possible to use Google Mock to mock a free function (i.e. a +C-style function or a static method). You just need to rewrite your +code to use an interface (abstract class). + +Instead of calling a free function (say, `OpenFile`) directly, +introduce an interface for it and have a concrete subclass that calls +the free function: + +``` +class FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) = 0; +}; + +class File : public FileInterface { + public: + ... + virtual bool Open(const char* path, const char* mode) { + return OpenFile(path, mode); + } +}; +``` + +Your code should talk to `FileInterface` to open a file. Now it's +easy to mock out the function. + +This may seem much hassle, but in practice you often have multiple +related functions that you can put in the same interface, so the +per-function syntactic overhead will be much lower. + +If you are concerned about the performance overhead incurred by +virtual functions, and profiling confirms your concern, you can +combine this with the recipe for [mocking non-virtual methods](#Mocking_Nonvirtual_Methods.md). + +## The Nice, the Strict, and the Naggy ## + +If a mock method has no `EXPECT_CALL` spec but is called, Google Mock +will print a warning about the "uninteresting call". The rationale is: + + * New methods may be added to an interface after a test is written. We shouldn't fail a test just because a method it doesn't know about is called. + * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, he can add an `EXPECT_CALL()` to suppress the warning. + +However, sometimes you may want to suppress all "uninteresting call" +warnings, while sometimes you may want the opposite, i.e. to treat all +of them as errors. Google Mock lets you make the decision on a +per-mock-object basis. + +Suppose your test uses a mock class `MockFoo`: + +``` +TEST(...) { + MockFoo mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +If a method of `mock_foo` other than `DoThis()` is called, it will be +reported by Google Mock as a warning. However, if you rewrite your +test to use `NiceMock` instead, the warning will be gone, +resulting in a cleaner test output: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +`NiceMock` is a subclass of `MockFoo`, so it can be used +wherever `MockFoo` is accepted. + +It also works if `MockFoo`'s constructor takes some arguments, as +`NiceMock` "inherits" `MockFoo`'s constructors: + +``` +using ::testing::NiceMock; + +TEST(...) { + NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... +} +``` + +The usage of `StrictMock` is similar, except that it makes all +uninteresting calls failures: + +``` +using ::testing::StrictMock; + +TEST(...) { + StrictMock mock_foo; + EXPECT_CALL(mock_foo, DoThis()); + ... code that uses mock_foo ... + + // The test will fail if a method of mock_foo other than DoThis() + // is called. +} +``` + +There are some caveats though (I don't like them just as much as the +next guy, but sadly they are side effects of C++'s limitations): + + 1. `NiceMock` and `StrictMock` only work for mock methods defined using the `MOCK_METHOD*` family of macros **directly** in the `MockFoo` class. If a mock method is defined in a **base class** of `MockFoo`, the "nice" or "strict" modifier may not affect it, depending on the compiler. In particular, nesting `NiceMock` and `StrictMock` (e.g. `NiceMock >`) is **not** supported. + 1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). + 1. During the constructor or destructor of `MockFoo`, the mock object is _not_ nice or strict. This may cause surprises if the constructor or destructor calls a mock method on `this` object. (This behavior, however, is consistent with C++'s general rule: if a constructor or destructor calls a virtual method of `this` object, that method is treated as non-virtual. In other words, to the base class's constructor or destructor, `this` object behaves like an instance of the base class, not the derived class. This rule is required for safety. Otherwise a base constructor may use members of a derived class before they are initialized, or a base destructor may use members of a derived class after they have been destroyed.) + +Finally, you should be **very cautious** about when to use naggy or strict mocks, as they tend to make tests more brittle and harder to maintain. When you refactor your code without changing its externally visible behavior, ideally you should't need to update any tests. If your code interacts with a naggy mock, however, you may start to get spammed with warnings as the result of your change. Worse, if your code interacts with a strict mock, your tests may start to fail and you'll be forced to fix them. Our general recommendation is to use nice mocks (not yet the default) most of the time, use naggy mocks (the current default) when developing or debugging tests, and use strict mocks only as the last resort. + +## Simplifying the Interface without Breaking Existing Code ## + +Sometimes a method has a long list of arguments that is mostly +uninteresting. For example, + +``` +class LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct tm* tm_time, + const char* message, size_t message_len) = 0; +}; +``` + +This method's argument list is lengthy and hard to work with (let's +say that the `message` argument is not even 0-terminated). If we mock +it as is, using the mock will be awkward. If, however, we try to +simplify this interface, we'll need to fix all clients depending on +it, which is often infeasible. + +The trick is to re-dispatch the method in the mock class: + +``` +class ScopedMockLog : public LogSink { + public: + ... + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, const tm* tm_time, + const char* message, size_t message_len) { + // We are only interested in the log severity, full file name, and + // log message. + Log(severity, full_filename, std::string(message, message_len)); + } + + // Implements the mock method: + // + // void Log(LogSeverity severity, + // const string& file_path, + // const string& message); + MOCK_METHOD3(Log, void(LogSeverity severity, const string& file_path, + const string& message)); +}; +``` + +By defining a new mock method with a trimmed argument list, we make +the mock class much more user-friendly. + +## Alternative to Mocking Concrete Classes ## + +Often you may find yourself using classes that don't implement +interfaces. In order to test your code that uses such a class (let's +call it `Concrete`), you may be tempted to make the methods of +`Concrete` virtual and then mock it. + +Try not to do that. + +Making a non-virtual function virtual is a big decision. It creates an +extension point where subclasses can tweak your class' behavior. This +weakens your control on the class because now it's harder to maintain +the class' invariants. You should make a function virtual only when +there is a valid reason for a subclass to override it. + +Mocking concrete classes directly is problematic as it creates a tight +coupling between the class and the tests - any small change in the +class may invalidate your tests and make test maintenance a pain. + +To avoid such problems, many programmers have been practicing "coding +to interfaces": instead of talking to the `Concrete` class, your code +would define an interface and talk to it. Then you implement that +interface as an adaptor on top of `Concrete`. In tests, you can easily +mock that interface to observe how your code is doing. + +This technique incurs some overhead: + + * You pay the cost of virtual function calls (usually not a problem). + * There is more abstraction for the programmers to learn. + +However, it can also bring significant benefits in addition to better +testability: + + * `Concrete`'s API may not fit your problem domain very well, as you may not be the only client it tries to serve. By designing your own interface, you have a chance to tailor it to your need - you may add higher-level functionalities, rename stuff, etc instead of just trimming the class. This allows you to write your code (user of the interface) in a more natural way, which means it will be more readable, more maintainable, and you'll be more productive. + * If `Concrete`'s implementation ever has to change, you don't have to rewrite everywhere it is used. Instead, you can absorb the change in your implementation of the interface, and your other code and tests will be insulated from this change. + +Some people worry that if everyone is practicing this technique, they +will end up writing lots of redundant code. This concern is totally +understandable. However, there are two reasons why it may not be the +case: + + * Different projects may need to use `Concrete` in different ways, so the best interfaces for them will be different. Therefore, each of them will have its own domain-specific interface on top of `Concrete`, and they will not be the same code. + * If enough projects want to use the same interface, they can always share it, just like they have been sharing `Concrete`. You can check in the interface and the adaptor somewhere near `Concrete` (perhaps in a `contrib` sub-directory) and let many projects use it. + +You need to weigh the pros and cons carefully for your particular +problem, but I'd like to assure you that the Java community has been +practicing this for a long time and it's a proven effective technique +applicable in a wide variety of situations. :-) + +## Delegating Calls to a Fake ## + +Some times you have a non-trivial fake implementation of an +interface. For example: + +``` +class Foo { + public: + virtual ~Foo() {} + virtual char DoThis(int n) = 0; + virtual void DoThat(const char* s, int* p) = 0; +}; + +class FakeFoo : public Foo { + public: + virtual char DoThis(int n) { + return (n > 0) ? '+' : + (n < 0) ? '-' : '0'; + } + + virtual void DoThat(const char* s, int* p) { + *p = strlen(s); + } +}; +``` + +Now you want to mock this interface such that you can set expectations +on it. However, you also want to use `FakeFoo` for the default +behavior, as duplicating it in the mock object is, well, a lot of +work. + +When you define the mock class using Google Mock, you can have it +delegate its default action to a fake class you already have, using +this pattern: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + // Normal mock method definitions using Google Mock. + MOCK_METHOD1(DoThis, char(int n)); + MOCK_METHOD2(DoThat, void(const char* s, int* p)); + + // Delegates the default actions of the methods to a FakeFoo object. + // This must be called *before* the custom ON_CALL() statements. + void DelegateToFake() { + ON_CALL(*this, DoThis(_)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThis)); + ON_CALL(*this, DoThat(_, _)) + .WillByDefault(Invoke(&fake_, &FakeFoo::DoThat)); + } + private: + FakeFoo fake_; // Keeps an instance of the fake in the mock. +}; +``` + +With that, you can use `MockFoo` in your tests as usual. Just remember +that if you don't explicitly set an action in an `ON_CALL()` or +`EXPECT_CALL()`, the fake will be called upon to do it: + +``` +using ::testing::_; + +TEST(AbcTest, Xyz) { + MockFoo foo; + foo.DelegateToFake(); // Enables the fake for delegation. + + // Put your ON_CALL(foo, ...)s here, if any. + + // No action specified, meaning to use the default action. + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(foo, DoThat(_, _)); + + int n = 0; + EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked. + foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. + EXPECT_EQ(2, n); +} +``` + +**Some tips:** + + * If you want, you can still override the default action by providing your own `ON_CALL()` or using `.WillOnce()` / `.WillRepeatedly()` in `EXPECT_CALL()`. + * In `DelegateToFake()`, you only need to delegate the methods whose fake implementation you intend to use. + * The general technique discussed here works for overloaded methods, but you'll need to tell the compiler which version you mean. To disambiguate a mock function (the one you specify inside the parentheses of `ON_CALL()`), see the "Selecting Between Overloaded Functions" section on this page; to disambiguate a fake function (the one you place inside `Invoke()`), use a `static_cast` to specify the function's type. For instance, if class `Foo` has methods `char DoThis(int n)` and `bool DoThis(double x) const`, and you want to invoke the latter, you need to write `Invoke(&fake_, static_cast(&FakeFoo::DoThis))` instead of `Invoke(&fake_, &FakeFoo::DoThis)` (The strange-looking thing inside the angled brackets of `static_cast` is the type of a function pointer to the second `DoThis()` method.). + * Having to mix a mock and a fake is often a sign of something gone wrong. Perhaps you haven't got used to the interaction-based way of testing yet. Or perhaps your interface is taking on too many roles and should be split up. Therefore, **don't abuse this**. We would only recommend to do it as an intermediate step when you are refactoring your code. + +Regarding the tip on mixing a mock and a fake, here's an example on +why it may be a bad sign: Suppose you have a class `System` for +low-level system operations. In particular, it does file and I/O +operations. And suppose you want to test how your code uses `System` +to do I/O, and you just want the file operations to work normally. If +you mock out the entire `System` class, you'll have to provide a fake +implementation for the file operation part, which suggests that +`System` is taking on too many roles. + +Instead, you can define a `FileOps` interface and an `IOOps` interface +and split `System`'s functionalities into the two. Then you can mock +`IOOps` without mocking `FileOps`. + +## Delegating Calls to a Real Object ## + +When using testing doubles (mocks, fakes, stubs, and etc), sometimes +their behaviors will differ from those of the real objects. This +difference could be either intentional (as in simulating an error such +that you can test the error handling code) or unintentional. If your +mocks have different behaviors than the real objects by mistake, you +could end up with code that passes the tests but fails in production. + +You can use the _delegating-to-real_ technique to ensure that your +mock has the same behavior as the real object while retaining the +ability to validate calls. This technique is very similar to the +delegating-to-fake technique, the difference being that we use a real +object instead of a fake. Here's an example: + +``` +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MockFoo() { + // By default, all calls are delegated to the real object. + ON_CALL(*this, DoThis()) + .WillByDefault(Invoke(&real_, &Foo::DoThis)); + ON_CALL(*this, DoThat(_)) + .WillByDefault(Invoke(&real_, &Foo::DoThat)); + ... + } + MOCK_METHOD0(DoThis, ...); + MOCK_METHOD1(DoThat, ...); + ... + private: + Foo real_; +}; +... + + MockFoo mock; + + EXPECT_CALL(mock, DoThis()) + .Times(3); + EXPECT_CALL(mock, DoThat("Hi")) + .Times(AtLeast(1)); + ... use mock in test ... +``` + +With this, Google Mock will verify that your code made the right calls +(with the right arguments, in the right order, called the right number +of times, etc), and a real object will answer the calls (so the +behavior will be the same as in production). This gives you the best +of both worlds. + +## Delegating Calls to a Parent Class ## + +Ideally, you should code to interfaces, whose methods are all pure +virtual. In reality, sometimes you do need to mock a virtual method +that is not pure (i.e, it already has an implementation). For example: + +``` +class Foo { + public: + virtual ~Foo(); + + virtual void Pure(int n) = 0; + virtual int Concrete(const char* str) { ... } +}; + +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); +}; +``` + +Sometimes you may want to call `Foo::Concrete()` instead of +`MockFoo::Concrete()`. Perhaps you want to do it as part of a stub +action, or perhaps your test doesn't need to mock `Concrete()` at all +(but it would be oh-so painful to have to define a new mock class +whenever you don't need to mock one of its methods). + +The trick is to leave a back door in your mock class for accessing the +real methods in the base class: + +``` +class MockFoo : public Foo { + public: + // Mocking a pure method. + MOCK_METHOD1(Pure, void(int n)); + // Mocking a concrete method. Foo::Concrete() is shadowed. + MOCK_METHOD1(Concrete, int(const char* str)); + + // Use this to call Concrete() defined in Foo. + int FooConcrete(const char* str) { return Foo::Concrete(str); } +}; +``` + +Now, you can call `Foo::Concrete()` inside an action by: + +``` +using ::testing::_; +using ::testing::Invoke; +... + EXPECT_CALL(foo, Concrete(_)) + .WillOnce(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +or tell the mock object that you don't want to mock `Concrete()`: + +``` +using ::testing::Invoke; +... + ON_CALL(foo, Concrete(_)) + .WillByDefault(Invoke(&foo, &MockFoo::FooConcrete)); +``` + +(Why don't we just write `Invoke(&foo, &Foo::Concrete)`? If you do +that, `MockFoo::Concrete()` will be called (and cause an infinite +recursion) since `Foo::Concrete()` is virtual. That's just how C++ +works.) + +# Using Matchers # + +## Matching Argument Values Exactly ## + +You can specify exactly which arguments a mock method is expecting: + +``` +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(5)) + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", bar)); +``` + +## Using Simple Matchers ## + +You can use matchers to match arguments that have a certain property: + +``` +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::Return; +... + EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. + .WillOnce(Return('a')); + EXPECT_CALL(foo, DoThat("Hello", NotNull())); + // The second argument must not be NULL. +``` + +A frequently used matcher is `_`, which matches anything: + +``` +using ::testing::_; +using ::testing::NotNull; +... + EXPECT_CALL(foo, DoThat(_, NotNull())); +``` + +## Combining Matchers ## + +You can build complex matchers from existing ones using `AllOf()`, +`AnyOf()`, and `Not()`: + +``` +using ::testing::AllOf; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +... + // The argument must be > 5 and != 10. + EXPECT_CALL(foo, DoThis(AllOf(Gt(5), + Ne(10)))); + + // The first argument must not contain sub-string "blah". + EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), + NULL)); +``` + +## Casting Matchers ## + +Google Mock matchers are statically typed, meaning that the compiler +can catch your mistake if you use a matcher of the wrong type (for +example, if you use `Eq(5)` to match a `string` argument). Good for +you! + +Sometimes, however, you know what you're doing and want the compiler +to give you some slack. One example is that you have a matcher for +`long` and the argument you want to match is `int`. While the two +types aren't exactly the same, there is nothing really wrong with +using a `Matcher` to match an `int` - after all, we can first +convert the `int` argument to a `long` before giving it to the +matcher. + +To support this need, Google Mock gives you the +`SafeMatcherCast(m)` function. It casts a matcher `m` to type +`Matcher`. To ensure safety, Google Mock checks that (let `U` be the +type `m` accepts): + + 1. Type `T` can be implicitly cast to type `U`; + 1. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and floating-point numbers), the conversion from `T` to `U` is not lossy (in other words, any value representable by `T` can also be represented by `U`); and + 1. When `U` is a reference, `T` must also be a reference (as the underlying matcher may be interested in the address of the `U` value). + +The code won't compile if any of these conditions isn't met. + +Here's one example: + +``` +using ::testing::SafeMatcherCast; + +// A base class and a child class. +class Base { ... }; +class Derived : public Base { ... }; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(DoThis, void(Derived* derived)); +}; +... + + MockFoo foo; + // m is a Matcher we got from somewhere. + EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); +``` + +If you find `SafeMatcherCast(m)` too limiting, you can use a similar +function `MatcherCast(m)`. The difference is that `MatcherCast` works +as long as you can `static_cast` type `T` to type `U`. + +`MatcherCast` essentially lets you bypass C++'s type system +(`static_cast` isn't always safe as it could throw away information, +for example), so be careful not to misuse/abuse it. + +## Selecting Between Overloaded Functions ## + +If you expect an overloaded function to be called, the compiler may +need some help on which overloaded version it is. + +To disambiguate functions overloaded on the const-ness of this object, +use the `Const()` argument wrapper. + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + ... + MOCK_METHOD0(GetBar, Bar&()); + MOCK_CONST_METHOD0(GetBar, const Bar&()); +}; +... + + MockFoo foo; + Bar bar1, bar2; + EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). + .WillOnce(ReturnRef(bar1)); + EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). + .WillOnce(ReturnRef(bar2)); +``` + +(`Const()` is defined by Google Mock and returns a `const` reference +to its argument.) + +To disambiguate overloaded functions with the same number of arguments +but different argument types, you may need to specify the exact type +of a matcher, either by wrapping your matcher in `Matcher()`, or +using a matcher whose type is fixed (`TypedEq`, `An()`, +etc): + +``` +using ::testing::An; +using ::testing::Lt; +using ::testing::Matcher; +using ::testing::TypedEq; + +class MockPrinter : public Printer { + public: + MOCK_METHOD1(Print, void(int n)); + MOCK_METHOD1(Print, void(char c)); +}; + +TEST(PrinterTest, Print) { + MockPrinter printer; + + EXPECT_CALL(printer, Print(An())); // void Print(int); + EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); + EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); + + printer.Print(3); + printer.Print(6); + printer.Print('a'); +} +``` + +## Performing Different Actions Based on the Arguments ## + +When a mock method is called, the _last_ matching expectation that's +still active will be selected (think "newer overrides older"). So, you +can make a method do different things depending on its argument values +like this: + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Return; +... + // The default case. + EXPECT_CALL(foo, DoThis(_)) + .WillRepeatedly(Return('b')); + + // The more specific case. + EXPECT_CALL(foo, DoThis(Lt(5))) + .WillRepeatedly(Return('a')); +``` + +Now, if `foo.DoThis()` is called with a value less than 5, `'a'` will +be returned; otherwise `'b'` will be returned. + +## Matching Multiple Arguments as a Whole ## + +Sometimes it's not enough to match the arguments individually. For +example, we may want to say that the first argument must be less than +the second argument. The `With()` clause allows us to match +all arguments of a mock function as a whole. For example, + +``` +using ::testing::_; +using ::testing::Lt; +using ::testing::Ne; +... + EXPECT_CALL(foo, InRange(Ne(0), _)) + .With(Lt()); +``` + +says that the first argument of `InRange()` must not be 0, and must be +less than the second argument. + +The expression inside `With()` must be a matcher of type +`Matcher >`, where `A1`, ..., `An` are the +types of the function arguments. + +You can also write `AllArgs(m)` instead of `m` inside `.With()`. The +two forms are equivalent, but `.With(AllArgs(Lt()))` is more readable +than `.With(Lt())`. + +You can use `Args(m)` to match the `n` selected arguments +(as a tuple) against `m`. For example, + +``` +using ::testing::_; +using ::testing::AllOf; +using ::testing::Args; +using ::testing::Lt; +... + EXPECT_CALL(foo, Blah(_, _, _)) + .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); +``` + +says that `Blah()` will be called with arguments `x`, `y`, and `z` where +`x < y < z`. + +As a convenience and example, Google Mock provides some matchers for +2-tuples, including the `Lt()` matcher above. See the [CheatSheet](V1_7_CheatSheet.md) for +the complete list. + +Note that if you want to pass the arguments to a predicate of your own +(e.g. `.With(Args<0, 1>(Truly(&MyPredicate)))`), that predicate MUST be +written to take a `tr1::tuple` as its argument; Google Mock will pass the `n` +selected arguments as _one_ single tuple to the predicate. + +## Using Matchers as Predicates ## + +Have you noticed that a matcher is just a fancy predicate that also +knows how to describe itself? Many existing algorithms take predicates +as arguments (e.g. those defined in STL's `` header), and +it would be a shame if Google Mock matchers are not allowed to +participate. + +Luckily, you can use a matcher where a unary predicate functor is +expected by wrapping it inside the `Matches()` function. For example, + +``` +#include +#include + +std::vector v; +... +// How many elements in v are >= 10? +const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); +``` + +Since you can build complex matchers from simpler ones easily using +Google Mock, this gives you a way to conveniently construct composite +predicates (doing the same using STL's `` header is just +painful). For example, here's a predicate that's satisfied by any +number that is >= 0, <= 100, and != 50: + +``` +Matches(AllOf(Ge(0), Le(100), Ne(50))) +``` + +## Using Matchers in Google Test Assertions ## + +Since matchers are basically predicates that also know how to describe +themselves, there is a way to take advantage of them in +[Google Test](http://code.google.com/p/googletest/) assertions. It's +called `ASSERT_THAT` and `EXPECT_THAT`: + +``` + ASSERT_THAT(value, matcher); // Asserts that value matches matcher. + EXPECT_THAT(value, matcher); // The non-fatal version. +``` + +For example, in a Google Test test you can write: + +``` +#include "gmock/gmock.h" + +using ::testing::AllOf; +using ::testing::Ge; +using ::testing::Le; +using ::testing::MatchesRegex; +using ::testing::StartsWith; +... + + EXPECT_THAT(Foo(), StartsWith("Hello")); + EXPECT_THAT(Bar(), MatchesRegex("Line \\d+")); + ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10))); +``` + +which (as you can probably guess) executes `Foo()`, `Bar()`, and +`Baz()`, and verifies that: + + * `Foo()` returns a string that starts with `"Hello"`. + * `Bar()` returns a string that matches regular expression `"Line \\d+"`. + * `Baz()` returns a number in the range [5, 10]. + +The nice thing about these macros is that _they read like +English_. They generate informative messages too. For example, if the +first `EXPECT_THAT()` above fails, the message will be something like: + +``` +Value of: Foo() + Actual: "Hi, world!" +Expected: starts with "Hello" +``` + +**Credit:** The idea of `(ASSERT|EXPECT)_THAT` was stolen from the +[Hamcrest](http://code.google.com/p/hamcrest/) project, which adds +`assertThat()` to JUnit. + +## Using Predicates as Matchers ## + +Google Mock provides a built-in set of matchers. In case you find them +lacking, you can use an arbitray unary predicate function or functor +as a matcher - as long as the predicate accepts a value of the type +you want. You do this by wrapping the predicate inside the `Truly()` +function, for example: + +``` +using ::testing::Truly; + +int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } +... + + // Bar() must be called with an even number. + EXPECT_CALL(foo, Bar(Truly(IsEven))); +``` + +Note that the predicate function / functor doesn't have to return +`bool`. It works as long as the return value can be used as the +condition in statement `if (condition) ...`. + +## Matching Arguments that Are Not Copyable ## + +When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, Google Mock saves +away a copy of `bar`. When `Foo()` is called later, Google Mock +compares the argument to `Foo()` with the saved copy of `bar`. This +way, you don't need to worry about `bar` being modified or destroyed +after the `EXPECT_CALL()` is executed. The same is true when you use +matchers like `Eq(bar)`, `Le(bar)`, and so on. + +But what if `bar` cannot be copied (i.e. has no copy constructor)? You +could define your own matcher function and use it with `Truly()`, as +the previous couple of recipes have shown. Or, you may be able to get +away from it if you can guarantee that `bar` won't be changed after +the `EXPECT_CALL()` is executed. Just tell Google Mock that it should +save a reference to `bar`, instead of a copy of it. Here's how: + +``` +using ::testing::Eq; +using ::testing::ByRef; +using ::testing::Lt; +... + // Expects that Foo()'s argument == bar. + EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar)))); + + // Expects that Foo()'s argument < bar. + EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar)))); +``` + +Remember: if you do this, don't change `bar` after the +`EXPECT_CALL()`, or the result is undefined. + +## Validating a Member of an Object ## + +Often a mock function takes a reference to object as an argument. When +matching the argument, you may not want to compare the entire object +against a fixed object, as that may be over-specification. Instead, +you may need to validate a certain member variable or the result of a +certain getter method of the object. You can do this with `Field()` +and `Property()`. More specifically, + +``` +Field(&Foo::bar, m) +``` + +is a matcher that matches a `Foo` object whose `bar` member variable +satisfies matcher `m`. + +``` +Property(&Foo::baz, m) +``` + +is a matcher that matches a `Foo` object whose `baz()` method returns +a value that satisfies matcher `m`. + +For example: + +> | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | +|:-----------------------------|:-----------------------------------| +> | `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | + +Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no +argument and be declared as `const`. + +BTW, `Field()` and `Property()` can also match plain pointers to +objects. For instance, + +``` +Field(&Foo::number, Ge(3)) +``` + +matches a plain pointer `p` where `p->number >= 3`. If `p` is `NULL`, +the match will always fail regardless of the inner matcher. + +What if you want to validate more than one members at the same time? +Remember that there is `AllOf()`. + +## Validating the Value Pointed to by a Pointer Argument ## + +C++ functions often take pointers as arguments. You can use matchers +like `IsNull()`, `NotNull()`, and other comparison matchers to match a +pointer, but what if you want to make sure the value _pointed to_ by +the pointer, instead of the pointer itself, has a certain property? +Well, you can use the `Pointee(m)` matcher. + +`Pointee(m)` matches a pointer iff `m` matches the value the pointer +points to. For example: + +``` +using ::testing::Ge; +using ::testing::Pointee; +... + EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); +``` + +expects `foo.Bar()` to be called with a pointer that points to a value +greater than or equal to 3. + +One nice thing about `Pointee()` is that it treats a `NULL` pointer as +a match failure, so you can write `Pointee(m)` instead of + +``` + AllOf(NotNull(), Pointee(m)) +``` + +without worrying that a `NULL` pointer will crash your test. + +Also, did we tell you that `Pointee()` works with both raw pointers +**and** smart pointers (`linked_ptr`, `shared_ptr`, `scoped_ptr`, and +etc)? + +What if you have a pointer to pointer? You guessed it - you can use +nested `Pointee()` to probe deeper inside the value. For example, +`Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer +that points to a number less than 3 (what a mouthful...). + +## Testing a Certain Property of an Object ## + +Sometimes you want to specify that an object argument has a certain +property, but there is no existing matcher that does this. If you want +good error messages, you should define a matcher. If you want to do it +quick and dirty, you could get away with writing an ordinary function. + +Let's say you have a mock function that takes an object of type `Foo`, +which has an `int bar()` method and an `int baz()` method, and you +want to constrain that the argument's `bar()` value plus its `baz()` +value is a given number. Here's how you can define a matcher to do it: + +``` +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class BarPlusBazEqMatcher : public MatcherInterface { + public: + explicit BarPlusBazEqMatcher(int expected_sum) + : expected_sum_(expected_sum) {} + + virtual bool MatchAndExplain(const Foo& foo, + MatchResultListener* listener) const { + return (foo.bar() + foo.baz()) == expected_sum_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "bar() + baz() equals " << expected_sum_; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "bar() + baz() does not equal " << expected_sum_; + } + private: + const int expected_sum_; +}; + +inline Matcher BarPlusBazEq(int expected_sum) { + return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); +} + +... + + EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; +``` + +## Matching Containers ## + +Sometimes an STL container (e.g. list, vector, map, ...) is passed to +a mock function and you may want to validate it. Since most STL +containers support the `==` operator, you can write +`Eq(expected_container)` or simply `expected_container` to match a +container exactly. + +Sometimes, though, you may want to be more flexible (for example, the +first element must be an exact match, but the second element can be +any positive number, and so on). Also, containers used in tests often +have a small number of elements, and having to define the expected +container out-of-line is a bit of a hassle. + +You can use the `ElementsAre()` or `UnorderedElementsAre()` matcher in +such cases: + +``` +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); +``` + +The above matcher says that the container must have 4 elements, which +must be 1, greater than 0, anything, and 5 respectively. + +If you instead write: + +``` +using ::testing::_; +using ::testing::Gt; +using ::testing::UnorderedElementsAre; +... + + MOCK_METHOD1(Foo, void(const vector& numbers)); +... + + EXPECT_CALL(mock, Foo(UnorderedElementsAre(1, Gt(0), _, 5))); +``` + +It means that the container must have 4 elements, which under some +permutation must be 1, greater than 0, anything, and 5 respectively. + +`ElementsAre()` and `UnorderedElementsAre()` are overloaded to take 0 +to 10 arguments. If more are needed, you can place them in a C-style +array and use `ElementsAreArray()` or `UnorderedElementsAreArray()` +instead: + +``` +using ::testing::ElementsAreArray; +... + + // ElementsAreArray accepts an array of element values. + const int expected_vector1[] = { 1, 5, 2, 4, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); + + // Or, an array of element matchers. + Matcher expected_vector2 = { 1, Gt(2), _, 3, ... }; + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); +``` + +In case the array needs to be dynamically created (and therefore the +array size cannot be inferred by the compiler), you can give +`ElementsAreArray()` an additional argument to specify the array size: + +``` +using ::testing::ElementsAreArray; +... + int* const expected_vector3 = new int[count]; + ... fill expected_vector3 with values ... + EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); +``` + +**Tips:** + + * `ElementsAre*()` can be used to match _any_ container that implements the STL iterator pattern (i.e. it has a `const_iterator` type and supports `begin()/end()`), not just the ones defined in STL. It will even work with container types yet to be written - as long as they follows the above pattern. + * You can use nested `ElementsAre*()` to match nested (multi-dimensional) containers. + * If the container is passed by pointer instead of by reference, just write `Pointee(ElementsAre*(...))`. + * The order of elements _matters_ for `ElementsAre*()`. Therefore don't use it with containers whose element order is undefined (e.g. `hash_map`). + +## Sharing Matchers ## + +Under the hood, a Google Mock matcher object consists of a pointer to +a ref-counted implementation object. Copying matchers is allowed and +very efficient, as only the pointer is copied. When the last matcher +that references the implementation object dies, the implementation +object will be deleted. + +Therefore, if you have some complex matcher that you want to use again +and again, there is no need to build it everytime. Just assign it to a +matcher variable and use that variable repeatedly! For example, + +``` + Matcher in_range = AllOf(Gt(5), Le(10)); + ... use in_range as a matcher in multiple EXPECT_CALLs ... +``` + +# Setting Expectations # + +## Knowing When to Expect ## + +`ON_CALL` is likely the single most under-utilized construct in Google Mock. + +There are basically two constructs for defining the behavior of a mock object: `ON_CALL` and `EXPECT_CALL`. The difference? `ON_CALL` defines what happens when a mock method is called, but _doesn't imply any expectation on the method being called._ `EXPECT_CALL` not only defines the behavior, but also sets an expectation that _the method will be called with the given arguments, for the given number of times_ (and _in the given order_ when you specify the order too). + +Since `EXPECT_CALL` does more, isn't it better than `ON_CALL`? Not really. Every `EXPECT_CALL` adds a constraint on the behavior of the code under test. Having more constraints than necessary is _baaad_ - even worse than not having enough constraints. + +This may be counter-intuitive. How could tests that verify more be worse than tests that verify less? Isn't verification the whole point of tests? + +The answer, lies in _what_ a test should verify. **A good test verifies the contract of the code.** If a test over-specifies, it doesn't leave enough freedom to the implementation. As a result, changing the implementation without breaking the contract (e.g. refactoring and optimization), which should be perfectly fine to do, can break such tests. Then you have to spend time fixing them, only to see them broken again the next time the implementation is changed. + +Keep in mind that one doesn't have to verify more than one property in one test. In fact, **it's a good style to verify only one thing in one test.** If you do that, a bug will likely break only one or two tests instead of dozens (which case would you rather debug?). If you are also in the habit of giving tests descriptive names that tell what they verify, you can often easily guess what's wrong just from the test log itself. + +So use `ON_CALL` by default, and only use `EXPECT_CALL` when you actually intend to verify that the call is made. For example, you may have a bunch of `ON_CALL`s in your test fixture to set the common mock behavior shared by all tests in the same group, and write (scarcely) different `EXPECT_CALL`s in different `TEST_F`s to verify different aspects of the code's behavior. Compared with the style where each `TEST` has many `EXPECT_CALL`s, this leads to tests that are more resilient to implementational changes (and thus less likely to require maintenance) and makes the intent of the tests more obvious (so they are easier to maintain when you do need to maintain them). + +## Ignoring Uninteresting Calls ## + +If you are not interested in how a mock method is called, just don't +say anything about it. In this case, if the method is ever called, +Google Mock will perform its default action to allow the test program +to continue. If you are not happy with the default action taken by +Google Mock, you can override it using `DefaultValue::Set()` +(described later in this document) or `ON_CALL()`. + +Please note that once you expressed interest in a particular mock +method (via `EXPECT_CALL()`), all invocations to it must match some +expectation. If this function is called but the arguments don't match +any `EXPECT_CALL()` statement, it will be an error. + +## Disallowing Unexpected Calls ## + +If a mock method shouldn't be called at all, explicitly say so: + +``` +using ::testing::_; +... + EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +If some calls to the method are allowed, but the rest are not, just +list all the expected calls: + +``` +using ::testing::AnyNumber; +using ::testing::Gt; +... + EXPECT_CALL(foo, Bar(5)); + EXPECT_CALL(foo, Bar(Gt(10))) + .Times(AnyNumber()); +``` + +A call to `foo.Bar()` that doesn't match any of the `EXPECT_CALL()` +statements will be an error. + +## Expecting Ordered Calls ## + +Although an `EXPECT_CALL()` statement defined earlier takes precedence +when Google Mock tries to match a function call with an expectation, +by default calls don't have to happen in the order `EXPECT_CALL()` +statements are written. For example, if the arguments match the +matchers in the third `EXPECT_CALL()`, but not those in the first two, +then the third expectation will be used. + +If you would rather have all calls occur in the order of the +expectations, put the `EXPECT_CALL()` statements in a block where you +define a variable of type `InSequence`: + +``` + using ::testing::_; + using ::testing::InSequence; + + { + InSequence s; + + EXPECT_CALL(foo, DoThis(5)); + EXPECT_CALL(bar, DoThat(_)) + .Times(2); + EXPECT_CALL(foo, DoThis(6)); + } +``` + +In this example, we expect a call to `foo.DoThis(5)`, followed by two +calls to `bar.DoThat()` where the argument can be anything, which are +in turn followed by a call to `foo.DoThis(6)`. If a call occurred +out-of-order, Google Mock will report an error. + +## Expecting Partially Ordered Calls ## + +Sometimes requiring everything to occur in a predetermined order can +lead to brittle tests. For example, we may care about `A` occurring +before both `B` and `C`, but aren't interested in the relative order +of `B` and `C`. In this case, the test should reflect our real intent, +instead of being overly constraining. + +Google Mock allows you to impose an arbitrary DAG (directed acyclic +graph) on the calls. One way to express the DAG is to use the +[After](http://code.google.com/p/googlemock/wiki/V1_7_CheatSheet#The_After_Clause) clause of `EXPECT_CALL`. + +Another way is via the `InSequence()` clause (not the same as the +`InSequence` class), which we borrowed from jMock 2. It's less +flexible than `After()`, but more convenient when you have long chains +of sequential calls, as it doesn't require you to come up with +different names for the expectations in the chains. Here's how it +works: + +If we view `EXPECT_CALL()` statements as nodes in a graph, and add an +edge from node A to node B wherever A must occur before B, we can get +a DAG. We use the term "sequence" to mean a directed path in this +DAG. Now, if we decompose the DAG into sequences, we just need to know +which sequences each `EXPECT_CALL()` belongs to in order to be able to +reconstruct the orginal DAG. + +So, to specify the partial order on the expectations we need to do two +things: first to define some `Sequence` objects, and then for each +`EXPECT_CALL()` say which `Sequence` objects it is part +of. Expectations in the same sequence must occur in the order they are +written. For example, + +``` + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(foo, A()) + .InSequence(s1, s2); + EXPECT_CALL(bar, B()) + .InSequence(s1); + EXPECT_CALL(bar, C()) + .InSequence(s2); + EXPECT_CALL(foo, D()) + .InSequence(s2); +``` + +specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> +C -> D`): + +``` + +---> B + | + A ---| + | + +---> C ---> D +``` + +This means that A must occur before B and C, and C must occur before +D. There's no restriction about the order other than these. + +## Controlling When an Expectation Retires ## + +When a mock method is called, Google Mock only consider expectations +that are still active. An expectation is active when created, and +becomes inactive (aka _retires_) when a call that has to occur later +has occurred. For example, in + +``` + using ::testing::_; + using ::testing::Sequence; + + Sequence s1, s2; + + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 + .Times(AnyNumber()) + .InSequence(s1, s2); + EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 + .InSequence(s1); + EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 + .InSequence(s2); +``` + +as soon as either #2 or #3 is matched, #1 will retire. If a warning +`"File too large."` is logged after this, it will be an error. + +Note that an expectation doesn't retire automatically when it's +saturated. For example, + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 +``` + +says that there will be exactly one warning with the message `"File +too large."`. If the second warning contains this message too, #2 will +match again and result in an upper-bound-violated error. + +If this is not what you want, you can ask an expectation to retire as +soon as it becomes saturated: + +``` +using ::testing::_; +... + EXPECT_CALL(log, Log(WARNING, _, _)); // #1 + EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 + .RetiresOnSaturation(); +``` + +Here #2 can be used only once, so if you have two warnings with the +message `"File too large."`, the first will match #2 and the second +will match #1 - there will be no error. + +# Using Actions # + +## Returning References from Mock Methods ## + +If a mock function's return type is a reference, you need to use +`ReturnRef()` instead of `Return()` to return a result: + +``` +using ::testing::ReturnRef; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetBar, Bar&()); +}; +... + + MockFoo foo; + Bar bar; + EXPECT_CALL(foo, GetBar()) + .WillOnce(ReturnRef(bar)); +``` + +## Returning Live Values from Mock Methods ## + +The `Return(x)` action saves a copy of `x` when the action is +_created_, and always returns the same value whenever it's +executed. Sometimes you may want to instead return the _live_ value of +`x` (i.e. its value at the time when the action is _executed_.). + +If the mock function's return type is a reference, you can do it using +`ReturnRef(x)`, as shown in the previous recipe ("Returning References +from Mock Methods"). However, Google Mock doesn't let you use +`ReturnRef()` in a mock function whose return type is not a reference, +as doing that usually indicates a user error. So, what shall you do? + +You may be tempted to try `ByRef()`: + +``` +using testing::ByRef; +using testing::Return; + +class MockFoo : public Foo { + public: + MOCK_METHOD0(GetValue, int()); +}; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(Return(ByRef(x))); + x = 42; + EXPECT_EQ(42, foo.GetValue()); +``` + +Unfortunately, it doesn't work here. The above code will fail with error: + +``` +Value of: foo.GetValue() + Actual: 0 +Expected: 42 +``` + +The reason is that `Return(value)` converts `value` to the actual +return type of the mock function at the time when the action is +_created_, not when it is _executed_. (This behavior was chosen for +the action to be safe when `value` is a proxy object that references +some temporary objects.) As a result, `ByRef(x)` is converted to an +`int` value (instead of a `const int&`) when the expectation is set, +and `Return(ByRef(x))` will always return 0. + +`ReturnPointee(pointer)` was provided to solve this problem +specifically. It returns the value pointed to by `pointer` at the time +the action is _executed_: + +``` +using testing::ReturnPointee; +... + int x = 0; + MockFoo foo; + EXPECT_CALL(foo, GetValue()) + .WillRepeatedly(ReturnPointee(&x)); // Note the & here. + x = 42; + EXPECT_EQ(42, foo.GetValue()); // This will succeed now. +``` + +## Combining Actions ## + +Want to do more than one thing when a function is called? That's +fine. `DoAll()` allow you to do sequence of actions every time. Only +the return value of the last action in the sequence will be used. + +``` +using ::testing::DoAll; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Bar, bool(int n)); +}; +... + + EXPECT_CALL(foo, Bar(_)) + .WillOnce(DoAll(action_1, + action_2, + ... + action_n)); +``` + +## Mocking Side Effects ## + +Sometimes a method exhibits its effect not via returning a value but +via side effects. For example, it may change some global state or +modify an output argument. To mock side effects, in general you can +define your own action by implementing `::testing::ActionInterface`. + +If all you need to do is to change an output argument, the built-in +`SetArgPointee()` action is convenient: + +``` +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + MOCK_METHOD2(Mutate, void(bool mutate, int* value)); + ... +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, Mutate(true, _)) + .WillOnce(SetArgPointee<1>(5)); +``` + +In this example, when `mutator.Mutate()` is called, we will assign 5 +to the `int` variable pointed to by argument #1 +(0-based). + +`SetArgPointee()` conveniently makes an internal copy of the +value you pass to it, removing the need to keep the value in scope and +alive. The implication however is that the value must have a copy +constructor and assignment operator. + +If the mock method also needs to return a value as well, you can chain +`SetArgPointee()` with `Return()` using `DoAll()`: + +``` +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; + +class MockMutator : public Mutator { + public: + ... + MOCK_METHOD1(MutateInt, bool(int* value)); +}; +... + + MockMutator mutator; + EXPECT_CALL(mutator, MutateInt(_)) + .WillOnce(DoAll(SetArgPointee<0>(5), + Return(true))); +``` + +If the output argument is an array, use the +`SetArrayArgument(first, last)` action instead. It copies the +elements in source range `[first, last)` to the array pointed to by +the `N`-th (0-based) argument: + +``` +using ::testing::NotNull; +using ::testing::SetArrayArgument; + +class MockArrayMutator : public ArrayMutator { + public: + MOCK_METHOD2(Mutate, void(int* values, int num_values)); + ... +}; +... + + MockArrayMutator mutator; + int values[5] = { 1, 2, 3, 4, 5 }; + EXPECT_CALL(mutator, Mutate(NotNull(), 5)) + .WillOnce(SetArrayArgument<0>(values, values + 5)); +``` + +This also works when the argument is an output iterator: + +``` +using ::testing::_; +using ::testing::SeArrayArgument; + +class MockRolodex : public Rolodex { + public: + MOCK_METHOD1(GetNames, void(std::back_insert_iterator >)); + ... +}; +... + + MockRolodex rolodex; + vector names; + names.push_back("George"); + names.push_back("John"); + names.push_back("Thomas"); + EXPECT_CALL(rolodex, GetNames(_)) + .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); +``` + +## Changing a Mock Object's Behavior Based on the State ## + +If you expect a call to change the behavior of a mock object, you can use `::testing::InSequence` to specify different behaviors before and after the call: + +``` +using ::testing::InSequence; +using ::testing::Return; + +... + { + InSequence seq; + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(my_mock, Flush()); + EXPECT_CALL(my_mock, IsDirty()) + .WillRepeatedly(Return(false)); + } + my_mock.FlushIfDirty(); +``` + +This makes `my_mock.IsDirty()` return `true` before `my_mock.Flush()` is called and return `false` afterwards. + +If the behavior change is more complex, you can store the effects in a variable and make a mock method get its return value from that variable: + +``` +using ::testing::_; +using ::testing::SaveArg; +using ::testing::Return; + +ACTION_P(ReturnPointee, p) { return *p; } +... + int previous_value = 0; + EXPECT_CALL(my_mock, GetPrevValue()) + .WillRepeatedly(ReturnPointee(&previous_value)); + EXPECT_CALL(my_mock, UpdateValue(_)) + .WillRepeatedly(SaveArg<0>(&previous_value)); + my_mock.DoSomethingToUpdateValue(); +``` + +Here `my_mock.GetPrevValue()` will always return the argument of the last `UpdateValue()` call. + +## Setting the Default Value for a Return Type ## + +If a mock method's return type is a built-in C++ type or pointer, by +default it will return 0 when invoked. You only need to specify an +action if this default value doesn't work for you. + +Sometimes, you may want to change this default value, or you may want +to specify a default value for types Google Mock doesn't know +about. You can do this using the `::testing::DefaultValue` class +template: + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD0(CalculateBar, Bar()); +}; +... + + Bar default_bar; + // Sets the default return value for type Bar. + DefaultValue::Set(default_bar); + + MockFoo foo; + + // We don't need to specify an action here, as the default + // return value works for us. + EXPECT_CALL(foo, CalculateBar()); + + foo.CalculateBar(); // This should return default_bar. + + // Unsets the default return value. + DefaultValue::Clear(); +``` + +Please note that changing the default value for a type can make you +tests hard to understand. We recommend you to use this feature +judiciously. For example, you may want to make sure the `Set()` and +`Clear()` calls are right next to the code that uses your mock. + +## Setting the Default Actions for a Mock Method ## + +You've learned how to change the default value of a given +type. However, this may be too coarse for your purpose: perhaps you +have two mock methods with the same return type and you want them to +have different behaviors. The `ON_CALL()` macro allows you to +customize your mock's behavior at the method level: + +``` +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Gt; +using ::testing::Return; +... + ON_CALL(foo, Sign(_)) + .WillByDefault(Return(-1)); + ON_CALL(foo, Sign(0)) + .WillByDefault(Return(0)); + ON_CALL(foo, Sign(Gt(0))) + .WillByDefault(Return(1)); + + EXPECT_CALL(foo, Sign(_)) + .Times(AnyNumber()); + + foo.Sign(5); // This should return 1. + foo.Sign(-9); // This should return -1. + foo.Sign(0); // This should return 0. +``` + +As you may have guessed, when there are more than one `ON_CALL()` +statements, the news order take precedence over the older ones. In +other words, the **last** one that matches the function arguments will +be used. This matching order allows you to set up the common behavior +in a mock object's constructor or the test fixture's set-up phase and +specialize the mock's behavior later. + +## Using Functions/Methods/Functors as Actions ## + +If the built-in actions don't suit you, you can easily use an existing +function, method, or functor as an action: + +``` +using ::testing::_; +using ::testing::Invoke; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(Sum, int(int x, int y)); + MOCK_METHOD1(ComplexJob, bool(int x)); +}; + +int CalculateSum(int x, int y) { return x + y; } + +class Helper { + public: + bool ComplexJob(int x); +}; +... + + MockFoo foo; + Helper helper; + EXPECT_CALL(foo, Sum(_, _)) + .WillOnce(Invoke(CalculateSum)); + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(Invoke(&helper, &Helper::ComplexJob)); + + foo.Sum(5, 6); // Invokes CalculateSum(5, 6). + foo.ComplexJob(10); // Invokes helper.ComplexJob(10); +``` + +The only requirement is that the type of the function, etc must be +_compatible_ with the signature of the mock function, meaning that the +latter's arguments can be implicitly converted to the corresponding +arguments of the former, and the former's return type can be +implicitly converted to that of the latter. So, you can invoke +something whose type is _not_ exactly the same as the mock function, +as long as it's safe to do so - nice, huh? + +## Invoking a Function/Method/Functor Without Arguments ## + +`Invoke()` is very useful for doing actions that are more complex. It +passes the mock function's arguments to the function or functor being +invoked such that the callee has the full context of the call to work +with. If the invoked function is not interested in some or all of the +arguments, it can simply ignore them. + +Yet, a common pattern is that a test author wants to invoke a function +without the arguments of the mock function. `Invoke()` allows her to +do that using a wrapper function that throws away the arguments before +invoking an underlining nullary function. Needless to say, this can be +tedious and obscures the intent of the test. + +`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except +that it doesn't pass the mock function's arguments to the +callee. Here's an example: + +``` +using ::testing::_; +using ::testing::InvokeWithoutArgs; + +class MockFoo : public Foo { + public: + MOCK_METHOD1(ComplexJob, bool(int n)); +}; + +bool Job1() { ... } +... + + MockFoo foo; + EXPECT_CALL(foo, ComplexJob(_)) + .WillOnce(InvokeWithoutArgs(Job1)); + + foo.ComplexJob(10); // Invokes Job1(). +``` + +## Invoking an Argument of the Mock Function ## + +Sometimes a mock function will receive a function pointer or a functor +(in other words, a "callable") as an argument, e.g. + +``` +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, bool(int n, bool (*fp)(int))); +}; +``` + +and you may want to invoke this callable argument: + +``` +using ::testing::_; +... + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(...); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +Arghh, you need to refer to a mock function argument but C++ has no +lambda (yet), so you have to define your own action. :-( Or do you +really? + +Well, Google Mock has an action to solve _exactly_ this problem: + +``` + InvokeArgument(arg_1, arg_2, ..., arg_m) +``` + +will invoke the `N`-th (0-based) argument the mock function receives, +with `arg_1`, `arg_2`, ..., and `arg_m`. No matter if the argument is +a function pointer or a functor, Google Mock handles them both. + +With that, you could write: + +``` +using ::testing::_; +using ::testing::InvokeArgument; +... + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(InvokeArgument<1>(5)); + // Will execute (*fp)(5), where fp is the + // second argument DoThis() receives. +``` + +What if the callable takes an argument by reference? No problem - just +wrap it inside `ByRef()`: + +``` +... + MOCK_METHOD1(Bar, bool(bool (*fp)(int, const Helper&))); +... +using ::testing::_; +using ::testing::ByRef; +using ::testing::InvokeArgument; +... + + MockFoo foo; + Helper helper; + ... + EXPECT_CALL(foo, Bar(_)) + .WillOnce(InvokeArgument<0>(5, ByRef(helper))); + // ByRef(helper) guarantees that a reference to helper, not a copy of it, + // will be passed to the callable. +``` + +What if the callable takes an argument by reference and we do **not** +wrap the argument in `ByRef()`? Then `InvokeArgument()` will _make a +copy_ of the argument, and pass a _reference to the copy_, instead of +a reference to the original value, to the callable. This is especially +handy when the argument is a temporary value: + +``` +... + MOCK_METHOD1(DoThat, bool(bool (*f)(const double& x, const string& s))); +... +using ::testing::_; +using ::testing::InvokeArgument; +... + + MockFoo foo; + ... + EXPECT_CALL(foo, DoThat(_)) + .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); + // Will execute (*f)(5.0, string("Hi")), where f is the function pointer + // DoThat() receives. Note that the values 5.0 and string("Hi") are + // temporary and dead once the EXPECT_CALL() statement finishes. Yet + // it's fine to perform this action later, since a copy of the values + // are kept inside the InvokeArgument action. +``` + +## Ignoring an Action's Result ## + +Sometimes you have an action that returns _something_, but you need an +action that returns `void` (perhaps you want to use it in a mock +function that returns `void`, or perhaps it needs to be used in +`DoAll()` and it's not the last in the list). `IgnoreResult()` lets +you do that. For example: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; + +int Process(const MyData& data); +string DoSomething(); + +class MockFoo : public Foo { + public: + MOCK_METHOD1(Abc, void(const MyData& data)); + MOCK_METHOD0(Xyz, bool()); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, Abc(_)) + // .WillOnce(Invoke(Process)); + // The above line won't compile as Process() returns int but Abc() needs + // to return void. + .WillOnce(IgnoreResult(Invoke(Process))); + + EXPECT_CALL(foo, Xyz()) + .WillOnce(DoAll(IgnoreResult(Invoke(DoSomething)), + // Ignores the string DoSomething() returns. + Return(true))); +``` + +Note that you **cannot** use `IgnoreResult()` on an action that already +returns `void`. Doing so will lead to ugly compiler errors. + +## Selecting an Action's Arguments ## + +Say you have a mock function `Foo()` that takes seven arguments, and +you have a custom action that you want to invoke when `Foo()` is +called. Trouble is, the custom action only wants three arguments: + +``` +using ::testing::_; +using ::testing::Invoke; +... + MOCK_METHOD7(Foo, bool(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight)); +... + +bool IsVisibleInQuadrant1(bool visible, int x, int y) { + return visible && x >= 0 && y >= 0; +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( +``` + +To please the compiler God, you can to define an "adaptor" that has +the same signature as `Foo()` and calls the custom action with the +right arguments: + +``` +using ::testing::_; +using ::testing::Invoke; + +bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, + const map, double>& weight, + double min_weight, double max_wight) { + return IsVisibleInQuadrant1(visible, x, y); +} +... + + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. +``` + +But isn't this awkward? + +Google Mock provides a generic _action adaptor_, so you can spend your +time minding more important business than writing your own +adaptors. Here's the syntax: + +``` + WithArgs(action) +``` + +creates an action that passes the arguments of the mock function at +the given indices (0-based) to the inner `action` and performs +it. Using `WithArgs`, our original example can be written as: + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::WithArgs; +... + EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) + .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); + // No need to define your own adaptor. +``` + +For better readability, Google Mock also gives you: + + * `WithoutArgs(action)` when the inner `action` takes _no_ argument, and + * `WithArg(action)` (no `s` after `Arg`) when the inner `action` takes _one_ argument. + +As you may have realized, `InvokeWithoutArgs(...)` is just syntactic +sugar for `WithoutArgs(Inovke(...))`. + +Here are more tips: + + * The inner action used in `WithArgs` and friends does not have to be `Invoke()` -- it can be anything. + * You can repeat an argument in the argument list if necessary, e.g. `WithArgs<2, 3, 3, 5>(...)`. + * You can change the order of the arguments, e.g. `WithArgs<3, 2, 1>(...)`. + * The types of the selected arguments do _not_ have to match the signature of the inner action exactly. It works as long as they can be implicitly converted to the corresponding arguments of the inner action. For example, if the 4-th argument of the mock function is an `int` and `my_action` takes a `double`, `WithArg<4>(my_action)` will work. + +## Ignoring Arguments in Action Functions ## + +The selecting-an-action's-arguments recipe showed us one way to make a +mock function and an action with incompatible argument lists fit +together. The downside is that wrapping the action in +`WithArgs<...>()` can get tedious for people writing the tests. + +If you are defining a function, method, or functor to be used with +`Invoke*()`, and you are not interested in some of its arguments, an +alternative to `WithArgs` is to declare the uninteresting arguments as +`Unused`. This makes the definition less cluttered and less fragile in +case the types of the uninteresting arguments change. It could also +increase the chance the action function can be reused. For example, +given + +``` + MOCK_METHOD3(Foo, double(const string& label, double x, double y)); + MOCK_METHOD3(Bar, double(int index, double x, double y)); +``` + +instead of + +``` +using ::testing::_; +using ::testing::Invoke; + +double DistanceToOriginWithLabel(const string& label, double x, double y) { + return sqrt(x*x + y*y); +} + +double DistanceToOriginWithIndex(int index, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOriginWithLabel)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOriginWithIndex)); +``` + +you could write + +``` +using ::testing::_; +using ::testing::Invoke; +using ::testing::Unused; + +double DistanceToOrigin(Unused, double x, double y) { + return sqrt(x*x + y*y); +} +... + + EXEPCT_CALL(mock, Foo("abc", _, _)) + .WillOnce(Invoke(DistanceToOrigin)); + EXEPCT_CALL(mock, Bar(5, _, _)) + .WillOnce(Invoke(DistanceToOrigin)); +``` + +## Sharing Actions ## + +Just like matchers, a Google Mock action object consists of a pointer +to a ref-counted implementation object. Therefore copying actions is +also allowed and very efficient. When the last action that references +the implementation object dies, the implementation object will be +deleted. + +If you have some complex action that you want to use again and again, +you may not have to build it from scratch everytime. If the action +doesn't have an internal state (i.e. if it always does the same thing +no matter how many times it has been called), you can assign it to an +action variable and use that variable repeatedly. For example: + +``` + Action set_flag = DoAll(SetArgPointee<0>(5), + Return(true)); + ... use set_flag in .WillOnce() and .WillRepeatedly() ... +``` + +However, if the action has its own state, you may be surprised if you +share the action object. Suppose you have an action factory +`IncrementCounter(init)` which creates an action that increments and +returns a counter whose initial value is `init`, using two actions +created from the same expression and using a shared action will +exihibit different behaviors. Example: + +``` + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(IncrementCounter(0)); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(IncrementCounter(0)); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 1 - Blah() uses a different + // counter than Bar()'s. +``` + +versus + +``` + Action increment = IncrementCounter(0); + + EXPECT_CALL(foo, DoThis()) + .WillRepeatedly(increment); + EXPECT_CALL(foo, DoThat()) + .WillRepeatedly(increment); + foo.DoThis(); // Returns 1. + foo.DoThis(); // Returns 2. + foo.DoThat(); // Returns 3 - the counter is shared. +``` + +# Misc Recipes on Using Google Mock # + +## Making the Compilation Faster ## + +Believe it or not, the _vast majority_ of the time spent on compiling +a mock class is in generating its constructor and destructor, as they +perform non-trivial tasks (e.g. verification of the +expectations). What's more, mock methods with different signatures +have different types and thus their constructors/destructors need to +be generated by the compiler separately. As a result, if you mock many +different types of methods, compiling your mock class can get really +slow. + +If you are experiencing slow compilation, you can move the definition +of your mock class' constructor and destructor out of the class body +and into a `.cpp` file. This way, even if you `#include` your mock +class in N files, the compiler only needs to generate its constructor +and destructor once, resulting in a much faster compilation. + +Let's illustrate the idea using an example. Here's the definition of a +mock class before applying this recipe: + +``` +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // Since we don't declare the constructor or the destructor, + // the compiler will generate them in every translation unit + // where this mock class is used. + + MOCK_METHOD0(DoThis, int()); + MOCK_METHOD1(DoThat, bool(const char* str)); + ... more mock methods ... +}; +``` + +After the change, it would look like: + +``` +// File mock_foo.h. +... +class MockFoo : public Foo { + public: + // The constructor and destructor are declared, but not defined, here. + MockFoo(); + virtual ~MockFoo(); + + MOCK_METHOD0(DoThis, int()); + MOCK_METHOD1(DoThat, bool(const char* str)); + ... more mock methods ... +}; +``` +and +``` +// File mock_foo.cpp. +#include "path/to/mock_foo.h" + +// The definitions may appear trivial, but the functions actually do a +// lot of things through the constructors/destructors of the member +// variables used to implement the mock methods. +MockFoo::MockFoo() {} +MockFoo::~MockFoo() {} +``` + +## Forcing a Verification ## + +When it's being destoyed, your friendly mock object will automatically +verify that all expectations on it have been satisfied, and will +generate [Google Test](http://code.google.com/p/googletest/) failures +if not. This is convenient as it leaves you with one less thing to +worry about. That is, unless you are not sure if your mock object will +be destoyed. + +How could it be that your mock object won't eventually be destroyed? +Well, it might be created on the heap and owned by the code you are +testing. Suppose there's a bug in that code and it doesn't delete the +mock object properly - you could end up with a passing test when +there's actually a bug. + +Using a heap checker is a good idea and can alleviate the concern, but +its implementation may not be 100% reliable. So, sometimes you do want +to _force_ Google Mock to verify a mock object before it is +(hopefully) destructed. You can do this with +`Mock::VerifyAndClearExpectations(&mock_object)`: + +``` +TEST(MyServerTest, ProcessesRequest) { + using ::testing::Mock; + + MockFoo* const foo = new MockFoo; + EXPECT_CALL(*foo, ...)...; + // ... other expectations ... + + // server now owns foo. + MyServer server(foo); + server.ProcessRequest(...); + + // In case that server's destructor will forget to delete foo, + // this will verify the expectations anyway. + Mock::VerifyAndClearExpectations(foo); +} // server is destroyed when it goes out of scope here. +``` + +**Tip:** The `Mock::VerifyAndClearExpectations()` function returns a +`bool` to indicate whether the verification was successful (`true` for +yes), so you can wrap that function call inside a `ASSERT_TRUE()` if +there is no point going further when the verification has failed. + +## Using Check Points ## + +Sometimes you may want to "reset" a mock object at various check +points in your test: at each check point, you verify that all existing +expectations on the mock object have been satisfied, and then you set +some new expectations on it as if it's newly created. This allows you +to work with a mock object in "phases" whose sizes are each +manageable. + +One such scenario is that in your test's `SetUp()` function, you may +want to put the object you are testing into a certain state, with the +help from a mock object. Once in the desired state, you want to clear +all expectations on the mock, such that in the `TEST_F` body you can +set fresh expectations on it. + +As you may have figured out, the `Mock::VerifyAndClearExpectations()` +function we saw in the previous recipe can help you here. Or, if you +are using `ON_CALL()` to set default actions on the mock object and +want to clear the default actions as well, use +`Mock::VerifyAndClear(&mock_object)` instead. This function does what +`Mock::VerifyAndClearExpectations(&mock_object)` does and returns the +same `bool`, **plus** it clears the `ON_CALL()` statements on +`mock_object` too. + +Another trick you can use to achieve the same effect is to put the +expectations in sequences and insert calls to a dummy "check-point" +function at specific places. Then you can verify that the mock +function calls do happen at the right time. For example, if you are +exercising code: + +``` +Foo(1); +Foo(2); +Foo(3); +``` + +and want to verify that `Foo(1)` and `Foo(3)` both invoke +`mock.Bar("a")`, but `Foo(2)` doesn't invoke anything. You can write: + +``` +using ::testing::MockFunction; + +TEST(FooTest, InvokesBarCorrectly) { + MyMock mock; + // Class MockFunction has exactly one mock method. It is named + // Call() and has type F. + MockFunction check; + { + InSequence s; + + EXPECT_CALL(mock, Bar("a")); + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(mock, Bar("a")); + } + Foo(1); + check.Call("1"); + Foo(2); + check.Call("2"); + Foo(3); +} +``` + +The expectation spec says that the first `Bar("a")` must happen before +check point "1", the second `Bar("a")` must happen after check point "2", +and nothing should happen between the two check points. The explicit +check points make it easy to tell which `Bar("a")` is called by which +call to `Foo()`. + +## Mocking Destructors ## + +Sometimes you want to make sure a mock object is destructed at the +right time, e.g. after `bar->A()` is called but before `bar->B()` is +called. We already know that you can specify constraints on the order +of mock function calls, so all we need to do is to mock the destructor +of the mock function. + +This sounds simple, except for one problem: a destructor is a special +function with special syntax and special semantics, and the +`MOCK_METHOD0` macro doesn't work for it: + +``` + MOCK_METHOD0(~MockFoo, void()); // Won't compile! +``` + +The good news is that you can use a simple pattern to achieve the same +effect. First, add a mock function `Die()` to your mock class and call +it in the destructor, like this: + +``` +class MockFoo : public Foo { + ... + // Add the following two lines to the mock class. + MOCK_METHOD0(Die, void()); + virtual ~MockFoo() { Die(); } +}; +``` + +(If the name `Die()` clashes with an existing symbol, choose another +name.) Now, we have translated the problem of testing when a `MockFoo` +object dies to testing when its `Die()` method is called: + +``` + MockFoo* foo = new MockFoo; + MockBar* bar = new MockBar; + ... + { + InSequence s; + + // Expects *foo to die after bar->A() and before bar->B(). + EXPECT_CALL(*bar, A()); + EXPECT_CALL(*foo, Die()); + EXPECT_CALL(*bar, B()); + } +``` + +And that's that. + +## Using Google Mock and Threads ## + +**IMPORTANT NOTE:** What we describe in this recipe is **ONLY** true on +platforms where Google Mock is thread-safe. Currently these are only +platforms that support the pthreads library (this includes Linux and Mac). +To make it thread-safe on other platforms we only need to implement +some synchronization operations in `"gtest/internal/gtest-port.h"`. + +In a **unit** test, it's best if you could isolate and test a piece of +code in a single-threaded context. That avoids race conditions and +dead locks, and makes debugging your test much easier. + +Yet many programs are multi-threaded, and sometimes to test something +we need to pound on it from more than one thread. Google Mock works +for this purpose too. + +Remember the steps for using a mock: + + 1. Create a mock object `foo`. + 1. Set its default actions and expectations using `ON_CALL()` and `EXPECT_CALL()`. + 1. The code under test calls methods of `foo`. + 1. Optionally, verify and reset the mock. + 1. Destroy the mock yourself, or let the code under test destroy it. The destructor will automatically verify it. + +If you follow the following simple rules, your mocks and threads can +live happily togeter: + + * Execute your _test code_ (as opposed to the code being tested) in _one_ thread. This makes your test easy to follow. + * Obviously, you can do step #1 without locking. + * When doing step #2 and #5, make sure no other thread is accessing `foo`. Obvious too, huh? + * #3 and #4 can be done either in one thread or in multiple threads - anyway you want. Google Mock takes care of the locking, so you don't have to do any - unless required by your test logic. + +If you violate the rules (for example, if you set expectations on a +mock while another thread is calling its methods), you get undefined +behavior. That's not fun, so don't do it. + +Google Mock guarantees that the action for a mock function is done in +the same thread that called the mock function. For example, in + +``` + EXPECT_CALL(mock, Foo(1)) + .WillOnce(action1); + EXPECT_CALL(mock, Foo(2)) + .WillOnce(action2); +``` + +if `Foo(1)` is called in thread 1 and `Foo(2)` is called in thread 2, +Google Mock will execute `action1` in thread 1 and `action2` in thread +2. + +Google Mock does _not_ impose a sequence on actions performed in +different threads (doing so may create deadlocks as the actions may +need to cooperate). This means that the execution of `action1` and +`action2` in the above example _may_ interleave. If this is a problem, +you should add proper synchronization logic to `action1` and `action2` +to make the test thread-safe. + + +Also, remember that `DefaultValue` is a global resource that +potentially affects _all_ living mock objects in your +program. Naturally, you won't want to mess with it from multiple +threads or when there still are mocks in action. + +## Controlling How Much Information Google Mock Prints ## + +When Google Mock sees something that has the potential of being an +error (e.g. a mock function with no expectation is called, a.k.a. an +uninteresting call, which is allowed but perhaps you forgot to +explicitly ban the call), it prints some warning messages, including +the arguments of the function and the return value. Hopefully this +will remind you to take a look and see if there is indeed a problem. + +Sometimes you are confident that your tests are correct and may not +appreciate such friendly messages. Some other times, you are debugging +your tests or learning about the behavior of the code you are testing, +and wish you could observe every mock call that happens (including +argument values and the return value). Clearly, one size doesn't fit +all. + +You can control how much Google Mock tells you using the +`--gmock_verbose=LEVEL` command-line flag, where `LEVEL` is a string +with three possible values: + + * `info`: Google Mock will print all informational messages, warnings, and errors (most verbose). At this setting, Google Mock will also log any calls to the `ON_CALL/EXPECT_CALL` macros. + * `warning`: Google Mock will print both warnings and errors (less verbose). This is the default. + * `error`: Google Mock will print errors only (least verbose). + +Alternatively, you can adjust the value of that flag from within your +tests like so: + +``` + ::testing::FLAGS_gmock_verbose = "error"; +``` + +Now, judiciously use the right flag to enable Google Mock serve you better! + +## Gaining Super Vision into Mock Calls ## + +You have a test using Google Mock. It fails: Google Mock tells you +that some expectations aren't satisfied. However, you aren't sure why: +Is there a typo somewhere in the matchers? Did you mess up the order +of the `EXPECT_CALL`s? Or is the code under test doing something +wrong? How can you find out the cause? + +Won't it be nice if you have X-ray vision and can actually see the +trace of all `EXPECT_CALL`s and mock method calls as they are made? +For each call, would you like to see its actual argument values and +which `EXPECT_CALL` Google Mock thinks it matches? + +You can unlock this power by running your test with the +`--gmock_verbose=info` flag. For example, given the test program: + +``` +using testing::_; +using testing::HasSubstr; +using testing::Return; + +class MockFoo { + public: + MOCK_METHOD2(F, void(const string& x, const string& y)); +}; + +TEST(Foo, Bar) { + MockFoo mock; + EXPECT_CALL(mock, F(_, _)).WillRepeatedly(Return()); + EXPECT_CALL(mock, F("a", "b")); + EXPECT_CALL(mock, F("c", HasSubstr("d"))); + + mock.F("a", "good"); + mock.F("a", "b"); +} +``` + +if you run it with `--gmock_verbose=info`, you will see this output: + +``` +[ RUN ] Foo.Bar + +foo_test.cc:14: EXPECT_CALL(mock, F(_, _)) invoked +foo_test.cc:15: EXPECT_CALL(mock, F("a", "b")) invoked +foo_test.cc:16: EXPECT_CALL(mock, F("c", HasSubstr("d"))) invoked +foo_test.cc:14: Mock function call matches EXPECT_CALL(mock, F(_, _))... + Function call: F(@0x7fff7c8dad40"a", @0x7fff7c8dad10"good") +foo_test.cc:15: Mock function call matches EXPECT_CALL(mock, F("a", "b"))... + Function call: F(@0x7fff7c8dada0"a", @0x7fff7c8dad70"b") +foo_test.cc:16: Failure +Actual function call count doesn't match EXPECT_CALL(mock, F("c", HasSubstr("d")))... + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] Foo.Bar +``` + +Suppose the bug is that the `"c"` in the third `EXPECT_CALL` is a typo +and should actually be `"a"`. With the above message, you should see +that the actual `F("a", "good")` call is matched by the first +`EXPECT_CALL`, not the third as you thought. From that it should be +obvious that the third `EXPECT_CALL` is written wrong. Case solved. + +## Running Tests in Emacs ## + +If you build and run your tests in Emacs, the source file locations of +Google Mock and [Google Test](http://code.google.com/p/googletest/) +errors will be highlighted. Just press `` on one of them and +you'll be taken to the offending line. Or, you can just type `C-x `` +to jump to the next error. + +To make it even easier, you can add the following lines to your +`~/.emacs` file: + +``` +(global-set-key "\M-m" 'compile) ; m is for make +(global-set-key [M-down] 'next-error) +(global-set-key [M-up] '(lambda () (interactive) (next-error -1))) +``` + +Then you can type `M-m` to start a build, or `M-up`/`M-down` to move +back and forth between errors. + +## Fusing Google Mock Source Files ## + +Google Mock's implementation consists of dozens of files (excluding +its own tests). Sometimes you may want them to be packaged up in +fewer files instead, such that you can easily copy them to a new +machine and start hacking there. For this we provide an experimental +Python script `fuse_gmock_files.py` in the `scripts/` directory +(starting with release 1.2.0). Assuming you have Python 2.4 or above +installed on your machine, just go to that directory and run +``` +python fuse_gmock_files.py OUTPUT_DIR +``` + +and you should see an `OUTPUT_DIR` directory being created with files +`gtest/gtest.h`, `gmock/gmock.h`, and `gmock-gtest-all.cc` in it. +These three files contain everything you need to use Google Mock (and +Google Test). Just copy them to anywhere you want and you are ready +to write tests and use mocks. You can use the +[scrpts/test/Makefile](http://code.google.com/p/googlemock/source/browse/trunk/scripts/test/Makefile) file as an example on how to compile your tests +against them. + +# Extending Google Mock # + +## Writing New Matchers Quickly ## + +The `MATCHER*` family of macros can be used to define custom matchers +easily. The syntax: + +``` +MATCHER(name, description_string_expression) { statements; } +``` + +will define a matcher with the given name that executes the +statements, which must return a `bool` to indicate if the match +succeeds. Inside the statements, you can refer to the value being +matched by `arg`, and refer to its type by `arg_type`. + +The description string is a `string`-typed expression that documents +what the matcher does, and is used to generate the failure message +when the match fails. It can (and should) reference the special +`bool` variable `negation`, and should evaluate to the description of +the matcher when `negation` is `false`, or that of the matcher's +negation when `negation` is `true`. + +For convenience, we allow the description string to be empty (`""`), +in which case Google Mock will use the sequence of words in the +matcher name as the description. + +For example: +``` +MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } +``` +allows you to write +``` + // Expects mock_foo.Bar(n) to be called where n is divisible by 7. + EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); +``` +or, +``` +using ::testing::Not; +... + EXPECT_THAT(some_expression, IsDivisibleBy7()); + EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7())); +``` +If the above assertions fail, they will print something like: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 +... + Value of: some_other_expression + Expected: not (is divisible by 7) + Actual: 21 +``` +where the descriptions `"is divisible by 7"` and `"not (is divisible +by 7)"` are automatically calculated from the matcher name +`IsDivisibleBy7`. + +As you may have noticed, the auto-generated descriptions (especially +those for the negation) may not be so great. You can always override +them with a string expression of your own: +``` +MATCHER(IsDivisibleBy7, std::string(negation ? "isn't" : "is") + + " divisible by 7") { + return (arg % 7) == 0; +} +``` + +Optionally, you can stream additional information to a hidden argument +named `result_listener` to explain the match result. For example, a +better definition of `IsDivisibleBy7` is: +``` +MATCHER(IsDivisibleBy7, "") { + if ((arg % 7) == 0) + return true; + + *result_listener << "the remainder is " << (arg % 7); + return false; +} +``` + +With this definition, the above assertion will give a better message: +``` + Value of: some_expression + Expected: is divisible by 7 + Actual: 27 (the remainder is 6) +``` + +You should let `MatchAndExplain()` print _any additional information_ +that can help a user understand the match result. Note that it should +explain why the match succeeds in case of a success (unless it's +obvious) - this is useful when the matcher is used inside +`Not()`. There is no need to print the argument value itself, as +Google Mock already prints it for you. + +**Notes:** + + 1. The type of the value being matched (`arg_type`) is determined by the context in which you use the matcher and is supplied to you by the compiler, so you don't need to worry about declaring it (nor can you). This allows the matcher to be polymorphic. For example, `IsDivisibleBy7()` can be used to match any type where the value of `(arg % 7) == 0` can be implicitly converted to a `bool`. In the `Bar(IsDivisibleBy7())` example above, if method `Bar()` takes an `int`, `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be `unsigned long`; and so on. + 1. Google Mock doesn't guarantee when or how many times a matcher will be invoked. Therefore the matcher logic must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). This requirement must be satisfied no matter how you define the matcher (e.g. using one of the methods described in the following recipes). In particular, a matcher can never call a mock function, as that will affect the state of the mock object and Google Mock. + +## Writing New Parameterized Matchers Quickly ## + +Sometimes you'll want to define a matcher that has parameters. For that you +can use the macro: +``` +MATCHER_P(name, param_name, description_string) { statements; } +``` +where the description string can be either `""` or a string expression +that references `negation` and `param_name`. + +For example: +``` +MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +``` +will allow you to write: +``` + EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +``` +which may lead to this message (assuming `n` is 10): +``` + Value of: Blah("a") + Expected: has absolute value 10 + Actual: -9 +``` + +Note that both the matcher description and its parameter are +printed, making the message human-friendly. + +In the matcher definition body, you can write `foo_type` to +reference the type of a parameter named `foo`. For example, in the +body of `MATCHER_P(HasAbsoluteValue, value)` above, you can write +`value_type` to refer to the type of `value`. + +Google Mock also provides `MATCHER_P2`, `MATCHER_P3`, ..., up to +`MATCHER_P10` to support multi-parameter matchers: +``` +MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; } +``` + +Please note that the custom description string is for a particular +**instance** of the matcher, where the parameters have been bound to +actual values. Therefore usually you'll want the parameter values to +be part of the description. Google Mock lets you do that by +referencing the matcher parameters in the description string +expression. + +For example, +``` + using ::testing::PrintToString; + MATCHER_P2(InClosedRange, low, hi, + std::string(negation ? "isn't" : "is") + " in range [" + + PrintToString(low) + ", " + PrintToString(hi) + "]") { + return low <= arg && arg <= hi; + } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the message: +``` + Expected: is in range [4, 6] +``` + +If you specify `""` as the description, the failure message will +contain the sequence of words in the matcher name followed by the +parameter values printed as a tuple. For example, +``` + MATCHER_P2(InClosedRange, low, hi, "") { ... } + ... + EXPECT_THAT(3, InClosedRange(4, 6)); +``` +would generate a failure that contains the text: +``` + Expected: in closed range (4, 6) +``` + +For the purpose of typing, you can view +``` +MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +``` +as shorthand for +``` +template +FooMatcherPk +Foo(p1_type p1, ..., pk_type pk) { ... } +``` + +When you write `Foo(v1, ..., vk)`, the compiler infers the types of +the parameters `v1`, ..., and `vk` for you. If you are not happy with +the result of the type inference, you can specify the types by +explicitly instantiating the template, as in `Foo(5, false)`. +As said earlier, you don't get to (or need to) specify +`arg_type` as that's determined by the context in which the matcher +is used. + +You can assign the result of expression `Foo(p1, ..., pk)` to a +variable of type `FooMatcherPk`. This can be +useful when composing matchers. Matchers that don't have a parameter +or have only one parameter have special types: you can assign `Foo()` +to a `FooMatcher`-typed variable, and assign `Foo(p)` to a +`FooMatcherP`-typed variable. + +While you can instantiate a matcher template with reference types, +passing the parameters by pointer usually makes your code more +readable. If, however, you still want to pass a parameter by +reference, be aware that in the failure message generated by the +matcher you will see the value of the referenced object but not its +address. + +You can overload matchers with different numbers of parameters: +``` +MATCHER_P(Blah, a, description_string_1) { ... } +MATCHER_P2(Blah, a, b, description_string_2) { ... } +``` + +While it's tempting to always use the `MATCHER*` macros when defining +a new matcher, you should also consider implementing +`MatcherInterface` or using `MakePolymorphicMatcher()` instead (see +the recipes that follow), especially if you need to use the matcher a +lot. While these approaches require more work, they give you more +control on the types of the value being matched and the matcher +parameters, which in general leads to better compiler error messages +that pay off in the long run. They also allow overloading matchers +based on parameter types (as opposed to just based on the number of +parameters). + +## Writing New Monomorphic Matchers ## + +A matcher of argument type `T` implements +`::testing::MatcherInterface` and does two things: it tests whether a +value of type `T` matches the matcher, and can describe what kind of +values it matches. The latter ability is used for generating readable +error messages when expectations are violated. + +The interface looks like this: + +``` +class MatchResultListener { + public: + ... + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x); + + // Returns the underlying ostream. + ::std::ostream* stream(); +}; + +template +class MatcherInterface { + public: + virtual ~MatcherInterface(); + + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Describes this matcher to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. + virtual void DescribeNegationTo(::std::ostream* os) const; +}; +``` + +If you need a custom matcher but `Truly()` is not a good option (for +example, you may not be happy with the way `Truly(predicate)` +describes itself, or you may want your matcher to be polymorphic as +`Eq(value)` is), you can define a matcher to do whatever you want in +two steps: first implement the matcher interface, and then define a +factory function to create a matcher instance. The second step is not +strictly needed but it makes the syntax of using the matcher nicer. + +For example, you can define a matcher to test whether an `int` is +divisible by 7 and then use it like this: +``` +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, MatchResultListener* listener) const { + return (n % 7) == 0; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "is divisible by 7"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "is not divisible by 7"; + } +}; + +inline Matcher DivisibleBy7() { + return MakeMatcher(new DivisibleBy7Matcher); +} +... + + EXPECT_CALL(foo, Bar(DivisibleBy7())); +``` + +You may improve the matcher message by streaming additional +information to the `listener` argument in `MatchAndExplain()`: + +``` +class DivisibleBy7Matcher : public MatcherInterface { + public: + virtual bool MatchAndExplain(int n, + MatchResultListener* listener) const { + const int remainder = n % 7; + if (remainder != 0) { + *listener << "the remainder is " << remainder; + } + return remainder == 0; + } + ... +}; +``` + +Then, `EXPECT_THAT(x, DivisibleBy7());` may general a message like this: +``` +Value of: x +Expected: is divisible by 7 + Actual: 23 (the remainder is 2) +``` + +## Writing New Polymorphic Matchers ## + +You've learned how to write your own matchers in the previous +recipe. Just one problem: a matcher created using `MakeMatcher()` only +works for one particular type of arguments. If you want a +_polymorphic_ matcher that works with arguments of several types (for +instance, `Eq(x)` can be used to match a `value` as long as `value` == +`x` compiles -- `value` and `x` don't have to share the same type), +you can learn the trick from `"gmock/gmock-matchers.h"` but it's a bit +involved. + +Fortunately, most of the time you can define a polymorphic matcher +easily with the help of `MakePolymorphicMatcher()`. Here's how you can +define `NotNull()` as an example: + +``` +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +using ::testing::NotNull; +using ::testing::PolymorphicMatcher; + +class NotNullMatcher { + public: + // To implement a polymorphic matcher, first define a COPYABLE class + // that has three members MatchAndExplain(), DescribeTo(), and + // DescribeNegationTo(), like the following. + + // In this example, we want to use NotNull() with any pointer, so + // MatchAndExplain() accepts a pointer of any type as its first argument. + // In general, you can define MatchAndExplain() as an ordinary method or + // a method template, or even overload it. + template + bool MatchAndExplain(T* p, + MatchResultListener* /* listener */) const { + return p != NULL; + } + + // Describes the property of a value matching this matcher. + void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } + + // Describes the property of a value NOT matching this matcher. + void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } +}; + +// To construct a polymorphic matcher, pass an instance of the class +// to MakePolymorphicMatcher(). Note the return type. +inline PolymorphicMatcher NotNull() { + return MakePolymorphicMatcher(NotNullMatcher()); +} +... + + EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. +``` + +**Note:** Your polymorphic matcher class does **not** need to inherit from +`MatcherInterface` or any other class, and its methods do **not** need +to be virtual. + +Like in a monomorphic matcher, you may explain the match result by +streaming additional information to the `listener` argument in +`MatchAndExplain()`. + +## Writing New Cardinalities ## + +A cardinality is used in `Times()` to tell Google Mock how many times +you expect a call to occur. It doesn't have to be exact. For example, +you can say `AtLeast(5)` or `Between(2, 4)`. + +If the built-in set of cardinalities doesn't suit you, you are free to +define your own by implementing the following interface (in namespace +`testing`): + +``` +class CardinalityInterface { + public: + virtual ~CardinalityInterface(); + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; +``` + +For example, to specify that a call must occur even number of times, +you can write + +``` +using ::testing::Cardinality; +using ::testing::CardinalityInterface; +using ::testing::MakeCardinality; + +class EvenNumberCardinality : public CardinalityInterface { + public: + virtual bool IsSatisfiedByCallCount(int call_count) const { + return (call_count % 2) == 0; + } + + virtual bool IsSaturatedByCallCount(int call_count) const { + return false; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "called even number of times"; + } +}; + +Cardinality EvenNumber() { + return MakeCardinality(new EvenNumberCardinality); +} +... + + EXPECT_CALL(foo, Bar(3)) + .Times(EvenNumber()); +``` + +## Writing New Actions Quickly ## + +If the built-in actions don't work for you, and you find it +inconvenient to use `Invoke()`, you can use a macro from the `ACTION*` +family to quickly define a new action that can be used in your code as +if it's a built-in action. + +By writing +``` +ACTION(name) { statements; } +``` +in a namespace scope (i.e. not inside a class or function), you will +define an action with the given name that executes the statements. +The value returned by `statements` will be used as the return value of +the action. Inside the statements, you can refer to the K-th +(0-based) argument of the mock function as `argK`. For example: +``` +ACTION(IncrementArg1) { return ++(*arg1); } +``` +allows you to write +``` +... WillOnce(IncrementArg1()); +``` + +Note that you don't need to specify the types of the mock function +arguments. Rest assured that your code is type-safe though: +you'll get a compiler error if `*arg1` doesn't support the `++` +operator, or if the type of `++(*arg1)` isn't compatible with the mock +function's return type. + +Another example: +``` +ACTION(Foo) { + (*arg2)(5); + Blah(); + *arg1 = 0; + return arg0; +} +``` +defines an action `Foo()` that invokes argument #2 (a function pointer) +with 5, calls function `Blah()`, sets the value pointed to by argument +#1 to 0, and returns argument #0. + +For more convenience and flexibility, you can also use the following +pre-defined symbols in the body of `ACTION`: + +| `argK_type` | The type of the K-th (0-based) argument of the mock function | +|:------------|:-------------------------------------------------------------| +| `args` | All arguments of the mock function as a tuple | +| `args_type` | The type of all arguments of the mock function as a tuple | +| `return_type` | The return type of the mock function | +| `function_type` | The type of the mock function | + +For example, when using an `ACTION` as a stub action for mock function: +``` +int DoSomething(bool flag, int* ptr); +``` +we have: +| **Pre-defined Symbol** | **Is Bound To** | +|:-----------------------|:----------------| +| `arg0` | the value of `flag` | +| `arg0_type` | the type `bool` | +| `arg1` | the value of `ptr` | +| `arg1_type` | the type `int*` | +| `args` | the tuple `(flag, ptr)` | +| `args_type` | the type `std::tr1::tuple` | +| `return_type` | the type `int` | +| `function_type` | the type `int(bool, int*)` | + +## Writing New Parameterized Actions Quickly ## + +Sometimes you'll want to parameterize an action you define. For that +we have another macro +``` +ACTION_P(name, param) { statements; } +``` + +For example, +``` +ACTION_P(Add, n) { return arg0 + n; } +``` +will allow you to write +``` +// Returns argument #0 + 5. +... WillOnce(Add(5)); +``` + +For convenience, we use the term _arguments_ for the values used to +invoke the mock function, and the term _parameters_ for the values +used to instantiate an action. + +Note that you don't need to provide the type of the parameter either. +Suppose the parameter is named `param`, you can also use the +Google-Mock-defined symbol `param_type` to refer to the type of the +parameter as inferred by the compiler. For example, in the body of +`ACTION_P(Add, n)` above, you can write `n_type` for the type of `n`. + +Google Mock also provides `ACTION_P2`, `ACTION_P3`, and etc to support +multi-parameter actions. For example, +``` +ACTION_P2(ReturnDistanceTo, x, y) { + double dx = arg0 - x; + double dy = arg1 - y; + return sqrt(dx*dx + dy*dy); +} +``` +lets you write +``` +... WillOnce(ReturnDistanceTo(5.0, 26.5)); +``` + +You can view `ACTION` as a degenerated parameterized action where the +number of parameters is 0. + +You can also easily define actions overloaded on the number of parameters: +``` +ACTION_P(Plus, a) { ... } +ACTION_P2(Plus, a, b) { ... } +``` + +## Restricting the Type of an Argument or Parameter in an ACTION ## + +For maximum brevity and reusability, the `ACTION*` macros don't ask +you to provide the types of the mock function arguments and the action +parameters. Instead, we let the compiler infer the types for us. + +Sometimes, however, we may want to be more explicit about the types. +There are several tricks to do that. For example: +``` +ACTION(Foo) { + // Makes sure arg0 can be converted to int. + int n = arg0; + ... use n instead of arg0 here ... +} + +ACTION_P(Bar, param) { + // Makes sure the type of arg1 is const char*. + ::testing::StaticAssertTypeEq(); + + // Makes sure param can be converted to bool. + bool flag = param; +} +``` +where `StaticAssertTypeEq` is a compile-time assertion in Google Test +that verifies two types are the same. + +## Writing New Action Templates Quickly ## + +Sometimes you want to give an action explicit template parameters that +cannot be inferred from its value parameters. `ACTION_TEMPLATE()` +supports that and can be viewed as an extension to `ACTION()` and +`ACTION_P*()`. + +The syntax: +``` +ACTION_TEMPLATE(ActionName, + HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), + AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +``` + +defines an action template that takes _m_ explicit template parameters +and _n_ value parameters, where _m_ is between 1 and 10, and _n_ is +between 0 and 10. `name_i` is the name of the i-th template +parameter, and `kind_i` specifies whether it's a `typename`, an +integral constant, or a template. `p_i` is the name of the i-th value +parameter. + +Example: +``` +// DuplicateArg(output) converts the k-th argument of the mock +// function to type T and copies it to *output. +ACTION_TEMPLATE(DuplicateArg, + // Note the comma between int and k: + HAS_2_TEMPLATE_PARAMS(int, k, typename, T), + AND_1_VALUE_PARAMS(output)) { + *output = T(std::tr1::get(args)); +} +``` + +To create an instance of an action template, write: +``` + ActionName(v1, ..., v_n) +``` +where the `t`s are the template arguments and the +`v`s are the value arguments. The value argument +types are inferred by the compiler. For example: +``` +using ::testing::_; +... + int n; + EXPECT_CALL(mock, Foo(_, _)) + .WillOnce(DuplicateArg<1, unsigned char>(&n)); +``` + +If you want to explicitly specify the value argument types, you can +provide additional template arguments: +``` + ActionName(v1, ..., v_n) +``` +where `u_i` is the desired type of `v_i`. + +`ACTION_TEMPLATE` and `ACTION`/`ACTION_P*` can be overloaded on the +number of value parameters, but not on the number of template +parameters. Without the restriction, the meaning of the following is +unclear: + +``` + OverloadedAction(x); +``` + +Are we using a single-template-parameter action where `bool` refers to +the type of `x`, or a two-template-parameter action where the compiler +is asked to infer the type of `x`? + +## Using the ACTION Object's Type ## + +If you are writing a function that returns an `ACTION` object, you'll +need to know its type. The type depends on the macro used to define +the action and the parameter types. The rule is relatively simple: +| **Given Definition** | **Expression** | **Has Type** | +|:---------------------|:---------------|:-------------| +| `ACTION(Foo)` | `Foo()` | `FooAction` | +| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | +| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | +| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `FooActionP` | +| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | +| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz(bool_value, int_value)` | `FooActionP2` | +| ... | ... | ... | + +Note that we have to pick different suffixes (`Action`, `ActionP`, +`ActionP2`, and etc) for actions with different numbers of value +parameters, or the action definitions cannot be overloaded on the +number of them. + +## Writing New Monomorphic Actions ## + +While the `ACTION*` macros are very convenient, sometimes they are +inappropriate. For example, despite the tricks shown in the previous +recipes, they don't let you directly specify the types of the mock +function arguments and the action parameters, which in general leads +to unoptimized compiler error messages that can baffle unfamiliar +users. They also don't allow overloading actions based on parameter +types without jumping through some hoops. + +An alternative to the `ACTION*` macros is to implement +`::testing::ActionInterface`, where `F` is the type of the mock +function in which the action will be used. For example: + +``` +template class ActionInterface { + public: + virtual ~ActionInterface(); + + // Performs the action. Result is the return type of function type + // F, and ArgumentTuple is the tuple of arguments of F. + // + // For example, if F is int(bool, const string&), then Result would + // be int, and ArgumentTuple would be tr1::tuple. + virtual Result Perform(const ArgumentTuple& args) = 0; +}; + +using ::testing::_; +using ::testing::Action; +using ::testing::ActionInterface; +using ::testing::MakeAction; + +typedef int IncrementMethod(int*); + +class IncrementArgumentAction : public ActionInterface { + public: + virtual int Perform(const tr1::tuple& args) { + int* p = tr1::get<0>(args); // Grabs the first argument. + return *p++; + } +}; + +Action IncrementArgument() { + return MakeAction(new IncrementArgumentAction); +} +... + + EXPECT_CALL(foo, Baz(_)) + .WillOnce(IncrementArgument()); + + int n = 5; + foo.Baz(&n); // Should return 5 and change n to 6. +``` + +## Writing New Polymorphic Actions ## + +The previous recipe showed you how to define your own action. This is +all good, except that you need to know the type of the function in +which the action will be used. Sometimes that can be a problem. For +example, if you want to use the action in functions with _different_ +types (e.g. like `Return()` and `SetArgPointee()`). + +If an action can be used in several types of mock functions, we say +it's _polymorphic_. The `MakePolymorphicAction()` function template +makes it easy to define such an action: + +``` +namespace testing { + +template +PolymorphicAction MakePolymorphicAction(const Impl& impl); + +} // namespace testing +``` + +As an example, let's define an action that returns the second argument +in the mock function's argument list. The first step is to define an +implementation class: + +``` +class ReturnSecondArgumentAction { + public: + template + Result Perform(const ArgumentTuple& args) const { + // To get the i-th (0-based) argument, use tr1::get(args). + return tr1::get<1>(args); + } +}; +``` + +This implementation class does _not_ need to inherit from any +particular class. What matters is that it must have a `Perform()` +method template. This method template takes the mock function's +arguments as a tuple in a **single** argument, and returns the result of +the action. It can be either `const` or not, but must be invokable +with exactly one template argument, which is the result type. In other +words, you must be able to call `Perform(args)` where `R` is the +mock function's return type and `args` is its arguments in a tuple. + +Next, we use `MakePolymorphicAction()` to turn an instance of the +implementation class into the polymorphic action we need. It will be +convenient to have a wrapper for this: + +``` +using ::testing::MakePolymorphicAction; +using ::testing::PolymorphicAction; + +PolymorphicAction ReturnSecondArgument() { + return MakePolymorphicAction(ReturnSecondArgumentAction()); +} +``` + +Now, you can use this polymorphic action the same way you use the +built-in ones: + +``` +using ::testing::_; + +class MockFoo : public Foo { + public: + MOCK_METHOD2(DoThis, int(bool flag, int n)); + MOCK_METHOD3(DoThat, string(int x, const char* str1, const char* str2)); +}; +... + + MockFoo foo; + EXPECT_CALL(foo, DoThis(_, _)) + .WillOnce(ReturnSecondArgument()); + EXPECT_CALL(foo, DoThat(_, _, _)) + .WillOnce(ReturnSecondArgument()); + ... + foo.DoThis(true, 5); // Will return 5. + foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". +``` + +## Teaching Google Mock How to Print Your Values ## + +When an uninteresting or unexpected call occurs, Google Mock prints the +argument values and the stack trace to help you debug. Assertion +macros like `EXPECT_THAT` and `EXPECT_EQ` also print the values in +question when the assertion fails. Google Mock and Google Test do this using +Google Test's user-extensible value printer. + +This printer knows how to print built-in C++ types, native arrays, STL +containers, and any type that supports the `<<` operator. For other +types, it prints the raw bytes in the value and hopes that you the +user can figure it out. +[Google Test's advanced guide](http://code.google.com/p/googletest/wiki/AdvancedGuide#Teaching_Google_Test_How_to_Print_Your_Values) +explains how to extend the printer to do a better job at +printing your particular type than to dump the bytes. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/Documentation.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/Documentation.md new file mode 100644 index 00000000..d9181f28 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/Documentation.md @@ -0,0 +1,12 @@ +This page lists all documentation wiki pages for Google Mock **(the SVN trunk version)** +- **if you use a released version of Google Mock, please read the documentation for that specific version instead.** + + * [ForDummies](V1_7_ForDummies.md) -- start here if you are new to Google Mock. + * [CheatSheet](V1_7_CheatSheet.md) -- a quick reference. + * [CookBook](V1_7_CookBook.md) -- recipes for doing various tasks using Google Mock. + * [FrequentlyAskedQuestions](V1_7_FrequentlyAskedQuestions.md) -- check here before asking a question on the mailing list. + +To contribute code to Google Mock, read: + + * [DevGuide](DevGuide.md) -- read this _before_ writing your first patch. + * [Pump Manual](http://code.google.com/p/googletest/wiki/PumpManual) -- how we generate some of Google Mock's source files. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/ForDummies.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/ForDummies.md new file mode 100644 index 00000000..2ed43007 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/ForDummies.md @@ -0,0 +1,439 @@ + + +(**Note:** If you get compiler errors that you don't understand, be sure to consult [Google Mock Doctor](http://code.google.com/p/googlemock/wiki/V1_7_FrequentlyAskedQuestions#How_am_I_supposed_to_make_sense_of_these_horrible_template_error).) + +# What Is Google C++ Mocking Framework? # +When you write a prototype or test, often it's not feasible or wise to rely on real objects entirely. A **mock object** implements the same interface as a real object (so it can be used as one), but lets you specify at run time how it will be used and what it should do (which methods will be called? in which order? how many times? with what arguments? what will they return? etc). + +**Note:** It is easy to confuse the term _fake objects_ with mock objects. Fakes and mocks actually mean very different things in the Test-Driven Development (TDD) community: + + * **Fake** objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake. + * **Mocks** are objects pre-programmed with _expectations_, which form a specification of the calls they are expected to receive. + +If all this seems too abstract for you, don't worry - the most important thing to remember is that a mock allows you to check the _interaction_ between itself and code that uses it. The difference between fakes and mocks will become much clearer once you start to use mocks. + +**Google C++ Mocking Framework** (or **Google Mock** for short) is a library (sometimes we also call it a "framework" to make it sound cool) for creating mock classes and using them. It does to C++ what [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/) do to Java. + +Using Google Mock involves three basic steps: + + 1. Use some simple macros to describe the interface you want to mock, and they will expand to the implementation of your mock class; + 1. Create some mock objects and specify its expectations and behavior using an intuitive syntax; + 1. Exercise code that uses the mock objects. Google Mock will catch any violation of the expectations as soon as it arises. + +# Why Google Mock? # +While mock objects help you remove unnecessary dependencies in tests and make them fast and reliable, using mocks manually in C++ is _hard_: + + * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distance to avoid it. + * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad hoc restrictions. + * The knowledge you gained from using one mock doesn't transfer to the next. + +In contrast, Java and Python programmers have some fine mock frameworks, which automate the creation of mocks. As a result, mocking is a proven effective technique and widely adopted practice in those communities. Having the right tool absolutely makes the difference. + +Google Mock was built to help C++ programmers. It was inspired by [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/), but designed with C++'s specifics in mind. It is your friend if any of the following problems is bothering you: + + * You are stuck with a sub-optimal design and wish you had done more prototyping before it was too late, but prototyping in C++ is by no means "rapid". + * Your tests are slow as they depend on too many libraries or use expensive resources (e.g. a database). + * Your tests are brittle as some resources they use are unreliable (e.g. the network). + * You want to test how your code handles a failure (e.g. a file checksum error), but it's not easy to cause one. + * You need to make sure that your module interacts with other modules in the right way, but it's hard to observe the interaction; therefore you resort to observing the side effects at the end of the action, which is awkward at best. + * You want to "mock out" your dependencies, except that they don't have mock implementations yet; and, frankly, you aren't thrilled by some of those hand-written mocks. + +We encourage you to use Google Mock as: + + * a _design_ tool, for it lets you experiment with your interface design early and often. More iterations lead to better designs! + * a _testing_ tool to cut your tests' outbound dependencies and probe the interaction between your module and its collaborators. + +# Getting Started # +Using Google Mock is easy! Inside your C++ source file, just #include `"gtest/gtest.h"` and `"gmock/gmock.h"`, and you are ready to go. + +# A Case for Mock Turtles # +Let's look at an example. Suppose you are developing a graphics program that relies on a LOGO-like API for drawing. How would you test that it does the right thing? Well, you can run it and compare the screen with a golden screen snapshot, but let's admit it: tests like this are expensive to run and fragile (What if you just upgraded to a shiny new graphics card that has better anti-aliasing? Suddenly you have to update all your golden images.). It would be too painful if all your tests are like this. Fortunately, you learned about Dependency Injection and know the right thing to do: instead of having your application talk to the drawing API directly, wrap the API in an interface (say, `Turtle`) and code to that interface: + +``` +class Turtle { + ... + virtual ~Turtle() {} + virtual void PenUp() = 0; + virtual void PenDown() = 0; + virtual void Forward(int distance) = 0; + virtual void Turn(int degrees) = 0; + virtual void GoTo(int x, int y) = 0; + virtual int GetX() const = 0; + virtual int GetY() const = 0; +}; +``` + +(Note that the destructor of `Turtle` **must** be virtual, as is the case for **all** classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you'll get corrupted program states like memory leaks.) + +You can control whether the turtle's movement will leave a trace using `PenUp()` and `PenDown()`, and control its movement using `Forward()`, `Turn()`, and `GoTo()`. Finally, `GetX()` and `GetY()` tell you the current position of the turtle. + +Your program will normally use a real implementation of this interface. In tests, you can use a mock implementation instead. This allows you to easily check what drawing primitives your program is calling, with what arguments, and in which order. Tests written this way are much more robust (they won't break because your new machine does anti-aliasing differently), easier to read and maintain (the intent of a test is expressed in the code, not in some binary images), and run _much, much faster_. + +# Writing the Mock Class # +If you are lucky, the mocks you need to use have already been implemented by some nice people. If, however, you find yourself in the position to write a mock class, relax - Google Mock turns this task into a fun game! (Well, almost.) + +## How to Define It ## +Using the `Turtle` interface as example, here are the simple steps you need to follow: + + 1. Derive a class `MockTurtle` from `Turtle`. + 1. Take a _virtual_ function of `Turtle` (while it's possible to [mock non-virtual methods using templates](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Mocking_Nonvirtual_Methods), it's much more involved). Count how many arguments it has. + 1. In the `public:` section of the child class, write `MOCK_METHODn();` (or `MOCK_CONST_METHODn();` if you are mocking a `const` method), where `n` is the number of the arguments; if you counted wrong, shame on you, and a compiler error will tell you so. + 1. Now comes the fun part: you take the function signature, cut-and-paste the _function name_ as the _first_ argument to the macro, and leave what's left as the _second_ argument (in case you're curious, this is the _type of the function_). + 1. Repeat until all virtual functions you want to mock are done. + +After the process, you should have something like: + +``` +#include "gmock/gmock.h" // Brings in Google Mock. +class MockTurtle : public Turtle { + public: + ... + MOCK_METHOD0(PenUp, void()); + MOCK_METHOD0(PenDown, void()); + MOCK_METHOD1(Forward, void(int distance)); + MOCK_METHOD1(Turn, void(int degrees)); + MOCK_METHOD2(GoTo, void(int x, int y)); + MOCK_CONST_METHOD0(GetX, int()); + MOCK_CONST_METHOD0(GetY, int()); +}; +``` + +You don't need to define these mock methods somewhere else - the `MOCK_METHOD*` macros will generate the definitions for you. It's that simple! Once you get the hang of it, you can pump out mock classes faster than your source-control system can handle your check-ins. + +**Tip:** If even this is too much work for you, you'll find the +`gmock_gen.py` tool in Google Mock's `scripts/generator/` directory (courtesy of the [cppclean](http://code.google.com/p/cppclean/) project) useful. This command-line +tool requires that you have Python 2.4 installed. You give it a C++ file and the name of an abstract class defined in it, +and it will print the definition of the mock class for you. Due to the +complexity of the C++ language, this script may not always work, but +it can be quite handy when it does. For more details, read the [user documentation](http://code.google.com/p/googlemock/source/browse/trunk/scripts/generator/README). + +## Where to Put It ## +When you define a mock class, you need to decide where to put its definition. Some people put it in a `*_test.cc`. This is fine when the interface being mocked (say, `Foo`) is owned by the same person or team. Otherwise, when the owner of `Foo` changes it, your test could break. (You can't really expect `Foo`'s maintainer to fix every test that uses `Foo`, can you?) + +So, the rule of thumb is: if you need to mock `Foo` and it's owned by others, define the mock class in `Foo`'s package (better, in a `testing` sub-package such that you can clearly separate production code and testing utilities), and put it in a `mock_foo.h`. Then everyone can reference `mock_foo.h` from their tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and only tests that depend on the changed methods need to be fixed. + +Another way to do it: you can introduce a thin layer `FooAdaptor` on top of `Foo` and code to this new interface. Since you own `FooAdaptor`, you can absorb changes in `Foo` much more easily. While this is more work initially, carefully choosing the adaptor interface can make your code easier to write and more readable (a net win in the long run), as you can choose `FooAdaptor` to fit your specific domain much better than `Foo` does. + +# Using Mocks in Tests # +Once you have a mock class, using it is easy. The typical work flow is: + + 1. Import the Google Mock names from the `testing` namespace such that you can use them unqualified (You only have to do it once per file. Remember that namespaces are a good idea and good for your health.). + 1. Create some mock objects. + 1. Specify your expectations on them (How many times will a method be called? With what arguments? What should it do? etc.). + 1. Exercise some code that uses the mocks; optionally, check the result using Google Test assertions. If a mock method is called more than expected or with wrong arguments, you'll get an error immediately. + 1. When a mock is destructed, Google Mock will automatically check whether all expectations on it have been satisfied. + +Here's an example: + +``` +#include "path/to/mock-turtle.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +using ::testing::AtLeast; // #1 + +TEST(PainterTest, CanDrawSomething) { + MockTurtle turtle; // #2 + EXPECT_CALL(turtle, PenDown()) // #3 + .Times(AtLeast(1)); + + Painter painter(&turtle); // #4 + + EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); +} // #5 + +int main(int argc, char** argv) { + // The following line must be executed to initialize Google Mock + // (and Google Test) before running the tests. + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} +``` + +As you might have guessed, this test checks that `PenDown()` is called at least once. If the `painter` object didn't call this method, your test will fail with a message like this: + +``` +path/to/my_test.cc:119: Failure +Actual function call count doesn't match this expectation: +Actually: never called; +Expected: called at least once. +``` + +**Tip 1:** If you run the test from an Emacs buffer, you can hit `` on the line number displayed in the error message to jump right to the failed expectation. + +**Tip 2:** If your mock objects are never deleted, the final verification won't happen. Therefore it's a good idea to use a heap leak checker in your tests when you allocate mocks on the heap. + +**Important note:** Google Mock requires expectations to be set **before** the mock functions are called, otherwise the behavior is **undefined**. In particular, you mustn't interleave `EXPECT_CALL()`s and calls to the mock functions. + +This means `EXPECT_CALL()` should be read as expecting that a call will occur _in the future_, not that a call has occurred. Why does Google Mock work like that? Well, specifying the expectation beforehand allows Google Mock to report a violation as soon as it arises, when the context (stack trace, etc) is still available. This makes debugging much easier. + +Admittedly, this test is contrived and doesn't do much. You can easily achieve the same effect without using Google Mock. However, as we shall reveal soon, Google Mock allows you to do _much more_ with the mocks. + +## Using Google Mock with Any Testing Framework ## +If you want to use something other than Google Test (e.g. [CppUnit](http://apps.sourceforge.net/mediawiki/cppunit/index.php?title=Main_Page) or +[CxxTest](http://cxxtest.tigris.org/)) as your testing framework, just change the `main()` function in the previous section to: +``` +int main(int argc, char** argv) { + // The following line causes Google Mock to throw an exception on failure, + // which will be interpreted by your testing framework as a test failure. + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + ... whatever your testing framework requires ... +} +``` + +This approach has a catch: it makes Google Mock throw an exception +from a mock object's destructor sometimes. With some compilers, this +sometimes causes the test program to crash. You'll still be able to +notice that the test has failed, but it's not a graceful failure. + +A better solution is to use Google Test's +[event listener API](http://code.google.com/p/googletest/wiki/AdvancedGuide#Extending_Google_Test_by_Handling_Test_Events) +to report a test failure to your testing framework properly. You'll need to +implement the `OnTestPartResult()` method of the event listener interface, but it +should be straightforward. + +If this turns out to be too much work, we suggest that you stick with +Google Test, which works with Google Mock seamlessly (in fact, it is +technically part of Google Mock.). If there is a reason that you +cannot use Google Test, please let us know. + +# Setting Expectations # +The key to using a mock object successfully is to set the _right expectations_ on it. If you set the expectations too strict, your test will fail as the result of unrelated changes. If you set them too loose, bugs can slip through. You want to do it just right such that your test can catch exactly the kind of bugs you intend it to catch. Google Mock provides the necessary means for you to do it "just right." + +## General Syntax ## +In Google Mock we use the `EXPECT_CALL()` macro to set an expectation on a mock method. The general syntax is: + +``` +EXPECT_CALL(mock_object, method(matchers)) + .Times(cardinality) + .WillOnce(action) + .WillRepeatedly(action); +``` + +The macro has two arguments: first the mock object, and then the method and its arguments. Note that the two are separated by a comma (`,`), not a period (`.`). (Why using a comma? The answer is that it was necessary for technical reasons.) + +The macro can be followed by some optional _clauses_ that provide more information about the expectation. We'll discuss how each clause works in the coming sections. + +This syntax is designed to make an expectation read like English. For example, you can probably guess that + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .Times(5) + .WillOnce(Return(100)) + .WillOnce(Return(150)) + .WillRepeatedly(Return(200)); +``` + +says that the `turtle` object's `GetX()` method will be called five times, it will return 100 the first time, 150 the second time, and then 200 every time. Some people like to call this style of syntax a Domain-Specific Language (DSL). + +**Note:** Why do we use a macro to do this? It serves two purposes: first it makes expectations easily identifiable (either by `grep` or by a human reader), and second it allows Google Mock to include the source file location of a failed expectation in messages, making debugging easier. + +## Matchers: What Arguments Do We Expect? ## +When a mock function takes arguments, we must specify what arguments we are expecting; for example: + +``` +// Expects the turtle to move forward by 100 units. +EXPECT_CALL(turtle, Forward(100)); +``` + +Sometimes you may not want to be too specific (Remember that talk about tests being too rigid? Over specification leads to brittle tests and obscures the intent of tests. Therefore we encourage you to specify only what's necessary - no more, no less.). If you care to check that `Forward()` will be called but aren't interested in its actual argument, write `_` as the argument, which means "anything goes": + +``` +using ::testing::_; +... +// Expects the turtle to move forward. +EXPECT_CALL(turtle, Forward(_)); +``` + +`_` is an instance of what we call **matchers**. A matcher is like a predicate and can test whether an argument is what we'd expect. You can use a matcher inside `EXPECT_CALL()` wherever a function argument is expected. + +A list of built-in matchers can be found in the [CheatSheet](V1_7_CheatSheet.md). For example, here's the `Ge` (greater than or equal) matcher: + +``` +using ::testing::Ge;... +EXPECT_CALL(turtle, Forward(Ge(100))); +``` + +This checks that the turtle will be told to go forward by at least 100 units. + +## Cardinalities: How Many Times Will It Be Called? ## +The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We call its argument a **cardinality** as it tells _how many times_ the call should occur. It allows us to repeat an expectation many times without actually writing it as many times. More importantly, a cardinality can be "fuzzy", just like a matcher can be. This allows a user to express the intent of a test exactly. + +An interesting special case is when we say `Times(0)`. You may have guessed - it means that the function shouldn't be called with the given arguments at all, and Google Mock will report a Google Test failure whenever the function is (wrongfully) called. + +We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the list of built-in cardinalities you can use, see the [CheatSheet](V1_7_CheatSheet.md). + +The `Times()` clause can be omitted. **If you omit `Times()`, Google Mock will infer the cardinality for you.** The rules are easy to remember: + + * If **neither** `WillOnce()` **nor** `WillRepeatedly()` is in the `EXPECT_CALL()`, the inferred cardinality is `Times(1)`. + * If there are `n WillOnce()`'s but **no** `WillRepeatedly()`, where `n` >= 1, the cardinality is `Times(n)`. + * If there are `n WillOnce()`'s and **one** `WillRepeatedly()`, where `n` >= 0, the cardinality is `Times(AtLeast(n))`. + +**Quick quiz:** what do you think will happen if a function is expected to be called twice but actually called four times? + +## Actions: What Should It Do? ## +Remember that a mock object doesn't really have a working implementation? We as users have to tell it what to do when a method is invoked. This is easy in Google Mock. + +First, if the return type of a mock function is a built-in type or a pointer, the function has a **default action** (a `void` function will just return, a `bool` function will return `false`, and other functions will return 0). If you don't say anything, this behavior will be used. + +Second, if a mock function doesn't have a default action, or the default action doesn't suit you, you can specify the action to be taken each time the expectation matches using a series of `WillOnce()` clauses followed by an optional `WillRepeatedly()`. For example, + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillOnce(Return(300)); +``` + +This says that `turtle.GetX()` will be called _exactly three times_ (Google Mock inferred this from how many `WillOnce()` clauses we've written, since we didn't explicitly write `Times()`), and will return 100, 200, and 300 respectively. + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) + .WillOnce(Return(100)) + .WillOnce(Return(200)) + .WillRepeatedly(Return(300)); +``` + +says that `turtle.GetY()` will be called _at least twice_ (Google Mock knows this as we've written two `WillOnce()` clauses and a `WillRepeatedly()` while having no explicit `Times()`), will return 100 the first time, 200 the second time, and 300 from the third time on. + +Of course, if you explicitly write a `Times()`, Google Mock will not try to infer the cardinality itself. What if the number you specified is larger than there are `WillOnce()` clauses? Well, after all `WillOnce()`s are used up, Google Mock will do the _default_ action for the function every time (unless, of course, you have a `WillRepeatedly()`.). + +What can we do inside `WillOnce()` besides `Return()`? You can return a reference using `ReturnRef(variable)`, or invoke a pre-defined function, among [others](http://code.google.com/p/googlemock/wiki/V1_7_CheatSheet#Actions). + +**Important note:** The `EXPECT_CALL()` statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want: + +``` +int n = 100; +EXPECT_CALL(turtle, GetX()) +.Times(4) +.WillRepeatedly(Return(n++)); +``` + +Instead of returning 100, 101, 102, ..., consecutively, this mock function will always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)` will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will return the same pointer every time. If you want the side effect to happen every time, you need to define a custom action, which we'll teach in the [CookBook](V1_7_CookBook.md). + +Time for another quiz! What do you think the following means? + +``` +using ::testing::Return;... +EXPECT_CALL(turtle, GetY()) +.Times(4) +.WillOnce(Return(100)); +``` + +Obviously `turtle.GetY()` is expected to be called four times. But if you think it will return 100 every time, think twice! Remember that one `WillOnce()` clause will be consumed each time the function is invoked and the default action will be taken afterwards. So the right answer is that `turtle.GetY()` will return 100 the first time, but **return 0 from the second time on**, as returning 0 is the default action for `int` functions. + +## Using Multiple Expectations ## +So far we've only shown examples where you have a single expectation. More realistically, you're going to specify expectations on multiple mock methods, which may be from multiple mock objects. + +By default, when a mock method is invoked, Google Mock will search the expectations in the **reverse order** they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones."). If the matching expectation cannot take any more calls, you will get an upper-bound-violated failure. Here's an example: + +``` +using ::testing::_;... +EXPECT_CALL(turtle, Forward(_)); // #1 +EXPECT_CALL(turtle, Forward(10)) // #2 + .Times(2); +``` + +If `Forward(10)` is called three times in a row, the third time it will be an error, as the last matching expectation (#2) has been saturated. If, however, the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, as now #1 will be the matching expectation. + +**Side note:** Why does Google Mock search for a match in the _reverse_ order of the expectations? The reason is that this allows a user to set up the default expectations in a mock object's constructor or the test fixture's set-up phase and then customize the mock by writing more specific expectations in the test body. So, if you have two expectations on the same method, you want to put the one with more specific matchers **after** the other, or the more specific rule would be shadowed by the more general one that comes after it. + +## Ordered vs Unordered Calls ## +By default, an expectation can match a call even though an earlier expectation hasn't been satisfied. In other words, the calls don't have to occur in the order the expectations are specified. + +Sometimes, you may want all the expected calls to occur in a strict order. To say this in Google Mock is easy: + +``` +using ::testing::InSequence;... +TEST(FooTest, DrawsLineSegment) { + ... + { + InSequence dummy; + + EXPECT_CALL(turtle, PenDown()); + EXPECT_CALL(turtle, Forward(100)); + EXPECT_CALL(turtle, PenUp()); + } + Foo(); +} +``` + +By creating an object of type `InSequence`, all expectations in its scope are put into a _sequence_ and have to occur _sequentially_. Since we are just relying on the constructor and destructor of this object to do the actual work, its name is really irrelevant. + +In this example, we test that `Foo()` calls the three expected functions in the order as written. If a call is made out-of-order, it will be an error. + +(What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](V1_7_CookBook#Expecting_Partially_Ordered_Calls.md).) + +## All Expectations Are Sticky (Unless Said Otherwise) ## +Now let's do a quick quiz to see how well you can use this mock stuff already. How would you test that the turtle is asked to go to the origin _exactly twice_ (you want to ignore any other instructions it receives)? + +After you've come up with your answer, take a look at ours and compare notes (solve it yourself first - don't cheat!): + +``` +using ::testing::_;... +EXPECT_CALL(turtle, GoTo(_, _)) // #1 + .Times(AnyNumber()); +EXPECT_CALL(turtle, GoTo(0, 0)) // #2 + .Times(2); +``` + +Suppose `turtle.GoTo(0, 0)` is called three times. In the third time, Google Mock will see that the arguments match expectation #2 (remember that we always pick the last matching expectation). Now, since we said that there should be only two such calls, Google Mock will report an error immediately. This is basically what we've told you in the "Using Multiple Expectations" section above. + +This example shows that **expectations in Google Mock are "sticky" by default**, in the sense that they remain active even after we have reached their invocation upper bounds. This is an important rule to remember, as it affects the meaning of the spec, and is **different** to how it's done in many other mocking frameworks (Why'd we do that? Because we think our rule makes the common cases easier to express and understand.). + +Simple? Let's see if you've really understood it: what does the following code say? + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)); +} +``` + +If you think it says that `turtle.GetX()` will be called `n` times and will return 10, 20, 30, ..., consecutively, think twice! The problem is that, as we said, expectations are sticky. So, the second time `turtle.GetX()` is called, the last (latest) `EXPECT_CALL()` statement will match, and will immediately lead to an "upper bound exceeded" error - this piece of code is not very useful! + +One correct way of saying that `turtle.GetX()` will return 10, 20, 30, ..., is to explicitly say that the expectations are _not_ sticky. In other words, they should _retire_ as soon as they are saturated: + +``` +using ::testing::Return; +... +for (int i = n; i > 0; i--) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); +} +``` + +And, there's a better way to do it: in this case, we expect the calls to occur in a specific order, and we line up the actions to match the order. Since the order is important here, we should make it explicit using a sequence: + +``` +using ::testing::InSequence; +using ::testing::Return; +... +{ + InSequence s; + + for (int i = 1; i <= n; i++) { + EXPECT_CALL(turtle, GetX()) + .WillOnce(Return(10*i)) + .RetiresOnSaturation(); + } +} +``` + +By the way, the other situation where an expectation may _not_ be sticky is when it's in a sequence - as soon as another expectation that comes after it in the sequence has been used, it automatically retires (and will never be used to match any call). + +## Uninteresting Calls ## +A mock object may have many methods, and not all of them are that interesting. For example, in some tests we may not care about how many times `GetX()` and `GetY()` get called. + +In Google Mock, if you are not interested in a method, just don't say anything about it. If a call to this method occurs, you'll see a warning in the test output, but it won't be a failure. + +# What Now? # +Congratulations! You've learned enough about Google Mock to start using it. Now, you might want to join the [googlemock](http://groups.google.com/group/googlemock) discussion group and actually write some tests using Google Mock - it will be fun. Hey, it may even be addictive - you've been warned. + +Then, if you feel like increasing your mock quotient, you should move on to the [CookBook](V1_7_CookBook.md). You can learn many advanced features of Google Mock there -- and advance your level of enjoyment and testing bliss. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/FrequentlyAskedQuestions.md b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/FrequentlyAskedQuestions.md new file mode 100644 index 00000000..fa21233a --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/docs/v1_7/FrequentlyAskedQuestions.md @@ -0,0 +1,628 @@ + + +Please send your questions to the +[googlemock](http://groups.google.com/group/googlemock) discussion +group. If you need help with compiler errors, make sure you have +tried [Google Mock Doctor](#How_am_I_supposed_to_make_sense_of_these_horrible_template_error.md) first. + +## When I call a method on my mock object, the method for the real object is invoked instead. What's the problem? ## + +In order for a method to be mocked, it must be _virtual_, unless you use the [high-perf dependency injection technique](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Mocking_Nonvirtual_Methods). + +## I wrote some matchers. After I upgraded to a new version of Google Mock, they no longer compile. What's going on? ## + +After version 1.4.0 of Google Mock was released, we had an idea on how +to make it easier to write matchers that can generate informative +messages efficiently. We experimented with this idea and liked what +we saw. Therefore we decided to implement it. + +Unfortunately, this means that if you have defined your own matchers +by implementing `MatcherInterface` or using `MakePolymorphicMatcher()`, +your definitions will no longer compile. Matchers defined using the +`MATCHER*` family of macros are not affected. + +Sorry for the hassle if your matchers are affected. We believe it's +in everyone's long-term interest to make this change sooner than +later. Fortunately, it's usually not hard to migrate an existing +matcher to the new API. Here's what you need to do: + +If you wrote your matcher like this: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` + +you'll need to change it to: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + ... +}; +``` +(i.e. rename `Matches()` to `MatchAndExplain()` and give it a second +argument of type `MatchResultListener*`.) + +If you were also using `ExplainMatchResultTo()` to improve the matcher +message: +``` +// Old matcher definition that doesn't work with the lastest +// Google Mock. +using ::testing::MatcherInterface; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetFoo() > 5; + } + + virtual void ExplainMatchResultTo(MyType value, + ::std::ostream* os) const { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Foo property is " << value.GetFoo(); + } + ... +}; +``` + +you should move the logic of `ExplainMatchResultTo()` into +`MatchAndExplain()`, using the `MatchResultListener` argument where +the `::std::ostream` was used: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; +... +class MyWonderfulMatcher : public MatcherInterface { + public: + ... + virtual bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Foo property is " << value.GetFoo(); + return value.GetFoo() > 5; + } + ... +}; +``` + +If your matcher is defined using `MakePolymorphicMatcher()`: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you should rename the `Matches()` method to `MatchAndExplain()` and +add a `MatchResultListener*` argument (the same as what you need to do +for matchers defined by implementing `MatcherInterface`): +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +If your polymorphic matcher uses `ExplainMatchResultTo()` for better +failure messages: +``` +// Old matcher definition that doesn't work with the latest +// Google Mock. +using ::testing::MakePolymorphicMatcher; +... +class MyGreatMatcher { + public: + ... + bool Matches(MyType value) const { + // Returns true if value matches. + return value.GetBar() < 42; + } + ... +}; +void ExplainMatchResultTo(const MyGreatMatcher& matcher, + MyType value, + ::std::ostream* os) { + // Prints some helpful information to os to help + // a user understand why value matches (or doesn't match). + *os << "the Bar property is " << value.GetBar(); +} +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +you'll need to move the logic inside `ExplainMatchResultTo()` to +`MatchAndExplain()`: +``` +// New matcher definition that works with the latest Google Mock. +using ::testing::MakePolymorphicMatcher; +using ::testing::MatchResultListener; +... +class MyGreatMatcher { + public: + ... + bool MatchAndExplain(MyType value, + MatchResultListener* listener) const { + // Returns true if value matches. + *listener << "the Bar property is " << value.GetBar(); + return value.GetBar() < 42; + } + ... +}; +... MakePolymorphicMatcher(MyGreatMatcher()) ... +``` + +For more information, you can read these +[two](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Writing_New_Monomorphic_Matchers) +[recipes](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Writing_New_Polymorphic_Matchers) +from the cookbook. As always, you +are welcome to post questions on `googlemock@googlegroups.com` if you +need any help. + +## When using Google Mock, do I have to use Google Test as the testing framework? I have my favorite testing framework and don't want to switch. ## + +Google Mock works out of the box with Google Test. However, it's easy +to configure it to work with any testing framework of your choice. +[Here](http://code.google.com/p/googlemock/wiki/V1_7_ForDummies#Using_Google_Mock_with_Any_Testing_Framework) is how. + +## How am I supposed to make sense of these horrible template errors? ## + +If you are confused by the compiler errors gcc threw at you, +try consulting the _Google Mock Doctor_ tool first. What it does is to +scan stdin for gcc error messages, and spit out diagnoses on the +problems (we call them diseases) your code has. + +To "install", run command: +``` +alias gmd='/scripts/gmock_doctor.py' +``` + +To use it, do: +``` + 2>&1 | gmd +``` + +For example: +``` +make my_test 2>&1 | gmd +``` + +Or you can run `gmd` and copy-n-paste gcc's error messages to it. + +## Can I mock a variadic function? ## + +You cannot mock a variadic function (i.e. a function taking ellipsis +(`...`) arguments) directly in Google Mock. + +The problem is that in general, there is _no way_ for a mock object to +know how many arguments are passed to the variadic method, and what +the arguments' types are. Only the _author of the base class_ knows +the protocol, and we cannot look into his head. + +Therefore, to mock such a function, the _user_ must teach the mock +object how to figure out the number of arguments and their types. One +way to do it is to provide overloaded versions of the function. + +Ellipsis arguments are inherited from C and not really a C++ feature. +They are unsafe to use and don't work with arguments that have +constructors or destructors. Therefore we recommend to avoid them in +C++ as much as possible. + +## MSVC gives me warning C4301 or C4373 when I define a mock method with a const parameter. Why? ## + +If you compile this using Microsoft Visual C++ 2005 SP1: +``` +class Foo { + ... + virtual void Bar(const int i) = 0; +}; + +class MockFoo : public Foo { + ... + MOCK_METHOD1(Bar, void(const int i)); +}; +``` +You may get the following warning: +``` +warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier +``` + +This is a MSVC bug. The same code compiles fine with gcc ,for +example. If you use Visual C++ 2008 SP1, you would get the warning: +``` +warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers +``` + +In C++, if you _declare_ a function with a `const` parameter, the +`const` modifier is _ignored_. Therefore, the `Foo` base class above +is equivalent to: +``` +class Foo { + ... + virtual void Bar(int i) = 0; // int or const int? Makes no difference. +}; +``` + +In fact, you can _declare_ Bar() with an `int` parameter, and _define_ +it with a `const int` parameter. The compiler will still match them +up. + +Since making a parameter `const` is meaningless in the method +_declaration_, we recommend to remove it in both `Foo` and `MockFoo`. +That should workaround the VC bug. + +Note that we are talking about the _top-level_ `const` modifier here. +If the function parameter is passed by pointer or reference, declaring +the _pointee_ or _referee_ as `const` is still meaningful. For +example, the following two declarations are _not_ equivalent: +``` +void Bar(int* p); // Neither p nor *p is const. +void Bar(const int* p); // p is not const, but *p is. +``` + +## I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? ## + +We've noticed that when the `/clr` compiler flag is used, Visual C++ +uses 5~6 times as much memory when compiling a mock class. We suggest +to avoid `/clr` when compiling native C++ mocks. + +## I can't figure out why Google Mock thinks my expectations are not satisfied. What should I do? ## + +You might want to run your test with +`--gmock_verbose=info`. This flag lets Google Mock print a trace +of every mock function call it receives. By studying the trace, +you'll gain insights on why the expectations you set are not met. + +## How can I assert that a function is NEVER called? ## + +``` +EXPECT_CALL(foo, Bar(_)) + .Times(0); +``` + +## I have a failed test where Google Mock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? ## + +When Google Mock detects a failure, it prints relevant information +(the mock function arguments, the state of relevant expectations, and +etc) to help the user debug. If another failure is detected, Google +Mock will do the same, including printing the state of relevant +expectations. + +Sometimes an expectation's state didn't change between two failures, +and you'll see the same description of the state twice. They are +however _not_ redundant, as they refer to _different points in time_. +The fact they are the same _is_ interesting information. + +## I get a heap check failure when using a mock object, but using a real object is fine. What can be wrong? ## + +Does the class (hopefully a pure interface) you are mocking have a +virtual destructor? + +Whenever you derive from a base class, make sure its destructor is +virtual. Otherwise Bad Things will happen. Consider the following +code: + +``` +class Base { + public: + // Not virtual, but should be. + ~Base() { ... } + ... +}; + +class Derived : public Base { + public: + ... + private: + std::string value_; +}; + +... + Base* p = new Derived; + ... + delete p; // Surprise! ~Base() will be called, but ~Derived() will not + // - value_ is leaked. +``` + +By changing `~Base()` to virtual, `~Derived()` will be correctly +called when `delete p` is executed, and the heap checker +will be happy. + +## The "newer expectations override older ones" rule makes writing expectations awkward. Why does Google Mock do that? ## + +When people complain about this, often they are referring to code like: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. However, I have to write the expectations in the +// reverse order. This sucks big time!!! +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); +``` + +The problem is that they didn't pick the **best** way to express the test's +intent. + +By default, expectations don't have to be matched in _any_ particular +order. If you want them to match in a certain order, you need to be +explicit. This is Google Mock's (and jMock's) fundamental philosophy: it's +easy to accidentally over-specify your tests, and we want to make it +harder to do so. + +There are two better ways to write the test spec. You could either +put the expectations in sequence: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. Using a sequence, we can write the expectations +// in their natural order. +{ + InSequence s; + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .RetiresOnSaturation(); + EXPECT_CALL(foo, Bar()) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +} +``` + +or you can put the sequence of actions in the same expectation: + +``` +// foo.Bar() should be called twice, return 1 the first time, and return +// 2 the second time. +EXPECT_CALL(foo, Bar()) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .RetiresOnSaturation(); +``` + +Back to the original questions: why does Google Mock search the +expectations (and `ON_CALL`s) from back to front? Because this +allows a user to set up a mock's behavior for the common case early +(e.g. in the mock's constructor or the test fixture's set-up phase) +and customize it with more specific rules later. If Google Mock +searches from front to back, this very useful pattern won't be +possible. + +## Google Mock prints a warning when a function without EXPECT\_CALL is called, even if I have set its behavior using ON\_CALL. Would it be reasonable not to show the warning in this case? ## + +When choosing between being neat and being safe, we lean toward the +latter. So the answer is that we think it's better to show the +warning. + +Often people write `ON_CALL`s in the mock object's +constructor or `SetUp()`, as the default behavior rarely changes from +test to test. Then in the test body they set the expectations, which +are often different for each test. Having an `ON_CALL` in the set-up +part of a test doesn't mean that the calls are expected. If there's +no `EXPECT_CALL` and the method is called, it's possibly an error. If +we quietly let the call go through without notifying the user, bugs +may creep in unnoticed. + +If, however, you are sure that the calls are OK, you can write + +``` +EXPECT_CALL(foo, Bar(_)) + .WillRepeatedly(...); +``` + +instead of + +``` +ON_CALL(foo, Bar(_)) + .WillByDefault(...); +``` + +This tells Google Mock that you do expect the calls and no warning should be +printed. + +Also, you can control the verbosity using the `--gmock_verbose` flag. +If you find the output too noisy when debugging, just choose a less +verbose level. + +## How can I delete the mock function's argument in an action? ## + +If you find yourself needing to perform some action that's not +supported by Google Mock directly, remember that you can define your own +actions using +[MakeAction()](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Writing_New_Actions) or +[MakePolymorphicAction()](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Writing_New_Polymorphic_Actions), +or you can write a stub function and invoke it using +[Invoke()](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Using_Functions_Methods_Functors). + +## MOCK\_METHODn()'s second argument looks funny. Why don't you use the MOCK\_METHODn(Method, return\_type, arg\_1, ..., arg\_n) syntax? ## + +What?! I think it's beautiful. :-) + +While which syntax looks more natural is a subjective matter to some +extent, Google Mock's syntax was chosen for several practical advantages it +has. + +Try to mock a function that takes a map as an argument: +``` +virtual int GetSize(const map& m); +``` + +Using the proposed syntax, it would be: +``` +MOCK_METHOD1(GetSize, int, const map& m); +``` + +Guess what? You'll get a compiler error as the compiler thinks that +`const map& m` are **two**, not one, arguments. To work +around this you can use `typedef` to give the map type a name, but +that gets in the way of your work. Google Mock's syntax avoids this +problem as the function's argument types are protected inside a pair +of parentheses: +``` +// This compiles fine. +MOCK_METHOD1(GetSize, int(const map& m)); +``` + +You still need a `typedef` if the return type contains an unprotected +comma, but that's much rarer. + +Other advantages include: + 1. `MOCK_METHOD1(Foo, int, bool)` can leave a reader wonder whether the method returns `int` or `bool`, while there won't be such confusion using Google Mock's syntax. + 1. The way Google Mock describes a function type is nothing new, although many people may not be familiar with it. The same syntax was used in C, and the `function` library in `tr1` uses this syntax extensively. Since `tr1` will become a part of the new version of STL, we feel very comfortable to be consistent with it. + 1. The function type syntax is also used in other parts of Google Mock's API (e.g. the action interface) in order to make the implementation tractable. A user needs to learn it anyway in order to utilize Google Mock's more advanced features. We'd as well stick to the same syntax in `MOCK_METHOD*`! + +## My code calls a static/global function. Can I mock it? ## + +You can, but you need to make some changes. + +In general, if you find yourself needing to mock a static function, +it's a sign that your modules are too tightly coupled (and less +flexible, less reusable, less testable, etc). You are probably better +off defining a small interface and call the function through that +interface, which then can be easily mocked. It's a bit of work +initially, but usually pays for itself quickly. + +This Google Testing Blog +[post](http://googletesting.blogspot.com/2008/06/defeat-static-cling.html) +says it excellently. Check it out. + +## My mock object needs to do complex stuff. It's a lot of pain to specify the actions. Google Mock sucks! ## + +I know it's not a question, but you get an answer for free any way. :-) + +With Google Mock, you can create mocks in C++ easily. And people might be +tempted to use them everywhere. Sometimes they work great, and +sometimes you may find them, well, a pain to use. So, what's wrong in +the latter case? + +When you write a test without using mocks, you exercise the code and +assert that it returns the correct value or that the system is in an +expected state. This is sometimes called "state-based testing". + +Mocks are great for what some call "interaction-based" testing: +instead of checking the system state at the very end, mock objects +verify that they are invoked the right way and report an error as soon +as it arises, giving you a handle on the precise context in which the +error was triggered. This is often more effective and economical to +do than state-based testing. + +If you are doing state-based testing and using a test double just to +simulate the real object, you are probably better off using a fake. +Using a mock in this case causes pain, as it's not a strong point for +mocks to perform complex actions. If you experience this and think +that mocks suck, you are just not using the right tool for your +problem. Or, you might be trying to solve the wrong problem. :-) + +## I got a warning "Uninteresting function call encountered - default action taken.." Should I panic? ## + +By all means, NO! It's just an FYI. + +What it means is that you have a mock function, you haven't set any +expectations on it (by Google Mock's rule this means that you are not +interested in calls to this function and therefore it can be called +any number of times), and it is called. That's OK - you didn't say +it's not OK to call the function! + +What if you actually meant to disallow this function to be called, but +forgot to write `EXPECT_CALL(foo, Bar()).Times(0)`? While +one can argue that it's the user's fault, Google Mock tries to be nice and +prints you a note. + +So, when you see the message and believe that there shouldn't be any +uninteresting calls, you should investigate what's going on. To make +your life easier, Google Mock prints the function name and arguments +when an uninteresting call is encountered. + +## I want to define a custom action. Should I use Invoke() or implement the action interface? ## + +Either way is fine - you want to choose the one that's more convenient +for your circumstance. + +Usually, if your action is for a particular function type, defining it +using `Invoke()` should be easier; if your action can be used in +functions of different types (e.g. if you are defining +`Return(value)`), `MakePolymorphicAction()` is +easiest. Sometimes you want precise control on what types of +functions the action can be used in, and implementing +`ActionInterface` is the way to go here. See the implementation of +`Return()` in `include/gmock/gmock-actions.h` for an example. + +## I'm using the set-argument-pointee action, and the compiler complains about "conflicting return type specified". What does it mean? ## + +You got this error as Google Mock has no idea what value it should return +when the mock method is called. `SetArgPointee()` says what the +side effect is, but doesn't say what the return value should be. You +need `DoAll()` to chain a `SetArgPointee()` with a `Return()`. + +See this [recipe](http://code.google.com/p/googlemock/wiki/V1_7_CookBook#Mocking_Side_Effects) for more details and an example. + + +## My question is not in your FAQ! ## + +If you cannot find the answer to your question in this FAQ, there are +some other resources you can use: + + 1. read other [wiki pages](http://code.google.com/p/googlemock/w/list), + 1. search the mailing list [archive](http://groups.google.com/group/googlemock/topics), + 1. ask it on [googlemock@googlegroups.com](mailto:googlemock@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googlemock) before you can post.). + +Please note that creating an issue in the +[issue tracker](http://code.google.com/p/googlemock/issues/list) is _not_ +a good way to get your answer, as it is monitored infrequently by a +very small number of people. + +When asking a question, it's helpful to provide as much of the +following information as possible (people cannot help you if there's +not enough information in your question): + + * the version (or the revision number if you check out from SVN directly) of Google Mock you use (Google Mock is under active development, so it's possible that your problem has been solved in a later version), + * your operating system, + * the name and version of your compiler, + * the complete command line flags you give to your compiler, + * the complete compiler error messages (if the question is about compilation), + * the _actual_ code (ideally, a minimal but complete program) that has the problem you encounter. \ No newline at end of file diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-actions.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-actions.h new file mode 100644 index 00000000..b3f654af --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-actions.h @@ -0,0 +1,1205 @@ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ + +#ifndef _WIN32_WCE +# include +#endif + +#include +#include + +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" + +#if GTEST_HAS_STD_TYPE_TRAITS_ // Defined by gtest-port.h via gmock-port.h. +#include +#endif + +namespace testing { + +// To implement an action Foo, define: +// 1. a class FooAction that implements the ActionInterface interface, and +// 2. a factory function that creates an Action object from a +// const FooAction*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Action objects can now be copied like plain values. + +namespace internal { + +template +class ActionAdaptor; + +// BuiltInDefaultValueGetter::Get() returns a +// default-constructed T value. BuiltInDefaultValueGetter::Get() crashes with an error. +// +// This primary template is used when kDefaultConstructible is true. +template +struct BuiltInDefaultValueGetter { + static T Get() { return T(); } +}; +template +struct BuiltInDefaultValueGetter { + static T Get() { + Assert(false, __FILE__, __LINE__, + "Default action undefined for the function return type."); + return internal::Invalid(); + // The above statement will never be reached, but is required in + // order for this function to compile. + } +}; + +// BuiltInDefaultValue::Get() returns the "built-in" default value +// for type T, which is NULL when T is a raw pointer type, 0 when T is +// a numeric type, false when T is bool, or "" when T is string or +// std::string. In addition, in C++11 and above, it turns a +// default-constructed T value if T is default constructible. For any +// other type T, the built-in default T value is undefined, and the +// function will abort the process. +template +class BuiltInDefaultValue { + public: +#if GTEST_HAS_STD_TYPE_TRAITS_ + // This function returns true iff type T has a built-in default value. + static bool Exists() { + return ::std::is_default_constructible::value; + } + + static T Get() { + return BuiltInDefaultValueGetter< + T, ::std::is_default_constructible::value>::Get(); + } + +#else // GTEST_HAS_STD_TYPE_TRAITS_ + // This function returns true iff type T has a built-in default value. + static bool Exists() { + return false; + } + + static T Get() { + return BuiltInDefaultValueGetter::Get(); + } + +#endif // GTEST_HAS_STD_TYPE_TRAITS_ +}; + +// This partial specialization says that we use the same built-in +// default value for T and const T. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return BuiltInDefaultValue::Exists(); } + static T Get() { return BuiltInDefaultValue::Get(); } +}; + +// This partial specialization defines the default values for pointer +// types. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return true; } + static T* Get() { return NULL; } +}; + +// The following specializations define the default values for +// specific types we care about. +#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ + template <> \ + class BuiltInDefaultValue { \ + public: \ + static bool Exists() { return true; } \ + static type Get() { return value; } \ + } + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT +#if GTEST_HAS_GLOBAL_STRING +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, ""); +#endif // GTEST_HAS_GLOBAL_STRING +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); + +// There's no need for a default action for signed wchar_t, as that +// type is the same as wchar_t for gcc, and invalid for MSVC. +// +// There's also no need for a default action for unsigned wchar_t, as +// that type is the same as unsigned int for gcc, and invalid for +// MSVC. +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT +#endif + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); + +#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ + +} // namespace internal + +// When an unexpected function call is encountered, Google Mock will +// let it return a default value if the user has specified one for its +// return type, or if the return type has a built-in default value; +// otherwise Google Mock won't know what value to return and will have +// to abort the process. +// +// The DefaultValue class allows a user to specify the +// default value for a type T that is both copyable and publicly +// destructible (i.e. anything that can be used as a function return +// type). The usage is: +// +// // Sets the default value for type T to be foo. +// DefaultValue::Set(foo); +template +class DefaultValue { + public: + // Sets the default value for type T; requires T to be + // copy-constructable and have a public destructor. + static void Set(T x) { + delete producer_; + producer_ = new FixedValueProducer(x); + } + + // Provides a factory function to be called to generate the default value. + // This method can be used even if T is only move-constructible, but it is not + // limited to that case. + typedef T (*FactoryFunction)(); + static void SetFactory(FactoryFunction factory) { + delete producer_; + producer_ = new FactoryValueProducer(factory); + } + + // Unsets the default value for type T. + static void Clear() { + delete producer_; + producer_ = NULL; + } + + // Returns true iff the user has set the default value for type T. + static bool IsSet() { return producer_ != NULL; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T if the user has set one; + // otherwise returns the built-in default value. Requires that Exists() + // is true, which ensures that the return value is well-defined. + static T Get() { + return producer_ == NULL ? + internal::BuiltInDefaultValue::Get() : producer_->Produce(); + } + + private: + class ValueProducer { + public: + virtual ~ValueProducer() {} + virtual T Produce() = 0; + }; + + class FixedValueProducer : public ValueProducer { + public: + explicit FixedValueProducer(T value) : value_(value) {} + virtual T Produce() { return value_; } + + private: + const T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer); + }; + + class FactoryValueProducer : public ValueProducer { + public: + explicit FactoryValueProducer(FactoryFunction factory) + : factory_(factory) {} + virtual T Produce() { return factory_(); } + + private: + const FactoryFunction factory_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer); + }; + + static ValueProducer* producer_; +}; + +// This partial specialization allows a user to set default values for +// reference types. +template +class DefaultValue { + public: + // Sets the default value for type T&. + static void Set(T& x) { // NOLINT + address_ = &x; + } + + // Unsets the default value for type T&. + static void Clear() { + address_ = NULL; + } + + // Returns true iff the user has set the default value for type T&. + static bool IsSet() { return address_ != NULL; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T& if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T& Get() { + return address_ == NULL ? + internal::BuiltInDefaultValue::Get() : *address_; + } + + private: + static T* address_; +}; + +// This specialization allows DefaultValue::Get() to +// compile. +template <> +class DefaultValue { + public: + static bool Exists() { return true; } + static void Get() {} +}; + +// Points to the user-set default value for type T. +template +typename DefaultValue::ValueProducer* DefaultValue::producer_ = NULL; + +// Points to the user-set default value for type T&. +template +T* DefaultValue::address_ = NULL; + +// Implement this interface to define an action for function type F. +template +class ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + ActionInterface() {} + virtual ~ActionInterface() {} + + // Performs the action. This method is not const, as in general an + // action can have side effects and be stateful. For example, a + // get-the-next-element-from-the-collection action will need to + // remember the current element. + virtual Result Perform(const ArgumentTuple& args) = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface); +}; + +// An Action is a copyable and IMMUTABLE (except by assignment) +// object that represents an action to be taken when a mock function +// of type F is called. The implementation of Action is just a +// linked_ptr to const ActionInterface, so copying is fairly cheap. +// Don't inherit from Action! +// +// You can view an object implementing ActionInterface as a +// concrete action (including its current state), and an Action +// object as a handle to it. +template +class Action { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + // Constructs a null Action. Needed for storing Action objects in + // STL containers. + Action() : impl_(NULL) {} + + // Constructs an Action from its implementation. A NULL impl is + // used to represent the "do-default" action. + explicit Action(ActionInterface* impl) : impl_(impl) {} + + // Copy constructor. + Action(const Action& action) : impl_(action.impl_) {} + + // This constructor allows us to turn an Action object into an + // Action, as long as F's arguments can be implicitly converted + // to Func's and Func's return type can be implicitly converted to + // F's. + template + explicit Action(const Action& action); + + // Returns true iff this is the DoDefault() action. + bool IsDoDefault() const { return impl_.get() == NULL; } + + // Performs the action. Note that this method is const even though + // the corresponding method in ActionInterface is not. The reason + // is that a const Action means that it cannot be re-bound to + // another concrete action, not that the concrete action it binds to + // cannot change state. (Think of the difference between a const + // pointer and a pointer to const.) + Result Perform(const ArgumentTuple& args) const { + internal::Assert( + !IsDoDefault(), __FILE__, __LINE__, + "You are using DoDefault() inside a composite action like " + "DoAll() or WithArgs(). This is not supported for technical " + "reasons. Please instead spell out the default action, or " + "assign the default action to an Action variable and use " + "the variable in various places."); + return impl_->Perform(args); + } + + private: + template + friend class internal::ActionAdaptor; + + internal::linked_ptr > impl_; +}; + +// The PolymorphicAction class template makes it easy to implement a +// polymorphic action (i.e. an action that can be used in mock +// functions of than one type, e.g. Return()). +// +// To define a polymorphic action, a user first provides a COPYABLE +// implementation class that has a Perform() method template: +// +// class FooAction { +// public: +// template +// Result Perform(const ArgumentTuple& args) const { +// // Processes the arguments and returns a result, using +// // tr1::get(args) to get the N-th (0-based) argument in the tuple. +// } +// ... +// }; +// +// Then the user creates the polymorphic action using +// MakePolymorphicAction(object) where object has type FooAction. See +// the definition of Return(void) and SetArgumentPointee(value) for +// complete examples. +template +class PolymorphicAction { + public: + explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} + + template + operator Action() const { + return Action(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + virtual Result Perform(const ArgumentTuple& args) { + return impl_.template Perform(args); + } + + private: + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); + }; + + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(PolymorphicAction); +}; + +// Creates an Action from its implementation and returns it. The +// created Action object owns the implementation. +template +Action MakeAction(ActionInterface* impl) { + return Action(impl); +} + +// Creates a polymorphic action from its implementation. This is +// easier to use than the PolymorphicAction constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicAction(foo); +// vs +// PolymorphicAction(foo); +template +inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { + return PolymorphicAction(impl); +} + +namespace internal { + +// Allows an Action object to pose as an Action, as long as F2 +// and F1 are compatible. +template +class ActionAdaptor : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit ActionAdaptor(const Action& from) : impl_(from.impl_) {} + + virtual Result Perform(const ArgumentTuple& args) { + return impl_->Perform(args); + } + + private: + const internal::linked_ptr > impl_; + + GTEST_DISALLOW_ASSIGN_(ActionAdaptor); +}; + +// Helper struct to specialize ReturnAction to execute a move instead of a copy +// on return. Useful for move-only types, but could be used on any type. +template +struct ByMoveWrapper { + explicit ByMoveWrapper(T value) : payload(internal::move(value)) {} + T payload; +}; + +// Implements the polymorphic Return(x) action, which can be used in +// any function that returns the type of x, regardless of the argument +// types. +// +// Note: The value passed into Return must be converted into +// Function::Result when this action is cast to Action rather than +// when that action is performed. This is important in scenarios like +// +// MOCK_METHOD1(Method, T(U)); +// ... +// { +// Foo foo; +// X x(&foo); +// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); +// } +// +// In the example above the variable x holds reference to foo which leaves +// scope and gets destroyed. If copying X just copies a reference to foo, +// that copy will be left with a hanging reference. If conversion to T +// makes a copy of foo, the above code is safe. To support that scenario, we +// need to make sure that the type conversion happens inside the EXPECT_CALL +// statement, and conversion of the result of Return to Action is a +// good place for that. +// +template +class ReturnAction { + public: + // Constructs a ReturnAction object from the value to be returned. + // 'value' is passed by value instead of by const reference in order + // to allow Return("string literal") to compile. + explicit ReturnAction(R value) : value_(new R(internal::move(value))) {} + + // This template type conversion operator allows Return(x) to be + // used in ANY function that returns x's type. + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename Function::Result Result; + GTEST_COMPILE_ASSERT_( + !is_reference::value, + use_ReturnRef_instead_of_Return_to_return_a_reference); + return Action(new Impl(value_)); + } + + private: + // Implements the Return(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + // The implicit cast is necessary when Result has more than one + // single-argument constructor (e.g. Result is std::vector) and R + // has a type conversion operator template. In that case, value_(value) + // won't compile as the compiler doesn't known which constructor of + // Result to call. ImplicitCast_ forces the compiler to convert R to + // Result without considering explicit constructors, thus resolving the + // ambiguity. value_ is then initialized using its copy constructor. + explicit Impl(const linked_ptr& value) + : value_before_cast_(*value), + value_(ImplicitCast_(value_before_cast_)) {} + + virtual Result Perform(const ArgumentTuple&) { return value_; } + + private: + GTEST_COMPILE_ASSERT_(!is_reference::value, + Result_cannot_be_a_reference_type); + // We save the value before casting just in case it is being cast to a + // wrapper type. + R value_before_cast_; + Result value_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl); + }; + + // Partially specialize for ByMoveWrapper. This version of ReturnAction will + // move its contents instead. + template + class Impl, F> : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const linked_ptr& wrapper) + : performed_(false), wrapper_(wrapper) {} + + virtual Result Perform(const ArgumentTuple&) { + GTEST_CHECK_(!performed_) + << "A ByMove() action should only be performed once."; + performed_ = true; + return internal::move(wrapper_->payload); + } + + private: + bool performed_; + const linked_ptr wrapper_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const linked_ptr value_; + + GTEST_DISALLOW_ASSIGN_(ReturnAction); +}; + +// Implements the ReturnNull() action. +class ReturnNullAction { + public: + // Allows ReturnNull() to be used in any pointer-returning function. In C++11 + // this is enforced by returning nullptr, and in non-C++11 by asserting a + // pointer type on compile time. + template + static Result Perform(const ArgumentTuple&) { +#if GTEST_LANG_CXX11 + return nullptr; +#else + GTEST_COMPILE_ASSERT_(internal::is_pointer::value, + ReturnNull_can_be_used_to_return_a_pointer_only); + return NULL; +#endif // GTEST_LANG_CXX11 + } +}; + +// Implements the Return() action. +class ReturnVoidAction { + public: + // Allows Return() to be used in any void-returning function. + template + static void Perform(const ArgumentTuple&) { + CompileAssertTypesEqual(); + } +}; + +// Implements the polymorphic ReturnRef(x) action, which can be used +// in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefAction { + public: + // Constructs a ReturnRefAction object from the reference to be returned. + explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT + + // This template type conversion operator allows ReturnRef(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRef(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_(internal::is_reference::value, + use_Return_instead_of_ReturnRef_to_return_a_value); + return Action(new Impl(ref_)); + } + + private: + // Implements the ReturnRef(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(T& ref) : ref_(ref) {} // NOLINT + + virtual Result Perform(const ArgumentTuple&) { + return ref_; + } + + private: + T& ref_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + T& ref_; + + GTEST_DISALLOW_ASSIGN_(ReturnRefAction); +}; + +// Implements the polymorphic ReturnRefOfCopy(x) action, which can be +// used in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefOfCopyAction { + public: + // Constructs a ReturnRefOfCopyAction object from the reference to + // be returned. + explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT + + // This template type conversion operator allows ReturnRefOfCopy(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRefOfCopy(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_( + internal::is_reference::value, + use_Return_instead_of_ReturnRefOfCopy_to_return_a_value); + return Action(new Impl(value_)); + } + + private: + // Implements the ReturnRefOfCopy(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const T& value) : value_(value) {} // NOLINT + + virtual Result Perform(const ArgumentTuple&) { + return value_; + } + + private: + T value_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const T value_; + + GTEST_DISALLOW_ASSIGN_(ReturnRefOfCopyAction); +}; + +// Implements the polymorphic DoDefault() action. +class DoDefaultAction { + public: + // This template type conversion operator allows DoDefault() to be + // used in any function. + template + operator Action() const { return Action(NULL); } +}; + +// Implements the Assign action to set a given pointer referent to a +// particular value. +template +class AssignAction { + public: + AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} + + template + void Perform(const ArgumentTuple& /* args */) const { + *ptr_ = value_; + } + + private: + T1* const ptr_; + const T2 value_; + + GTEST_DISALLOW_ASSIGN_(AssignAction); +}; + +#if !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetErrnoAndReturn action to simulate return from +// various system calls and libc functions. +template +class SetErrnoAndReturnAction { + public: + SetErrnoAndReturnAction(int errno_value, T result) + : errno_(errno_value), + result_(result) {} + template + Result Perform(const ArgumentTuple& /* args */) const { + errno = errno_; + return result_; + } + + private: + const int errno_; + const T result_; + + GTEST_DISALLOW_ASSIGN_(SetErrnoAndReturnAction); +}; + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetArgumentPointee(x) action for any function +// whose N-th argument (0-based) is a pointer to x's type. The +// template parameter kIsProto is true iff type A is ProtocolMessage, +// proto2::Message, or a sub-class of those. +template +class SetArgumentPointeeAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'value'. + explicit SetArgumentPointeeAction(const A& value) : value_(value) {} + + template + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual(); + *::testing::get(args) = value_; + } + + private: + const A value_; + + GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); +}; + +template +class SetArgumentPointeeAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'proto'. Both ProtocolMessage and + // proto2::Message have the CopyFrom() method, so the same + // implementation works for both. + explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) { + proto_->CopyFrom(proto); + } + + template + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual(); + ::testing::get(args)->CopyFrom(*proto_); + } + + private: + const internal::linked_ptr proto_; + + GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); +}; + +// Implements the InvokeWithoutArgs(f) action. The template argument +// FunctionImpl is the implementation type of f, which can be either a +// function pointer or a functor. InvokeWithoutArgs(f) can be used as an +// Action as long as f's type is compatible with F (i.e. f can be +// assigned to a tr1::function). +template +class InvokeWithoutArgsAction { + public: + // The c'tor makes a copy of function_impl (either a function + // pointer or a functor). + explicit InvokeWithoutArgsAction(FunctionImpl function_impl) + : function_impl_(function_impl) {} + + // Allows InvokeWithoutArgs(f) to be used as any action whose type is + // compatible with f. + template + Result Perform(const ArgumentTuple&) { return function_impl_(); } + + private: + FunctionImpl function_impl_; + + GTEST_DISALLOW_ASSIGN_(InvokeWithoutArgsAction); +}; + +// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. +template +class InvokeMethodWithoutArgsAction { + public: + InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr) + : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {} + + template + Result Perform(const ArgumentTuple&) const { + return (obj_ptr_->*method_ptr_)(); + } + + private: + Class* const obj_ptr_; + const MethodPtr method_ptr_; + + GTEST_DISALLOW_ASSIGN_(InvokeMethodWithoutArgsAction); +}; + +// Implements the IgnoreResult(action) action. +template +class IgnoreResultAction { + public: + explicit IgnoreResultAction(const A& action) : action_(action) {} + + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename internal::Function::Result Result; + + // Asserts at compile time that F returns void. + CompileAssertTypesEqual(); + + return Action(new Impl(action_)); + } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const A& action) : action_(action) {} + + virtual void Perform(const ArgumentTuple& args) { + // Performs the action and ignores its result. + action_.Perform(args); + } + + private: + // Type OriginalFunction is the same as F except that its return + // type is IgnoredValue. + typedef typename internal::Function::MakeResultIgnoredValue + OriginalFunction; + + const Action action_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const A action_; + + GTEST_DISALLOW_ASSIGN_(IgnoreResultAction); +}; + +// A ReferenceWrapper object represents a reference to type T, +// which can be either const or not. It can be explicitly converted +// from, and implicitly converted to, a T&. Unlike a reference, +// ReferenceWrapper can be copied and can survive template type +// inference. This is used to support by-reference arguments in the +// InvokeArgument(...) action. The idea was from "reference +// wrappers" in tr1, which we don't have in our source tree yet. +template +class ReferenceWrapper { + public: + // Constructs a ReferenceWrapper object from a T&. + explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {} // NOLINT + + // Allows a ReferenceWrapper object to be implicitly converted to + // a T&. + operator T&() const { return *pointer_; } + private: + T* pointer_; +}; + +// Allows the expression ByRef(x) to be printed as a reference to x. +template +void PrintTo(const ReferenceWrapper& ref, ::std::ostream* os) { + T& value = ref; + UniversalPrinter::Print(value, os); +} + +// Does two actions sequentially. Used for implementing the DoAll(a1, +// a2, ...) action. +template +class DoBothAction { + public: + DoBothAction(Action1 action1, Action2 action2) + : action1_(action1), action2_(action2) {} + + // This template type conversion operator allows DoAll(a1, ..., a_n) + // to be used in ANY function of compatible type. + template + operator Action() const { + return Action(new Impl(action1_, action2_)); + } + + private: + // Implements the DoAll(...) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + typedef typename Function::MakeResultVoid VoidResult; + + Impl(const Action& action1, const Action& action2) + : action1_(action1), action2_(action2) {} + + virtual Result Perform(const ArgumentTuple& args) { + action1_.Perform(args); + return action2_.Perform(args); + } + + private: + const Action action1_; + const Action action2_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + Action1 action1_; + Action2 action2_; + + GTEST_DISALLOW_ASSIGN_(DoBothAction); +}; + +} // namespace internal + +// An Unused object can be implicitly constructed from ANY value. +// This is handy when defining actions that ignore some or all of the +// mock function arguments. For example, given +// +// MOCK_METHOD3(Foo, double(const string& label, double x, double y)); +// MOCK_METHOD3(Bar, double(int index, double x, double y)); +// +// instead of +// +// double DistanceToOriginWithLabel(const string& label, double x, double y) { +// return sqrt(x*x + y*y); +// } +// double DistanceToOriginWithIndex(int index, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXEPCT_CALL(mock, Foo("abc", _, _)) +// .WillOnce(Invoke(DistanceToOriginWithLabel)); +// EXEPCT_CALL(mock, Bar(5, _, _)) +// .WillOnce(Invoke(DistanceToOriginWithIndex)); +// +// you could write +// +// // We can declare any uninteresting argument as Unused. +// double DistanceToOrigin(Unused, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXEPCT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); +// EXEPCT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); +typedef internal::IgnoredValue Unused; + +// This constructor allows us to turn an Action object into an +// Action, as long as To's arguments can be implicitly converted +// to From's and From's return type cann be implicitly converted to +// To's. +template +template +Action::Action(const Action& from) + : impl_(new internal::ActionAdaptor(from)) {} + +// Creates an action that returns 'value'. 'value' is passed by value +// instead of const reference - otherwise Return("string literal") +// will trigger a compiler error about using array as initializer. +template +internal::ReturnAction Return(R value) { + return internal::ReturnAction(internal::move(value)); +} + +// Creates an action that returns NULL. +inline PolymorphicAction ReturnNull() { + return MakePolymorphicAction(internal::ReturnNullAction()); +} + +// Creates an action that returns from a void function. +inline PolymorphicAction Return() { + return MakePolymorphicAction(internal::ReturnVoidAction()); +} + +// Creates an action that returns the reference to a variable. +template +inline internal::ReturnRefAction ReturnRef(R& x) { // NOLINT + return internal::ReturnRefAction(x); +} + +// Creates an action that returns the reference to a copy of the +// argument. The copy is created when the action is constructed and +// lives as long as the action. +template +inline internal::ReturnRefOfCopyAction ReturnRefOfCopy(const R& x) { + return internal::ReturnRefOfCopyAction(x); +} + +// Modifies the parent action (a Return() action) to perform a move of the +// argument instead of a copy. +// Return(ByMove()) actions can only be executed once and will assert this +// invariant. +template +internal::ByMoveWrapper ByMove(R x) { + return internal::ByMoveWrapper(internal::move(x)); +} + +// Creates an action that does the default action for the give mock function. +inline internal::DoDefaultAction DoDefault() { + return internal::DoDefaultAction(); +} + +// Creates an action that sets the variable pointed by the N-th +// (0-based) function argument to 'value'. +template +PolymorphicAction< + internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value> > +SetArgPointee(const T& x) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value>(x)); +} + +#if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN) +// This overload allows SetArgPointee() to accept a string literal. +// GCC prior to the version 4.0 and Symbian C++ compiler cannot distinguish +// this overload from the templated version and emit a compile error. +template +PolymorphicAction< + internal::SetArgumentPointeeAction > +SetArgPointee(const char* p) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, const char*, false>(p)); +} + +template +PolymorphicAction< + internal::SetArgumentPointeeAction > +SetArgPointee(const wchar_t* p) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, const wchar_t*, false>(p)); +} +#endif + +// The following version is DEPRECATED. +template +PolymorphicAction< + internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value> > +SetArgumentPointee(const T& x) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value>(x)); +} + +// Creates an action that sets a pointer referent to a given value. +template +PolymorphicAction > Assign(T1* ptr, T2 val) { + return MakePolymorphicAction(internal::AssignAction(ptr, val)); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +// Creates an action that sets errno and returns the appropriate error. +template +PolymorphicAction > +SetErrnoAndReturn(int errval, T result) { + return MakePolymorphicAction( + internal::SetErrnoAndReturnAction(errval, result)); +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Various overloads for InvokeWithoutArgs(). + +// Creates an action that invokes 'function_impl' with no argument. +template +PolymorphicAction > +InvokeWithoutArgs(FunctionImpl function_impl) { + return MakePolymorphicAction( + internal::InvokeWithoutArgsAction(function_impl)); +} + +// Creates an action that invokes the given method on the given object +// with no argument. +template +PolymorphicAction > +InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) { + return MakePolymorphicAction( + internal::InvokeMethodWithoutArgsAction( + obj_ptr, method_ptr)); +} + +// Creates an action that performs an_action and throws away its +// result. In other words, it changes the return type of an_action to +// void. an_action MUST NOT return void, or the code won't compile. +template +inline internal::IgnoreResultAction IgnoreResult(const A& an_action) { + return internal::IgnoreResultAction(an_action); +} + +// Creates a reference wrapper for the given L-value. If necessary, +// you can explicitly specify the type of the reference. For example, +// suppose 'derived' is an object of type Derived, ByRef(derived) +// would wrap a Derived&. If you want to wrap a const Base& instead, +// where Base is a base class of Derived, just write: +// +// ByRef(derived) +template +inline internal::ReferenceWrapper ByRef(T& l_value) { // NOLINT + return internal::ReferenceWrapper(l_value); +} + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-cardinalities.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-cardinalities.h new file mode 100644 index 00000000..fc315f92 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-cardinalities.h @@ -0,0 +1,147 @@ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used cardinalities. More +// cardinalities can be defined by the user implementing the +// CardinalityInterface interface if necessary. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ + +#include +#include // NOLINT +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +namespace testing { + +// To implement a cardinality Foo, define: +// 1. a class FooCardinality that implements the +// CardinalityInterface interface, and +// 2. a factory function that creates a Cardinality object from a +// const FooCardinality*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Cardinality objects can now be copied like plain values. + +// The implementation of a cardinality. +class CardinalityInterface { + public: + virtual ~CardinalityInterface() {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return 0; } + virtual int ConservativeUpperBound() const { return INT_MAX; } + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; + +// A Cardinality is a copyable and IMMUTABLE (except by assignment) +// object that specifies how many times a mock function is expected to +// be called. The implementation of Cardinality is just a linked_ptr +// to const CardinalityInterface, so copying is fairly cheap. +// Don't inherit from Cardinality! +class GTEST_API_ Cardinality { + public: + // Constructs a null cardinality. Needed for storing Cardinality + // objects in STL containers. + Cardinality() {} + + // Constructs a Cardinality from its implementation. + explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } + int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } + + // Returns true iff call_count calls will satisfy this cardinality. + bool IsSatisfiedByCallCount(int call_count) const { + return impl_->IsSatisfiedByCallCount(call_count); + } + + // Returns true iff call_count calls will saturate this cardinality. + bool IsSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count); + } + + // Returns true iff call_count calls will over-saturate this + // cardinality, i.e. exceed the maximum number of allowed calls. + bool IsOverSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count) && + !impl_->IsSatisfiedByCallCount(call_count); + } + + // Describes self to an ostream + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the given actual call count to an ostream. + static void DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os); + + private: + internal::linked_ptr impl_; +}; + +// Creates a cardinality that allows at least n calls. +GTEST_API_ Cardinality AtLeast(int n); + +// Creates a cardinality that allows at most n calls. +GTEST_API_ Cardinality AtMost(int n); + +// Creates a cardinality that allows any number of calls. +GTEST_API_ Cardinality AnyNumber(); + +// Creates a cardinality that allows between min and max calls. +GTEST_API_ Cardinality Between(int min, int max); + +// Creates a cardinality that allows exactly n calls. +GTEST_API_ Cardinality Exactly(int n); + +// Creates a cardinality from its implementation. +inline Cardinality MakeCardinality(const CardinalityInterface* c) { + return Cardinality(c); +} + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-actions.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-actions.h new file mode 100644 index 00000000..b5a889c0 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-actions.h @@ -0,0 +1,2377 @@ +// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! + +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + +#include "gmock/gmock-actions.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { +namespace internal { + +// InvokeHelper knows how to unpack an N-tuple and invoke an N-ary +// function or method with the unpacked values, where F is a function +// type that takes N arguments. +template +class InvokeHelper; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple<>&) { + return function(); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple<>&) { + return (obj_ptr->*method_ptr)(); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args), get<4>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args), get<4>(args), get<5>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args), get<4>(args), get<5>(args), + get<6>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), + get<7>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args), get<4>(args), get<5>(args), + get<6>(args), get<7>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), + get<7>(args), get<8>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args), get<4>(args), get<5>(args), + get<6>(args), get<7>(args), get<8>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple& args) { + return function(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), + get<7>(args), get<8>(args), get<9>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple& args) { + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), + get<2>(args), get<3>(args), get<4>(args), get<5>(args), + get<6>(args), get<7>(args), get<8>(args), get<9>(args)); + } +}; + +// An INTERNAL macro for extracting the type of a tuple field. It's +// subject to change without notice - DO NOT USE IN USER CODE! +#define GMOCK_FIELD_(Tuple, N) \ + typename ::testing::tuple_element::type + +// SelectArgs::type is the +// type of an n-ary function whose i-th (1-based) argument type is the +// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple +// type, and whose return type is Result. For example, +// SelectArgs, 0, 3>::type +// is int(bool, long). +// +// SelectArgs::Select(args) +// returns the selected fields (k1, k2, ..., k_n) of args as a tuple. +// For example, +// SelectArgs, 2, 0>::Select( +// ::testing::make_tuple(true, 'a', 2.5)) +// returns tuple (2.5, true). +// +// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be +// in the range [0, 10]. Duplicates are allowed and they don't have +// to be in an ascending or descending order. + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9), + GMOCK_FIELD_(ArgumentTuple, k10)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& /* args */) { + return SelectedArgs(); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args), get(args)); + } +}; + +#undef GMOCK_FIELD_ + +// Implements the WithArgs action. +template +class WithArgsAction { + public: + explicit WithArgsAction(const InnerAction& action) : action_(action) {} + + template + operator Action() const { return MakeAction(new Impl(action_)); } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const InnerAction& action) : action_(action) {} + + virtual Result Perform(const ArgumentTuple& args) { + return action_.Perform(SelectArgs::Select(args)); + } + + private: + typedef typename SelectArgs::type InnerFunctionType; + + Action action_; + }; + + const InnerAction action_; + + GTEST_DISALLOW_ASSIGN_(WithArgsAction); +}; + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: + static Result Perform(Impl* impl, const ::testing::tuple<>& args) { + return impl->template gmock_PerformImpl<>(args, ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::testing::tuple& args) { + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + get<9>(args)); + } +}; + +} // namespace internal + +// Various overloads for Invoke(). + +// WithArgs(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. C++ doesn't support default arguments for +// function templates, so we have to overload it. +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. +template +inline internal::DoBothAction +DoAll(Action1 a1, Action2 a2) { + return internal::DoBothAction(a1, a2); +} + +template +inline internal::DoBothAction > +DoAll(Action1 a1, Action2 a2, Action3 a3) { + return DoAll(a1, DoAll(a2, a3)); +} + +template +inline internal::DoBothAction > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) { + return DoAll(a1, DoAll(a2, a3, a4)); +} + +template +inline internal::DoBothAction > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) { + return DoAll(a1, DoAll(a2, a3, a4, a5)); +} + +template +inline internal::DoBothAction > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6)); +} + +template +inline internal::DoBothAction > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7)); +} + +template +inline internal::DoBothAction > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8)); +} + +template +inline internal::DoBothAction > > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8, Action9 a9) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9)); +} + +template +inline internal::DoBothAction > > > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8, Action9 a9, Action10 a10) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10)); +} + +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using ACTION*() inside +// a function. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_, \ + arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_, \ + arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_, \ + arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_, \ + arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_, \ + arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_, \ + arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_, \ + arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_, \ + arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_, \ + arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_, \ + arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_ + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(::testing::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. +#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0 +#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) kind0 name0, kind1 name1 +#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) kind0 name0, kind1 name1, kind2 name2 +#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3 +#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \ + kind2 name2, kind3 name3, kind4 name4 +#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5 +#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \ + kind5 name5, kind6 name6 +#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \ + kind4 name4, kind5 name5, kind6 name6, kind7 name7 +#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \ + kind8 name8 +#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \ + kind6 name6, kind7 name7, kind8 name8, kind9 name9 + +// Lists the template parameters. +#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0 +#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) name0, name1 +#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) name0, name1, name2 +#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) name0, name1, name2, name3 +#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \ + name4 +#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \ + name2, name3, name4, name5 +#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) name0, name1, name2, name3, name4, name5, name6 +#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7 +#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \ + name6, name7, name8 +#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \ + name3, name4, name5, name6, name7, name8, name9 + +// Declares the types of value parameters. +#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \ + typename p0##_type, typename p1##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \ + typename p0##_type, typename p1##_type, typename p2##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type, typename p8##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \ + typename p2##_type, typename p3##_type, typename p4##_type, \ + typename p5##_type, typename p6##_type, typename p7##_type, \ + typename p8##_type, typename p9##_type + +// Initializes the value parameters. +#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\ + () +#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\ + (p0##_type gmock_p0) : p0(gmock_p0) +#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\ + (p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), p1(gmock_p1) +#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\ + (p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) +#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3) +#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) +#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) +#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) +#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) +#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) +#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8), p9(gmock_p9) + +// Declares the fields for storing the value parameters. +#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0; +#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \ + p1##_type p1; +#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \ + p1##_type p1; p2##_type p2; +#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \ + p1##_type p1; p2##_type p2; p3##_type p3; +#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; +#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; +#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; +#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; p7##_type p7; +#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; +#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \ + p9##_type p9; + +// Lists the value parameters. +#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0 +#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1 +#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2 +#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3 +#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \ + p2, p3, p4 +#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \ + p1, p2, p3, p4, p5 +#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0, p1, p2, p3, p4, p5, p6 +#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0, p1, p2, p3, p4, p5, p6, p7 +#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8 +#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 + +// Lists the value parameter types. +#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \ + p1##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \ + p1##_type, p2##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + p0##_type, p1##_type, p2##_type, p3##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \ + p6##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type, p9##_type + +// Declares the value parameters. +#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0 +#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \ + p1##_type p1 +#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \ + p1##_type p1, p2##_type p2 +#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3 +#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4 +#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5 +#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6 +#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6, p7##_type p7 +#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8 +#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9 + +// The suffix of the class template implementing the action template. +#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P +#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2 +#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3 +#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4 +#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5 +#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6 +#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7 +#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) P8 +#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) P9 +#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) P10 + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + explicit GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION(name)\ + class name##Action {\ + public:\ + name##Action() {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl() {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl());\ + }\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##Action);\ + };\ + inline name##Action name() {\ + return name##Action();\ + }\ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##Action::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P(name, p0)\ + template \ + class name##ActionP {\ + public:\ + explicit name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0));\ + }\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP);\ + };\ + template \ + inline name##ActionP name(p0##_type p0) {\ + return name##ActionP(p0);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P2(name, p0, p1)\ + template \ + class name##ActionP2 {\ + public:\ + name##ActionP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP2);\ + };\ + template \ + inline name##ActionP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##ActionP2(p0, p1);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP2::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P3(name, p0, p1, p2)\ + template \ + class name##ActionP3 {\ + public:\ + name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP3);\ + };\ + template \ + inline name##ActionP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##ActionP3(p0, p1, p2);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP3::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P4(name, p0, p1, p2, p3)\ + template \ + class name##ActionP4 {\ + public:\ + name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP4);\ + };\ + template \ + inline name##ActionP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##ActionP4(p0, p1, \ + p2, p3);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP4::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P5(name, p0, p1, p2, p3, p4)\ + template \ + class name##ActionP5 {\ + public:\ + name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \ + p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP5);\ + };\ + template \ + inline name##ActionP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##ActionP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP5::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\ + template \ + class name##ActionP6 {\ + public:\ + name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP6);\ + };\ + template \ + inline name##ActionP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##ActionP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP6::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\ + template \ + class name##ActionP7 {\ + public:\ + name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \ + p6(gmock_p6) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP7);\ + };\ + template \ + inline name##ActionP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##ActionP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP7::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\ + template \ + class name##ActionP8 {\ + public:\ + name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \ + p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \ + p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP8);\ + };\ + template \ + inline name##ActionP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##ActionP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP8::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\ + template \ + class name##ActionP9 {\ + public:\ + name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP9);\ + };\ + template \ + inline name##ActionP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##ActionP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP9::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\ + template \ + class name##ActionP10 {\ + public:\ + name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP10);\ + };\ + template \ + inline name##ActionP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##ActionP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP10::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +namespace testing { + + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +namespace internal { +namespace invoke_argument { + +// Appears in InvokeArgumentAdl's argument list to help avoid +// accidental calls to user functions of the same name. +struct AdlTag {}; + +// InvokeArgumentAdl - a helper for InvokeArgument. +// The basic overloads are provided here for generic functors. +// Overloads for other custom-callables are provided in the +// internal/custom/callback-actions.h header. + +template +R InvokeArgumentAdl(AdlTag, F f) { + return f(); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1) { + return f(a1); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2) { + return f(a1, a2); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3) { + return f(a1, a2, a3); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4) { + return f(a1, a2, a3, a4); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + return f(a1, a2, a3, a4, a5); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + return f(a1, a2, a3, a4, a5, a6); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7) { + return f(a1, a2, a3, a4, a5, a6, a7); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8) { + return f(a1, a2, a3, a4, a5, a6, a7, a8); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9) { + return f(a1, a2, a3, a4, a5, a6, a7, a8, a9); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9, A10 a10) { + return f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); +} +} // namespace invoke_argument +} // namespace internal + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args)); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p0)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_2_VALUE_PARAMS(p0, p1)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + return new T(); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(p0)) { + return new T(p0); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_2_VALUE_PARAMS(p0, p1)) { + return new T(p0, p1); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + return new T(p0, p1, p2); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + return new T(p0, p1, p2, p3); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + return new T(p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + return new T(p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + return new T(p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +// Include any custom actions 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 "gmock/internal/custom/gmock-generated-actions.h" + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-actions.h.pump b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-actions.h.pump new file mode 100644 index 00000000..66d9f9d5 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-actions.h.pump @@ -0,0 +1,794 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-actions.h. +$$ +$var n = 10 $$ The maximum arity we support. +$$}} This meta comment fixes auto-indentation in editors. +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + +#include "gmock/gmock-actions.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { +namespace internal { + +// InvokeHelper knows how to unpack an N-tuple and invoke an N-ary +// function or method with the unpacked values, where F is a function +// type that takes N arguments. +template +class InvokeHelper; + + +$range i 0..n +$for i [[ +$range j 1..i +$var types = [[$for j [[, typename A$j]]]] +$var as = [[$for j, [[A$j]]]] +$var args = [[$if i==0 [[]] $else [[ args]]]] +$var gets = [[$for j, [[get<$(j - 1)>(args)]]]] +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::testing::tuple<$as>&$args) { + return function($gets); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::testing::tuple<$as>&$args) { + return (obj_ptr->*method_ptr)($gets); + } +}; + + +]] +// An INTERNAL macro for extracting the type of a tuple field. It's +// subject to change without notice - DO NOT USE IN USER CODE! +#define GMOCK_FIELD_(Tuple, N) \ + typename ::testing::tuple_element::type + +$range i 1..n + +// SelectArgs::type is the +// type of an n-ary function whose i-th (1-based) argument type is the +// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple +// type, and whose return type is Result. For example, +// SelectArgs, 0, 3>::type +// is int(bool, long). +// +// SelectArgs::Select(args) +// returns the selected fields (k1, k2, ..., k_n) of args as a tuple. +// For example, +// SelectArgs, 2, 0>::Select( +// ::testing::make_tuple(true, 'a', 2.5)) +// returns tuple (2.5, true). +// +// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be +// in the range [0, $n]. Duplicates are allowed and they don't have +// to be in an ascending or descending order. + +template +class SelectArgs { + public: + typedef Result type($for i, [[GMOCK_FIELD_(ArgumentTuple, k$i)]]); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + return SelectedArgs($for i, [[get(args)]]); + } +}; + + +$for i [[ +$range j 1..n +$range j1 1..i-1 +template +class SelectArgs { + public: + typedef Result type($for j1, [[GMOCK_FIELD_(ArgumentTuple, k$j1)]]); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& [[]] +$if i == 1 [[/* args */]] $else [[args]]) { + return SelectedArgs($for j1, [[get(args)]]); + } +}; + + +]] +#undef GMOCK_FIELD_ + +$var ks = [[$for i, [[k$i]]]] + +// Implements the WithArgs action. +template +class WithArgsAction { + public: + explicit WithArgsAction(const InnerAction& action) : action_(action) {} + + template + operator Action() const { return MakeAction(new Impl(action_)); } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const InnerAction& action) : action_(action) {} + + virtual Result Perform(const ArgumentTuple& args) { + return action_.Perform(SelectArgs::Select(args)); + } + + private: + typedef typename SelectArgs::type InnerFunctionType; + + Action action_; + }; + + const InnerAction action_; + + GTEST_DISALLOW_ASSIGN_(WithArgsAction); +}; + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: +$range i 0..n +$for i + +[[ +$var template = [[$if i==0 [[]] $else [[ +$range j 0..i-1 + template <$for j, [[typename A$j]]> +]]]] +$range j 0..i-1 +$var As = [[$for j, [[A$j]]]] +$var as = [[$for j, [[get<$j>(args)]]]] +$range k 1..n-i +$var eas = [[$for k, [[ExcessiveArg()]]]] +$var arg_list = [[$if (i==0) | (i==n) [[$as$eas]] $else [[$as, $eas]]]] +$template + static Result Perform(Impl* impl, const ::testing::tuple<$As>& args) { + return impl->template gmock_PerformImpl<$As>(args, $arg_list); + } + +]] +}; + +} // namespace internal + +// Various overloads for Invoke(). + +// WithArgs(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. C++ doesn't support default arguments for +// function templates, so we have to overload it. + +$range i 1..n +$for i [[ +$range j 1..i +template <$for j [[int k$j, ]]typename InnerAction> +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + + +]] +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. +$range i 2..n +$for i [[ +$range j 2..i +$var types = [[$for j, [[typename Action$j]]]] +$var Aas = [[$for j [[, Action$j a$j]]]] + +template +$range k 1..i-1 + +inline $for k [[internal::DoBothAction]] + +DoAll(Action1 a1$Aas) { +$if i==2 [[ + + return internal::DoBothAction(a1, a2); +]] $else [[ +$range j2 2..i + + return DoAll(a1, DoAll($for j2, [[a$j2]])); +]] + +} + +]] + +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P$n to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using ACTION*() inside +// a function. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +$range i 0..n +$range k 0..n-1 + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_ +$for k [[, \ + arg$k[[]]_type arg$k GTEST_ATTRIBUTE_UNUSED_]] + + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(::testing::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. + +$range j 1..n +$for j [[ +$range m 0..j-1 +#define GMOCK_INTERNAL_DECL_HAS_$j[[]] +_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[kind$m name$m]] + + +]] + +// Lists the template parameters. + +$for j [[ +$range m 0..j-1 +#define GMOCK_INTERNAL_LIST_HAS_$j[[]] +_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[name$m]] + + +]] + +// Declares the types of value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DECL_TYPE_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[, typename p$j##_type]] + + +]] + +// Initializes the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_INIT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])\ + ($for j, [[p$j##_type gmock_p$j]])$if i>0 [[ : ]]$for j, [[p$j(gmock_p$j)]] + + +]] + +// Declares the fields for storing the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DEFN_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[p$j##_type p$j; ]] + + +]] + +// Lists the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_LIST_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j, [[p$j]] + + +]] + +// Lists the value parameter types. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_LIST_TYPE_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[, p$j##_type]] + + +]] + +// Declares the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DECL_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] +$for j, [[p$j##_type p$j]] + + +]] + +// The suffix of the class template implementing the action template. +$for i [[ + + +$range j 0..i-1 +#define GMOCK_INTERNAL_COUNT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] +$if i==1 [[P]] $elif i>=2 [[P$i]] +]] + + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +$range k 0..n-1 + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + explicit GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template <$for k, [[typename arg$k[[]]_type]]>\ + return_type gmock_PerformImpl(const args_type& args[[]] +$for k [[, arg$k[[]]_type arg$k]]) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +$for i + +[[ +$var template = [[$if i==0 [[]] $else [[ +$range j 0..i-1 + + template <$for j, [[typename p$j##_type]]>\ +]]]] +$var class_name = [[name##Action[[$if i==0 [[]] $elif i==1 [[P]] + $else [[P$i]]]]]] +$range j 0..i-1 +$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] +$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] +$var param_field_decls = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var param_field_decls2 = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var params = [[$for j, [[p$j]]]] +$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] +$var typename_arg_types = [[$for k, [[typename arg$k[[]]_type]]]] +$var arg_types_and_names = [[$for k, [[arg$k[[]]_type arg$k]]]] +$var macro_name = [[$if i==0 [[ACTION]] $elif i==1 [[ACTION_P]] + $else [[ACTION_P$i]]]] + +#define $macro_name(name$for j [[, p$j]])\$template + class $class_name {\ + public:\ + [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + [[$if i==1 [[explicit ]]]]gmock_Impl($ctor_param_list)$inits {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template <$typename_arg_types>\ + return_type gmock_PerformImpl(const args_type& args, [[]] +$arg_types_and_names) const;\$param_field_decls + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl($params));\ + }\$param_field_decls2 + private:\ + GTEST_DISALLOW_ASSIGN_($class_name);\ + };\$template + inline $class_name$param_types name($param_types_and_names) {\ + return $class_name$param_types($params);\ + }\$template + template \ + template <$typename_arg_types>\ + typename ::testing::internal::Function::Result\ + $class_name$param_types::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const +]] +$$ } // This meta comment fixes auto-indentation in Emacs. It won't +$$ // show up in the generated code. + + +namespace testing { + + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +namespace internal { +namespace invoke_argument { + +// Appears in InvokeArgumentAdl's argument list to help avoid +// accidental calls to user functions of the same name. +struct AdlTag {}; + +// InvokeArgumentAdl - a helper for InvokeArgument. +// The basic overloads are provided here for generic functors. +// Overloads for other custom-callables are provided in the +// internal/custom/callback-actions.h header. + +$range i 0..n +$for i +[[ +$range j 1..i + +template +R InvokeArgumentAdl(AdlTag, F f[[$for j [[, A$j a$j]]]]) { + return f([[$for j, [[a$j]]]]); +} +]] + +} // namespace invoke_argument +} // namespace internal + +$range i 0..n +$for i [[ +$range j 0..i-1 + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::testing::get(args)$for j [[, p$j]]); +} + +]] + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +$range i 0..n +$for i [[ +$range j 0..i-1 +$var ps = [[$for j, [[p$j]]]] + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_$i[[]]_VALUE_PARAMS($ps)) { + return new T($ps); +} + +]] + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +// Include any custom callback actions 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 "gmock/internal/custom/gmock-generated-actions.h" + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-function-mockers.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-function-mockers.h new file mode 100644 index 00000000..4fa5ca94 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-function-mockers.h @@ -0,0 +1,1095 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-function-mockers.h.pump +// DO NOT EDIT BY HAND!!! + +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" + +#if GTEST_HAS_STD_FUNCTION_ +# include +#endif + +namespace testing { +namespace internal { + +template +class FunctionMockerBase; + +// Note: class FunctionMocker really belongs to the ::testing +// namespace. However if we define it in ::testing, MSVC will +// complain when classes in ::testing::internal declare it as a +// friend class template. To workaround this compiler bug, we define +// FunctionMocker in ::testing::internal and import it into ::testing. +template +class FunctionMocker; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With() { + return this->current_spec(); + } + + R Invoke() { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple()); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1) { + this->current_spec().SetMatchers(::testing::make_tuple(m1)); + return this->current_spec(); + } + + R Invoke(A1 a1) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5, + m6)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5, + m6, m7)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7, A8); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7, const Matcher& m8) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5, + m6, m7, m8)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7, const Matcher& m8, + const Matcher& m9) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5, + m6, m7, m8, m9)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7, const Matcher& m8, + const Matcher& m9, const Matcher& m10) { + this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5, + m6, m7, m8, m9, m10)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, + A10 a10) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10)); + } +}; + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; + +// GMOCK_RESULT_(tn, F) expands to the result type of function type F. +// We define this as a variadic macro in case F contains unprotected +// commas (the same reason that we use variadic macros in other places +// in this file). +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_RESULT_(tn, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::Result + +// The type of argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_ARG_(tn, N, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::Argument##N + +// The matcher type for argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MATCHER_(tn, N, ...) \ + const ::testing::Matcher& + +// The variable for mocking the given method. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + ) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 0), \ + this_method_does_not_take_0_arguments); \ + GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(0, constness, Method).Invoke(); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method() constness { \ + GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(0, constness, Method).With(); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD1_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 1), \ + this_method_does_not_take_1_argument); \ + GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(1, constness, Method).Invoke(gmock_a1); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \ + GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 2), \ + this_method_does_not_take_2_arguments); \ + GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \ + GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD3_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 3), \ + this_method_does_not_take_3_arguments); \ + GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(3, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness { \ + GMOCK_MOCKER_(3, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(3, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD4_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 4), \ + this_method_does_not_take_4_arguments); \ + GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(4, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4) constness { \ + GMOCK_MOCKER_(4, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(4, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD5_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 5), \ + this_method_does_not_take_5_arguments); \ + GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(5, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5) constness { \ + GMOCK_MOCKER_(5, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(5, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD6_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 6), \ + this_method_does_not_take_6_arguments); \ + GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(6, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6) constness { \ + GMOCK_MOCKER_(6, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(6, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(6, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD7_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 7), \ + this_method_does_not_take_7_arguments); \ + GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(7, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7) constness { \ + GMOCK_MOCKER_(7, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(7, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(7, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD8_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 8), \ + this_method_does_not_take_8_arguments); \ + GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(8, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8) constness { \ + GMOCK_MOCKER_(8, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(8, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(8, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD9_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 9), \ + this_method_does_not_take_9_arguments); \ + GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(9, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \ + gmock_a9); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9) constness { \ + GMOCK_MOCKER_(9, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(9, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \ + gmock_a9); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(9, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD10_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \ + GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \ + == 10), \ + this_method_does_not_take_10_arguments); \ + GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(10, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \ + gmock_a10); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9, \ + GMOCK_MATCHER_(tn, 10, \ + __VA_ARGS__) gmock_a10) constness { \ + GMOCK_MOCKER_(10, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(10, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \ + gmock_a10); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(10, constness, \ + Method) + +#define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__) +#define MOCK_METHOD1(m, ...) GMOCK_METHOD1_(, , , m, __VA_ARGS__) +#define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__) +#define MOCK_METHOD3(m, ...) GMOCK_METHOD3_(, , , m, __VA_ARGS__) +#define MOCK_METHOD4(m, ...) GMOCK_METHOD4_(, , , m, __VA_ARGS__) +#define MOCK_METHOD5(m, ...) GMOCK_METHOD5_(, , , m, __VA_ARGS__) +#define MOCK_METHOD6(m, ...) GMOCK_METHOD6_(, , , m, __VA_ARGS__) +#define MOCK_METHOD7(m, ...) GMOCK_METHOD7_(, , , m, __VA_ARGS__) +#define MOCK_METHOD8(m, ...) GMOCK_METHOD8_(, , , m, __VA_ARGS__) +#define MOCK_METHOD9(m, ...) GMOCK_METHOD9_(, , , m, __VA_ARGS__) +#define MOCK_METHOD10(m, ...) GMOCK_METHOD10_(, , , m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0(m, ...) GMOCK_METHOD0_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD1(m, ...) GMOCK_METHOD1_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD3(m, ...) GMOCK_METHOD3_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD4(m, ...) GMOCK_METHOD4_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD5(m, ...) GMOCK_METHOD5_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD6(m, ...) GMOCK_METHOD6_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD7(m, ...) GMOCK_METHOD7_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD8(m, ...) GMOCK_METHOD8_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD9(m, ...) GMOCK_METHOD9_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD10(m, ...) GMOCK_METHOD10_(, const, , m, __VA_ARGS__) + +#define MOCK_METHOD0_T(m, ...) GMOCK_METHOD0_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD1_T(m, ...) GMOCK_METHOD1_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD2_T(m, ...) GMOCK_METHOD2_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD3_T(m, ...) GMOCK_METHOD3_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD4_T(m, ...) GMOCK_METHOD4_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD5_T(m, ...) GMOCK_METHOD5_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD6_T(m, ...) GMOCK_METHOD6_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD7_T(m, ...) GMOCK_METHOD7_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD8_T(m, ...) GMOCK_METHOD8_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD9_T(m, ...) GMOCK_METHOD9_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD10_T(m, ...) GMOCK_METHOD10_(typename, , , m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T(m, ...) \ + GMOCK_METHOD0_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T(m, ...) \ + GMOCK_METHOD1_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T(m, ...) \ + GMOCK_METHOD2_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T(m, ...) \ + GMOCK_METHOD3_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T(m, ...) \ + GMOCK_METHOD4_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T(m, ...) \ + GMOCK_METHOD5_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T(m, ...) \ + GMOCK_METHOD6_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T(m, ...) \ + GMOCK_METHOD7_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T(m, ...) \ + GMOCK_METHOD8_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T(m, ...) \ + GMOCK_METHOD9_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T(m, ...) \ + GMOCK_METHOD10_(typename, const, , m, __VA_ARGS__) + +#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(, , ct, m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(, const, ct, m, __VA_ARGS__) + +#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(typename, , ct, m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(typename, const, ct, m, __VA_ARGS__) + +// A MockFunction class has one mock method whose type is F. It is +// useful when you just want your test code to emit some messages and +// have Google Mock verify the right messages are sent (and perhaps at +// the right times). For example, if you are exercising code: +// +// Foo(1); +// Foo(2); +// Foo(3); +// +// and want to verify that Foo(1) and Foo(3) both invoke +// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: +// +// TEST(FooTest, InvokesBarCorrectly) { +// MyMock mock; +// MockFunction check; +// { +// InSequence s; +// +// EXPECT_CALL(mock, Bar("a")); +// EXPECT_CALL(check, Call("1")); +// EXPECT_CALL(check, Call("2")); +// EXPECT_CALL(mock, Bar("a")); +// } +// Foo(1); +// check.Call("1"); +// Foo(2); +// check.Call("2"); +// Foo(3); +// } +// +// The expectation spec says that the first Bar("a") must happen +// before check point "1", the second Bar("a") must happen after check +// point "2", and nothing should happen between the two check +// points. The explicit check points make it easy to tell which +// Bar("a") is called by which call to Foo(). +// +// MockFunction can also be used to exercise code that accepts +// std::function callbacks. To do so, use AsStdFunction() method +// to create std::function proxy forwarding to original object's Call. +// Example: +// +// TEST(FooTest, RunsCallbackWithBarArgument) { +// MockFunction callback; +// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1)); +// Foo(callback.AsStdFunction()); +// } +template +class MockFunction; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD0_T(Call, R()); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this]() -> R { + return this->Call(); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD1_T(Call, R(A0)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0) -> R { + return this->Call(a0); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD2_T(Call, R(A0, A1)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1) -> R { + return this->Call(a0, a1); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD3_T(Call, R(A0, A1, A2)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2) -> R { + return this->Call(a0, a1, a2); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD4_T(Call, R(A0, A1, A2, A3)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3) -> R { + return this->Call(a0, a1, a2, a3); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD5_T(Call, R(A0, A1, A2, A3, A4)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) -> R { + return this->Call(a0, a1, a2, a3, a4); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD6_T(Call, R(A0, A1, A2, A3, A4, A5)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) -> R { + return this->Call(a0, a1, a2, a3, a4, a5); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD7_T(Call, R(A0, A1, A2, A3, A4, A5, A6)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) -> R { + return this->Call(a0, a1, a2, a3, a4, a5, a6); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD8_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) -> R { + return this->Call(a0, a1, a2, a3, a4, a5, a6, a7); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD9_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, + A8 a8) -> R { + return this->Call(a0, a1, a2, a3, a4, a5, a6, a7, a8); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD10_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, + A8 a8, A9 a9) -> R { + return this->Call(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-function-mockers.h.pump b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-function-mockers.h.pump new file mode 100644 index 00000000..811502d0 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-function-mockers.h.pump @@ -0,0 +1,291 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-function-mockers.h. +$$ +$var n = 10 $$ The maximum arity we support. +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" + +#if GTEST_HAS_STD_FUNCTION_ +# include +#endif + +namespace testing { +namespace internal { + +template +class FunctionMockerBase; + +// Note: class FunctionMocker really belongs to the ::testing +// namespace. However if we define it in ::testing, MSVC will +// complain when classes in ::testing::internal declare it as a +// friend class template. To workaround this compiler bug, we define +// FunctionMocker in ::testing::internal and import it into ::testing. +template +class FunctionMocker; + + +$range i 0..n +$for i [[ +$range j 1..i +$var typename_As = [[$for j [[, typename A$j]]]] +$var As = [[$for j, [[A$j]]]] +$var as = [[$for j, [[a$j]]]] +$var Aas = [[$for j, [[A$j a$j]]]] +$var ms = [[$for j, [[m$j]]]] +$var matchers = [[$for j, [[const Matcher& m$j]]]] +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F($As); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With($matchers) { + +$if i >= 1 [[ + this->current_spec().SetMatchers(::testing::make_tuple($ms)); + +]] + return this->current_spec(); + } + + R Invoke($Aas) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple($as)); + } +}; + + +]] +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; + +// GMOCK_RESULT_(tn, F) expands to the result type of function type F. +// We define this as a variadic macro in case F contains unprotected +// commas (the same reason that we use variadic macros in other places +// in this file). +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_RESULT_(tn, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::Result + +// The type of argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_ARG_(tn, N, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::Argument##N + +// The matcher type for argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MATCHER_(tn, N, ...) \ + const ::testing::Matcher& + +// The variable for mocking the given method. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + + +$for i [[ +$range j 1..i +$var arg_as = [[$for j, \ + [[GMOCK_ARG_(tn, $j, __VA_ARGS__) gmock_a$j]]]] +$var as = [[$for j, [[gmock_a$j]]]] +$var matcher_as = [[$for j, \ + [[GMOCK_MATCHER_(tn, $j, __VA_ARGS__) gmock_a$j]]]] +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD$i[[]]_(tn, constness, ct, Method, ...) \ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + $arg_as) constness { \ + GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ + tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value == $i), \ + this_method_does_not_take_$i[[]]_argument[[$if i != 1 [[s]]]]); \ + GMOCK_MOCKER_($i, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_($i, constness, Method).Invoke($as); \ + } \ + ::testing::MockSpec<__VA_ARGS__>& \ + gmock_##Method($matcher_as) constness { \ + GMOCK_MOCKER_($i, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_($i, constness, Method).With($as); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_($i, constness, Method) + + +]] +$for i [[ +#define MOCK_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, , , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, const, , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_T(m, ...) GMOCK_METHOD$i[[]]_(typename, , , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_T(m, ...) \ + GMOCK_METHOD$i[[]]_(typename, const, , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(, , ct, m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(, const, ct, m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(typename, , ct, m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(typename, const, ct, m, __VA_ARGS__) + +]] + +// A MockFunction class has one mock method whose type is F. It is +// useful when you just want your test code to emit some messages and +// have Google Mock verify the right messages are sent (and perhaps at +// the right times). For example, if you are exercising code: +// +// Foo(1); +// Foo(2); +// Foo(3); +// +// and want to verify that Foo(1) and Foo(3) both invoke +// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: +// +// TEST(FooTest, InvokesBarCorrectly) { +// MyMock mock; +// MockFunction check; +// { +// InSequence s; +// +// EXPECT_CALL(mock, Bar("a")); +// EXPECT_CALL(check, Call("1")); +// EXPECT_CALL(check, Call("2")); +// EXPECT_CALL(mock, Bar("a")); +// } +// Foo(1); +// check.Call("1"); +// Foo(2); +// check.Call("2"); +// Foo(3); +// } +// +// The expectation spec says that the first Bar("a") must happen +// before check point "1", the second Bar("a") must happen after check +// point "2", and nothing should happen between the two check +// points. The explicit check points make it easy to tell which +// Bar("a") is called by which call to Foo(). +// +// MockFunction can also be used to exercise code that accepts +// std::function callbacks. To do so, use AsStdFunction() method +// to create std::function proxy forwarding to original object's Call. +// Example: +// +// TEST(FooTest, RunsCallbackWithBarArgument) { +// MockFunction callback; +// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1)); +// Foo(callback.AsStdFunction()); +// } +template +class MockFunction; + + +$for i [[ +$range j 0..i-1 +$var ArgTypes = [[$for j, [[A$j]]]] +$var ArgNames = [[$for j, [[a$j]]]] +$var ArgDecls = [[$for j, [[A$j a$j]]]] +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD$i[[]]_T(Call, R($ArgTypes)); + +#if GTEST_HAS_STD_FUNCTION_ + std::function AsStdFunction() { + return [this]($ArgDecls) -> R { + return this->Call($ArgNames); + }; + } +#endif // GTEST_HAS_STD_FUNCTION_ + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + + +]] +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-matchers.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-matchers.h new file mode 100644 index 00000000..57056fd9 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-matchers.h @@ -0,0 +1,2179 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-matchers.h.pump +// DO NOT EDIT BY HAND!!! + +// 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 Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic matchers. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ + +#include +#include +#include +#include +#include "gmock/gmock-matchers.h" + +namespace testing { +namespace internal { + +// The type of the i-th (0-based) field of Tuple. +#define GMOCK_FIELD_TYPE_(Tuple, i) \ + typename ::testing::tuple_element::type + +// TupleFields is for selecting fields from a +// tuple of type Tuple. It has two members: +// +// type: a tuple type whose i-th field is the ki-th field of Tuple. +// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple. +// +// For example, in class TupleFields, 2, 0>, we have: +// +// type is tuple, and +// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true). + +template +class TupleFields; + +// This generic version is used when there are 10 selectors. +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t), get(t), get(t), get(t)); + } +}; + +// The following specialization is used for 0 ~ 9 selectors. + +template +class TupleFields { + public: + typedef ::testing::tuple<> type; + static type GetSelectedFields(const Tuple& /* t */) { + return type(); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t), get(t), + get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::testing::tuple type; + static type GetSelectedFields(const Tuple& t) { + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t), get(t), get(t)); + } +}; + +#undef GMOCK_FIELD_TYPE_ + +// Implements the Args() matcher. +template +class ArgsMatcherImpl : public MatcherInterface { + public: + // ArgsTuple may have top-level const or reference modifiers. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple; + typedef typename internal::TupleFields::type SelectedArgs; + typedef Matcher MonomorphicInnerMatcher; + + template + explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) + : inner_matcher_(SafeMatcherCast(inner_matcher)) {} + + virtual bool MatchAndExplain(ArgsTuple args, + MatchResultListener* listener) const { + const SelectedArgs& selected_args = GetSelectedArgs(args); + if (!listener->IsInterested()) + return inner_matcher_.Matches(selected_args); + + PrintIndices(listener->stream()); + *listener << "are " << PrintToString(selected_args); + + StringMatchResultListener inner_listener; + const bool match = inner_matcher_.MatchAndExplain(selected_args, + &inner_listener); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return match; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeNegationTo(os); + } + + private: + static SelectedArgs GetSelectedArgs(ArgsTuple args) { + return TupleFields::GetSelectedFields(args); + } + + // Prints the indices of the selected fields. + static void PrintIndices(::std::ostream* os) { + *os << "whose fields ("; + const int indices[10] = { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 }; + for (int i = 0; i < 10; i++) { + if (indices[i] < 0) + break; + + if (i >= 1) + *os << ", "; + + *os << "#" << indices[i]; + } + *os << ") "; + } + + const MonomorphicInnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl); +}; + +template +class ArgsMatcher { + public: + explicit ArgsMatcher(const InnerMatcher& inner_matcher) + : inner_matcher_(inner_matcher) {} + + template + operator Matcher() const { + return MakeMatcher(new ArgsMatcherImpl(inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcher); +}; + +// A set of metafunctions for computing the result type of AllOf. +// AllOf(m1, ..., mN) returns +// AllOfResultN::type. + +// Although AllOf isn't defined for one argument, AllOfResult1 is defined +// to simplify the implementation. +template +struct AllOfResult1 { + typedef M1 type; +}; + +template +struct AllOfResult2 { + typedef BothOfMatcher< + typename AllOfResult1::type, + typename AllOfResult1::type + > type; +}; + +template +struct AllOfResult3 { + typedef BothOfMatcher< + typename AllOfResult1::type, + typename AllOfResult2::type + > type; +}; + +template +struct AllOfResult4 { + typedef BothOfMatcher< + typename AllOfResult2::type, + typename AllOfResult2::type + > type; +}; + +template +struct AllOfResult5 { + typedef BothOfMatcher< + typename AllOfResult2::type, + typename AllOfResult3::type + > type; +}; + +template +struct AllOfResult6 { + typedef BothOfMatcher< + typename AllOfResult3::type, + typename AllOfResult3::type + > type; +}; + +template +struct AllOfResult7 { + typedef BothOfMatcher< + typename AllOfResult3::type, + typename AllOfResult4::type + > type; +}; + +template +struct AllOfResult8 { + typedef BothOfMatcher< + typename AllOfResult4::type, + typename AllOfResult4::type + > type; +}; + +template +struct AllOfResult9 { + typedef BothOfMatcher< + typename AllOfResult4::type, + typename AllOfResult5::type + > type; +}; + +template +struct AllOfResult10 { + typedef BothOfMatcher< + typename AllOfResult5::type, + typename AllOfResult5::type + > type; +}; + +// A set of metafunctions for computing the result type of AnyOf. +// AnyOf(m1, ..., mN) returns +// AnyOfResultN::type. + +// Although AnyOf isn't defined for one argument, AnyOfResult1 is defined +// to simplify the implementation. +template +struct AnyOfResult1 { + typedef M1 type; +}; + +template +struct AnyOfResult2 { + typedef EitherOfMatcher< + typename AnyOfResult1::type, + typename AnyOfResult1::type + > type; +}; + +template +struct AnyOfResult3 { + typedef EitherOfMatcher< + typename AnyOfResult1::type, + typename AnyOfResult2::type + > type; +}; + +template +struct AnyOfResult4 { + typedef EitherOfMatcher< + typename AnyOfResult2::type, + typename AnyOfResult2::type + > type; +}; + +template +struct AnyOfResult5 { + typedef EitherOfMatcher< + typename AnyOfResult2::type, + typename AnyOfResult3::type + > type; +}; + +template +struct AnyOfResult6 { + typedef EitherOfMatcher< + typename AnyOfResult3::type, + typename AnyOfResult3::type + > type; +}; + +template +struct AnyOfResult7 { + typedef EitherOfMatcher< + typename AnyOfResult3::type, + typename AnyOfResult4::type + > type; +}; + +template +struct AnyOfResult8 { + typedef EitherOfMatcher< + typename AnyOfResult4::type, + typename AnyOfResult4::type + > type; +}; + +template +struct AnyOfResult9 { + typedef EitherOfMatcher< + typename AnyOfResult4::type, + typename AnyOfResult5::type + > type; +}; + +template +struct AnyOfResult10 { + typedef EitherOfMatcher< + typename AnyOfResult5::type, + typename AnyOfResult5::type + > type; +}; + +} // namespace internal + +// Args(a_matcher) matches a tuple if the selected +// fields of it matches a_matcher. C++ doesn't support default +// arguments for function templates, so we have to overload it. +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +// ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with +// n elements, where the i-th element in the container must +// match the i-th argument in the list. Each argument of +// ElementsAre() can be either a value or a matcher. We support up to +// 10 arguments. +// +// The use of DecayArray in the implementation allows ElementsAre() +// to accept string literals, whose type is const char[N], but we +// want to treat them as const char*. +// +// NOTE: Since ElementsAre() cares about the order of the elements, it +// must not be used with containers whose elements's order is +// undefined (e.g. hash_map). + +inline internal::ElementsAreMatcher< + ::testing::tuple<> > +ElementsAre() { + typedef ::testing::tuple<> Args; + return internal::ElementsAreMatcher(Args()); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type> > +ElementsAre(const T1& e1) { + typedef ::testing::tuple< + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, + e8)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, + e8, e9)); +} + +template +inline internal::ElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9, + const T10& e10) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, + e8, e9, e10)); +} + +// UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension +// that matches n elements in any order. We support up to n=10 arguments. + +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple<> > +UnorderedElementsAre() { + typedef ::testing::tuple<> Args; + return internal::UnorderedElementsAreMatcher(Args()); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1) { + typedef ::testing::tuple< + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, + e6)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, + e6, e7)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, + e6, e7, e8)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, + e6, e7, e8, e9)); +} + +template +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> > +UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9, + const T10& e10) { + typedef ::testing::tuple< + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type, + typename internal::DecayArray::type> Args; + return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, + e6, e7, e8, e9, e10)); +} + +// AllOf(m1, m2, ..., mk) matches any value that matches all of the given +// sub-matchers. AllOf is called fully qualified to prevent ADL from firing. + +template +inline typename internal::AllOfResult2::type +AllOf(M1 m1, M2 m2) { + return typename internal::AllOfResult2::type( + m1, + m2); +} + +template +inline typename internal::AllOfResult3::type +AllOf(M1 m1, M2 m2, M3 m3) { + return typename internal::AllOfResult3::type( + m1, + ::testing::AllOf(m2, m3)); +} + +template +inline typename internal::AllOfResult4::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4) { + return typename internal::AllOfResult4::type( + ::testing::AllOf(m1, m2), + ::testing::AllOf(m3, m4)); +} + +template +inline typename internal::AllOfResult5::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) { + return typename internal::AllOfResult5::type( + ::testing::AllOf(m1, m2), + ::testing::AllOf(m3, m4, m5)); +} + +template +inline typename internal::AllOfResult6::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) { + return typename internal::AllOfResult6::type( + ::testing::AllOf(m1, m2, m3), + ::testing::AllOf(m4, m5, m6)); +} + +template +inline typename internal::AllOfResult7::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) { + return typename internal::AllOfResult7::type( + ::testing::AllOf(m1, m2, m3), + ::testing::AllOf(m4, m5, m6, m7)); +} + +template +inline typename internal::AllOfResult8::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) { + return typename internal::AllOfResult8::type( + ::testing::AllOf(m1, m2, m3, m4), + ::testing::AllOf(m5, m6, m7, m8)); +} + +template +inline typename internal::AllOfResult9::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) { + return typename internal::AllOfResult9::type( + ::testing::AllOf(m1, m2, m3, m4), + ::testing::AllOf(m5, m6, m7, m8, m9)); +} + +template +inline typename internal::AllOfResult10::type +AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) { + return typename internal::AllOfResult10::type( + ::testing::AllOf(m1, m2, m3, m4, m5), + ::testing::AllOf(m6, m7, m8, m9, m10)); +} + +// AnyOf(m1, m2, ..., mk) matches any value that matches any of the given +// sub-matchers. AnyOf is called fully qualified to prevent ADL from firing. + +template +inline typename internal::AnyOfResult2::type +AnyOf(M1 m1, M2 m2) { + return typename internal::AnyOfResult2::type( + m1, + m2); +} + +template +inline typename internal::AnyOfResult3::type +AnyOf(M1 m1, M2 m2, M3 m3) { + return typename internal::AnyOfResult3::type( + m1, + ::testing::AnyOf(m2, m3)); +} + +template +inline typename internal::AnyOfResult4::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4) { + return typename internal::AnyOfResult4::type( + ::testing::AnyOf(m1, m2), + ::testing::AnyOf(m3, m4)); +} + +template +inline typename internal::AnyOfResult5::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) { + return typename internal::AnyOfResult5::type( + ::testing::AnyOf(m1, m2), + ::testing::AnyOf(m3, m4, m5)); +} + +template +inline typename internal::AnyOfResult6::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) { + return typename internal::AnyOfResult6::type( + ::testing::AnyOf(m1, m2, m3), + ::testing::AnyOf(m4, m5, m6)); +} + +template +inline typename internal::AnyOfResult7::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) { + return typename internal::AnyOfResult7::type( + ::testing::AnyOf(m1, m2, m3), + ::testing::AnyOf(m4, m5, m6, m7)); +} + +template +inline typename internal::AnyOfResult8::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) { + return typename internal::AnyOfResult8::type( + ::testing::AnyOf(m1, m2, m3, m4), + ::testing::AnyOf(m5, m6, m7, m8)); +} + +template +inline typename internal::AnyOfResult9::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) { + return typename internal::AnyOfResult9::type( + ::testing::AnyOf(m1, m2, m3, m4), + ::testing::AnyOf(m5, m6, m7, m8, m9)); +} + +template +inline typename internal::AnyOfResult10::type +AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) { + return typename internal::AnyOfResult10::type( + ::testing::AnyOf(m1, m2, m3, m4, m5), + ::testing::AnyOf(m6, m7, m8, m9, m10)); +} + +} // namespace testing + + +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using MATCHER*() inside +// a function. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +#define MATCHER(name, description)\ + class name##Matcher {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl()\ + {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple<>()));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl());\ + }\ + name##Matcher() {\ + }\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##Matcher);\ + };\ + inline name##Matcher name() {\ + return name##Matcher();\ + }\ + template \ + bool name##Matcher::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P(name, p0, description)\ + template \ + class name##MatcherP {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + explicit gmock_Impl(p0##_type gmock_p0)\ + : p0(gmock_p0) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0));\ + }\ + explicit name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\ + }\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP);\ + };\ + template \ + inline name##MatcherP name(p0##_type p0) {\ + return name##MatcherP(p0);\ + }\ + template \ + template \ + bool name##MatcherP::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P2(name, p0, p1, description)\ + template \ + class name##MatcherP2 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\ + : p0(gmock_p0), p1(gmock_p1) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1));\ + }\ + name##MatcherP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP2);\ + };\ + template \ + inline name##MatcherP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##MatcherP2(p0, p1);\ + }\ + template \ + template \ + bool name##MatcherP2::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P3(name, p0, p1, p2, description)\ + template \ + class name##MatcherP3 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, \ + p2)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2));\ + }\ + name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP3);\ + };\ + template \ + inline name##MatcherP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##MatcherP3(p0, p1, p2);\ + }\ + template \ + template \ + bool name##MatcherP3::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P4(name, p0, p1, p2, p3, description)\ + template \ + class name##MatcherP4 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, p3)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3));\ + }\ + name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP4);\ + };\ + template \ + inline name##MatcherP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##MatcherP4(p0, \ + p1, p2, p3);\ + }\ + template \ + template \ + bool name##MatcherP4::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\ + template \ + class name##MatcherP5 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, p3, p4)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP5);\ + };\ + template \ + inline name##MatcherP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##MatcherP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + bool name##MatcherP5::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\ + template \ + class name##MatcherP6 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, p3, p4, p5)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP6);\ + };\ + template \ + inline name##MatcherP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##MatcherP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + bool name##MatcherP6::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\ + template \ + class name##MatcherP7 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, p3, p4, p5, \ + p6)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6));\ + }\ + name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \ + p6(gmock_p6) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP7);\ + };\ + template \ + inline name##MatcherP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##MatcherP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + bool name##MatcherP7::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\ + template \ + class name##MatcherP8 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, \ + p3, p4, p5, p6, p7)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7));\ + }\ + name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP8);\ + };\ + template \ + inline name##MatcherP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##MatcherP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + bool name##MatcherP8::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\ + template \ + class name##MatcherP9 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8));\ + }\ + name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP9);\ + };\ + template \ + inline name##MatcherP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##MatcherP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + bool name##MatcherP9::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\ + template \ + class name##MatcherP10 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8), p9(gmock_p9) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\ + }\ + name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP10);\ + };\ + template \ + inline name##MatcherP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##MatcherP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + bool name##MatcherP10::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-matchers.h.pump b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-matchers.h.pump new file mode 100644 index 00000000..de30c2c9 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-matchers.h.pump @@ -0,0 +1,672 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-actions.h. +$$ +$var n = 10 $$ The maximum arity we support. +$$ }} This line fixes auto-indentation of the following code in Emacs. +// 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 Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic matchers. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ + +#include +#include +#include +#include +#include "gmock/gmock-matchers.h" + +namespace testing { +namespace internal { + +$range i 0..n-1 + +// The type of the i-th (0-based) field of Tuple. +#define GMOCK_FIELD_TYPE_(Tuple, i) \ + typename ::testing::tuple_element::type + +// TupleFields is for selecting fields from a +// tuple of type Tuple. It has two members: +// +// type: a tuple type whose i-th field is the ki-th field of Tuple. +// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple. +// +// For example, in class TupleFields, 2, 0>, we have: +// +// type is tuple, and +// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true). + +template +class TupleFields; + +// This generic version is used when there are $n selectors. +template +class TupleFields { + public: + typedef ::testing::tuple<$for i, [[GMOCK_FIELD_TYPE_(Tuple, k$i)]]> type; + static type GetSelectedFields(const Tuple& t) { + return type($for i, [[get(t)]]); + } +}; + +// The following specialization is used for 0 ~ $(n-1) selectors. + +$for i [[ +$$ }}} +$range j 0..i-1 +$range k 0..n-1 + +template +class TupleFields { + public: + typedef ::testing::tuple<$for j, [[GMOCK_FIELD_TYPE_(Tuple, k$j)]]> type; + static type GetSelectedFields(const Tuple& $if i==0 [[/* t */]] $else [[t]]) { + return type($for j, [[get(t)]]); + } +}; + +]] + +#undef GMOCK_FIELD_TYPE_ + +// Implements the Args() matcher. + +$var ks = [[$for i, [[k$i]]]] +template +class ArgsMatcherImpl : public MatcherInterface { + public: + // ArgsTuple may have top-level const or reference modifiers. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple; + typedef typename internal::TupleFields::type SelectedArgs; + typedef Matcher MonomorphicInnerMatcher; + + template + explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) + : inner_matcher_(SafeMatcherCast(inner_matcher)) {} + + virtual bool MatchAndExplain(ArgsTuple args, + MatchResultListener* listener) const { + const SelectedArgs& selected_args = GetSelectedArgs(args); + if (!listener->IsInterested()) + return inner_matcher_.Matches(selected_args); + + PrintIndices(listener->stream()); + *listener << "are " << PrintToString(selected_args); + + StringMatchResultListener inner_listener; + const bool match = inner_matcher_.MatchAndExplain(selected_args, + &inner_listener); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return match; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeNegationTo(os); + } + + private: + static SelectedArgs GetSelectedArgs(ArgsTuple args) { + return TupleFields::GetSelectedFields(args); + } + + // Prints the indices of the selected fields. + static void PrintIndices(::std::ostream* os) { + *os << "whose fields ("; + const int indices[$n] = { $ks }; + for (int i = 0; i < $n; i++) { + if (indices[i] < 0) + break; + + if (i >= 1) + *os << ", "; + + *os << "#" << indices[i]; + } + *os << ") "; + } + + const MonomorphicInnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl); +}; + +template +class ArgsMatcher { + public: + explicit ArgsMatcher(const InnerMatcher& inner_matcher) + : inner_matcher_(inner_matcher) {} + + template + operator Matcher() const { + return MakeMatcher(new ArgsMatcherImpl(inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcher); +}; + +// A set of metafunctions for computing the result type of AllOf. +// AllOf(m1, ..., mN) returns +// AllOfResultN::type. + +// Although AllOf isn't defined for one argument, AllOfResult1 is defined +// to simplify the implementation. +template +struct AllOfResult1 { + typedef M1 type; +}; + +$range i 1..n + +$range i 2..n +$for i [[ +$range j 2..i +$var m = i/2 +$range k 1..m +$range t m+1..i + +template +struct AllOfResult$i { + typedef BothOfMatcher< + typename AllOfResult$m<$for k, [[M$k]]>::type, + typename AllOfResult$(i-m)<$for t, [[M$t]]>::type + > type; +}; + +]] + +// A set of metafunctions for computing the result type of AnyOf. +// AnyOf(m1, ..., mN) returns +// AnyOfResultN::type. + +// Although AnyOf isn't defined for one argument, AnyOfResult1 is defined +// to simplify the implementation. +template +struct AnyOfResult1 { + typedef M1 type; +}; + +$range i 1..n + +$range i 2..n +$for i [[ +$range j 2..i +$var m = i/2 +$range k 1..m +$range t m+1..i + +template +struct AnyOfResult$i { + typedef EitherOfMatcher< + typename AnyOfResult$m<$for k, [[M$k]]>::type, + typename AnyOfResult$(i-m)<$for t, [[M$t]]>::type + > type; +}; + +]] + +} // namespace internal + +// Args(a_matcher) matches a tuple if the selected +// fields of it matches a_matcher. C++ doesn't support default +// arguments for function templates, so we have to overload it. + +$range i 0..n +$for i [[ +$range j 1..i +template <$for j [[int k$j, ]]typename InnerMatcher> +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + + +]] +// ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with +// n elements, where the i-th element in the container must +// match the i-th argument in the list. Each argument of +// ElementsAre() can be either a value or a matcher. We support up to +// $n arguments. +// +// The use of DecayArray in the implementation allows ElementsAre() +// to accept string literals, whose type is const char[N], but we +// want to treat them as const char*. +// +// NOTE: Since ElementsAre() cares about the order of the elements, it +// must not be used with containers whose elements's order is +// undefined (e.g. hash_map). + +$range i 0..n +$for i [[ + +$range j 1..i + +$if i>0 [[ + +template <$for j, [[typename T$j]]> +]] + +inline internal::ElementsAreMatcher< + ::testing::tuple< +$for j, [[ + + typename internal::DecayArray::type]]> > +ElementsAre($for j, [[const T$j& e$j]]) { + typedef ::testing::tuple< +$for j, [[ + + typename internal::DecayArray::type]]> Args; + return internal::ElementsAreMatcher(Args($for j, [[e$j]])); +} + +]] + +// UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension +// that matches n elements in any order. We support up to n=$n arguments. + +$range i 0..n +$for i [[ + +$range j 1..i + +$if i>0 [[ + +template <$for j, [[typename T$j]]> +]] + +inline internal::UnorderedElementsAreMatcher< + ::testing::tuple< +$for j, [[ + + typename internal::DecayArray::type]]> > +UnorderedElementsAre($for j, [[const T$j& e$j]]) { + typedef ::testing::tuple< +$for j, [[ + + typename internal::DecayArray::type]]> Args; + return internal::UnorderedElementsAreMatcher(Args($for j, [[e$j]])); +} + +]] + +// AllOf(m1, m2, ..., mk) matches any value that matches all of the given +// sub-matchers. AllOf is called fully qualified to prevent ADL from firing. + +$range i 2..n +$for i [[ +$range j 1..i +$var m = i/2 +$range k 1..m +$range t m+1..i + +template <$for j, [[typename M$j]]> +inline typename internal::AllOfResult$i<$for j, [[M$j]]>::type +AllOf($for j, [[M$j m$j]]) { + return typename internal::AllOfResult$i<$for j, [[M$j]]>::type( + $if m == 1 [[m1]] $else [[::testing::AllOf($for k, [[m$k]])]], + $if m+1 == i [[m$i]] $else [[::testing::AllOf($for t, [[m$t]])]]); +} + +]] + +// AnyOf(m1, m2, ..., mk) matches any value that matches any of the given +// sub-matchers. AnyOf is called fully qualified to prevent ADL from firing. + +$range i 2..n +$for i [[ +$range j 1..i +$var m = i/2 +$range k 1..m +$range t m+1..i + +template <$for j, [[typename M$j]]> +inline typename internal::AnyOfResult$i<$for j, [[M$j]]>::type +AnyOf($for j, [[M$j m$j]]) { + return typename internal::AnyOfResult$i<$for j, [[M$j]]>::type( + $if m == 1 [[m1]] $else [[::testing::AnyOf($for k, [[m$k]])]], + $if m+1 == i [[m$i]] $else [[::testing::AnyOf($for t, [[m$t]])]]); +} + +]] + +} // namespace testing +$$ } // This Pump meta comment fixes auto-indentation in Emacs. It will not +$$ // show up in the generated code. + + +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using MATCHER*() inside +// a function. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +$range i 0..n +$for i + +[[ +$var macro_name = [[$if i==0 [[MATCHER]] $elif i==1 [[MATCHER_P]] + $else [[MATCHER_P$i]]]] +$var class_name = [[name##Matcher[[$if i==0 [[]] $elif i==1 [[P]] + $else [[P$i]]]]]] +$range j 0..i-1 +$var template = [[$if i==0 [[]] $else [[ + + template <$for j, [[typename p$j##_type]]>\ +]]]] +$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var impl_ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var impl_inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] +$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] +$var params = [[$for j, [[p$j]]]] +$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] +$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] +$var param_field_decls = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var param_field_decls2 = [[$for j +[[ + + p$j##_type p$j;\ +]]]] + +#define $macro_name(name$for j [[, p$j]], description)\$template + class $class_name {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\ + $impl_inits {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\$param_field_decls + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::testing::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]])));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl($params));\ + }\ + [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {\ + }\$param_field_decls2 + private:\ + GTEST_DISALLOW_ASSIGN_($class_name);\ + };\$template + inline $class_name$param_types name($param_types_and_names) {\ + return $class_name$param_types($params);\ + }\$template + template \ + bool $class_name$param_types::gmock_Impl::MatchAndExplain(\ + arg_type arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const +]] + + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-nice-strict.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-nice-strict.h new file mode 100644 index 00000000..4095f4d5 --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-nice-strict.h @@ -0,0 +1,397 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-nice-strict.h.pump +// DO NOT EDIT BY HAND!!! + +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements class templates NiceMock, NaggyMock, and StrictMock. +// +// Given a mock class MockFoo that is created using Google Mock, +// NiceMock is a subclass of MockFoo that allows +// uninteresting calls (i.e. calls to mock methods that have no +// EXPECT_CALL specs), NaggyMock is a subclass of MockFoo +// that prints a warning when an uninteresting call occurs, and +// StrictMock is a subclass of MockFoo that treats all +// uninteresting calls as errors. +// +// Currently a mock is naggy by default, so MockFoo and +// NaggyMock behave like the same. However, we will soon +// switch the default behavior of mocks to be nice, as that in general +// leads to more maintainable tests. When that happens, MockFoo will +// stop behaving like NaggyMock and start behaving like +// NiceMock. +// +// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of +// their respective base class, with up-to 10 arguments. Therefore +// you can write NiceMock(5, "a") to construct a nice mock +// where MockFoo has a constructor that accepts (int, const char*), +// for example. +// +// A known limitation is that NiceMock, NaggyMock, +// and StrictMock only works for mock methods defined using +// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. +// If a mock method is defined in a base class of MockFoo, the "nice" +// or "strict" modifier may not affect it, depending on the compiler. +// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT +// supported. +// +// Another known limitation is that the constructors of the base mock +// cannot have arguments passed by non-const reference, which are +// banned by the Google C++ style guide anyway. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { + +template +class NiceMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + NiceMock() { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + // C++ doesn't (yet) allow inheritance of constructors, so we have + // to define it for each arity. + template + explicit NiceMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + template + NiceMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, + const A4& a4) : MockClass(a1, a2, a3, a4) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5) : MockClass(a1, a2, a3, a4, a5) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, + a6, a7) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, + a2, a3, a4, a5, a6, a7, a8) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, + const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, + const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + virtual ~NiceMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock); +}; + +template +class NaggyMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + NaggyMock() { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + // C++ doesn't (yet) allow inheritance of constructors, so we have + // to define it for each arity. + template + explicit NaggyMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + template + NaggyMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, + const A4& a4) : MockClass(a1, a2, a3, a4) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5) : MockClass(a1, a2, a3, a4, a5) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, + a6, a7) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, + a2, a3, a4, a5, a6, a7, a8) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, + const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, + const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { + ::testing::Mock::WarnUninterestingCalls( + internal::ImplicitCast_(this)); + } + + virtual ~NaggyMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock); +}; + +template +class StrictMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + StrictMock() { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + // C++ doesn't (yet) allow inheritance of constructors, so we have + // to define it for each arity. + template + explicit StrictMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + template + StrictMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, + const A4& a4) : MockClass(a1, a2, a3, a4) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5) : MockClass(a1, a2, a3, a4, a5) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, + a6, a7) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, + a2, a3, a4, a5, a6, a7, a8) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, + const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, + const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + virtual ~StrictMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock); +}; + +// The following specializations catch some (relatively more common) +// user errors of nesting nice and strict mocks. They do NOT catch +// all possible errors. + +// These specializations are declared but not defined, as NiceMock, +// NaggyMock, and StrictMock cannot be nested. + +template +class NiceMock >; +template +class NiceMock >; +template +class NiceMock >; + +template +class NaggyMock >; +template +class NaggyMock >; +template +class NaggyMock >; + +template +class StrictMock >; +template +class StrictMock >; +template +class StrictMock >; + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-nice-strict.h.pump b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-nice-strict.h.pump new file mode 100644 index 00000000..3ee1ce7f --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-generated-nice-strict.h.pump @@ -0,0 +1,161 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-nice-strict.h. +$$ +$var n = 10 $$ The maximum arity we support. +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements class templates NiceMock, NaggyMock, and StrictMock. +// +// Given a mock class MockFoo that is created using Google Mock, +// NiceMock is a subclass of MockFoo that allows +// uninteresting calls (i.e. calls to mock methods that have no +// EXPECT_CALL specs), NaggyMock is a subclass of MockFoo +// that prints a warning when an uninteresting call occurs, and +// StrictMock is a subclass of MockFoo that treats all +// uninteresting calls as errors. +// +// Currently a mock is naggy by default, so MockFoo and +// NaggyMock behave like the same. However, we will soon +// switch the default behavior of mocks to be nice, as that in general +// leads to more maintainable tests. When that happens, MockFoo will +// stop behaving like NaggyMock and start behaving like +// NiceMock. +// +// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of +// their respective base class, with up-to $n arguments. Therefore +// you can write NiceMock(5, "a") to construct a nice mock +// where MockFoo has a constructor that accepts (int, const char*), +// for example. +// +// A known limitation is that NiceMock, NaggyMock, +// and StrictMock only works for mock methods defined using +// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. +// If a mock method is defined in a base class of MockFoo, the "nice" +// or "strict" modifier may not affect it, depending on the compiler. +// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT +// supported. +// +// Another known limitation is that the constructors of the base mock +// cannot have arguments passed by non-const reference, which are +// banned by the Google C++ style guide anyway. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { + +$range kind 0..2 +$for kind [[ + +$var clazz=[[$if kind==0 [[NiceMock]] + $elif kind==1 [[NaggyMock]] + $else [[StrictMock]]]] + +$var method=[[$if kind==0 [[AllowUninterestingCalls]] + $elif kind==1 [[WarnUninterestingCalls]] + $else [[FailUninterestingCalls]]]] + +template +class $clazz : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + $clazz() { + ::testing::Mock::$method( + internal::ImplicitCast_(this)); + } + + // C++ doesn't (yet) allow inheritance of constructors, so we have + // to define it for each arity. + template + explicit $clazz(const A1& a1) : MockClass(a1) { + ::testing::Mock::$method( + internal::ImplicitCast_(this)); + } + +$range i 2..n +$for i [[ +$range j 1..i + template <$for j, [[typename A$j]]> + $clazz($for j, [[const A$j& a$j]]) : MockClass($for j, [[a$j]]) { + ::testing::Mock::$method( + internal::ImplicitCast_(this)); + } + + +]] + virtual ~$clazz() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_($clazz); +}; + +]] + +// The following specializations catch some (relatively more common) +// user errors of nesting nice and strict mocks. They do NOT catch +// all possible errors. + +// These specializations are declared but not defined, as NiceMock, +// NaggyMock, and StrictMock cannot be nested. + +template +class NiceMock >; +template +class NiceMock >; +template +class NiceMock >; + +template +class NaggyMock >; +template +class NaggyMock >; +template +class NaggyMock >; + +template +class StrictMock >; +template +class StrictMock >; +template +class StrictMock >; + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ diff --git a/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-matchers.h b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-matchers.h new file mode 100644 index 00000000..33b37a7a --- /dev/null +++ b/vendor/googletest/gtest-1.7.0/googlemock/include/gmock/gmock-matchers.h @@ -0,0 +1,4399 @@ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used argument matchers. More +// matchers can be defined by the user implementing the +// MatcherInterface interface if necessary. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + +#include +#include +#include +#include +#include // NOLINT +#include +#include +#include +#include + +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +#if GTEST_HAS_STD_INITIALIZER_LIST_ +# include // NOLINT -- must be after gtest.h +#endif + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherImpl that implements the +// MatcherInterface interface, and +// 2. a factory function that creates a Matcher object from a +// FooMatcherImpl*. +// +// The two-level delegation design makes it possible to allow a user +// to write "v" instead of "Eq(v)" where a Matcher is expected, which +// is impossible if we pass matchers by pointers. It also eases +// ownership management as Matcher objects can now be copied like +// plain values. + +// MatchResultListener is an abstract class. Its << operator can be +// used by a matcher to explain why a value matches or doesn't match. +// +// TODO(wan@google.com): add method +// bool InterestedInWhy(bool result) const; +// to indicate whether the listener is interested in why the match +// result is 'result'. +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_ != NULL) + *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true iff 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_ != NULL; } + + private: + ::std::ostream* const stream_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); +}; + +inline MatchResultListener::~MatchResultListener() { +} + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() {} + + // 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 iff 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 Mock) 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; +}; + +// A match result listener that stores the explanation in a string. +class StringMatchResultListener : public MatchResultListener { + public: + StringMatchResultListener() : MatchResultListener(&ss_) {} + + // Returns the explanation accumulated so far. + internal::string str() const { return ss_.str(); } + + // Clears the explanation accumulated so far. + void Clear() { ss_.str(""); } + + private: + ::std::stringstream ss_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener); +}; + +namespace internal { + +struct AnyEq { + template + bool operator()(const A& a, const B& b) const { return a == b; } +}; +struct AnyNe { + template + bool operator()(const A& a, const B& b) const { return a != b; } +}; +struct AnyLt { + template + bool operator()(const A& a, const B& b) const { return a < b; } +}; +struct AnyGt { + template + bool operator()(const A& a, const B& b) const { return a > b; } +}; +struct AnyLe { + template + bool operator()(const A& a, const B& b) const { return a <= b; } +}; +struct AnyGe { + template + bool operator()(const A& a, const B& b) const { return a >= b; } +}; + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(NULL) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); +}; + +// 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: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); +}; + +// 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 { + public: + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + bool MatchAndExplain(T x, MatchResultListener* listener) const { + return impl_->MatchAndExplain(x, listener); + } + + // Returns true iff this matcher matches x. + bool Matches(T x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const { + impl_->DescribeNegationTo(os); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(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 { + return impl_.get(); + } + + protected: + MatcherBase() {} + + // Constructs a matcher from its implementation. + explicit MatcherBase(const MatcherInterface* impl) + : impl_(impl) {} + + virtual ~MatcherBase() {} + + private: + // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar + // interfaces. The former dynamically allocates a chunk of memory + // to hold the reference count, while the latter tracks all + // references using a circular linked list without allocating + // memory. It has been observed that linked_ptr performs better in + // typical scenarios. However, shared_ptr can out-perform + // linked_ptr when there are many more uses of the copy constructor + // than the default constructor. + // + // If performance becomes a problem, we should see if using + // shared_ptr helps. + ::testing::internal::linked_ptr > impl_; +}; + +} // 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 linked_ptr to const +// MatcherInterface, so copying is fairly cheap. 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) {} + + // 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 string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::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() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_HAS_STRING_PIECE_ +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a StringPiece +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass StringPieces directly. + Matcher(StringPiece s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass StringPieces directly. + Matcher(StringPiece s); // NOLINT +}; +#endif // GTEST_HAS_STRING_PIECE_ + +// 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) {} + + virtual void DescribeTo(::std::ostream* os) const { + impl_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + impl_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + + GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); + }; + + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(PolymorphicMatcher); +}; + +// Creates a matcher from its implementation. This is easier to use +// than the Matcher constructor as it doesn't require you to +// explicitly write the template argument, e.g. +// +// MakeMatcher(foo); +// vs +// Matcher(foo); +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); +} + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// The MatcherCastImpl class template is a helper for implementing +// MatcherCast(). We need this helper in order to partially +// specialize the implementation of MatcherCast() (C++ allows +// class/struct templates to be partially specialized, but not +// function templates.). + +// This general version is used when MatcherCast()'s argument is a +// polymorphic matcher (i.e. something that can be converted to a +// Matcher but is not one yet; for example, Eq(value)) or a value (for +// example, "hello"). +template +class MatcherCastImpl { + public: + static Matcher Cast(const M& polymorphic_matcher_or_value) { + // M can be a polymorhic matcher, in which case we want to use + // its conversion operator to create Matcher. Or it can be a value + // that should be passed to the Matcher's constructor. + // + // We can't call Matcher(polymorphic_matcher_or_value) when M is a + // polymorphic matcher because it'll be ambiguous if T has an implicit + // constructor from M (this usually happens when T has an implicit + // constructor from any type). + // + // It won't work to unconditionally implict_cast + // polymorphic_matcher_or_value to Matcher because it won't trigger + // a user-defined conversion from M to T if one exists (assuming M is + // a value). + return CastImpl( + polymorphic_matcher_or_value, + BooleanConstant< + internal::ImplicitlyConvertible >::value>()); + } + + private: + static Matcher CastImpl(const M& value, BooleanConstant) { + // M can't be implicitly converted to Matcher, so M isn't a polymorphic + // matcher. It must be a value then. Use direct initialization to create + // a matcher. + return Matcher(ImplicitCast_(value)); + } + + static Matcher CastImpl(const M& polymorphic_matcher_or_value, + BooleanConstant) { + // M is implicitly convertible to Matcher, which means that either + // M is a polymorhpic matcher or Matcher has an implicit constructor + // from M. In both cases using the implicit conversion will produce a + // matcher. + // + // Even if T has an implicit constructor from M, it won't be called because + // creating Matcher would require a chain of two user-defined conversions + // (first to create T from M and then to create Matcher from T). + return polymorphic_matcher_or_value; + } +}; + +// This more specialized version is used when MatcherCast()'s argument +// is already a Matcher. This only compiles when type T can be +// statically converted to type U. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& source_matcher) { + return Matcher(new Impl(source_matcher)); + } + + private: + class Impl : public MatcherInterface { + public: + explicit Impl(const Matcher& source_matcher) + : source_matcher_(source_matcher) {} + + // We delegate the matching logic to the source matcher. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return source_matcher_.MatchAndExplain(static_cast(x), listener); + } + + virtual void DescribeTo(::std::ostream* os) const { + source_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + source_matcher_.DescribeNegationTo(os); + } + + private: + const Matcher source_matcher_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; +}; + +// This even more specialized version is used for efficiently casting +// a matcher to its own type. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& matcher) { return matcher; } +}; + +} // namespace internal + +// In order to be safe and clear, casting between different matcher +// types is done explicitly via MatcherCast(m), which takes a +// matcher m and returns a Matcher. It compiles only when T can be +// statically converted to the argument type of m. +template +inline Matcher MatcherCast(const M& matcher) { + return internal::MatcherCastImpl::Cast(matcher); +} + +// Implements SafeMatcherCast(). +// +// We use an intermediate class to do the actual safe casting as Nokia's +// Symbian compiler cannot decide between +// template ... (M) and +// template ... (const Matcher&) +// for function templates but can for member function templates. +template +class SafeMatcherCastImpl { + public: + // This overload handles polymorphic matchers and values only since + // monomorphic matchers are handled by the next one. + template + static inline Matcher Cast(const M& polymorphic_matcher_or_value) { + return internal::MatcherCastImpl::Cast(polymorphic_matcher_or_value); + } + + // This overload handles monomorphic matchers. + // + // In general, if type T can be implicitly converted to type U, we can + // safely convert a Matcher to a Matcher (i.e. Matcher is + // contravariant): just keep a copy of the original Matcher, convert the + // argument from type T to U, and then pass it to the underlying Matcher. + // The only exception is when U is a reference and T is not, as the + // underlying Matcher may be interested in the argument's address, which + // is not preserved in the conversion from T to U. + template + static inline Matcher Cast(const Matcher& matcher) { + // Enforce that T can be implicitly converted to U. + GTEST_COMPILE_ASSERT_((internal::ImplicitlyConvertible::value), + T_must_be_implicitly_convertible_to_U); + // Enforce that we are not converting a non-reference type T to a reference + // type U. + GTEST_COMPILE_ASSERT_( + internal::is_reference::value || !internal::is_reference::value, + cannot_convert_non_referentce_arg_to_reference); + // In case both T and U are arithmetic types, enforce that the + // conversion is not lossy. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT; + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU; + const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther; + const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther; + GTEST_COMPILE_ASSERT_( + kTIsOther || kUIsOther || + (internal::LosslessArithmeticConvertible::value), + conversion_of_arithmetic_types_must_be_lossless); + return MatcherCast(matcher); + } +}; + +template +inline Matcher SafeMatcherCast(const M& polymorphic_matcher) { + return SafeMatcherCastImpl::Cast(polymorphic_matcher); +} + +// A() returns a matcher that matches any value of type T. +template +Matcher A(); + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// If the explanation is not empty, prints it to the ostream. +inline void PrintIfNotEmpty(const internal::string& explanation, + ::std::ostream* os) { + if (explanation != "" && os != NULL) { + *os << ", " << explanation; + } +} + +// Returns true if the given type name is easy to read by a human. +// This is used to decide whether printing the type of a value might +// be helpful. +inline bool IsReadableTypeName(const string& type_name) { + // We consider a type name readable if it's short or doesn't contain + // a template or function type. + return (type_name.length() <= 20 || + type_name.find_first_of("<(") == string::npos); +} + +// Matches the value against the given matcher, prints the value and explains +// the match result to the listener. Returns the match result. +// 'listener' must not be NULL. +// Value cannot be passed by const reference, because some matchers take a +// non-const argument. +template +bool MatchPrintAndExplain(Value& value, const Matcher& matcher, + MatchResultListener* listener) { + if (!listener->IsInterested()) { + // If the listener is not interested, we do not need to construct the + // inner explanation. + return matcher.Matches(value); + } + + StringMatchResultListener inner_listener; + const bool match = matcher.MatchAndExplain(value, &inner_listener); + + UniversalPrint(value, listener->stream()); +#if GTEST_HAS_RTTI + const string& type_name = GetTypeName(); + if (IsReadableTypeName(type_name)) + *listener->stream() << " (of type " << type_name << ")"; +#endif + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + + return match; +} + +// An internal helper class for doing compile-time loop on a tuple's +// fields. +template +class TuplePrefix { + public: + // TuplePrefix::Matches(matcher_tuple, value_tuple) returns true + // iff the first N fields of matcher_tuple matches the first N + // fields of value_tuple, respectively. + template + static bool Matches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + return TuplePrefix::Matches(matcher_tuple, value_tuple) + && get(matcher_tuple).Matches(get(value_tuple)); + } + + // TuplePrefix::ExplainMatchFailuresTo(matchers, values, os) + // describes failures in matching the first N fields of matchers + // against the first N fields of values. If there is no failure, + // nothing will be streamed to os. + template + static void ExplainMatchFailuresTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + // First, describes failures in the first N - 1 fields. + TuplePrefix::ExplainMatchFailuresTo(matchers, values, os); + + // Then describes the failure (if any) in the (N - 1)-th (0-based) + // field. + typename tuple_element::type matcher = + get(matchers); + typedef typename tuple_element::type Value; + Value value = get(values); + StringMatchResultListener listener; + if (!matcher.MatchAndExplain(value, &listener)) { + // TODO(wan): include in the message the name of the parameter + // as used in MOCK_METHOD*() when possible. + *os << " Expected arg #" << N - 1 << ": "; + get(matchers).DescribeTo(os); + *os << "\n Actual: "; + // We remove the reference in type Value to prevent the + // universal printer from printing the address of value, which + // isn't interesting to the user most of the time. The + // matcher's MatchAndExplain() method handles the case when + // the address is interesting. + internal::UniversalPrint(value, os); + PrintIfNotEmpty(listener.str(), os); + *os << "\n"; + } + } +}; + +// The base case. +template <> +class TuplePrefix<0> { + public: + template + static bool Matches(const MatcherTuple& /* matcher_tuple */, + const ValueTuple& /* value_tuple */) { + return true; + } + + template + static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */, + const ValueTuple& /* values */, + ::std::ostream* /* os */) {} +}; + +// TupleMatches(matcher_tuple, value_tuple) returns true iff all +// matchers in matcher_tuple match the corresponding fields in +// value_tuple. It is a compiler error if matcher_tuple and +// value_tuple have different number of fields or incompatible field +// types. +template +bool TupleMatches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + // Makes sure that matcher_tuple and value_tuple have the same + // number of fields. + GTEST_COMPILE_ASSERT_(tuple_size::value == + tuple_size::value, + matcher_and_value_have_different_numbers_of_fields); + return TuplePrefix::value>:: + Matches(matcher_tuple, value_tuple); +} + +// Describes failures in matching matchers against values. If there +// is no failure, nothing will be streamed to os. +template +void ExplainMatchFailureTupleTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + TuplePrefix::value>::ExplainMatchFailuresTo( + matchers, values, os); +} + +// TransformTupleValues and its helper. +// +// TransformTupleValuesHelper hides the internal machinery that +// TransformTupleValues uses to implement a tuple traversal. +template +class TransformTupleValuesHelper { + private: + typedef ::testing::tuple_size TupleSize; + + public: + // For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'. + // Returns the final value of 'out' in case the caller needs it. + static OutIter Run(Func f, const Tuple& t, OutIter out) { + return IterateOverTuple()(f, t, out); + } + + private: + template + struct IterateOverTuple { + OutIter operator() (Func f, const Tup& t, OutIter out) const { + *out++ = f(::testing::get(t)); + return IterateOverTuple()(f, t, out); + } + }; + template + struct IterateOverTuple { + OutIter operator() (Func /* f */, const Tup& /* t */, OutIter out) const { + return out; + } + }; +}; + +// Successively invokes 'f(element)' on each element of the tuple 't', +// appending each result to the 'out' iterator. Returns the final value +// of 'out'. +template +OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) { + return TransformTupleValuesHelper::Run(f, t, out); +} + +// Implements A(). +template +class AnyMatcherImpl : public MatcherInterface { + public: + virtual bool MatchAndExplain( + T /* x */, MatchResultListener* /* listener */) const { return true; } + virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; } + virtual void DescribeNegationTo(::std::ostream* os) const { + // This is mostly for completeness' safe, as it's not very useful + // to write Not(A()). However we cannot completely rule out + // such a possibility, and it doesn't hurt to be prepared. + *os << "never matches"; + } +}; + +// Implements _, a matcher that matches any value of any +// type. This is a polymorphic matcher, so we need a template type +// conversion operator to make it appearing as a Matcher for any +// type T. +class AnythingMatcher { + public: + template + operator Matcher() const { return A(); } +}; + +// 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) {} + template + operator Matcher() const { + return MakeMatcher(new Impl(rhs_)); + } + + private: + template + class Impl : public MatcherInterface { + public: + explicit Impl(const Rhs& rhs) : rhs_(rhs) {} + virtual bool MatchAndExplain( + Lhs lhs, MatchResultListener* /* listener */) const { + return Op()(lhs, rhs_); + } + virtual void DescribeTo(::std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(rhs_, os); + } + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(rhs_, os); + } + private: + Rhs rhs_; + GTEST_DISALLOW_ASSIGN_(Impl); + }; + Rhs rhs_; + GTEST_DISALLOW_ASSIGN_(ComparisonBase); +}; + +template +class EqMatcher : public ComparisonBase, Rhs, AnyEq> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyEq>(rhs) { } + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template +class NeMatcher : public ComparisonBase, Rhs, AnyNe> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyNe>(rhs) { } + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template +class LtMatcher : public ComparisonBase, Rhs, AnyLt> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLt>(rhs) { } + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template +class GtMatcher : public ComparisonBase, Rhs, AnyGt> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGt>(rhs) { } + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template +class LeMatcher : public ComparisonBase, Rhs, AnyLe> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLe>(rhs) { } + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template +class GeMatcher : public ComparisonBase, Rhs, AnyGe> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGe>(rhs) { } + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +// Implements the polymorphic IsNull() matcher, which matches any raw or smart +// pointer that is NULL. +class IsNullMatcher { + public: + template + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { +#if GTEST_LANG_CXX11 + return p == nullptr; +#else // GTEST_LANG_CXX11 + return GetRawPointer(p) == NULL; +#endif // GTEST_LANG_CXX11 + } + + void DescribeTo(::std::ostream* os) const { *os << "is NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "isn't NULL"; + } +}; + +// Implements the polymorphic NotNull() matcher, which matches any raw or smart +// pointer that is not NULL. +class NotNullMatcher { + public: + template + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { +#if GTEST_LANG_CXX11 + return p != nullptr; +#else // GTEST_LANG_CXX11 + return GetRawPointer(p) != NULL; +#endif // GTEST_LANG_CXX11 + } + + void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "is NULL"; + } +}; + +// Ref(variable) matches any argument that is a reference to +// 'variable'. This matcher is polymorphic as it can match any +// super type of the type of 'variable'. +// +// The RefMatcher template class implements Ref(variable). It can +// only be instantiated with a reference type. This prevents a user +// from mistakenly using Ref(x) to match a non-reference function +// argument. For example, the following will righteously cause a +// compiler error: +// +// int n; +// Matcher m1 = Ref(n); // This won't compile. +// Matcher m2 = Ref(n); // This will compile. +template +class RefMatcher; + +template +class RefMatcher { + // Google Mock is a generic framework and thus needs to support + // mocking any function types, including those that take non-const + // reference arguments. Therefore the template parameter T (and + // Super below) can be instantiated to either a const type or a + // non-const type. + public: + // RefMatcher() takes a T& instead of const T&, as we want the + // compiler to catch using Ref(const_value) as a matcher for a + // non-const reference. + explicit RefMatcher(T& x) : object_(x) {} // NOLINT + + template + operator Matcher() const { + // By passing object_ (type T&) to Impl(), which expects a Super&, + // we make sure that Super is a super type of T. In particular, + // this catches using Ref(const_value) as a matcher for a + // non-const reference, as you cannot implicitly convert a const + // reference to a non-const reference. + return MakeMatcher(new Impl(object_)); + } + + private: + template + class Impl : public MatcherInterface { + public: + explicit Impl(Super& x) : object_(x) {} // NOLINT + + // MatchAndExplain() takes a Super& (as opposed to const Super&) + // in order to match the interface MatcherInterface. + virtual bool MatchAndExplain( + Super& x, MatchResultListener* listener) const { + *listener << "which is located @" << static_cast(&x); + return &x == &object_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "references the variable "; + UniversalPrinter::Print(object_, os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not reference the variable "; + UniversalPrinter::Print(object_, os); + } + + private: + const Super& object_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + T& object_; + + GTEST_DISALLOW_ASSIGN_(RefMatcher); +}; + +// Polymorphic helper functions for narrow and wide string matchers. +inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) { + return String::CaseInsensitiveCStringEquals(lhs, rhs); +} + +inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + return String::CaseInsensitiveWideCStringEquals(lhs, rhs); +} + +// String comparison for narrow or wide strings that can have embedded NUL +// characters. +template +bool CaseInsensitiveStringEquals(const StringType& s1, + const StringType& s2) { + // Are the heads equal? + if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) { + return false; + } + + // Skip the equal heads. + const typename StringType::value_type nul = 0; + const size_t i1 = s1.find(nul), i2 = s2.find(nul); + + // Are we at the end of either s1 or s2? + if (i1 == StringType::npos || i2 == StringType::npos) { + return i1 == i2; + } + + // Are the tails equal? + return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1)); +} + +// String matchers. + +// Implements equality-based string matchers like StrEq, StrCaseNe, and etc. +template +class StrEqualityMatcher { + public: + StrEqualityMatcher(const StringType& str, bool expect_eq, + bool case_sensitive) + : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + if (s == NULL) { + return !expect_eq_; + } + return MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + const bool eq = case_sensitive_ ? s2 == string_ : + CaseInsensitiveStringEquals(s2, string_); + return expect_eq_ == eq; + } + + void DescribeTo(::std::ostream* os) const { + DescribeToHelper(expect_eq_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + DescribeToHelper(!expect_eq_, os); + } + + private: + void DescribeToHelper(bool expect_eq, ::std::ostream* os) const { + *os << (expect_eq ? "is " : "isn't "); + *os << "equal to "; + if (!case_sensitive_) { + *os << "(ignoring case) "; + } + UniversalPrint(string_, os); + } + + const StringType string_; + const bool expect_eq_; + const bool case_sensitive_; + + GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher); +}; + +// Implements the polymorphic HasSubstr(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class HasSubstrMatcher { + public: + explicit HasSubstrMatcher(const StringType& substring) + : substring_(substring) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.find(substring_) != StringType::npos; + } + + // Describes what this matcher matches. + void DescribeTo(::std::ostream* os) const { + *os << "has substring "; + UniversalPrint(substring_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "has no substring "; + UniversalPrint(substring_, os); + } + + private: + const StringType substring_; + + GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher); +}; + +// Implements the polymorphic StartsWith(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class StartsWithMatcher { + public: + explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) { + } + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.length() >= prefix_.length() && + s2.substr(0, prefix_.length()) == prefix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "starts with "; + UniversalPrint(prefix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't start with "; + UniversalPrint(prefix_, os); + } + + private: + const StringType prefix_; + + GTEST_DISALLOW_ASSIGN_(StartsWithMatcher); +}; + +// Implements the polymorphic EndsWith(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class EndsWithMatcher { + public: + explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.length() >= suffix_.length() && + s2.substr(s2.length() - suffix_.length()) == suffix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "ends with "; + UniversalPrint(suffix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't end with "; + UniversalPrint(suffix_, os); + } + + private: + const StringType suffix_; + + GTEST_DISALLOW_ASSIGN_(EndsWithMatcher); +}; + +// 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) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(internal::string(s), listener); + } + + // Matches anything that can convert to internal::string. + // + // This is a template, not just a plain function with const internal::string&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const internal::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 internal::linked_ptr regex_; + const bool full_match_; + + GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher); +}; + +// Implements a matcher that compares the two fields of a 2-tuple +// using one of the ==, <=, <, etc, operators. The two fields being +// compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq() can be +// used to match a tuple, a tuple, +// etc). Therefore we use a template type conversion operator in the +// implementation. +template +class PairMatchBase { + public: + template + operator Matcher< ::testing::tuple >() const { + return MakeMatcher(new Impl< ::testing::tuple >); + } + template + operator Matcher&>() const { + return MakeMatcher(new Impl&>); + } + + private: + static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT + return os << D::Desc(); + } + + template + class Impl : public MatcherInterface { + public: + virtual bool MatchAndExplain( + Tuple args, + MatchResultListener* /* listener */) const { + return Op()(::testing::get<0>(args), ::testing::get<1>(args)); + } + virtual void DescribeTo(::std::ostream* os) const { + *os << "are " << GetDesc; + } + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "aren't " << GetDesc; + } + }; +}; + +class Eq2Matcher : public PairMatchBase { + public: + static const char* Desc() { return "an equal pair"; } +}; +class Ne2Matcher : public PairMatchBase { + public: + static const char* Desc() { return "an unequal pair"; } +}; +class Lt2Matcher : public PairMatchBase { + public: + static const char* Desc() { return "a pair where the first < the second"; } +}; +class Gt2Matcher : public PairMatchBase { + public: + static const char* Desc() { return "a pair where the first > the second"; } +}; +class Le2Matcher : public PairMatchBase { + public: + static const char* Desc() { return "a pair where the first <= the second"; } +}; +class Ge2Matcher : public PairMatchBase { + public: + static const char* Desc() { return "a pair where the first >= the second"; } +}; + +// Implements the Not(...) matcher for a particular argument type T. +// We do not nest it inside the NotMatcher class template, as that +// will prevent different instantiations of NotMatcher from sharing +// the same NotMatcherImpl class. +template +class NotMatcherImpl : public MatcherInterface { + public: + explicit NotMatcherImpl(const Matcher& matcher) + : matcher_(matcher) {} + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return !matcher_.MatchAndExplain(x, listener); + } + + virtual void DescribeTo(::std::ostream* os) const { + matcher_.DescribeNegationTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + matcher_.DescribeTo(os); + } + + private: + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(NotMatcherImpl); +}; + +// Implements the Not(m) matcher, which matches a value that doesn't +// match matcher m. +template +class NotMatcher { + public: + explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {} + + // This template type conversion operator allows Not(m) to be used + // to match any type m can match. + template + operator Matcher() const { + return Matcher(new NotMatcherImpl(SafeMatcherCast(matcher_))); + } + + private: + InnerMatcher matcher_; + + GTEST_DISALLOW_ASSIGN_(NotMatcher); +}; + +// Implements the AllOf(m1, m2) matcher for a particular argument type +// T. We do not nest it inside the BothOfMatcher class template, as +// that will prevent different instantiations of BothOfMatcher from +// sharing the same BothOfMatcherImpl class. +template +class BothOfMatcherImpl : public MatcherInterface { + public: + BothOfMatcherImpl(const Matcher& matcher1, const Matcher& matcher2) + : matcher1_(matcher1), matcher2_(matcher2) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeTo(os); + *os << ") and ("; + matcher2_.DescribeTo(os); + *os << ")"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeNegationTo(os); + *os << ") or ("; + matcher2_.DescribeNegationTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + // If either matcher1_ or matcher2_ doesn't match x, we only need + // to explain why one of them fails. + StringMatchResultListener listener1; + if (!matcher1_.MatchAndExplain(x, &listener1)) { + *listener << listener1.str(); + return false; + } + + StringMatchResultListener listener2; + if (!matcher2_.MatchAndExplain(x, &listener2)) { + *listener << listener2.str(); + return false; + } + + // Otherwise we need to explain why *both* of them match. + const internal::string s1 = listener1.str(); + const internal::string s2 = listener2.str(); + + if (s1 == "") { + *listener << s2; + } else { + *listener << s1; + if (s2 != "") { + *listener << ", and " << s2; + } + } + return true; + } + + private: + const Matcher matcher1_; + const Matcher matcher2_; + + GTEST_DISALLOW_ASSIGN_(BothOfMatcherImpl); +}; + +#if GTEST_LANG_CXX11 +// MatcherList provides mechanisms for storing a variable number of matchers in +// a list structure (ListType) and creating a combining matcher from such a +// list. +// The template is defined recursively using the following template paramters: +// * kSize is the length of the MatcherList. +// * Head is the type of the first matcher of the list. +// * Tail denotes the types of the remaining matchers of the list. +template +struct MatcherList { + typedef MatcherList MatcherListTail; + typedef ::std::pair ListType; + + // BuildList stores variadic type values in a nested pair structure. + // Example: + // MatcherList<3, int, string, float>::BuildList(5, "foo", 2.0) will return + // the corresponding result of type pair>. + static ListType BuildList(const Head& matcher, const Tail&... tail) { + return ListType(matcher, MatcherListTail::BuildList(tail...)); + } + + // CreateMatcher creates a Matcher from a given list of matchers (built + // by BuildList()). CombiningMatcher is used to combine the matchers of the + // list. CombiningMatcher must implement MatcherInterface and have a + // constructor taking two Matchers as input. + template class CombiningMatcher> + static Matcher CreateMatcher(const ListType& matchers) { + return Matcher(new CombiningMatcher( + SafeMatcherCast(matchers.first), + MatcherListTail::template CreateMatcher( + matchers.second))); + } +}; + +// The following defines the base case for the recursive definition of +// MatcherList. +template +struct MatcherList<2, Matcher1, Matcher2> { + typedef ::std::pair ListType; + + static ListType BuildList(const Matcher1& matcher1, + const Matcher2& matcher2) { + return ::std::pair(matcher1, matcher2); + } + + template class CombiningMatcher> + static Matcher CreateMatcher(const ListType& matchers) { + return Matcher(new CombiningMatcher( + SafeMatcherCast(matchers.first), + SafeMatcherCast(matchers.second))); + } +}; + +// VariadicMatcher is used for the variadic implementation of +// AllOf(m_1, m_2, ...) and AnyOf(m_1, m_2, ...). +// CombiningMatcher is used to recursively combine the provided matchers +// (of type Args...). +template