Merge branch 'develop' of https://github.com/cryfs/cryfs into develop

This commit is contained in:
Sebastian Messmer 2020-02-23 19:34:11 -08:00
commit 8373abf216
765 changed files with 2111 additions and 119277 deletions

View File

@ -55,7 +55,14 @@ references:
sudo chmod o-w /etc/apt/sources.list.d/clang.list
DEBIAN_FRONTEND=noninteractive sudo apt-get update -qq
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git ccache $APT_COMPILER_PACKAGE cmake make libcurl4-openssl-dev libssl-dev libfuse-dev python
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git ccache $APT_COMPILER_PACKAGE cmake make libcurl4-openssl-dev libssl-dev libfuse-dev
# install conan
pyenv global 3.7.0
python3 -m pip install -U pip
python3 -m pip install conan
source ~/.profile
# Use /dev/urandom when /dev/random is accessed to use less entropy
sudo cp -a /dev/urandom /dev/random
@ -67,14 +74,25 @@ references:
sudo ln -s /usr/bin/run-clang-tidy-8.py /usr/bin/run-clang-tidy.py
fi
# Setup build cache
sudo mkdir -p /build_cache/ccache
sudo mkdir -p /build_cache/conan
sudo chown -R circleci:circleci /build_cache
# Setup conan cache
echo 'export CONAN_USER_HOME=/build_cache/conan' >> $BASH_ENV
if [[ "${CXX}" == *"g++"* ]]; then
# Conan uses old gcc ABI by default but we can only build with the cxx11 ABI.
conan profile new default --detect
conan profile update settings.compiler.libcxx=libstdc++11 default
fi
# Setup ccache
sudo ln -s /usr/bin/ccache /usr/local/bin/$CC
sudo ln -s /usr/bin/ccache /usr/local/bin/$CXX
sudo mkdir /ccache_data
sudo chown circleci:circleci /ccache_data
echo 'export CCACHE_COMPILERCHECK=content' >> $BASH_ENV
echo 'export CCACHE_COMPRESS=1' >> $BASH_ENV
echo 'export CCACHE_DIR=/ccache_data' >> $BASH_ENV
echo 'export CCACHE_DIR=/build_cache/ccache' >> $BASH_ENV
echo 'export CCACHE_SLOPPINESS=include_file_mtime' >> $BASH_ENV
sudo mkdir -p /tmp/aptcache
@ -88,60 +106,20 @@ references:
cmake --version
/usr/local/bin/$CC --version
/usr/local/bin/$CXX --version
upgrade_boost_pre: &upgrade_boost_pre
restore_cache:
keys:
# Find the most recent cache from any branch
- v5_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
upgrade_boost_post: &upgrade_boost_post
save_cache:
key: v5_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
paths:
- /tmp/boost_1_65_1
upgrade_boost: &upgrade_boost
run:
name: Upgrade Boost
command: |
# Detect number of CPU cores
export NUMCORES=`nproc`
echo Using $NUMCORES cores
# Download and prepare boost (only if not already present from cache)
if [ ! -d "/tmp/boost_1_65_1" ]; then
echo "Didn't find boost in cache. Downloading and building."
wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.65.1/boost_1_65_1.tar.bz2/download
if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "a9e6866d3bb3e7c198f442ff09f5322f58064dca79bc420f2f0168eb63964226dfbc4f034a5a5e5958281fdf7518a1b057c894fbda0b61fced59c1661bf30f1a" ]; then
echo Correct sha512sum
else
echo Wrong sha512sum
sha512sum boost.tar.bz2
exit 1
fi
echo Extracting...
tar -xf /tmp/boost.tar.bz2 -C /tmp
rm -rf boost.tar.bz2
cd /tmp/boost_1_65_1
./bootstrap.sh --with-toolset=${BUILD_TOOLSET} --with-libraries=filesystem,thread,chrono,program_options
cd ..
else
echo Found boost in cache. Use cache and build.
fi
# Compile and install boost (if cached, this should be fast)
cd /tmp/boost_1_65_1
sudo ./b2 toolset=${BUILD_TOOLSET} link=static cxxflags=-fPIC -d0 -j$NUMCORES install
build_pre: &build_pre
restore_cache:
keys:
# Find most recent cache from any revision on the same branch (cache keys are prefix matched)
# CIRCLE_PR_NUMBER is only set if this is a pull request.
- v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}
- v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}
# Fallback to less specific caches if the one above wasn't found
- v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}
- v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
- v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}
- v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
build_post: &build_post
save_cache:
key: v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}_{{ .Revision }}
key: v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}_{{ .Revision }}
paths:
- /ccache_data
- /build_cache
build: &build
run:
name: Build
@ -164,6 +142,7 @@ references:
# Build
mkdir cmake
cd cmake
conan install .. -s build_type=${BUILD_TYPE} --build=missing
cmake .. -DBUILD_TESTING=on -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${OPENMP_PARAMS} ${CMAKE_FLAGS}
make -j$NUMCORES
@ -175,14 +154,14 @@ references:
command: |
if "${RUN_TESTS}"; then
cd cmake
./test/gitversion/gitversion-test ${GTEST_ARGS}
./test/cpp-utils/cpp-utils-test ${GTEST_ARGS}
if [ ! "$DISABLE_BROKEN_ASAN_TESTS" = true ] ; then ./test/fspp/fspp-test ${GTEST_ARGS} ; fi
./test/parallelaccessstore/parallelaccessstore-test ${GTEST_ARGS}
./test/blockstore/blockstore-test ${GTEST_ARGS}
./test/blobstore/blobstore-test ${GTEST_ARGS}
./test/cryfs/cryfs-test ${GTEST_ARGS}
./test/cryfs-cli/cryfs-cli-test ${GTEST_ARGS}
./bin/gitversion-test ${GTEST_ARGS}
./bin/cpp-utils-test ${GTEST_ARGS}
if [ ! "$DISABLE_BROKEN_ASAN_TESTS" = true ] ; then ./bin/fspp-test ${GTEST_ARGS} ; fi
./bin/parallelaccessstore-test ${GTEST_ARGS}
./bin/blockstore-test ${GTEST_ARGS}
./bin/blobstore-test ${GTEST_ARGS}
./bin/cryfs-test ${GTEST_ARGS}
./bin/cryfs-cli-test ${GTEST_ARGS}
fi
job_definition: &job_definition
<<: *container_config
@ -191,9 +170,6 @@ references:
- <<: *container_setup_pre
- <<: *container_setup
- <<: *container_setup_post
- <<: *upgrade_boost_pre
- <<: *upgrade_boost
- <<: *upgrade_boost_post
- checkout
- <<: *build_pre
- <<: *build
@ -205,30 +181,6 @@ references:
only: /.*/
jobs:
gcc_5_debug:
<<: *job_definition
environment:
CC: gcc-5
CXX: g++-5
BUILD_TOOLSET: gcc
APT_COMPILER_PACKAGE: "g++-5"
CXXFLAGS: ""
BUILD_TYPE: "Debug"
GTEST_ARGS: ""
CMAKE_FLAGS: ""
RUN_TESTS: true
gcc_5_release:
<<: *job_definition
environment:
CC: gcc-5
CXX: g++-5
BUILD_TOOLSET: gcc
APT_COMPILER_PACKAGE: "g++-5"
CXXFLAGS: ""
BUILD_TYPE: "Release"
GTEST_ARGS: ""
CMAKE_FLAGS: ""
RUN_TESTS: true
gcc_6_debug:
<<: *job_definition
environment:
@ -551,9 +503,6 @@ jobs:
- <<: *container_setup_pre
- <<: *container_setup
- <<: *container_setup_post
- <<: *upgrade_boost_pre
- <<: *upgrade_boost
- <<: *upgrade_boost_post
- checkout
- run:
name: clang-tidy
@ -580,10 +529,6 @@ workflows:
build_and_test:
jobs:
- gcc_5_debug:
<<: *enable_for_tags
- gcc_5_release:
<<: *enable_for_tags
- gcc_6_debug:
<<: *enable_for_tags
- gcc_6_release:

View File

@ -32,19 +32,20 @@ cmake --version
# Build
echo Build target: ${BUILD_TARGET}
conan install .. -s build_type=${BUILD_TARGET} --build=missing
cmake .. -DBUILD_TESTING=on -DCMAKE_BUILD_TYPE=${BUILD_TARGET}
make -j$NUMCORES
ccache --show-stats
# Test
./test/gitversion/gitversion-test
./test/cpp-utils/cpp-utils-test
./test/parallelaccessstore/parallelaccessstore-test
./test/blockstore/blockstore-test
./test/blobstore/blobstore-test
./test/cryfs/cryfs-test
./bin/gitversion-test
./bin/cpp-utils-test
./bin/parallelaccessstore-test
./bin/blockstore-test
./bin/blobstore-test
./bin/cryfs-test
# TODO Also run once fixed
# ./test/fspp/fspp-test
# ./test/cryfs-cli/cryfs-cli-test
# ./bin/fspp-test
# ./bin/cryfs-cli-test

View File

@ -18,5 +18,7 @@ brew install libomp
# By default, travis only fetches the newest 50 commits. We need more in case we're further from the last version tag, so the build doesn't fail because it can't generate the version number.
git fetch --unshallow --tags
pip install conan
# Setup ccache
brew install ccache

View File

@ -26,6 +26,20 @@ option(USE_CLANG_TIDY "build with clang-tidy checks enabled" OFF)
option(USE_IWYU "build with iwyu checks enabled" OFF)
option(CLANG_TIDY_WARNINGS_AS_ERRORS "treat clang-tidy warnings as errors" OFF)
set(CONAN_BUILD_INFO_FILE ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
if(EXISTS ${CONAN_BUILD_INFO_FILE})
include(${CONAN_BUILD_INFO_FILE})
else()
message(WARNING "The file ${CONAN_BUILD_INFO_FILE} doesn't exist, you have to run conan install first")
endif()
conan_basic_setup(TARGETS SKIP_STD)
if(CONAN_SETTINGS_COMPILER_LIBCXX STREQUAL "libstdc++")
# TODO Test this warning works correctly and that the proposed solution in the warning message works.
message(FATAL_ERROR "Conan is set up to build against libstdc++ (i.e. the legacy GCC ABI). We only support libstdc++11 (i.e. the new GCC ABI).\nPlease add the '-s compiler.libcxx=libstdc++11' argument when running 'conan install'.")
endif()
if(USE_IWYU)
# note: for iwyu, we need cmake 3.3
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

View File

@ -7,7 +7,7 @@
"inheritEnvironments": [ "msvc_x86" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
@ -18,7 +18,7 @@
"inheritEnvironments": [ "msvc_x86" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
@ -29,7 +29,7 @@
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
@ -40,7 +40,7 @@
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}

View File

@ -1,3 +1,12 @@
Version 0.11.0 (unreleased)
---------------
Build changes:
* Switch to Conan package manager
New features:
* Add support for atime mount options (noatime, strictatime, relatime, atime, nodiratime). As before, relatime is the default.
Version 0.10.2
---------------
Fixed bugs:

View File

@ -43,15 +43,10 @@ Building from source
Requirements
------------
- Git (for getting the source code)
- GCC version >= 5.0 or Clang >= 4.0
- GCC version >= 6.5 or Clang >= 4.0
- CMake version >= 3.1
- Conan package manager
- libcurl4 (including development headers)
- Boost libraries version >= 1.65.1 (including development headers)
- filesystem
- system
- chrono
- program_options
- thread
- SSL development libraries (including development headers, e.g. libssl-dev)
- libFUSE version >= 2.8.6 (including development headers), on Mac OS X instead install osxfuse from https://osxfuse.github.io/
- Python >= 2.7
@ -60,13 +55,16 @@ Requirements
You can use the following commands to install these requirements
# Ubuntu
$ sudo apt install git g++ cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libssl-dev libfuse-dev python
$ sudo apt install git g++ cmake make libcurl4-openssl-dev libssl-dev libfuse-dev python
$ sudo pip install conan
# Fedora
sudo dnf install git gcc-c++ cmake make libcurl-devel boost-devel boost-static openssl-devel fuse-devel python
$ sudo dnf install git gcc-c++ cmake make libcurl-devel openssl-devel fuse-devel python
$ sudo pip install conan
# Macintosh
brew install cmake boost openssl libomp
$ brew install cmake openssl libomp
$ sudo pip install conan
Build & Install
---------------
@ -79,6 +77,7 @@ Build & Install
2. Build
$ mkdir cmake && cd cmake
$ conan install .. --build=missing
$ cmake ..
$ make
@ -97,7 +96,6 @@ Building on Windows (experimental)
Build with Visual Studio 2019 and pass in the following flags to CMake:
-DDOKAN_PATH=[dokan library location, e.g. "C:\Program Files\Dokan\DokanLibrary-1.2.1"]
-DBOOST_ROOT=[path to root of boost installation]
If you set these variables correctly in the `CMakeSettings.json` file, you should be able to open the cryfs source folder with Visual Studio 2019.
@ -106,35 +104,25 @@ Troubleshooting
On most systems, CMake should find the libraries automatically. However, that doesn't always work.
1. **Boost headers not found**
Pass in the boost include path with
cmake .. -DBoost_INCLUDE_DIRS=/path/to/boost/headers
If you want to link boost dynamically (e.g. you don't have the static libraries), use the following:
cmake .. -DBoost_USE_STATIC_LIBS=off
2. **Fuse/Osxfuse library not found**
1. **Fuse/Osxfuse library not found**
Pass in the library path with
cmake .. -DFUSE_LIB_PATH=/path/to/fuse/or/osxfuse
3. **Fuse/Osxfuse headers not found**
2. **Fuse/Osxfuse headers not found**
Pass in the include path with
cmake .. -DCMAKE_CXX_FLAGS="-I/path/to/fuse/or/osxfuse/headers"
4. **Openssl headers not found**
3. **Openssl headers not found**
Pass in the include path with
cmake .. -DCMAKE_C_FLAGS="-I/path/to/openssl/include"
5. **OpenMP not found (osx)**
4. **OpenMP not found (osx)**
Either build it without OpenMP
@ -151,9 +139,11 @@ On most systems, CMake should find the libraries automatically. However, that do
Creating .deb and .rpm packages
-------------------------------
It is recommended to install CryFS using packages, because that allows for an easy way to uninstall it again once you don't need it anymore.
There are additional requirements if you want to create packages. They are:
- CMake version >= 3.3
- rpmbuild for creating .rpm package
- rpmbuild if you're creating a .rpm package
1. Clone repository
@ -163,6 +153,7 @@ There are additional requirements if you want to create packages. They are:
2. Build
$ mkdir cmake && cd cmake
$ conan install .. --build=missing
$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=off
$ make package

View File

@ -1,6 +1,6 @@
image:
- Visual Studio 2017
#- Visual Studio 2017 Preview
- Visual Studio 2019
#- Visual Studio 2019 Preview
platform:
- x64
@ -20,29 +20,36 @@ init:
- echo %APPVEYOR_BUILD_WORKER_IMAGE%
- set arch=32
- if "%PLATFORM%"=="x64" ( set arch=64)
- set VisualStudioVersion=2017
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017 Preview" ( set VisualStudioVersion=Preview)
- set VisualStudioVersion=2019
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2019 Preview" ( set VisualStudioVersion=Preview)
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\%VisualStudioVersion%\Community\VC\Auxiliary\Build\vcvars%arch%.bat"
install:
- choco install -y dokany --version 1.2.1.2000 --installargs INSTALLDEVFILES=1
- pip install conan
- conan --version
- cmake --version
- conan profile new default --detect
# note: Conan misdetects our x86 CI platform as x64, fix that
- if "%PLATFORM%"=="x86" ( conan profile update settings.arch=x86 default )
- if "%PLATFORM%"=="x86" ( conan profile update settings.arch_build=x86 default )
build_script:
- cmd: mkdir build
- cmd: cd build
# note: The cmake+ninja workflow requires us to set build type in both cmake commands ('cmake' and 'cmake --build'), otherwise the cryfs.exe will depend on debug versions of the visual studio c++ runtime (i.e. msvcp140d.dll)
- cmd: cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_67_0" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.2.1"
- cmd: conan install .. -s build_type=%CONFIGURATION% --build=missing
- cmd: cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_TESTING=on -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.2.1"
- cmd: cmake --build . --config %CONFIGURATION%
- cmd: .\test\gitversion\gitversion-test.exe
- cmd: .\bin\gitversion-test.exe
# cpp-utils-test disables ThreadDebuggingTest_ThreadName.*_thenIsCorrect because the appveyor image is too old to support the API needed for that
- cmd: .\test\cpp-utils\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect
#- cmd: .\test\fspp\fspp-test.exe
- cmd: .\test\parallelaccessstore\parallelaccessstore-test.exe
- cmd: .\test\blockstore\blockstore-test.exe
- cmd: .\test\blobstore\blobstore-test.exe
- cmd: .\test\cryfs\cryfs-test.exe
#- cmd: .\test\cryfs-cli\cryfs-cli-test.exe
- cmd: .\bin\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect
#- cmd: .\bin\fspp-test.exe
- cmd: .\bin\parallelaccessstore-test.exe
- cmd: .\bin\blockstore-test.exe
- cmd: .\bin\blobstore-test.exe
- cmd: .\bin\cryfs-test.exe
#- cmd: .\bin\cryfs-cli-test.exe
- cmd: cpack -C %CONFIGURATION% --verbose -G WIX

View File

@ -6,22 +6,13 @@ include(CheckCXXCompilerFlag)
# Uses: target_activate_cpp14(buildtarget)
###################################################
function(target_activate_cpp14 TARGET)
if("${CMAKE_VERSION}" VERSION_GREATER "3.1")
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 14)
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD_REQUIRED ON)
else("${CMAKE_VERSION}" VERSION_GREATER "3.1")
check_cxx_compiler_flag("-std=c++14" COMPILER_HAS_CPP14_SUPPORT)
if (COMPILER_HAS_CPP14_SUPPORT)
target_compile_options(${TARGET} PRIVATE -std=c++14)
else(COMPILER_HAS_CPP14_SUPPORT)
check_cxx_compiler_flag("-std=c++1y" COMPILER_HAS_CPP14_PARTIAL_SUPPORT)
if (COMPILER_HAS_CPP14_PARTIAL_SUPPORT)
target_compile_options(${TARGET} PRIVATE -std=c++1y)
if(MSVC)
# Required by range-v3, see its README.md
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 17)
else()
message(FATAL_ERROR "Compiler doesn't support C++14")
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 14)
endif()
endif(COMPILER_HAS_CPP14_SUPPORT)
endif("${CMAKE_VERSION}" VERSION_GREATER "3.1")
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD_REQUIRED ON)
# Ideally, we'd like to use libc++ on linux as well, but:
# - http://stackoverflow.com/questions/37096062/get-a-basic-c-program-to-compile-using-clang-on-ubuntu-16
# - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=808086
@ -106,26 +97,8 @@ endfunction(target_enable_style_warnings)
# target_add_boost(buildtarget system filesystem) # list all libraries to link against in the dependencies
##################################################
function(target_add_boost TARGET)
# Load boost libraries
if(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
# Many supported systems don't have boost >= 1.65.1. Better link it statically.
message(STATUS "Boost will be statically linked")
set(Boost_USE_STATIC_LIBS ON)
else(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
message(STATUS "Boost will be dynamically linked")
set(Boost_USE_STATIC_LIBS OFF)
endif(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
set(BOOST_THREAD_VERSION 4)
find_package(Boost 1.65.1
REQUIRED
COMPONENTS ${ARGN})
target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET} PUBLIC ${Boost_LIBRARIES})
target_link_libraries(${TARGET} PUBLIC CONAN_PKG::boost)
target_compile_definitions(${TARGET} PUBLIC BOOST_THREAD_VERSION=4)
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# Also link to rt, because boost thread needs that.
target_link_libraries(${TARGET} PUBLIC rt)
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
endfunction(target_add_boost)
##################################################

40
conanfile.py Normal file
View File

@ -0,0 +1,40 @@
from conans import ConanFile, CMake
class CryFSConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = [
"range-v3/0.9.1@ericniebler/stable",
"spdlog/1.4.2",
]
generators = "cmake"
default_options = {
# Need to disable boost-math because that doesn't compile for some reason on CI
"boost:without_math": True,
"boost:without_wave": True,
"boost:without_container": True,
"boost:without_exception": True,
"boost:without_graph": True,
"boost:without_iostreams": True,
"boost:without_locale": True,
"boost:without_log": True,
"boost:without_random": True,
"boost:without_regex": True,
"boost:without_mpi": True,
"boost:without_serialization": True,
"boost:without_coroutine": True,
"boost:without_fiber": True,
"boost:without_context": True,
"boost:without_timer": True,
"boost:without_date_time": True,
"boost:without_atomic": True,
"boost:without_graph_parallel": True,
"boost:without_python": True,
"boost:without_test": True,
"boost:without_type_erasure": True,
}
def requirements(self):
if self.settings.os == "Windows":
self.requires("boost/1.69.0@conan/stable")
else:
self.requires("boost/1.65.1@conan/stable")

View File

@ -12,6 +12,7 @@ export NUMCORES=`nproc` && if [ ! -n "$NUMCORES" ]; then export NUMCORES=`sysctl
echo Using ${NUMCORES} cores
# Run cmake in current working directory, but on source that is in the same directory as this script file
conan install .. --build=missing
cmake -DBUILD_TESTING=on -DCMAKE_EXPORT_COMPILE_COMMANDS=ON "${0%/*}"
# Filter all third party code from the compilation database

View File

@ -3,6 +3,7 @@
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_QUEUEMAP_H_
#include <memory>
#include <array>
#include <unordered_map>
#include <cassert>
#include <boost/optional.hpp>

View File

@ -89,8 +89,13 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS})
target_link_libraries(${PROJECT_NAME} PUBLIC spdlog cryptopp)
target_link_libraries(${PROJECT_NAME} PUBLIC CONAN_PKG::spdlog cryptopp CONAN_PKG::range-v3)
target_add_boost(${PROJECT_NAME} filesystem system thread chrono)
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})
if(MSVC)
# Required by range-v3, see its README.md
target_compile_options(${PROJECT_NAME} PUBLIC /experimental:preprocessor /permissive- /Zc:twoPhase-)
endif()

View File

@ -4,6 +4,7 @@
#include <vendor_cryptopp/hex.h>
#include <string>
#include <array>
#include <cstring>
#include "../assert/assert.h"

View File

@ -244,8 +244,7 @@ namespace cryfs_cli {
}
};
const bool missingBlockIsIntegrityViolation = config.configFile->config()->missingBlockIsIntegrityViolation();
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId,
options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId, options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
_sanityCheckFilesystem(_device->get());
auto initFilesystem = [&] (fspp::fuse::Fuse *fs){

View File

@ -81,12 +81,10 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
if (vm.count("missing-block-is-integrity-violation")) {
missingBlockIsIntegrityViolation = vm["missing-block-is-integrity-violation"].as<bool>();
}
if (vm.count("fuse-option")) {
auto options = vm["fuse-option"].as<vector<string>>();
for (const auto& option: options) {
if (option == "noatime" || option == "atime") {
LOG(WARN, "CryFS currently doesn't support noatime/atime flags. Using relatime behavior.");
}
fuseOptions.push_back("-o");
fuseOptions.push_back(option);
}

View File

@ -21,7 +21,8 @@ ProgramOptions::ProgramOptions(bf::path baseDir, bf::path mountDir, optional<bf:
_foreground(foreground),
_allowFilesystemUpgrade(allowFilesystemUpgrade), _allowReplacedFilesystem(allowReplacedFilesystem), _allowIntegrityViolations(allowIntegrityViolations),
_cipher(std::move(cipher)), _blocksizeBytes(std::move(blocksizeBytes)), _unmountAfterIdleMinutes(std::move(unmountAfterIdleMinutes)),
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)), _fuseOptions(std::move(fuseOptions)) {
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)),
_fuseOptions(std::move(fuseOptions)) {
if (!_mountDirIsDriveLetter) {
_mountDir = bf::absolute(std::move(_mountDir));
}

View File

@ -11,6 +11,7 @@ add_library(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils cryfs fspp-fuse)
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})
target_add_boost(${PROJECT_NAME} program_options)
add_executable(${PROJECT_NAME}_bin main_unmount.cpp)
set_target_properties(${PROJECT_NAME}_bin PROPERTIES OUTPUT_NAME cryfs-unmount)

View File

@ -51,7 +51,7 @@ set(LIB_SOURCES
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})
target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils fspp-interface blockstore blobstore gitversion)
target_add_boost(${PROJECT_NAME} program_options chrono) # TODO Check that dependent projects don't get boost added (use PRIVATE here)
target_add_boost(${PROJECT_NAME} chrono) # TODO Check that dependent projects don't get boost added (use PRIVATE here)
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})

View File

@ -22,8 +22,10 @@
#include "cryfs/impl/localstate/LocalStateDir.h"
#include <cryfs/impl/CryfsException.h>
using std::string;
//TODO Get rid of this in favor of exception hierarchy
using fspp::fuse::FuseErrnoException;

View File

@ -15,6 +15,7 @@
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/FileBlobRef.h"
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h"
namespace cryfs {
class CryDevice final: public fspp::Device {

View File

@ -9,7 +9,6 @@
#include "CryFile.h"
#include "CryOpenFile.h"
#include <cpp-utils/system/time.h>
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
//TODO Get rid of this in favor of exception hierarchy
using fspp::fuse::FuseErrnoException;
@ -72,7 +71,7 @@ vector<fspp::Dir::Entry> CryDir::children() {
device()->callFsActionCallbacks();
if (!isRootDir()) { // NOLINT (workaround https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82481 )
//TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function)
parent()->updateAccessTimestampForChild(blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
parent()->updateAccessTimestampForChild(blockId(), timestampUpdateBehavior());
}
vector<fspp::Dir::Entry> children;
children.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));

View File

@ -70,6 +70,10 @@ optional<DirBlobRef*> CryNode::grandparent() {
return _grandparent->get();
}
fspp::TimestampUpdateBehavior CryNode::timestampUpdateBehavior() const {
return _device->getContext().timestampUpdateBehavior();
}
void CryNode::rename(const bf::path &to) {
device()->callFsActionCallbacks();
if (_parent == none) {

View File

@ -16,6 +16,7 @@ public:
// TODO grandparent is only needed to set the timestamps of the parent directory on rename and remove. Delete grandparent parameter once we store timestamps in the blob itself instead of in the directory listing.
CryNode(CryDevice *device, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> grandparent, const blockstore::BlockId &blockId);
void access(int mask) const override;
stat_info stat() const override;
void chmod(fspp::mode_t mode) override;
@ -37,6 +38,7 @@ protected:
std::shared_ptr<const parallelaccessfsblobstore::DirBlobRef> parent() const;
std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent();
boost::optional<parallelaccessfsblobstore::DirBlobRef*> grandparent();
fspp::TimestampUpdateBehavior timestampUpdateBehavior() const;
virtual fspp::Dir::EntryType getType() const = 0;

View File

@ -43,7 +43,7 @@ void CryOpenFile::truncate(fspp::num_bytes_t size) const {
fspp::num_bytes_t CryOpenFile::read(void *buf, fspp::num_bytes_t count, fspp::num_bytes_t offset) const {
_device->callFsActionCallbacks();
_parent->updateAccessTimestampForChild(_fileBlob->blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
_parent->updateAccessTimestampForChild(_fileBlob->blockId(), timestampUpdateBehavior());
return _fileBlob->read(buf, offset, count);
}
@ -64,4 +64,8 @@ void CryOpenFile::fdatasync() {
_fileBlob->flush();
}
fspp::TimestampUpdateBehavior CryOpenFile::timestampUpdateBehavior() const {
return _device->getContext().timestampUpdateBehavior();
}
}

View File

@ -21,6 +21,7 @@ public:
void flush() override;
void fsync() override;
void fdatasync() override;
fspp::TimestampUpdateBehavior timestampUpdateBehavior() const;
private:
const CryDevice *_device;

View File

@ -4,7 +4,6 @@
#include "CryDevice.h"
#include "CrySymlink.h"
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h"
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
//TODO Get rid of this in favor of exception hierarchy
@ -43,7 +42,7 @@ fspp::Dir::EntryType CrySymlink::getType() const {
bf::path CrySymlink::target() {
device()->callFsActionCallbacks();
parent()->updateAccessTimestampForChild(blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
parent()->updateAccessTimestampForChild(blockId(), timestampUpdateBehavior());
auto blob = LoadBlob(); // NOLINT (workaround https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82481 )
return blob->target();
}

View File

@ -4,8 +4,8 @@
#include "FsBlobRef.h"
#include "cryfs/impl/filesystem/fsblobstore/DirBlob.h"
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
#include <fspp/fs_interface/Node.h>
#include <fspp/fs_interface/Context.h>
namespace cryfs {
namespace cachingfsblobstore {
@ -62,7 +62,7 @@ public:
return _base->statChildWithKnownSize(blockId, size);
}
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
return _base->updateAccessTimestampForChild(blockId, timestampUpdateBehavior);
}

View File

@ -173,7 +173,7 @@ fspp::Node::stat_info DirBlob::statChildWithKnownSize(const BlockId &blockId, fs
return result;
}
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
std::unique_lock<std::mutex> lock(_entriesAndChangedMutex);
if (_entries.updateAccessTimestampForChild(blockId, timestampUpdateBehavior)) {
_changed = true;

View File

@ -61,7 +61,7 @@ namespace cryfs {
fspp::Node::stat_info statChildWithKnownSize(const blockstore::BlockId &blockId, fspp::num_bytes_t size) const;
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior);
void updateModificationTimestampForChild(const blockstore::BlockId &blockId);

View File

@ -228,22 +228,30 @@ void DirEntryList::setAccessTimes(const blockstore::BlockId &blockId, timespec l
found->setLastModificationTime(lastModificationTime);
}
bool DirEntryList::updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {
ASSERT(timestampUpdateBehavior == TimestampUpdateBehavior::RELATIME, "Currently only relatime supported");
bool DirEntryList::updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
auto found = _findById(blockId);
const timespec lastAccessTime = found->lastAccessTime();
const timespec lastModificationTime = found->lastModificationTime();
const timespec now = cpputils::time::now();
const timespec yesterday {
/*.tv_sec = */ now.tv_sec - 60*60*24,
/*.tv_nsec = */ now.tv_nsec
};
bool changed = false;
if (lastAccessTime < lastModificationTime || lastAccessTime < yesterday) {
switch (found->type()) {
case fspp::Dir::EntryType::FILE:
// fallthrough
case fspp::Dir::EntryType::SYMLINK:
if (timestampUpdateBehavior->shouldUpdateATimeOnFileRead(lastAccessTime, lastModificationTime, now)) {
found->setLastAccessTime(now);
changed = true;
return true;
}
return changed;
return false;
case fspp::Dir::EntryType::DIR:
if (timestampUpdateBehavior->shouldUpdateATimeOnDirectoryRead(lastAccessTime, lastModificationTime, now)) {
found->setLastAccessTime(now);
return true;
}
return false;
}
throw std::logic_error("Unhandled case");
}
void DirEntryList::updateModificationTimestampForChild(const blockstore::BlockId &blockId) {

View File

@ -3,10 +3,10 @@
#define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRYLIST_H
#include <cpp-utils/data/Data.h>
#include <fspp/fs_interface/Context.h>
#include "DirEntry.h"
#include <vector>
#include <string>
#include "TimestampUpdateBehavior.h"
//TODO Address elements by name instead of by blockId when accessing them. Who knows whether there is two hard links for the same blob.
@ -40,7 +40,7 @@ namespace cryfs {
void setMode(const blockstore::BlockId &blockId, fspp::mode_t mode);
bool setUidGid(const blockstore::BlockId &blockId, fspp::uid_t uid, fspp::gid_t gid);
void setAccessTimes(const blockstore::BlockId &blockId, timespec lastAccessTime, timespec lastModificationTime);
bool updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);
bool updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior);
void updateModificationTimestampForChild(const blockstore::BlockId &blockId);
private:

View File

@ -1,17 +0,0 @@
#pragma once
#ifndef CRYFS_TIMESTAMPUPDATEBEHAVIOR_H
#define CRYFS_TIMESTAMPUPDATEBEHAVIOR_H
namespace cryfs {
namespace fsblobstore {
enum class TimestampUpdateBehavior : uint8_t {
// currently only relatime supported
RELATIME
};
}
}
#endif

View File

@ -4,7 +4,6 @@
#include "FsBlobRef.h"
#include "cryfs/impl/filesystem/cachingfsblobstore/DirBlobRef.h"
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
#include <fspp/fs_interface/Node.h>
namespace cryfs {
@ -58,7 +57,7 @@ public:
return _base->statChildWithKnownSize(blockId, size);
}
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
return _base->updateAccessTimestampForChild(blockId, timestampUpdateBehavior);
}

View File

@ -7,7 +7,9 @@ set(SOURCES
Node.cpp
OpenFile.cpp
Symlink.cpp
Types.cpp)
Types.cpp
Context.cpp
)
add_library(${PROJECT_NAME} STATIC ${SOURCES})

View File

@ -0,0 +1 @@
#include "Context.h"

View File

@ -0,0 +1,128 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_CONTEXT_H_
#define MESSMER_FSPP_FSINTERFACE_CONTEXT_H_
#include <cstdint>
#include <memory>
#include <cpp-utils/system/time.h>
namespace fspp {
namespace detail {
class TimestampUpdateBehaviorBase {
public:
virtual bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const = 0;
virtual bool shouldUpdateATimeOnDirectoryRead(timespec oldATime, timespec oldMTime, timespec newATime) const = 0;
};
}
// Defines how atime timestamps of files and directories are accessed on read accesses
// (e.g. atime, strictatime, relatime, nodiratime)
using TimestampUpdateBehavior = std::shared_ptr<detail::TimestampUpdateBehaviorBase>;
// atime attribute (of both files and directories) is updated only during write access.
inline TimestampUpdateBehavior noatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// This causes the atime attribute to update with every file access. (accessing file data, not just the metadata/attributes)
inline TimestampUpdateBehavior strictatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return true;
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return true;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// This option causes the atime attribute to update only if the previous atime is older than mtime or ctime, or the previous atime is over 24 hours old.
inline TimestampUpdateBehavior relatime() {
// This option causes the atime attribute to update only if the previous atime is older than mtime or ctime, or the previous atime is over 24 hours old.
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
const timespec yesterday {
/*.tv_sec = */ newATime.tv_sec - 60*60*24,
/*.tv_nsec = */ newATime.tv_nsec
};
return oldATime < oldMTime || oldATime < yesterday;
}
bool shouldUpdateATimeOnDirectoryRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
return shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// atime of directories is updated only during write access, can be combined with relatime. atime of files follows the relatime rules.
inline TimestampUpdateBehavior nodiratime_relatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
return relatime()->shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// atime of directories is updated only during write access, can be combined with relatime. atime of files follows the strictatime rules.
inline TimestampUpdateBehavior nodiratime_strictatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
return strictatime()->shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
class Context final {
public:
explicit Context(TimestampUpdateBehavior timestampUpdateBehavior)
: _timestampUpdateBehavior(std::move(timestampUpdateBehavior)) {}
const TimestampUpdateBehavior& timestampUpdateBehavior() const {
return _timestampUpdateBehavior;
}
void setTimestampUpdateBehavior(TimestampUpdateBehavior value) {
_timestampUpdateBehavior = std::move(value);
}
private:
TimestampUpdateBehavior _timestampUpdateBehavior;
};
}
#endif

View File

@ -5,6 +5,8 @@
#include <boost/filesystem.hpp>
#include <cpp-utils/pointer/unique_ref.h>
#include "Types.h"
#include "Context.h"
#include <boost/optional.hpp>
namespace fspp {
class Node;
@ -28,6 +30,19 @@ public:
virtual boost::optional<cpputils::unique_ref<Dir>> LoadDir(const boost::filesystem::path &path) = 0;
virtual boost::optional<cpputils::unique_ref<Symlink>> LoadSymlink(const boost::filesystem::path &path) = 0;
const Context& getContext() const {
ASSERT(_context != boost::none, "Tried to call getContext() but file system isn't running yet.");
return *_context;
}
// called by fspp system on file system init. Don't call this manually.
// TODO Is there a better way to do this?
void setContext(Context&& context) {
_context = std::move(context);
}
private:
boost::optional<Context> _context;
};
}

View File

@ -8,14 +8,19 @@ template<class ConcreteFileSystemTestFixture>
class FsppDeviceTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
public:
void Test_Load_While_Loaded() {
auto operation = [this] {
auto node = this->CreateNode("/mynode");
auto operation = [this] () {
return [this] {
this->device->Load("/mynode");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Load_While_Not_Loaded() {
this->testBuilder().withAnyAtimeConfig([&] {
fspp::Node::stat_info oldStat{};
{
auto node = this->CreateNode("/mynode");
@ -32,6 +37,7 @@ public:
EXPECT_EQ(oldStat.atime, newStat.atime);
EXPECT_EQ(oldStat.mtime, newStat.mtime);
EXPECT_EQ(oldStat.ctime, newStat.ctime);
});
}
};

View File

@ -11,23 +11,32 @@ public:
TYPED_TEST_SUITE_P(FsppDirTest_Timestamps);
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto operation = [&dir] () {
return [dir = std::move(dir)] {
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto operation = [&dir] () {
return [dir = std::move(dir)] {
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}*/
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile) {
this->testBuilder().withAnyAtimeConfig([&] {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
@ -36,26 +45,37 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile)
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child);
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, createDir) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto operation = [&dir] () {
return [dir = std::move(dir)] {
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
TYPED_TEST_P(FsppDirTest_Timestamps, createDir_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto operation = [&dir] () {
return [dir = std::move(dir)] {
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) {
this->testBuilder().withAnyAtimeConfig([&] {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
@ -64,26 +84,37 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) {
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child);
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto operation = [&dir] () {
return [dir = std::move(dir)] {
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto operation = [&dir] () {
return [dir = std::move(dir)] {
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) {
this->testBuilder().withAnyAtimeConfig([&] {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
@ -92,144 +123,389 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) {
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child);
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, children_empty) {
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_empty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
this->setModificationTimestampLaterThanAccessTimestamp("/mydir"); // to make sure that even in relatime behavior, the read access below changes the access timestamp
auto operation = [&dir] () {
this->setAtimeOlderThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_empty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
this->setAtimeNewerThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_empty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
this->setAtimeNewerThanMtimeButBeforeYesterday("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
TYPED_TEST_P(FsppDirTest_Timestamps, children_empty_inRootDir) {
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_empty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto operation = [&dir] () {
this->setAtimeOlderThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}*/
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty) {
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_empty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
this->setAtimeNewerThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_empty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
this->setAtimeNewerThanMtimeButBeforeYesterday("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
*/
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_nonempty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [&dir] () {
this->setAtimeOlderThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_nonempty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtimeButBeforeYesterday("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty_inRootDir) {
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_nonempty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [&dir] () {
this->setAtimeOlderThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}*/
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_nonempty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtimeButBeforeYesterday("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
*/
template<class ConcreteFileSystemTestFixture>
class FsppDirTest_Timestamps_Entries: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
public:
void Test_deleteChild() {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
auto operation = [&child]() {
return [child = std::move(child)] {
child->remove();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
void Test_deleteChild_inRootDir() {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto child = this->CreateNode("/childname");
auto operation = [&child] () {
auto child = this->CreateNode("/mydir/childname");
return [child = std::move(child)] {
child->remove();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
void Test_renameChild() {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
auto operation = [&child]() {
return [child = std::move(child)] {
child->rename("/mydir/mychild");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&]{
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
void Test_renameChild_inRootDir() {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto child = this->CreateNode("/childname");
auto operation = [&child] () {
child->rename("/mydir/mychild");
return [child = std::move(child)] {
child->rename("/mychild");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
};
this->testBuilder().withAnyAtimeConfig([&]{
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
void Test_moveChildIn() {
auto operation = [this] {
auto sourceDir = this->CreateDir("/sourcedir");
auto child = this->CreateNode("/sourcedir/childname");
auto dir = this->CreateDir("/mydir");
auto operation = [&child]() {
return [child = std::move(child)] {
child->rename("/mydir/mychild");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
void Test_moveChildIn_inRootDir() {
auto operation = [this] {
auto sourceDir = this->CreateDir("/sourcedir");
auto child = this->CreateNode("/sourcedir/childname");
auto dir = this->LoadDir("/");
auto operation = [&child] () {
return [child = std::move(child)] {
child->rename("/mychild");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
void Test_moveChildOut() {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
this->CreateDir("/targetdir");
auto operation = [&child]() {
return [child = std::move(child)] {
child->rename("/targetdir/mychild");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
/* TODO Re-enable this test once the root dir handles timestamps correctly
void Test_moveChildOut_inRootDir() {
auto operation = [this] {
auto dir = this->LoadDir("/");
auto child = this->CreateNode("/childname");
this->CreateDir("/targetdir");
auto operation = [&child] () {
return [child = std::move(child)] {
child->rename("/targetdir/mychild");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
};
REGISTER_TYPED_TEST_SUITE_P(FsppDirTest_Timestamps,
@ -239,8 +515,12 @@ REGISTER_TYPED_TEST_SUITE_P(FsppDirTest_Timestamps,
createDir_TimestampsOfCreatedDir,
createSymlink,
createSymlink_TimestampsOfCreatedSymlink,
children_empty,
children_nonempty
givenAtimeNewerThanMtime_children_empty,
givenAtimeOlderThanMtime_children_empty,
givenAtimeNewerThanMtimeButBeforeYesterday_children_empty,
givenAtimeNewerThanMtime_children_nonempty,
givenAtimeOlderThanMtime_children_nonempty,
givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty
);
REGISTER_NODE_TEST_SUITE(FsppDirTest_Timestamps_Entries,

View File

@ -17,75 +17,111 @@ public:
TYPED_TEST_SUITE_P(FsppFileTest_Timestamps);
TYPED_TEST_P(FsppFileTest_Timestamps, open_nomode) {
auto operation = [this] {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
return [file = std::move(file)] {
file->open(fspp::openflags_t(0));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, open_rdonly) {
auto operation = [this] {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
return [file = std::move(file)] {
file->open(fspp::openflags_t::RDONLY());
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, open_wronly) {
auto operation = [this] {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
return [file = std::move(file)] {
file->open(fspp::openflags_t::WRONLY());
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, open_rdwr) {
auto operation = [this] {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
return [file = std::move(file)] {
file->open(fspp::openflags_t::RDWR());
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_empty_to_empty) {
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&file] () {
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(0));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_empty_to_nonempty) {
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&file] () {
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(10));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_empty) {
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&file] () {
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(0));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_nonempty_shrink) {
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&file] () {
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(5));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_nonempty_grow) {
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&file] () {
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(20));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
REGISTER_TYPED_TEST_SUITE_P(FsppFileTest_Timestamps,

View File

@ -13,64 +13,71 @@ class FsppNodeTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture
public:
void Test_Create() {
this->testBuilder().withAnyAtimeConfig([&] {
timespec lowerBound = cpputils::time::now();
auto node = this->CreateNode("/mynode");
timespec upperBound = cpputils::time::now();
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
});
}
void Test_Stat() {
auto operation = [this] {
auto node = this->CreateNode("/mynode");
auto operation = [&node] () {
return [node = std::move(node)] {
node->stat();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Chmod() {
auto operation = [this] {
auto node = this->CreateNode("/mynode");
fspp::mode_t mode = this->stat(*node).mode;
auto operation = [&node, mode] () {
return [mode, node = std::move(node)] {
node->chmod(mode);
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Chown() {
auto operation = [this] {
auto node = this->CreateNode("/mynode");
fspp::uid_t uid = this->stat(*node).uid;
fspp::gid_t gid = this->stat(*node).gid;
auto operation = [&node, uid, gid] () {
return [uid, gid, node = std::move(node)] {
node->chown(uid, gid);
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Access() {
auto operation = [this] {
auto node = this->CreateNode("/mynode");
auto operation = [&node] () {
return [node = std::move(node)] {
node->access(0);
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Error_TargetParentDirDoesntExist() {
auto operation = [this] {
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
try {
node->rename("/notexistingdir/newname");
EXPECT_TRUE(false); // expect rename to fail
@ -78,15 +85,17 @@ public:
EXPECT_EQ(ENOENT, e.getErrno()); //Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Error_TargetParentDirIsFile() {
auto operation = [this] {
auto node = this->CreateNode("/oldname");
this->CreateFile("/somefile");
auto operation = [&node] () {
return [node = std::move(node)] {
try {
node->rename("/somefile/newname");
EXPECT_TRUE(false); // expect rename to fail
@ -94,16 +103,18 @@ public:
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Error_RootDir() {
// TODO Re-enable this test once the root dir stores timestamps correctly
/*
auto operation = [this] {
auto root = this->Load("/");
auto operation = [&root] () {
return [root = std::move(root)] {
try {
root->rename("/newname");
EXPECT_TRUE(false); // expect throws
@ -111,162 +122,164 @@ public:
EXPECT_EQ(EBUSY, e.getErrno()); //Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
*/
}
void Test_Rename_InRoot() {
auto operation = [this] {
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_InNested() {
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/mydir/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/mydir/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/mydir/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_RootToNested_SameName() {
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/mydir/oldname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_RootToNested_NewName() {
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/mydir/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToRoot_SameName() {
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/oldname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToRoot_NewName() {
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToNested_SameName() {
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
auto node = this->CreateNode("/mydir1/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/mydir2/oldname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToNested_NewName() {
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
auto node = this->CreateNode("/mydir1/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/mydir2/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_ToItself() {
auto operation = [this] {
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/oldname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_Overwrite_InSameDir() {
auto operation = [this] {
auto node = this->CreateNode("/oldname");
this->CreateNode("/newname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_Overwrite_InDifferentDir() {
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateNode("/mydir2/newname");
auto node = this->CreateNode("/mydir1/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
node->rename("/mydir2/newname");
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_Overwrite_Error_DirWithFile_InSameDir() {
auto operation = [this] {
this->CreateFile("/oldname");
this->CreateDir("/newname");
auto node = this->Load("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
try {
node->rename("/newname");
EXPECT_TRUE(false); // expect rename to fail
@ -274,18 +287,20 @@ public:
EXPECT_EQ(EISDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Overwrite_Error_DirWithFile_InDifferentDir() {
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateFile("/mydir1/oldname");
this->CreateDir("/mydir2/newname");
auto node = this->Load("/mydir1/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
try {
node->rename("/mydir2/newname");
EXPECT_TRUE(false); // expect rename to fail
@ -293,16 +308,18 @@ public:
EXPECT_EQ(EISDIR, e.getErrno());//Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Overwrite_Error_FileWithDir_InSameDir() {
auto operation = [this] {
this->CreateDir("/oldname");
this->CreateFile("/newname");
auto node = this->Load("/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
try {
node->rename("/newname");
EXPECT_TRUE(false); // expect rename to fail
@ -310,18 +327,20 @@ public:
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Overwrite_Error_FileWithDir_InDifferentDir() {
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateDir("/mydir1/oldname");
this->CreateFile("/mydir2/newname");
auto node = this->Load("/mydir1/oldname");
auto operation = [&node] () {
return [node = std::move(node)] {
try {
node->rename("/mydir2/newname");
EXPECT_TRUE(false); // expect rename to fail
@ -329,16 +348,18 @@ public:
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
};
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Utimens() {
this->testBuilder().withAnyAtimeConfig([&] {
auto node = this->CreateNode("/mynode");
timespec atime = this->xSecondsAgo(100);
timespec mtime = this->xSecondsAgo(200);
auto operation = [&node, atime, mtime] () {
auto operation = [atime, mtime, &node] {
node->utimens(atime, mtime);
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
@ -346,6 +367,7 @@ public:
});
EXPECT_EQ(atime, this->stat(*node).atime);
EXPECT_EQ(mtime, this->stat(*node).mtime);
});
}
};

View File

@ -14,120 +14,343 @@ public:
auto file = this->CreateFile(path);
file->truncate(size);
auto openFile = file->open(fspp::openflags_t::RDWR());
assert(this->stat(*openFile).size == size);
assert(this->stat(*this->Load(path)).size == size);
ASSERT(this->stat(*openFile).size == size, "");
ASSERT(this->stat(*this->Load(path)).size == size, "");
return openFile;
}
void CreateFileWithSize(const boost::filesystem::path &path, fspp::num_bytes_t size) {
auto file = this->CreateFile(path);
file->truncate(size);
}
cpputils::unique_ref<fspp::OpenFile> OpenFile(const boost::filesystem::path &path, fspp::num_bytes_t size) {
auto file = this->LoadFile(path);
auto openFile = file->open(fspp::openflags_t::RDWR());
ASSERT(this->stat(*openFile).size == size, "");
ASSERT(this->stat(*this->Load(path)).size == size, "");
return openFile;
}
};
TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps);
TYPED_TEST_P(FsppOpenFileTest_Timestamps, stat) {
auto openFile = this->CreateAndOpenFile("/mynode");
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->stat();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFile("/mynode");
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_empty_to_empty) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(0));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_empty_to_nonempty) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(10));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_empty) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(0));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_nonempty_shrink) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(5));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_nonempty_grow) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(20));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, read_inbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_read_inbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtimeButBeforeYesterday("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
});
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, read_outofbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtime_read_inbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeOlderThanMtime_read_inbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeOlderThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_read_outofbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtimeButBeforeYesterday("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
});
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtime_read_outofbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeOlderThanMtime_read_outofbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeOlderThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, write_inbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, write_outofbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(2));
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, flush) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [] (fspp::OpenFile* openFile){
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [&openFile] () {
return [openFile] {
openFile->flush();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, fsync) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [] (fspp::OpenFile* openFile){
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [&openFile] () {
return [openFile] {
openFile->fsync();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, fdatasync) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [] (fspp::OpenFile* openFile){
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [&openFile] () {
return [openFile] {
openFile->fdatasync();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
};
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
REGISTER_TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps,
@ -137,8 +360,12 @@ REGISTER_TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps,
truncate_nonempty_to_empty,
truncate_nonempty_to_nonempty_shrink,
truncate_nonempty_to_nonempty_grow,
read_inbounds,
read_outofbounds,
givenAtimeNewerThanMtimeButBeforeYesterday_read_inbounds,
givenAtimeNewerThanMtime_read_inbounds,
givenAtimeOlderThanMtime_read_inbounds,
givenAtimeNewerThanMtimeButBeforeYesterday_read_outofbounds,
givenAtimeNewerThanMtime_read_outofbounds,
givenAtimeOlderThanMtime_read_outofbounds,
write_inbounds,
write_outofbounds,
flush,

View File

@ -10,17 +10,76 @@ public:
};
TYPED_TEST_SUITE_P(FsppSymlinkTest_Timestamps);
TYPED_TEST_P(FsppSymlinkTest_Timestamps, target) {
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_target) {
auto operation = [this] {
auto symlink = this->CreateSymlink("/mysymlink");
this->setModificationTimestampLaterThanAccessTimestamp("/mysymlink"); // to make sure that even in relatime behavior, the read access below changes the access timestamp
auto operation = [&symlink] () {
this->setAtimeNewerThanMtimeButBeforeYesterday("/mysymlink");
return [symlink = std::move(symlink)] {
symlink->target();
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
};
this->testBuilder()
.withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeOlderThanMtime_target) {
auto operation = [this] {
auto symlink = this->CreateSymlink("/mysymlink");
this->setAtimeOlderThanMtime("/mysymlink");
return [symlink = std::move(symlink)] {
symlink->target();
};
};
this->testBuilder()
.withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeNewerThanMtime_target) {
auto operation = [this] {
auto symlink = this->CreateSymlink("/mysymlink");
this->setAtimeNewerThanMtime("/mysymlink");
return [symlink = std::move(symlink)] {
symlink->target();
};
};
this->testBuilder()
.withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
REGISTER_TYPED_TEST_SUITE_P(FsppSymlinkTest_Timestamps,
target
givenAtimeNewerThanMtimeButBeforeYesterday_target,
givenAtimeNewerThanMtime_target,
givenAtimeOlderThanMtime_target
);
#endif

View File

@ -30,10 +30,20 @@ public:
"Given test fixture for instantiating the (type parameterized) FileSystemTest must inherit from FileSystemTestFixture"
);
FileSystemTest(): fixture(), device(fixture.createDevice()) {}
FileSystemTest(): fixture(nullptr), device(nullptr) {
resetFilesystem(fspp::Context{fspp::relatime()});
}
ConcreteFileSystemTestFixture fixture;
cpputils::unique_ref<fspp::Device> device;
void resetFilesystem(fspp::Context&& context) {
device = nullptr;
fixture = nullptr;
fixture = std::make_unique<ConcreteFileSystemTestFixture>();
device = fixture->createDevice();
device->setContext(std::move(context));
}
std::unique_ptr<ConcreteFileSystemTestFixture> fixture;
std::unique_ptr<fspp::Device> device;
static constexpr fspp::mode_t MODE_PUBLIC = fspp::mode_t()
.addUserReadFlag().addUserWriteFlag().addUserExecFlag()
@ -91,10 +101,36 @@ public:
EXPECT_NE(nullptr, dynamic_cast<const fspp::Symlink*>(node.get()));
}
void setModificationTimestampLaterThanAccessTimestamp(const boost::filesystem::path& path) {
void setAtimeOlderThanMtime(const boost::filesystem::path& path) {
auto node = device->Load(path).value();
auto st = node->stat();
st.mtime.tv_nsec = st.mtime.tv_nsec + 1;
st.atime.tv_nsec = st.mtime.tv_nsec - 1;
node->utimens(
st.atime,
st.mtime
);
}
void setAtimeNewerThanMtime(const boost::filesystem::path& path) {
auto node = device->Load(path).value();
auto st = node->stat();
st.atime.tv_nsec = st.mtime.tv_nsec + 1;
node->utimens(
st.atime,
st.mtime
);
}
void setAtimeNewerThanMtimeButBeforeYesterday(const boost::filesystem::path& path) {
auto node = device->Load(path).value();
auto st = node->stat();
const timespec now = cpputils::time::now();
const timespec before_yesterday {
/*.tv_sec = */ now.tv_sec - 60*60*24 - 1,
/*.tv_nsec = */ now.tv_nsec
};
st.atime = before_yesterday;
st.mtime.tv_nsec = st.atime.tv_nsec - 1;
node->utimens(
st.atime,
st.mtime

View File

@ -10,17 +10,22 @@
template<class ConcreteFileSystemTestFixture>
class TimestampTestUtils : public virtual FileSystemTest<ConcreteFileSystemTestFixture> {
public:
using TimestampUpdateBehavior = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
using TimestampUpdateExpectation = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
static TimestampUpdateBehavior ExpectUpdatesAccessTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateAccessTimestamp;
static TimestampUpdateBehavior ExpectUpdatesModificationTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateModificationTimestamp;
static TimestampUpdateBehavior ExpectUpdatesMetadataTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateMetadataTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateAnyTimestamps;
static TimestampUpdateExpectation ExpectUpdatesAccessTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateAccessTimestamp;
static TimestampUpdateExpectation ExpectUpdatesModificationTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateModificationTimestamp;
static TimestampUpdateExpectation ExpectUpdatesMetadataTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateMetadataTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateAnyTimestamps;
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<fspp::Node::stat_info()> statOld, std::function<fspp::Node::stat_info()> statNew, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
void setTimestampUpdateBehavior(fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
FileSystemTest<ConcreteFileSystemTestFixture>::device->setContext(fspp::Context { timestampUpdateBehavior });
}
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<fspp::Node::stat_info()> statOld, std::function<fspp::Node::stat_info()> statNew, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
auto oldStat = statOld();
ensureNodeTimestampsAreOld(oldStat);
timespec timeBeforeOperation = cpputils::time::now();
@ -32,26 +37,29 @@ public:
}
}
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const fspp::OpenFile &node, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const fspp::OpenFile &node, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(
[this, &node](){return this->stat(node);},
[this, &node](){return this->stat(node);},
operation,
std::forward<Operation>(operation),
behaviorChecks
);
}
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &oldPath, const boost::filesystem::path &newPath, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &oldPath, const boost::filesystem::path &newPath, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(
[this, oldPath](){return this->stat(*this->Load(oldPath));},
[this, newPath](){return this->stat(*this->Load(newPath));},
operation,
std::forward<Operation>(operation),
behaviorChecks
);
}
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &path, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(path, path, operation, behaviorChecks);
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &path, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(path, path, std::forward<Operation>(operation), behaviorChecks);
}
void EXPECT_ACCESS_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) {
@ -90,6 +98,55 @@ public:
EXPECT_LT(nodeStat.ctime, cpputils::time::now());
}
class TestBuilder final {
public:
explicit TestBuilder(TimestampTestUtils* fixture): _fixture(fixture) {}
const TestBuilder& withNoatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::noatime()});
expectations();
return *this;
}
const TestBuilder& withStrictatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::strictatime()});
expectations();
return *this;
}
const TestBuilder& withRelatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::relatime()});
expectations();
return *this;
}
const TestBuilder& withNodiratimeRelatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::nodiratime_relatime()});
expectations();
return *this;
}
const TestBuilder& withNodiratimeStrictatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::nodiratime_strictatime()});
expectations();
return *this;
}
const TestBuilder& withAnyAtimeConfig(std::function<void()> expectations) const {
return withNoatime(expectations)
.withStrictatime(expectations)
.withRelatime(expectations)
.withNodiratimeRelatime(expectations)
.withNodiratimeStrictatime(expectations);
}
private:
TimestampTestUtils* _fixture;
};
TestBuilder testBuilder() {
return TestBuilder(this);
}
private:
void waitUntilClockProgresses() {
@ -101,7 +158,7 @@ private:
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation);
UNUSED(timeBeforeOperation);
@ -111,7 +168,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation);
@ -119,7 +176,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.mtime);
@ -127,7 +184,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation);
@ -135,7 +192,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.ctime);
@ -143,7 +200,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation);
@ -151,7 +208,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
ExpectDoesntUpdateAccessTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);
ExpectDoesntUpdateModificationTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);

View File

@ -39,6 +39,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CMAKE_FIND_FRAMEWORK LAST)
find_library_with_path(FUSE "osxfuse" FUSE_LIB_PATH)
target_link_libraries(${PROJECT_NAME} PUBLIC ${FUSE})
target_include_directories(${PROJECT_NAME} PUBLIC /usr/local/include/osxfuse/)
else() # Linux
find_library_with_path(FUSE "fuse" FUSE_LIB_PATH)
target_link_libraries(${PROJECT_NAME} PUBLIC ${FUSE})

View File

@ -6,6 +6,7 @@
#include <cpp-utils/pointer/unique_ref.h>
#include <sys/stat.h>
#include "../fs_interface/Dir.h"
#include "../fs_interface/Context.h"
#if defined(_MSC_VER)
#include <fuse/fuse.h>
#else
@ -19,6 +20,8 @@ class Filesystem {
public:
virtual ~Filesystem() {}
virtual void setContext(Context&& context) = 0;
//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;

View File

@ -13,6 +13,10 @@
#include "InvalidFilesystem.h"
#include <codecvt>
#include <range/v3/view/split.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/filter.hpp>
#if defined(_MSC_VER)
#include <codecvt>
#include <dokan/dokan.h>
@ -265,19 +269,19 @@ void Fuse::_logUnknownException() {
LOG(ERR, "Unknown exception thrown");
}
void Fuse::runInForeground(const bf::path &mountdir, const vector<string> &fuseOptions) {
vector<string> realFuseOptions = fuseOptions;
void Fuse::runInForeground(const bf::path &mountdir, vector<string> fuseOptions) {
vector<string> realFuseOptions = std::move(fuseOptions);
if (std::find(realFuseOptions.begin(), realFuseOptions.end(), "-f") == realFuseOptions.end()) {
realFuseOptions.push_back("-f");
}
_run(mountdir, realFuseOptions);
_run(mountdir, std::move(realFuseOptions));
}
void Fuse::runInBackground(const bf::path &mountdir, const vector<string> &fuseOptions) {
vector<string> realFuseOptions = fuseOptions;
void Fuse::runInBackground(const bf::path &mountdir, vector<string> fuseOptions) {
vector<string> realFuseOptions = std::move(fuseOptions);
_removeAndWarnIfExists(&realFuseOptions, "-f");
_removeAndWarnIfExists(&realFuseOptions, "-d");
_run(mountdir, realFuseOptions);
_run(mountdir, std::move(realFuseOptions));
}
void Fuse::_removeAndWarnIfExists(vector<string> *fuseOptions, const std::string &option) {
@ -291,7 +295,52 @@ void Fuse::_removeAndWarnIfExists(vector<string> *fuseOptions, const std::string
}
}
void Fuse::_run(const bf::path &mountdir, const vector<string> &fuseOptions) {
namespace {
void extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(string* csv_options, vector<string>* result) {
const auto is_fuse_supported_atime_flag = [] (const std::string& flag) {
constexpr std::array<const char*, 2> flags = {"noatime", "atime"};
return flags.end() != std::find(flags.begin(), flags.end(), flag);
};
const auto is_fuse_unsupported_atime_flag = [] (const std::string& flag) {
constexpr std::array<const char*, 3> flags = {"strictatime", "relatime", "nodiratime"};
return flags.end() != std::find(flags.begin(), flags.end(), flag);
};
*csv_options = *csv_options
| ranges::view::split(',')
| ranges::view::filter(
[&] (const std::string& elem) {
if (is_fuse_unsupported_atime_flag(elem)) {
result->push_back(elem);
return false;
}
if (is_fuse_supported_atime_flag(elem)) {
result->push_back(elem);
}
return true;
})
| ranges::view::join(',')
| ranges::to<string>();
}
// Return a list of all atime options (e.g. atime, noatime, relatime, strictatime, nodiratime) that occur in the
// fuseOptions input. They must be preceded by a '-o', i.e. {..., '-o', 'noatime', ...} and multiple ones can be
// csv-concatenated, i.e. {..., '-o', 'atime,nodiratime', ...}.
// Also, this function removes all of these atime options that are unknown to libfuse (i.e. all except atime and noatime)
// from the input fuseOptions so we can pass it on to libfuse without crashing.
vector<string> extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(vector<string>* fuseOptions) {
vector<string> result;
bool lastOptionWasDashO = false;
for (string& option : *fuseOptions) {
if (lastOptionWasDashO) {
extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(&option, &result);
}
lastOptionWasDashO = (option == "-o");
}
return result;
}
}
void Fuse::_run(const bf::path &mountdir, vector<string> fuseOptions) {
#if defined(__GLIBC__)|| defined(__APPLE__) || defined(_MSC_VER)
// Avoid encoding errors for non-utf8 characters, see https://github.com/cryfs/cryfs/issues/247
// this is ifdef'd out for non-glibc linux, because musl doesn't handle this correctly.
@ -302,11 +351,66 @@ void Fuse::_run(const bf::path &mountdir, const vector<string> &fuseOptions) {
ASSERT(_argv.size() == 0, "Filesystem already started");
vector<string> atimeOptions = extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(&fuseOptions);
_createContext(atimeOptions);
_argv = _build_argv(mountdir, fuseOptions);
fuse_main(_argv.size(), _argv.data(), operations(), this);
}
void Fuse::_createContext(const vector<string> &fuseOptions) {
const bool has_atime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "atime");
const bool has_noatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "noatime");
const bool has_relatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "relatime");
const bool has_strictatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "strictatime");
const bool has_nodiratime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "nodiratime");
// Default is RELATIME
_context = Context(relatime());
if (has_noatime_flag) {
ASSERT(!has_atime_flag, "Cannot have both, noatime and atime flags set.");
ASSERT(!has_relatime_flag, "Cannot have both, noatime and relatime flags set.");
ASSERT(!has_strictatime_flag, "Cannot have both, noatime and strictatime flags set.");
// note: can have nodiratime flag set but that is ignored because it is already included in the noatime policy.
_context->setTimestampUpdateBehavior(noatime());
} else if (has_relatime_flag) {
// note: can have atime and relatime both set, they're identical
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above.");
ASSERT(!has_strictatime_flag, "Cannot have both, relatime and strictatime flags set.");
if (has_nodiratime_flag) {
_context->setTimestampUpdateBehavior(nodiratime_relatime());
} else {
_context->setTimestampUpdateBehavior(relatime());
}
} else if (has_atime_flag) {
// note: can have atime and relatime both set, they're identical
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_strictatime_flag, "Cannot have both, atime and strictatime flags set.");
if (has_nodiratime_flag) {
_context->setTimestampUpdateBehavior(nodiratime_relatime());
} else {
_context->setTimestampUpdateBehavior(relatime());
}
} else if (has_strictatime_flag) {
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_atime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_relatime_flag, "This shouldn't happen, or we would have hit a case above");
if (has_nodiratime_flag) {
_context->setTimestampUpdateBehavior(nodiratime_strictatime());
} else {
_context->setTimestampUpdateBehavior(strictatime());
}
} else if (has_nodiratime_flag) {
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_atime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_relatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_strictatime_flag, "This shouldn't happen, or we would have hit a case above");
_context->setTimestampUpdateBehavior(nodiratime_relatime()); // use relatime by default
}
}
vector<char *> Fuse::_build_argv(const bf::path &mountdir, const vector<string> &fuseOptions) {
vector<char *> argv;
argv.reserve(6 + fuseOptions.size()); // fuseOptions + executable name + mountdir + 2x fuse options (subtype, fsname), each taking 2 entries ("-o", "key=value").
@ -1100,6 +1204,9 @@ void Fuse::init(fuse_conn_info *conn) {
ThreadNameForDebugging _threadName("init");
_fs = _init(this);
ASSERT(_context != boost::none, "Context should have been initialized in Fuse::run() but somehow didn't");
_fs->setContext(fspp::Context { *_context });
LOG(INFO, "Filesystem started.");
_running = true;

View File

@ -12,6 +12,7 @@
#include <cpp-utils/macros.h>
#include <atomic>
#include "stat_compatibility.h"
#include <fspp/fs_interface/Context.h>
namespace fspp {
class Device;
@ -24,8 +25,8 @@ public:
explicit Fuse(std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname);
~Fuse();
void runInBackground(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void runInForeground(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void runInBackground(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
void runInForeground(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
bool running() const;
void stop();
@ -67,11 +68,12 @@ private:
static void _logUnknownException();
static char *_create_c_string(const std::string &str);
static void _removeAndWarnIfExists(std::vector<std::string> *fuseOptions, const std::string &option);
void _run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void _run(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
static bool _has_option(const std::vector<char *> &vec, const std::string &key);
static bool _has_entry_with_prefix(const std::string &prefix, const std::vector<char *> &vec);
std::vector<char *> _build_argv(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void _add_fuse_option_if_not_exists(std::vector<char *> *argv, const std::string &key, const std::string &value);
void _createContext(const std::vector<std::string> &fuseOptions);
std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> _init;
std::function<void()> _onMounted;
@ -81,6 +83,7 @@ private:
std::atomic<bool> _running;
std::string _fstype;
boost::optional<std::string> _fsname;
boost::optional<Context> _context;
DISALLOW_COPY_AND_ASSIGN(Fuse);
};

View File

@ -7,6 +7,10 @@
namespace fspp {
namespace fuse {
class InvalidFilesystem final : public Filesystem {
void setContext(Context&&) override {
throw std::logic_error("Filesystem not initialized yet");
}
int createAndOpenFile(const boost::filesystem::path &, ::mode_t , ::uid_t , ::gid_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}

View File

@ -3,14 +3,6 @@
#define MESSMER_FSPP_FUSE_PARAMS_H_
#define FUSE_USE_VERSION 26
#if defined(__linux__) || defined(__FreeBSD__)
#include <fuse.h>
#elif __APPLE__
#include <osxfuse/fuse.h>
#elif defined(_MSC_VER)
#include <fuse.h> // Dokany fuse
#else
#error System not supported
#endif
#endif

View File

@ -91,6 +91,10 @@ FilesystemImpl::~FilesystemImpl() {
#endif
}
void FilesystemImpl::setContext(Context&& context) {
_device->setContext(std::move(context));
}
unique_ref<File> FilesystemImpl::LoadFile(const bf::path &path) {
PROFILE(_loadFileNanosec);
auto file = _device->LoadFile(path);

View File

@ -24,6 +24,8 @@ public:
explicit FilesystemImpl(cpputils::unique_ref<Device> device);
virtual ~FilesystemImpl();
void setContext(Context&& context) override;
int openFile(const boost::filesystem::path &path, int flags) override;
void flush(int descriptor) override;
void closeFile(int descriptor) override;

View File

@ -128,7 +128,7 @@ TEST(BacktraceTest, ShowBacktraceOnSigIll) {
#else
TEST(BacktraceTest, ContainsBacktrace) {
string backtrace = cpputils::backtrace();
EXPECT_THAT(backtrace, HasSubstr("#1"));
EXPECT_THAT(backtrace, HasSubstr("#0"));
}
TEST(BacktraceTest, ShowBacktraceOnNullptrAccess) {
auto output = call_process_exiting_with_nullptr_violation();

View File

@ -137,4 +137,3 @@ TEST_F(ProgramOptionsTest, SomeFuseOptions) {
//Fuse should have the mount dir as first parameter
EXPECT_VECTOR_EQ({"-f", "--longoption"}, testobj.fuseOptions());
}

View File

@ -65,8 +65,10 @@ auto failOnIntegrityViolation() {
TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
}
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
auto rootDir = dev.LoadDir(bf::path("/"));
rootDir.value()->children();
}
@ -74,10 +76,12 @@ TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
TEST_F(CryFsTest, LoadingFilesystemDoesntModifyConfigFile) {
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
}
Data configAfterCreating = Data::LoadFromFile(config.path()).value();
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
}
Data configAfterLoading = Data::LoadFromFile(config.path()).value();
EXPECT_EQ(configAfterCreating, configAfterLoading);

View File

@ -20,6 +20,7 @@ public:
CryTestBase(): _tempLocalStateDir(), _localStateDir(_tempLocalStateDir.path()), _configFile(false), _device(nullptr) {
auto fakeBlockStore = cpputils::make_unique_ref<blockstore::inmemory::InMemoryBlockStore2>();
_device = std::make_unique<cryfs::CryDevice>(configFile(), std::move(fakeBlockStore), _localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
_device->setContext(fspp::Context { fspp::relatime() });
}
std::shared_ptr<cryfs::CryConfigFile> configFile() {

View File

@ -69,6 +69,7 @@ set(SOURCES
fuse/access/FuseAccessModeTest.cpp
fuse/access/FuseAccessErrorTest.cpp
fuse/BasicFuseTest.cpp
fuse/TimestampTest.cpp
fuse/rmdir/testutils/FuseRmdirTest.cpp
fuse/rmdir/FuseRmdirErrorTest.cpp
fuse/rmdir/FuseRmdirDirnameTest.cpp

View File

@ -0,0 +1,313 @@
#include "../testutils/FuseTest.h"
#include <gmock/gmock.h>
using namespace fspp::fuse;
typedef FuseTest FuseTimestampTest;
// Single flag
TEST_F(FuseTimestampTest, whenCalledWithoutAnyAtimeFlag_thenHasRelatimeBehavior) {
auto fs = TestFS({});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeFlag_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeFlag_thenHasStrictatimeBehavior) {
auto fs = TestFS({"-o", "strictatime"});
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeFlag_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeFlag_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeFlag_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
// Flag combinations
TEST_F(FuseTimestampTest, whenCalledWithAtimeAtimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime,atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeAtimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime", "-o", "atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNoatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime,noatime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNoatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime", "-o", "noatime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeRelatimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime,relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeRelatimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime", "-o", "relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeStrictatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime,strictatime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeStrictatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime", "-o", "strictatime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "atime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "atime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeAtime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime,atime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeAtime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime", "-o", "atime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNoatimeFlag_withCsv_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime,noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNoatimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime", "-o", "noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeRelatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime,relatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeRelatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime", "-o", "relatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeStrictatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime,strictatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeStrictatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime", "-o", "strictatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNodiratimeFlag_withCsv_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime,nodiratime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNodiratimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime", "-o", "nodiratime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeAtimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime,atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeAtimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime", "-o", "atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNoatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime,noatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNoatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime", "-o", "noatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeRelatimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime,relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeRelatimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime", "-o", "relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeStrictatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime,strictatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeStrictatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime", "-o", "strictatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeAtimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime,atime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeAtimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime", "-o", "atime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNoatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime,noatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNoatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime", "-o", "noatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeRelatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime,relatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeRelatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime", "-o", "relatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeStrictatimeFlag_withCsv_thenHasStrictatimeBehavior) {
auto fs = TestFS({"-o", "strictatime,strictatime"});
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeStrictatimeFlag_withSeparateFlags_thenHasStrictatimeBehavior) {
auto fs = TestFS({"-o", "strictatime", "-o", "strictatime"});
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "strictatime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "strictatime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeAtimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,atime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeAtimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "atime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNoatimeFlag_withCsv_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNoatimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeRelatimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,relatime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeRelatimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "relatime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeStrictatimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,strictatime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeStrictatimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "strictatime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}

View File

@ -19,7 +19,7 @@ using namespace fspp::fuse;
MockFilesystem::MockFilesystem() {}
MockFilesystem::~MockFilesystem() {}
FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()) {
FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()), _context(boost::none) {
auto defaultAction = Throw(FuseErrnoException(EIO));
ON_CALL(*fsimpl, openFile(_,_)).WillByDefault(defaultAction);
ON_CALL(*fsimpl, closeFile(_)).WillByDefault(defaultAction);
@ -48,18 +48,23 @@ FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()) {
ON_CALL(*fsimpl, readSymlink(_,_,_)).WillByDefault(defaultAction);
EXPECT_CALL(*fsimpl, access(_,_)).WillRepeatedly(Return());
ON_CALL(*fsimpl, setContext(_)).WillByDefault(Invoke([this] (fspp::Context context) {
_context = std::move(context);
}));
ReturnIsDirOnLstat("/");
}
unique_ref<FuseTest::TempTestFS> FuseTest::TestFS() {
return make_unique_ref<TempTestFS>(fsimpl);
unique_ref<FuseTest::TempTestFS> FuseTest::TestFS(const std::vector<std::string>& fuseOptions) {
return make_unique_ref<TempTestFS>(fsimpl, fuseOptions);
}
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl)
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl, const std::vector<std::string>& fuseOptions)
:_mountDir(),
_fuse([fsimpl] (Fuse*) {return fsimpl;}, []{}, "fusetest", boost::none), _fuse_thread(&_fuse) {
_fuse_thread.start(_mountDir.path(), {});
_fuse_thread.start(_mountDir.path(), fuseOptions);
}
FuseTest::TempTestFS::~TempTestFS() {

View File

@ -20,6 +20,7 @@ public:
MockFilesystem();
virtual ~MockFilesystem();
MOCK_METHOD(void, setContext, (fspp::Context&&), (override));
MOCK_METHOD(int, openFile, (const boost::filesystem::path&, int), (override));
MOCK_METHOD(void, closeFile, (int), (override));
MOCK_METHOD(void, lstat, (const boost::filesystem::path&, fspp::fuse::STAT*), (override));
@ -54,7 +55,7 @@ public:
class TempTestFS {
public:
TempTestFS(std::shared_ptr<MockFilesystem> fsimpl);
TempTestFS(std::shared_ptr<MockFilesystem> fsimpl, const std::vector<std::string>& fuseOptions = {});
virtual ~TempTestFS();
public:
const boost::filesystem::path &mountDir() const;
@ -64,10 +65,18 @@ public:
FuseThread _fuse_thread;
};
cpputils::unique_ref<TempTestFS> TestFS();
cpputils::unique_ref<TempTestFS> TestFS(const std::vector<std::string>& fuseOptions = {});
std::shared_ptr<MockFilesystem> fsimpl;
const fspp::Context& context() const {
ASSERT(_context != boost::none, "Context wasn't correctly initialized");
return *_context;
}
private:
boost::optional<fspp::Context> _context;
public:
//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<void(const boost::filesystem::path&, fspp::fuse::STAT*)> ReturnIsFile;

View File

@ -1,4 +1,2 @@
add_subdirectory(googletest)
add_subdirectory(spdlog)
add_subdirectory(cryptopp)
add_subdirectory(range-v3)

3
vendor/README vendored
View File

@ -1,7 +1,6 @@
This directory contains external projects, taken from the following locations:
googletest: https://github.com/google/googletest/tree/release-1.10.0
spdlog: https://github.com/gabime/spdlog/tree/v1.4.2/include/spdlog
- changed: https://github.com/google/googletest/pull/2514
cryptopp: https://github.com/weidai11/cryptopp/tree/CRYPTOPP_8_2_0
- changed: added CMakeLists.txt and cryptopp-config.cmake from https://github.com/noloader/cryptopp-cmake/tree/CRYPTOPP_8_2_0
- changed: commented out line including winapifamily.h in CMakeLists.txt
range-v3: https://github.com/ericniebler/range-v3/tree/0.9.1

View File

@ -1,19 +1,6 @@
#ifndef THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
#define THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
#undef GMOCK_PP_INTERNAL_USE_MSVC
#if defined(__clang__)
#define GMOCK_PP_INTERNAL_USE_MSVC 0
#elif defined(_MSC_VER)
// TODO(iserna): Also verify tradional versus comformant preprocessor.
static_assert(
_MSC_VER >= 1900,
"MSVC version not supported. There is support for MSVC 14.0 and above.");
#define GMOCK_PP_INTERNAL_USE_MSVC 1
#else
#define GMOCK_PP_INTERNAL_USE_MSVC 0
#endif
// Expands and concatenates the arguments. Constructed macros reevaluate.
#define GMOCK_PP_CAT(_1, _2) GMOCK_PP_INTERNAL_CAT(_1, _2)
@ -29,10 +16,6 @@ static_assert(
// Returns the only argument.
#define GMOCK_PP_IDENTITY(_1) _1
// MSVC preprocessor collapses __VA_ARGS__ in a single argument, we use a
// CAT-like directive to force correct evaluation. Each macro has its own.
#if GMOCK_PP_INTERNAL_USE_MSVC
// Evaluates to the number of arguments after expansion.
//
// #define PAIR x, y
@ -44,44 +27,28 @@ static_assert(
//
// Requires: the number of arguments after expansion is at most 15.
#define GMOCK_PP_NARG(...) \
GMOCK_PP_INTERNAL_NARG_CAT( \
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, \
8, 7, 6, 5, 4, 3, 2, 1), )
GMOCK_PP_INTERNAL_16TH( \
(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
// Returns 1 if the expansion of arguments has an unprotected comma. Otherwise
// returns 0. Requires no more than 15 unprotected commas.
#define GMOCK_PP_HAS_COMMA(...) \
GMOCK_PP_INTERNAL_HAS_COMMA_CAT( \
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 0), )
GMOCK_PP_INTERNAL_16TH( \
(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
// Returns the first argument.
#define GMOCK_PP_HEAD(...) \
GMOCK_PP_INTERNAL_HEAD_CAT(GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__), )
GMOCK_PP_INTERNAL_HEAD((__VA_ARGS__))
// Returns the tail. A variadic list of all arguments minus the first. Requires
// at least one argument.
#define GMOCK_PP_TAIL(...) \
GMOCK_PP_INTERNAL_TAIL_CAT(GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__), )
GMOCK_PP_INTERNAL_TAIL((__VA_ARGS__))
// Calls CAT(_Macro, NARG(__VA_ARGS__))(__VA_ARGS__)
#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT( \
GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__), )
#else // GMOCK_PP_INTERNAL_USE_MSVC
#define GMOCK_PP_NARG(...) \
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, \
7, 6, 5, 4, 3, 2, 1)
#define GMOCK_PP_HAS_COMMA(...) \
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 0)
#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__)
#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__)
#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#endif // GMOCK_PP_INTERNAL_USE_MSVC
GMOCK_PP_IDENTITY( \
GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__))
// If the arguments after expansion have no tokens, evaluates to `1`. Otherwise
// evaluates to `0`.
@ -140,7 +107,7 @@ static_assert(
// Expands to 1 if the first argument starts with something in parentheses,
// otherwise to 0.
#define GMOCK_PP_IS_BEGIN_PARENS(...) \
GMOCK_PP_INTERNAL_ALTERNATE_HEAD( \
GMOCK_PP_HEAD( \
GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \
GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__))
@ -179,10 +146,6 @@ static_assert(
#define GMOCK_PP_INTENRAL_EMPTY_TUPLE (, , , , , , , , , , , , , , , )
#define GMOCK_PP_INTERNAL_CAT(_1, _2) _1##_2
#define GMOCK_PP_INTERNAL_STRINGIZE(...) #__VA_ARGS__
#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
_10, _11, _12, _13, _14, _15, _16, \
...) \
_16
#define GMOCK_PP_INTERNAL_CAT_5(_1, _2, _3, _4, _5) _1##_2##_3##_4##_5
#define GMOCK_PP_INTERNAL_IS_EMPTY(_1, _2, _3, _4) \
GMOCK_PP_HAS_COMMA(GMOCK_PP_INTERNAL_CAT_5(GMOCK_PP_INTERNAL_IS_EMPTY_CASE_, \
@ -190,30 +153,24 @@ static_assert(
#define GMOCK_PP_INTERNAL_IS_EMPTY_CASE_0001 ,
#define GMOCK_PP_INTERNAL_IF_1(_Then, _Else) _Then
#define GMOCK_PP_INTERNAL_IF_0(_Then, _Else) _Else
#define GMOCK_PP_INTERNAL_HEAD(_1, ...) _1
#define GMOCK_PP_INTERNAL_TAIL(_1, ...) __VA_ARGS__
#if GMOCK_PP_INTERNAL_USE_MSVC
#define GMOCK_PP_INTERNAL_NARG_CAT(_1, _2) GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2)
#define GMOCK_PP_INTERNAL_HEAD_CAT(_1, _2) GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2)
#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT(_1, _2) \
GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2)
#define GMOCK_PP_INTERNAL_TAIL_CAT(_1, _2) GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2)
#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT(_1, _2) \
GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2)
#define GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2) _1##_2
#define GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2) _1##_2
#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2) _1##_2
#define GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2) _1##_2
#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2) _1##_2
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) \
GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(GMOCK_PP_HEAD(__VA_ARGS__), )
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(_1, _2) \
GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2)
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2) _1##_2
#else // GMOCK_PP_INTERNAL_USE_MSVC
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) GMOCK_PP_HEAD(__VA_ARGS__)
#endif // GMOCK_PP_INTERNAL_USE_MSVC
// Because of MSVC treating a token with a comma in it as a single token when passed
// to another macro, we need to force it to evaluate it as multiple tokens. We do that
// by using a "IDENTITY(MACRO PARENTHESIZED_ARGS)" macro.
// We define one per possible macro that relies on this behavior.
// Note "_Args" must be parenthesized.
#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
_10, _11, _12, _13, _14, _15, _16, \
...) \
_16
#define GMOCK_PP_INTERNAL_16TH(_Args) \
GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_16TH _Args)
#define GMOCK_PP_INTERNAL_INTERNAL_HEAD(_1, ...) _1
#define GMOCK_PP_INTERNAL_HEAD(_Args) \
GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_HEAD _Args)
#define GMOCK_PP_INTERNAL_INTERNAL_TAIL(_1, ...) __VA_ARGS__
#define GMOCK_PP_INTERNAL_TAIL(_Args) \
GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_TAIL _Args)
#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C(...) 1 _
#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_1 1,

View File

@ -1,5 +1,10 @@
#include "gmock/internal/gmock-pp.h"
// Used to test MSVC treating __VA_ARGS__ with a comma in it as one value
#define GMOCK_TEST_REPLACE_comma_WITH_COMMA_I_comma ,
#define GMOCK_TEST_REPLACE_comma_WITH_COMMA(x) \
GMOCK_PP_CAT(GMOCK_TEST_REPLACE_comma_WITH_COMMA_I_, x)
// Static assertions.
namespace testing {
namespace internal {
@ -17,6 +22,11 @@ static_assert(GMOCK_PP_NARG(x, y, z, w) == 4, "");
static_assert(!GMOCK_PP_HAS_COMMA(), "");
static_assert(GMOCK_PP_HAS_COMMA(b, ), "");
static_assert(!GMOCK_PP_HAS_COMMA((, )), "");
static_assert(
GMOCK_PP_HAS_COMMA(GMOCK_TEST_REPLACE_comma_WITH_COMMA(comma)), "");
static_assert(
GMOCK_PP_HAS_COMMA(GMOCK_TEST_REPLACE_comma_WITH_COMMA(comma(unrelated))),
"");
static_assert(!GMOCK_PP_IS_EMPTY(, ), "");
static_assert(!GMOCK_PP_IS_EMPTY(a), "");
static_assert(!GMOCK_PP_IS_EMPTY(()), "");

View File

@ -1,2 +0,0 @@
add_library(range-v3 INTERFACE)
target_include_directories(range-v3 SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/range-v3/include)

View File

View File

@ -1,92 +0,0 @@
{
AccessModifierOffset: -4,
AlignAfterOpenBracket: Align,
AlignEscapedNewlinesLeft: true,
AlignTrailingComments: true,
AllowAllParametersOfDeclarationOnNextLine: false,
AllowShortBlocksOnASingleLine: true,
AllowShortCaseLabelsOnASingleLine: false,
AllowShortFunctionsOnASingleLine: None,
AllowShortIfStatementsOnASingleLine: false,
AllowShortLoopsOnASingleLine: false,
AlwaysBreakBeforeMultilineStrings: true,
AlwaysBreakAfterReturnType: None,
AlwaysBreakTemplateDeclarations: true,
BinPackArguments: false,
BinPackParameters: true,
BraceWrapping: {
AfterCaseLabel: true,
AfterClass: true,
AfterControlStatement: true,
AfterEnum: true,
AfterFunction: true,
AfterNamespace: true,
AfterStruct: true,
AfterUnion: true,
AfterExternBlock: true,
BeforeCatch: true,
BeforeElse: true,
IndentBraces: false,
SplitEmptyFunction: false,
SplitEmptyRecord: false,
SplitEmptyNamespace: true,
},
BreakBeforeBinaryOperators: false,
BreakBeforeBraces: Custom,
BreakBeforeTernaryOperators: true,
BreakConstructorInitializers: BeforeComma,
BreakInheritanceList: BeforeComma,
ColumnLimit: 90,
ConstructorInitializerAllOnOneLineOrOnePerLine: false,
ConstructorInitializerIndentWidth: 2,
ContinuationIndentWidth: 4,
Cpp11BracedListStyle: true,
DerivePointerAlignment: false,
ExperimentalAutoDetectBinPacking: false,
ForEachMacros: ['RANGES_FOR',],
IncludeBlocks: Regroup,
IncludeCategories: [
{ Regex: '^<range/v3/range_fwd.hpp',
Priority: 5},
{ Regex: '^<range/v3',
Priority: 6},
{ Regex: '^<concepts/',
Priority: 4},
{ Regex: '^<meta/',
Priority: 3},
{ Regex: '^<std/.*>$',
Priority: 2},
{ Regex: '^<.*>$',
Priority: 1},
],
IndentCaseLabels: false,
IndentFunctionDeclarationAfterType: false,
IndentWidth: 4,
KeepEmptyLinesAtTheStartOfBlocks: true,
Language: Cpp,
MaxEmptyLinesToKeep: 1,
MacroBlockBegin: "^(RANGES|META)_BEGIN_(NAMESPACE_(STD|VERSION|CONTAINER)|NIEBLOID)$",
MacroBlockEnd: "^(RANGES|META)_END_(NAMESPACE_(STD|VERSION|CONTAINER)|NIEBLOID)$",
NamespaceIndentation: All,
PenaltyBreakBeforeFirstCallParameter: 10,
PenaltyReturnTypeOnItsOwnLine: 1000,
PointerAlignment: Middle,
SpaceAfterControlStatementKeyword: false,
SpaceAfterTemplateKeyword: false,
SpaceBeforeAssignmentOperators: true,
SpaceBeforeParens: Never,
SpaceInEmptyParentheses: false,
SpacesBeforeTrailingComments: 1,
SpacesInAngles: false,
SpacesInCStyleCastParentheses: false,
SpacesInParentheses: false,
Standard: Cpp11,
StatementMacros: [
'RANGES_INLINE_VARIABLE',
'RANGES_DEFINE_CPO',
'CPP_member',
'CPP_broken_friend_member',
],
TabWidth: 4,
UseTab: Never,
}

View File

@ -1,237 +0,0 @@
## Copyright (c) 2013 GitHub, Inc.
##
## 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.
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Clion files
.idea
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
x64/
build*/
bld/
[Bb]in/
[Oo]bj/
bazel-*
cmake-build-*/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.azurePubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
## TODO: If the tool you use requires repositories.config, also uncomment the next line
#!packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Buck build artefacts
.buckd/
buck-out/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Visual Studio stuff
*.VC.db
*.VC.opendb
.vscode/
.vs/
\#*#
.#*

View File

@ -1,303 +0,0 @@
# Copyright Louis Dionne 2013-2016
# Copyright Gonzalo BG 2014-2017
# Copyright Julian Becker 2015
# Copyright Manu Sánchez 2015
# Copyright Casey Carter 2015-2017
# Copyright Eric Niebler 2015-2016
# Copyright Paul Fultz II 2015-2016
# Copyright Jakub Szuppe 2016.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at http://boost.org/LICENSE_1_0.txt)
# Adapted from various sources, including:
# - Louis Dionne's Hana: https://github.com/ldionne/hana
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
language: cpp
dist: xenial
script: cmake
git:
depth: 1
env:
global:
- DEPS_DIR=${TRAVIS_BUILD_DIR}/deps
- CMAKE_VERSION="3.12.0"
cache:
directories:
- ${DEPS_DIR}/cmake-${CMAKE_VERSION}
matrix:
include:
- env: BUILD_TYPE=Release CPP=1z SYSTEM_LIBCXX=On
os: osx
compiler: clang
# The ASAN build in install_libcxx.sh doesn't work for versions < 4
# clang 3.6 C++17/14 Release libc++
- env: CLANG_VERSION=3.6 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
os: linux
addons: &clang36
apt:
packages:
- clang-3.6
- libstdc++-5-dev
- valgrind
sources:
- ubuntu-toolchain-r-test
# clang 3.7 C++17/14 Release libc++
- env: CLANG_VERSION=3.7 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
os: linux
addons: &clang37
apt:
packages:
- clang-3.7
- libstdc++-5-dev
- valgrind
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-3.7
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.7 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
# clang 3.8 C++17/14 Release libc++
- env: CLANG_VERSION=3.8 BUILD_TYPE=Release CPP=1z LIBCXX=On
os: linux
addons: &clang38
apt:
packages:
- util-linux
- clang-3.8
- libstdc++-5-dev
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: CLANG_VERSION=3.8 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
os: linux
addons: *clang38
# clang 3.9 C++17/14 Release libc++
- env: CLANG_VERSION=3.9 BUILD_TYPE=Release CPP=1z LIBCXX=On
os: linux
addons: &clang39
apt:
packages:
- util-linux
- clang-3.9
- libstdc++-6-dev
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: CLANG_VERSION=3.9 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
os: linux
addons: *clang39
# clang 5 C++17/14/1z Debug/Release-ASAN libc++, 17 Debug libstdc++
- env: CLANG_VERSION=5.0 BUILD_TYPE=Debug CPP=1z LIBCXX=On
os: linux
addons: &clang5
apt:
packages:
- clang-5.0
- libstdc++-6-dev
- valgrind
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-5.0
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
- env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=1z ASAN=On LIBCXX=On HEADERS=On
os: linux
addons: *clang5
- env: CLANG_VERSION=5.0 BUILD_TYPE=Debug CPP=14 LIBCXX=On
os: linux
addons: *clang5
- env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=14 ASAN=On LIBCXX=On
os: linux
addons: *clang5
- env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=1z
os: linux
addons: *clang5
# Module build is on the floor
# - env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=1z MSAN=On LIBCXX=On CLANG_MODULES=On
# os: linux
# addons: *clang5
# gcc-5 C++17/C++14 Release
- env: GCC_VERSION=5 BUILD_TYPE=Release CPP=1z
os: linux
addons: &gcc5
apt:
packages:
- g++-5
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: GCC_VERSION=5 BUILD_TYPE=Release CPP=14 HEADERS=On
os: linux
addons: *gcc5
# gcc-6 C++17/14/1z Debug/Release
- env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=1z
os: linux
addons: &gcc6
apt:
packages:
- g++-6
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: GCC_VERSION=6 BUILD_TYPE=Release CPP=1z
os: linux
addons: *gcc6
- env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=14 HEADERS=On
os: linux
addons: *gcc6
- env: GCC_VERSION=6 BUILD_TYPE=Release CPP=14
os: linux
addons: *gcc6
# gcc-7 C++17/14/1z Debug/Release
- env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=1z
os: linux
addons: &gcc7
apt:
packages:
- g++-7
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=1z
os: linux
addons: *gcc7
- env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=14 HEADERS=On
os: linux
addons: *gcc7
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=14
os: linux
addons: *gcc7
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=1z CONCEPTS=On
os: linux
addons: *gcc7
# gcc-8 C++17/14/1z Debug/Release
- env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=1z
os: linux
addons: &gcc8
apt:
packages:
- g++-8
- valgrind
sources:
- ubuntu-toolchain-r-test
- env: GCC_VERSION=8 BUILD_TYPE=Release CPP=1z
os: linux
addons: *gcc8
- env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=14 HEADERS=On
os: linux
addons: *gcc8
- env: GCC_VERSION=8 BUILD_TYPE=Release CPP=14
os: linux
addons: *gcc8
- env: GCC_VERSION=8 BUILD_TYPE=Release CPP=1z CONCEPTS=On
os: linux
addons: *gcc8
# Install dependencies
before_install:
- set -e
- |
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
brew update
brew install gnu-sed
brew install gnu-which
brew upgrade cmake
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
elif [ "$BUILD_TYPE" == "Release" -a "$ASAN" != "On" -a "$MSAN" != "On" ]; then
USE_VALGRIND=On
fi
- |
if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
if [ -f ${DEPS_DIR}/cmake-${CMAKE_VERSION}/cached ]; then
echo "Using cached cmake version ${CMAKE_VERSION}."
else
CMAKE_URL="https://cmake.org/files/v3.12/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz"
mkdir -p ${DEPS_DIR}/cmake-${CMAKE_VERSION}
travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C ${DEPS_DIR}/cmake-${CMAKE_VERSION}
touch ${DEPS_DIR}/cmake-${CMAKE_VERSION}/cached
fi
export PATH="${DEPS_DIR}/cmake-${CMAKE_VERSION}/bin:${PATH}"
fi
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
- which $CXX && $CXX --version
- which $CC
- if [ "$USE_VALGRIND" == "On" ]; then which valgrind; fi
- if [ "$ASAN" == "On" ]; then export SANITIZER="Address;Undefined"; fi
- if [ "$MSAN" == "On" ]; then export SANITIZER="MemoryWithOrigins"; fi
- if [ -n "$CLANG_VERSION" ]; then PATH="${PATH}" CXX="$CXX" CC="$CC" ./install_libcxx.sh; fi
install:
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
# It is fixed in valgrind 3.10 so this won't be necessary if someone
# replaces the current valgrind (3.7) with valgrind-3.10
- |
if [ "$USE_VALGRIND" == "On" ]; then
sed -i 's/march=native/msse4.2/' cmake/ranges_flags.cmake
# We need to explicitly initialize std::random_device on libstdc++ to avoid using RDRAND
# since valgrind doesn't understand the instruction.
CXX_FLAGS="${CXX_FLAGS} -DRANGES_WORKAROUND_VALGRIND_RDRAND"
fi
- if [ "$GCC_VERSION" == "5" ]; then CXX_FLAGS="${CXX_FLAGS} -DRANGES_CXX_CONSTEXPR=RANGES_CXX_CONSTEXPR11"; fi
- |
if [ "$LIBCXX" == "On" ]; then
CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -nostdinc++ -cxx-isystem ${TRAVIS_BUILD_DIR}/llvm/include/c++/v1/ -Wno-unused-command-line-argument"
CXX_LINKER_FLAGS="${CXX_LINKER_FLAGS} -L ${TRAVIS_BUILD_DIR}/llvm/lib -Wl,-rpath,${TRAVIS_BUILD_DIR}/llvm/lib -lc++abi"
if [ -n "$CLANG_VERSION" ]; then
if [ "$ASAN" == "On" ]; then
CXX_FLAGS="${CXX_FLAGS} -fsanitize=address"
elif [ "$MSAN" == "On" ]; then
CXX_FLAGS="${CXX_FLAGS} -fsanitize=memory"
fi
fi
fi
- mkdir -p build
# This cd works, but causes the shell to exit on OSX with set -e. I don't even.
- set +e; cd build; set -e; pwd
- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${CXX_LINKER_FLAGS}" -DRANGES_CXX_STD=$CPP -DRANGE_V3_HEADER_CHECKS=$HEADERS -DRANGES_PREFER_REAL_CONCEPTS=$CONCEPTS -DRANGES_VERBOSE_BUILD=On -DRANGES_ASAN=$ASAN -DRANGES_MSAN=$MSAN -DRANGES_DEEP_STL_INTEGRATION=On -Wdev
- cat CMakeFiles/CMakeError.log || true
- cat CMakeFiles/CMakeOutput.log || true
- if [ "$CLANG_MODULES" == "On" -a "$LIBCXX" == "On" ]; then cmake .. -DRANGES_MODULES=On -DRANGES_LIBCXX_MODULE="${TRAVIS_BUILD_DIR}/llvm/include/c++/v1/module.modulemap"; fi
- make -j2 VERBOSE=1
script:
- if [ "$USE_VALGRIND" == "On" ]; then CTEST_FLAGS="-D ExperimentalMemCheck"; fi
- ctest -j2 -VV ${CTEST_FLAGS}
notifications:
email: false

View File

@ -1,42 +0,0 @@
prebuilt_cxx_library(
name = 'concepts',
header_namespace = 'concepts',
header_only = True,
exported_headers = subdir_glob([
('include/concepts', '**/*.hpp'),
]),
licenses = [
'LICENSE.txt',
],
)
prebuilt_cxx_library(
name = 'meta',
header_namespace = 'meta',
header_only = True,
exported_headers = subdir_glob([
('include/meta', '**/*.hpp'),
]),
licenses = [
'LICENSE.txt',
],
)
prebuilt_cxx_library(
name = 'range-v3',
header_namespace = 'range/v3',
header_only = True,
exported_headers = subdir_glob([
('include/range/v3', '**/*.hpp'),
]),
licenses = [
'LICENSE.txt',
],
visibility = [
'PUBLIC'
],
deps = [
':concepts',
':meta',
],
)

View File

@ -1,42 +0,0 @@
cc_library(
name = 'concepts',
hdrs = glob([
'include/concepts/**/*.hpp',
]),
includes = [
"include",
],
)
cc_library(
name = 'meta',
hdrs = glob([
'include/meta/**/*.hpp',
]),
includes = [
"include",
],
)
cc_library(
name = 'std',
hdrs = glob([
'include/std/**/*.hpp',
]),
includes = [
"include",
],
)
cc_library(
name = 'range-v3',
hdrs = glob([
'include/range/v3/**/*.hpp',
]),
visibility = ["//visibility:public"],
deps = [
':concepts',
':meta',
':std',
],
)

View File

@ -1,171 +0,0 @@
# Copyright Eric Niebler 2014
# Copyright Gonzalo Brito Gadeschi 2014, 2017
# Copyright Louis Dionne 2015
# Copyright Casey Carter 2016
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
cmake_minimum_required(VERSION 3.6)
get_directory_property(is_subproject PARENT_DIRECTORY)
if(NOT is_subproject)
set(is_standalone YES)
else()
set(is_standalone NO)
endif()
project(Range-v3 CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export compilation data-base
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
add_library(meta INTERFACE)
target_include_directories(meta INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
target_include_directories(meta SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
target_compile_options(meta INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/permissive->)
add_library(concepts INTERFACE)
target_include_directories(concepts INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
target_include_directories(concepts SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
target_compile_options(concepts INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/permissive- /experimental:preprocessor /wd5105>)
target_link_libraries(concepts INTERFACE meta)
add_library(range-v3 INTERFACE)
target_include_directories(range-v3 INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
target_include_directories(range-v3 SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
target_compile_options(range-v3 INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/permissive->)
target_link_libraries(range-v3 INTERFACE concepts meta)
function(rv3_add_test TESTNAME EXENAME FIRSTSOURCE)
add_executable(${EXENAME} ${FIRSTSOURCE} ${ARGN})
target_link_libraries(${EXENAME} range-v3)
add_test(${TESTNAME} ${EXENAME})
endfunction(rv3_add_test)
include(ranges_options)
include(ranges_env)
include(ranges_flags)
if(RANGE_V3_DOCS)
add_subdirectory(doc)
endif()
if(RANGE_V3_TESTS)
include(CTest)
add_subdirectory(test)
endif()
if(RANGE_V3_EXAMPLES)
add_subdirectory(example)
endif()
if(RANGE_V3_PERF)
add_subdirectory(perf)
endif()
# Add header files as sources to fix MSVS 2017 not finding source during debugging
file(GLOB_RECURSE RANGE_V3_PUBLIC_HEADERS_ABSOLUTE
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
add_custom_target(headers SOURCES ${RANGE_V3_PUBLIC_HEADERS_ABSOLUTE})
set_target_properties(headers PROPERTIES FOLDER "header")
# Test all headers
if(RANGE_V3_HEADER_CHECKS)
include(TestHeaders)
file(GLOB_RECURSE RANGE_V3_PUBLIC_HEADERS
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
# This header is not meant to be included directly:
list(REMOVE_ITEM RANGE_V3_PUBLIC_HEADERS std/detail/associated_types.hpp)
# Deprecated headers
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
foreach(header ${RANGE_V3_PUBLIC_HEADERS})
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/${header}" is_deprecated
LIMIT_COUNT 1
REGEX ".*RANGES_DEPRECATED_HEADER.*")
if(is_deprecated)
list(APPEND RANGE_V3_DEPRECATED_PUBLIC_HEADERS "${header}")
endif()
endforeach()
endif()
add_header_test(test.range.v3.headers
EXCLUDE ${RANGE_V3_DEPRECATED_PUBLIC_HEADERS}
HEADERS ${RANGE_V3_PUBLIC_HEADERS})
target_link_libraries(test.range.v3.headers PRIVATE range-v3)
endif()
# Grab the range-v3 version numbers:
include(${CMAKE_CURRENT_SOURCE_DIR}/Version.cmake)
set(RANGE_V3_VERSION ${RANGE_V3_MAJOR}.${RANGE_V3_MINOR}.${RANGE_V3_PATCHLEVEL})
# Try to build a new version.hpp
configure_file(version.hpp.in include/range/v3/version.hpp @ONLY)
file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/include/range/v3/version.hpp RANGE_V3_OLD_VERSION_HPP)
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/include/range/v3/version.hpp RANGE_V3_NEW_VERSION_HPP)
# If the new version.hpp is materially different from the one in the source
# directory, update it, commit, and tag.
if(NOT RANGE_V3_NEW_VERSION_HPP STREQUAL RANGE_V3_OLD_VERSION_HPP)
# Check that README.md and Version.cmake are the only changed file:
execute_process(
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" status --porcelain -uno
OUTPUT_VARIABLE RANGE_V3_GIT_STATUS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REPLACE "\n" ";" RANGE_V3_GIT_STATUS ${RANGE_V3_GIT_STATUS})
if (NOT "x${RANGE_V3_GIT_STATUS}" STREQUAL "x M Version.cmake; M doc/release_notes.md")
message(FATAL_ERROR "Cannot update version.hpp: range-v3 source directory has a dirty status")
endif()
file(
COPY ${CMAKE_CURRENT_BINARY_DIR}/include/range/v3/version.hpp
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include/range/v3
)
execute_process(
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" add -u
)
execute_process(
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" commit -m "${RANGE_V3_VERSION}"
)
execute_process(
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" tag -f -a "${RANGE_V3_VERSION}" -m "${RANGE_V3_VERSION}"
)
find_program(CONAN_EXECUTABLE NAMES conan conan.exe)
if (NOT "x${CONAN_EXECUTABLE}" STREQUAL "xCONAN_EXECUTABLE-NOTFOUND")
message(STATUS "Exporting conanfile for new version")
execute_process(
COMMAND ${CONAN_EXECUTABLE} create . range-v3/${RANGE_V3_VERSION}@ericniebler/stable
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
endif()
message(STATUS "Version updated to ${RANGE_V3_VERSION}. Don't forget to:")
message(STATUS " git push origin <feature-branch>")
message(STATUS "and (after that is merged to master) then:")
message(STATUS " conan create ${CMAKE_CURRENT_SOURCE_DIR} range-v3/${RANGE_V3_VERSION}@ericniebler/stable")
message(STATUS " conan upload --all range-v3/${RANGE_V3_VERSION}@ericniebler/stable")
endif()
include(CMakePackageConfigHelpers)
# write_basic_package_version_file(...) gained ARCH_INDEPENDENT in CMake 3.14.
# For CMake 3.6, this workaround makes the version file ARCH_INDEPENDENT
# by making CMAKE_SIZEOF_VOID_P empty.
set(OLD_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
set(CMAKE_SIZEOF_VOID_P "")
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/range-v3-config-version.cmake
VERSION ${RANGE_V3_VERSION}
COMPATIBILITY ExactVersion
)
set(CMAKE_SIZEOF_VOID_P ${OLD_CMAKE_SIZEOF_VOID_P})
install(TARGETS concepts meta range-v3 EXPORT range-v3-targets DESTINATION lib)
install(EXPORT range-v3-targets FILE range-v3-config.cmake DESTINATION lib/cmake/range-v3)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/range-v3-config-version.cmake
DESTINATION lib/cmake/range-v3)
install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*")
export(EXPORT range-v3-targets FILE range-v3-config.cmake)

View File

@ -1,48 +0,0 @@
Acknowledgements
----------------
In range-v3, I have integrated many ideas that come from other people. I would be remiss to not mention them. Many others helped either directly or indirectly in a variety of ways. In no particular order...
| Contributor | Contribution |
|-----------------------------|------|
| Jeremy Siek | Container algorithms (in Boost pre-history), Boost.Iterators |
| Thorston Ottoson | Boost.Range v1 |
| Neil Groves | Boost.Range v2 |
| David Abrahams, Thomas Witt | Boost.Iterators, Sentinels |
| Sean Parent | ASL, Projections, View / Range distinction, much Generic Program wisdom besides |
| Dietmar Kühl | Array Traits, Property Map |
| Andrew Sutton | C++ Concepts "Lite", Origin Libraries, Palo Alto Report |
| Doug Gregor | C++0x Concepts |
| Casey Carter | Co-author and Editor, Ranges TS; major code contributions |
| Gonzalo Brito Gadeschi | Many ideas, bug reports, and code contributions |
| Alexander Stepanov | STL, Generic Programming, Iterators, Elements of Programming, etc. |
| Bjarne Stroustrup | A tireless effort to add proper support for Generic Programming to C++, early support for my Ranges proposal |
| Herb Sutter | Early support for my Ranges proposal |
| The Standard C++ Foundation | A generous grant supporting my Ranges work |
An Abreviated History
--------------------
**Range v1**
I came to Boost in the early 2000's. By that time, Boost already had a Range library (Thorston Ottoson's). At this time, Boost.Range was little more that the `begin` and `end` free functions, and range-based overloads of the STL algorithms that dispatched to the iterator-based overloads in namespace `std`.
Boost also already had the Iterators library by David Abrahams and Jeremy Siek. This library had iterator adaptors like `filter_iterator` and `transform_iterator`.
**Range v2**
It seemed natural to me that the Range library and the adaptors from the Iterators library should be combined. I wrote the `filter` and `transform` range adaptors, commandeered the pipe operator (`|`) from bash for chaining, and put a rough library together called Range_ex in the Boost File Vault (which would later become the Boost Sandbox). I saw problems with my design and never finished it.
A few years later, Neil Groves picked up some of the ideas in my Range\_ex, polished them a great deal, published his own Range\_ex library, and submitted it to Boost. It became Boost.Range v2. At the time of writing (March, 2017), it is the version still shipping with Boost.
**Range v3**
In 2013, I published a blog post called ["Out Parameters, Move Semantics, and Stateful Algorithms"](http://ericniebler.com/2013/10/13/out-parameters-vs-move-semantics/) that turned my mind to ranges once again. Following that, it became clear to me that the Boost.Range library, designed for C++98, needed a facelift for the post-C++11 world. I began what I believed at the time would be a quick hack to bring Boost.Range into the modern world. I called it "Range v3", thinking it would become the third major version of the Boost.Range library. Subsequent posts detailed the evolution of my thinking as range-v3 took shape.
**Standardization**
Around this time, some big thinkers in the C++ community were working to resurrect the effort to add Concepts to C++. They published a paper ([N3351](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf)) that would become known as the **"Palo Alto Report"** which detailed the necessary and sufficient language and library support for a concept-checked version of the Standard Template Library. The authors of the paper included Alexander Stepanov, Bjarne Stroustrup, Sean Parent, Andrew Sutton, and more. Andrew Sutton began working in earnest to realize the core language changes, an effort that became known as "Concepts Lite". (It is now the Concepts TS.)
I decided early on that Concepts Lite, or something like it, would become part of Standard C++. Recognizing that C++ would need a concept-ified Standard Library to go with the language feature, I began evolving range-v3 in that direction, eventually submitting ["Ranges for the Standard Library, Revision 1"](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4128.html) to the C++ Standardization Committee, together with Andrew Sutton and Sean Parent. The Committee approved the direction in late 2014, and so it goes...
Today (2017-03), we are very close to a final Ranges TS and are on target to integrate into Standard C++ in 2020, with *much* more to come. Stay tuned.

View File

@ -1,151 +0,0 @@
========================================================
Boost Software License - Version 1.0 - August 17th, 2003
========================================================
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
==============================================================================
libc++ License
==============================================================================
The libc++ library is dual licensed under both the University of Illinois
"BSD-Like" license and the MIT license. As a user of this code you may choose
to use it under either license. As a contributor, you agree to allow your code
to be used under both.
Full text of the relevant licenses is included below.
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
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
CONTRIBUTORS 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 WITH THE
SOFTWARE.
==============================================================================
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
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.
==============================================================================
Stepanov and McJones, "Elements of Programming" license
==============================================================================
// Copyright (c) 2009 Alexander Stepanov and Paul McJones
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without
// fee, provided that the above copyright notice appear in all copies
// and that both that copyright notice and this permission notice
// appear in supporting documentation. The authors make no
// representations about the suitability of this software for any
// purpose. It is provided "as is" without express or implied
// warranty.
//
// Algorithms from
// Elements of Programming
// by Alexander Stepanov and Paul McJones
// Addison-Wesley Professional, 2009
==============================================================================
SGI C++ Standard Template Library license
==============================================================================
// Copyright (c) 1994
// Hewlett-Packard Company
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation. Hewlett-Packard Company makes no
// representations about the suitability of this software for any
// purpose. It is provided "as is" without express or implied warranty.
//
// Copyright (c) 1996
// Silicon Graphics Computer Systems, Inc.
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation. Silicon Graphics makes no
// representations about the suitability of this software for any
// purpose. It is provided "as is" without express or implied warranty.
//

View File

@ -1,77 +0,0 @@
range-v3
========
Range library for C++14/17/20. This code was the basis of [a formal proposal](https://ericniebler.github.io/std/wg21/D4128.html) to add range support to the C++ standard library. That proposal evolved through a Technical Specification, and finally into [P0896R4 "The One Ranges Proposal"](https://wg21.link/p0896r4) which was merged into the C++20 working drafts in November 2018.
About:
------
Ranges are an extension of the Standard Template Library that makes its iterators and algorithms more powerful by making them _composable_. Unlike other range-like solutions which seek to do away with iterators, in range-v3 ranges are an abstration layer _on top_ of iterators.
Range-v3 is built on three pillars: Views, Actions, and Algorithms. The algorithms are the same as those with which you are already familiar in the STL, except that in range-v3 all the algorithms have overloads that take ranges in addition to the overloads that take iterators. Views are composable adaptations of ranges where the adaptation happens lazily as the view is iterated. And an action is an eager application of an algorithm to a container that mutates the container in-place and returns it for further processing.
Views and actions use the pipe syntax (e.g., `rng | adapt1 | adapt2 | ...`) so your code is terse and readable from left to right.
Documentation:
--------------
Check out the (woefully incomplete) documentation [here](https://ericniebler.github.io/range-v3/).
Other resources (mind the dates, the library probably has changed since then):
- Usage:
- Talk: [CppCon 2015: Eric Niebler "Ranges for the Standard Library"](https://www.youtube.com/watch?v=mFUXNMfaciE), 2015.
- [A slice of Python in C++](http://ericniebler.com/2014/12/07/a-slice-of-python-in-c/), 07.12.2014.
- Actions (back then called [Container Algorithms](http://ericniebler.com/2014/11/23/container-algorithms/)), 23.11.2014.
- [Range comprehensions](http://ericniebler.com/2014/04/27/range-comprehensions/), 27.04.2014.
- [Input iterators vs input ranges](http://ericniebler.com/2013/11/07/input-iterators-vs-input-ranges/), 07.11.2013.
- Design / Implementation:
- Rationale behind range-v3: [N4128: Ranges for the standard library Revision 1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4128.html), 2014.
- Ranges TS: [N4560: C++ Extensions for Ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4560.pdf), 2015.
- Implementation of customization points in range-v3:
- [N4381: Suggested Design for Customization Points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html), 2015.
- [P0386: Inline variables](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r0.pdf), 2016.
- [Customization Point Design in C++11 and Beyond](http://ericniebler.com/2014/10/21/customization-point-design-in-c11-and-beyond/), 2014.
- Proxy iterators in range-v3:
- [D0022: Proxy Iterators for the Ranges Extensions](https://ericniebler.github.io/std/wg21/D0022.html).
- [To Be or Not to Be (an Iterator)](http://ericniebler.com/2015/01/28/to-be-or-not-to-be-an-iterator/), 2015.
- [Iterators++: Part1](http://ericniebler.com/2015/02/03/iterators-plus-plus-part-1/), 2015.
- [Iterators++: Part2](http://ericniebler.com/2015/02/13/iterators-plus-plus-part-2/), 2015.
- [Iterators++: Part3](http://ericniebler.com/2015/03/03/iterators-plus-plus-part-3/), 2015.
- Metaprogramming utilities:
- See the [meta documentation](https://ericniebler.github.io/meta/index.html), the library has changed significantly since the [2014 blog post](http://ericniebler.com/2014/11/13/tiny-metaprogramming-library/).
- Concept emulation layer: [Concept checking in C++11](http://ericniebler.com/2013/11/23/concept-checking-in-c11/), 2013.
- [C++Now 2014: Eric Niebler "C++11 Library Design"](https://www.youtube.com/watch?v=zgOF4NrQllo), 2014.
License:
--------
Most of the source code in this project are mine, and those are under the Boost Software License. Parts are taken from Alex Stepanov's Elements of Programming, Howard Hinnant's libc++, and from the SGI STL. Please see the attached LICENSE file and the CREDITS file for the licensing and acknowledgments.
Supported Compilers
-------------------
The code is known to work on the following compilers:
- clang 3.6.2 (or later)
- GCC 5.0.2 (or later) (C++14 "extended constexpr" support is poor before 6.1.)
- Clang/LLVM 6 (or later) on Windows (older versions may work - we haven't tested.)
- Visual Studio 2019 Preview 4 (or later) on Windows, with some caveats due to range-v3's strict conformance requirements:
- range-v3 needs `/std:c++17 /permissive-`
- range-v3 needs a fully conforming preprocessor, so `/experimental:preprocessor` is necessary. Note that the conforming preprocessor diagnoses `C5105` "macro expansion producing 'defined' has undefined behavior" in some of the Windows SDK headers, so you'll probably want to suppress that warning with `/wd5105`.
[ Note: We've "retired" support for Clang/C2 with the VS2015 toolset (i.e., the `v140_clang_c2` toolset) which Microsoft no longer supports for C++ use. We no longer have CI runs, but haven't gone out of our way to break anything, so it will likely continue to work. ]
**Development Status:** This code is fairly stable, well-tested, and suitable for casual use, although currently lacking documentation. _In general_, no promise is made about support or long-term stability. This code *will* evolve without regard to backwards compatibility.
A notable exception is anything found within the `ranges::cpp20` namespace. Those components will change rarely or (preferably) never at all.
**Build status**
- on Travis-CI: [![Travis Build Status](https://travis-ci.org/ericniebler/range-v3.svg?branch=master)](https://travis-ci.org/ericniebler/range-v3)
- on AppVeyor: [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/fwl9ymc2t6ukn9qj/branch/master?svg=true)](https://ci.appveyor.com/project/ericniebler/range-v3)
Say Thanks!
-----------
I do this work because I love it and because I love C++ and want it to be as excellent as I know it can be. If you like my work and are looking for a way to say thank you, you can leave a supportive comment on [my blog](http://ericniebler.com). Or you could leave me some kudos on my Open Hub range-v3 contribution page. Just click the **Give Kudos** button [here](https://www.openhub.net/p/range-v3/contributors/3053743222308608).

View File

@ -1,26 +0,0 @@
* Add contiguous iterator utilities. How about `is_contiguous_iterator` and `as_contiguous_range`:
```
CPP_template(typename I, typename S)(
requires RandomAccessIterator<I> &&
SizedSentinel<S, I> &&
is_contiguous_iterator<I>())
subrange<std::add_pointer_t<iter_reference_t<I>>>
as_contiguous_range(I begin, S end)
{
if(begin == end)
return {nullptr, nullptr};
else
return {addressof(*begin), addressof(*begin) + (end - begin)};
}
```
* Longer-term goals:
- Make `inplace_merge` work with forward iterators
- Make the sorting algorithms work with forward iterators
* Maybe iterators are not necessarily countable. Is there a relation between
the ability to be able to subtract two iterators to find the distance, and
with the existence of a DistanceType associated type? Think of:
- counted iterators (subtractable regardless of traversal category)
- repeat_view iterators (*not* subtractable but could be random access otherwise)
- infinite ranges (only countable with an infinite precision integer which we lack)

View File

@ -1,5 +0,0 @@
# To update the range-v3 version, from a *CLEAN* working directory, update the version numbers below.
# This makefile will generate a new version.hpp, *AMEND THE MOST RECENT COMMIT*, and git-tag the commit.
set(RANGE_V3_MAJOR 0)
set(RANGE_V3_MINOR 9)
set(RANGE_V3_PATCHLEVEL 1)

View File

View File

@ -1,59 +0,0 @@
shallow_clone: true
image: Visual Studio 2019
platform:
- x64_x86
- x64
configuration:
- Debug
- Release
environment:
matrix:
- CXX: clang-cl
CPP: latest
- CXX: cl
CPP: 17
- CXX: cl
CPP: latest
cache:
- C:\ninja-1.8.2
install:
- ps: |
if (![IO.File]::Exists("C:\ninja-1.8.2\ninja.exe")) {
Start-FileDownload 'https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip'
7z x -y ninja-win.zip -oC:\ninja-1.8.2
}
$env:PATH="C:\ninja-1.8.2;$env:PATH"
- for /f "tokens=1* delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath') do call "%%i\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
- cmake --version
- ninja --version
- clang-cl --version
- cl /Bv || exit 0
build_script:
- mkdir build && cd build
- ps: |
$env:CC=$env:CXX
$env:HEADER_CHECK=0
if ($env:PLATFORM -eq "x64") {
if ($env:CONFIGURATION -eq "Debug") {
$env:HEADER_CHECK=1
}
} elseif ($env:CXX -eq "clang-cl") {
$env:CXXFLAGS='-m32'
$env:CFLAGS='-m32'
}
- cmake .. -G Ninja -Wdev -DRANGE_V3_HEADER_CHECKS=%HEADER_CHECK% -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DRANGES_CXX_STD=%CPP%
- ninja
test_script:
- ctest -j2 --output-on-failure
deploy: off

View File

@ -1,79 +0,0 @@
{
"configurations": [
{
"name": "Mac",
"includePath": [
"/Users/eniebler/llvm-install/include/c++/v1",
"/usr/local/include",
"/Users/eniebler/llvm-install/lib/clang/6.0.0/include",
"/usr/include",
"${workspaceRoot}/include",
"/usr/local/opt/boost@1.67/incluse"
],
"defines": [],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/Users/eniebler/llvm-install/include/c++/v1",
"/usr/local/include",
"/Users/eniebler/llvm-install/lib/clang/6.0.0/include",
"/usr/include",
"${workspaceRoot}/include",
"/usr/local/opt/boost@1.67/incluse"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"macFrameworkPath": [
"/System/Library/Frameworks",
"/Library/Frameworks"
],
"compilerPath": "/Users/eniebler/llvm-install/bin/clang++",
"cStandard": "c11",
"cppStandard": "c++11",
"configurationProvider": "vector-of-bool.cmake-tools"
},
{
"name": "Linux",
"includePath": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}/include"
],
"defines": [],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}/include"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++11"
},
{
"name": "Win32",
"includePath": [
"${workspaceRoot}/include"
],
"defines": [
"_DEBUG",
"UNICODE"
],
"intelliSenseMode": "msvc-x64",
"browse": {
"path": [
"${workspaceRoot}/include"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++11"
}
],
"version": 4
}

View File

@ -1,128 +0,0 @@
# Copyright Louis Dionne 2013-2017
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
#
#
# This CMake module provides a function generating a unit test to make sure
# that every public header can be included on its own.
#
# When a C++ library or application has many header files, it can happen that
# a header does not include all the other headers it depends on. When this is
# the case, it can happen that including that header file on its own will
# break the compilation. This CMake module generates a dummy executable
# comprised of many .cpp files, each of which includes a header file that
# is part of the public API. In other words, the executable is comprised
# of .cpp files of the form:
#
# #include <the/public/header.hpp>
#
# and then exactly one `main` function. If this succeeds to compile, it means
# that the header can be included on its own, which is what clients expect.
# Otherwise, you have a problem. Since writing these dumb unit tests by hand
# is tedious and repetitive, you can use this CMake module to automate this
# task.
# add_header_test(<target> [EXCLUDE_FROM_ALL] [EXCLUDE excludes...] HEADERS headers...)
#
# Generates header-inclusion unit tests for all the specified headers.
#
# This function creates a target which builds a dummy executable including
# each specified header file individually. If this target builds successfully,
# it means that all the specified header files can be included individually.
#
# Parameters
# ----------
# <target>:
# The name of the target to generate.
#
# HEADERS headers:
# A list of header files to generate the inclusion tests for. All headers
# in this list must be represented as relative paths from the root of the
# include directory added to the compiler's header search path. In other
# words, it should be possible to include all headers in this list as
#
# #include <${header}>
#
# For example, for a library with the following structure:
#
# project/
# doc/
# test/
# ...
# include/
# boost/
# hana.hpp
# hana/
# transform.hpp
# tuple.hpp
# pair.hpp
# ...
#
# When building the unit tests for that library, we'll add `-I project/include'
# to the compiler's arguments. The list of public headers should therefore contain
#
# boost/hana.hpp
# boost/hana/transform.hpp
# boost/hana/tuple.hpp
# boost/hana/pair.hpp
# ...
#
# Usually, all the 'public' header files of a library should be tested for
# standalone inclusion. A header is considered 'public' if a client should
# be able to include that header on its own.
#
# [EXCLUDE excludes]:
# An optional list of headers or regexes for which no unit test should be
# generated. Basically, any header in the list specified by the `HEADERS`
# argument that matches anything in `EXCLUDE` will be skipped.
#
# [EXCLUDE_FROM_ALL]:
# If provided, the generated target is excluded from the 'all' target.
#
function(add_header_test target)
cmake_parse_arguments(ARGS "EXCLUDE_FROM_ALL" # options
"" # 1 value args
"HEADERS;EXCLUDE" # multivalued args
${ARGN})
if (NOT ARGS_HEADERS)
message(FATAL_ERROR "The `HEADERS` argument must be provided.")
endif()
if (ARGS_EXCLUDE_FROM_ALL)
set(ARGS_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL")
else()
set(ARGS_EXCLUDE_FROM_ALL "")
endif()
foreach(header ${ARGS_HEADERS})
set(skip FALSE)
foreach(exclude ${ARGS_EXCLUDE})
if (${header} MATCHES ${exclude})
set(skip TRUE)
break()
endif()
endforeach()
if (skip)
continue()
endif()
get_filename_component(filename "${header}" NAME_WE)
get_filename_component(directory "${header}" DIRECTORY)
set(source "${CMAKE_CURRENT_BINARY_DIR}/headers/${directory}/${filename}.cpp")
if (NOT EXISTS "${source}")
file(WRITE "${source}" "#include <${header}>")
endif()
list(APPEND sources "${source}")
endforeach()
set(standalone_main "${CMAKE_CURRENT_BINARY_DIR}/headers/_standalone_main.cpp")
if (NOT EXISTS "${standalone_main}")
file(WRITE "${standalone_main}" "int main() { }")
endif()
add_executable(${target}
${ARGS_EXCLUDE_FROM_ALL}
${sources}
"${CMAKE_CURRENT_BINARY_DIR}/headers/_standalone_main.cpp"
)
endfunction()

View File

@ -1,6 +0,0 @@
#include <new>
int main() {
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 4) S {};
(void) ::operator new(sizeof(S), static_cast<std::align_val_t>(alignof(S)));
}

View File

@ -1,19 +0,0 @@
template<class>
concept bool True = true;
template<class T>
constexpr bool test(T)
{
return false;
}
template<True T>
constexpr bool test(T)
{
return true;
}
int main()
{
static_assert(::test(42), "");
}

View File

@ -1,34 +0,0 @@
#include <experimental/coroutine>
struct present
{
struct promise_type
{
int result;
present get_return_object() { return {*this}; }
std::experimental::suspend_never initial_suspend() { return {}; }
std::experimental::suspend_never final_suspend() { return {}; }
void return_value(int i) { result = i; }
void unhandled_exception() {}
};
promise_type& promise;
bool await_ready() const { return true; }
void await_suspend(std::experimental::coroutine_handle<>) const {}
int await_resume() const { return promise.result; }
};
present f(int n)
{
if (n < 2)
co_return 1;
else
co_return n * co_await f(n - 1);
}
int main()
{
return f(5).promise.result != 120;
}

View File

@ -1,43 +0,0 @@
# Copyright Louis Dionne 2015
# Copyright Gonzalo Brito Gadeschi 2015
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
#
# Setup compiler flags (more can be set on a per-target basis or in
# subdirectories)
# Enable all warnings:
ranges_append_flag(RANGES_HAS_WEVERYTHING -Weverything)
ranges_append_flag(RANGES_HAS_PEDANTIC -pedantic)
ranges_append_flag(RANGES_HAS_PEDANTIC_ERRORS -pedantic-errors)
# Selectively disable those warnings that are not worth it:
ranges_append_flag(RANGES_HAS_WNO_CXX98_COMPAT -Wno-c++98-compat)
ranges_append_flag(RANGES_HAS_WNO_CXX98_COMPAT_PEDANTIC -Wno-c++98-compat-pedantic)
ranges_append_flag(RANGES_HAS_WNO_WEAK_VTABLES -Wno-weak-vtables)
ranges_append_flag(RANGES_HAS_WNO_PADDED -Wno-padded)
ranges_append_flag(RANGES_HAS_WNO_MISSING_PROTOTYPES -Wno-missing-prototypes)
ranges_append_flag(RANGES_HAS_WNO_MISSING_VARIABLE_DECLARATIONS -Wno-missing-variable-declarations)
ranges_append_flag(RANGES_HAS_WNO_DOCUMENTATION -Wno-documentation)
ranges_append_flag(RANGES_HAS_WNO_DOCUMENTATION_UNKNOWN_COMMAND -Wno-documentation-unknown-command)
ranges_append_flag(RANGES_HAS_WNO_OLD_STYLE_CAST -Wno-old-style-cast)
if (RANGES_ENV_MACOSX)
ranges_append_flag(RANGES_HAS_WNO_GLOBAL_CONSTRUCTORS -Wno-global-constructors)
ranges_append_flag(RANGES_HAS_WNO_EXIT_TIME_DESTRUCTORS -Wno-exit-time-destructors)
endif()
if (RANGES_CXX_COMPILER_GCC)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.0")
ranges_append_flag(RANGES_HAS_WNO_STRICT_OVERFLOW -Wno-strict-overflow)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0")
ranges_append_flag(RANGES_HAS_WNO_MISSING_FIELD_INITIALIZERS -Wno-missing-field-initializers)
endif()
elseif ((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0") OR (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "7.0"))
ranges_append_flag(RANGES_HAS_WNO_NOEXCEPT_TYPE -Wno-noexcept-type)
endif()
endif()
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: test C++ flags: ${CMAKE_CXX_FLAGS}")
endif()

View File

@ -1,94 +0,0 @@
# Copyright Gonzalo Brito Gadeschi 2015
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
#
# Detects the C++ compiler, system, build-type, etc.
include(CheckCXXCompilerFlag)
if("x${CMAKE_CXX_COMPILER_ID}" MATCHES "x.*Clang")
if("x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
set (RANGES_CXX_COMPILER_CLANGCL TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: compiler is clang-cl.")
endif()
else()
set (RANGES_CXX_COMPILER_CLANG TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: compiler is clang.")
endif()
endif()
elseif(CMAKE_COMPILER_IS_GNUCXX)
set (RANGES_CXX_COMPILER_GCC TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: compiler is gcc.")
endif()
elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
set (RANGES_CXX_COMPILER_MSVC TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: compiler is msvc.")
endif()
else()
message(WARNING "[range-v3 warning]: unknown compiler ${CMAKE_CXX_COMPILER_ID} !")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set (RANGES_ENV_MACOSX TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: system is MacOSX.")
endif()
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
set (RANGES_ENV_LINUX TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: system is Linux.")
endif()
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
set (RANGES_ENV_WINDOWS TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: system is Windows.")
endif()
else()
message(WARNING "[range-v3 warning]: unknown system ${CMAKE_SYSTEM_NAME} !")
endif()
if (RANGES_CXX_COMPILER_MSVC AND RANGES_CXX_STD LESS 17)
# MSVC is currently supported only in 17+ mode
set(RANGES_CXX_STD 17)
elseif(RANGES_CXX_STD LESS 14)
set(RANGES_CXX_STD 14)
endif()
# Build type
set(RANGES_DEBUG_BUILD FALSE)
set(RANGES_RELEASE_BUILD FALSE)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(RANGES_DEBUG_BUILD TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: build type is debug.")
endif()
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
set(RANGES_RELEASE_BUILD TRUE)
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: build type is release.")
endif()
else()
message(WARNING "[range-v3 warning]: unknown build type, defaults to release!")
set(CMAKE_BUILD_TYPE "Release")
set(RANGES_RELEASE_BUILD TRUE)
endif()
# Find Valgrind
find_program(MEMORYCHECK_COMMAND valgrind)
if(MEMORYCHECK_COMMAND)
set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full")
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: valgrind found at path: ${MEMORYCHECK_COMMAND}")
endif()
else()
if (RANGES_VERBOSE_BUILD)
message(WARNING "[range-v3 warning]: valgrind not found!")
endif()
endif()
find_package(Doxygen)
find_package(Git)

View File

@ -1,247 +0,0 @@
# Copyright Louis Dionne 2015
# Copyright Gonzalo Brito Gadeschi 2015
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
#
# Setup compiler flags (more can be set on a per-target basis or in
# subdirectories)
# Compilation flags
include(CheckCXXCompilerFlag)
macro(ranges_append_flag testname flag)
# As -Wno-* flags do not lead to build failure when there are no other
# diagnostics, we check positive option to determine their applicability.
# Of course, we set the original flag that is requested in the parameters.
string(REGEX REPLACE "^-Wno-" "-W" alt ${flag})
check_cxx_compiler_flag(${alt} ${testname})
if (${testname})
add_compile_options(${flag})
endif()
endmacro()
# All compilation flags
# Language flag: version of the C++ standard to use
message(STATUS "[range-v3]: C++ std=${RANGES_CXX_STD}")
if (RANGES_CXX_COMPILER_CLANGCL OR RANGES_CXX_COMPILER_MSVC)
ranges_append_flag(RANGES_HAS_CXXSTDCOLON "/std:c++${RANGES_CXX_STD}")
set(RANGES_STD_FLAG "/std:c++${RANGES_CXX_STD}")
if (RANGES_CXX_COMPILER_CLANGCL)
ranges_append_flag(RANGES_HAS_FNO_MS_COMPATIBIILITY "-fno-ms-compatibility")
ranges_append_flag(RANGES_HAS_FNO_DELAYED_TEMPLATE_PARSING "-fno-delayed-template-parsing")
endif()
# Enable "normal" warnings and make them errors:
ranges_append_flag(RANGES_HAS_W3 /W3)
ranges_append_flag(RANGES_HAS_WX /WX)
else()
ranges_append_flag(RANGES_HAS_CXXSTD "-std=c++${RANGES_CXX_STD}")
set(RANGES_STD_FLAG "-std=c++${RANGES_CXX_STD}")
# Enable "normal" warnings and make them errors:
ranges_append_flag(RANGES_HAS_WALL -Wall)
ranges_append_flag(RANGES_HAS_WEXTRA -Wextra)
ranges_append_flag(RANGES_HAS_WERROR -Werror)
endif()
if (RANGES_ENV_LINUX AND RANGES_CXX_COMPILER_CLANG)
# On linux libc++ re-exports the system math headers. The ones from libstdc++
# use the GCC __extern_always_inline intrinsic which is not supported by clang
# versions 3.6, 3.7, 3.8, 3.9, 4.0, and current trunk 5.0 (as of 2017.04.13).
#
# This works around it by replacing __extern_always_inline with inline using a
# macro:
ranges_append_flag(RANGES_HAS_D__EXTERN_ALWAYS_INLINE -D__extern_always_inline=inline)
endif()
# Deep integration support
if (RANGES_DEEP_STL_INTEGRATION)
if (RANGES_CXX_COMPILER_MSVC)
add_compile_options(/I "${PROJECT_SOURCE_DIR}/include/std")
else()
add_compile_options(-isystem "${PROJECT_SOURCE_DIR}/include/std")
endif()
add_compile_options(-DRANGES_DEEP_STL_INTEGRATION=1)
endif()
# Template diagnostic flags
ranges_append_flag(RANGES_HAS_FDIAGNOSTIC_SHOW_TEMPLATE_TREE -fdiagnostics-show-template-tree)
ranges_append_flag(RANGES_HAS_FTEMPLATE_BACKTRACE_LIMIT "-ftemplate-backtrace-limit=0")
ranges_append_flag(RANGES_HAS_FMACRO_BACKTRACE_LIMIT "-fmacro-backtrace-limit=1")
# Clang modules support
if (RANGES_MODULES)
ranges_append_flag(RANGES_HAS_MODULES -fmodules)
ranges_append_flag(RANGES_HAS_MODULE_MAP_FILE "-fmodule-map-file=${PROJECT_SOURCE_DIR}/include/module.modulemap")
ranges_append_flag(RANGES_HAS_MODULE_CACHE_PATH "-fmodules-cache-path=${PROJECT_BINARY_DIR}/module.cache")
if (RANGES_LIBCXX_MODULE)
ranges_append_flag(RANGES_HAS_LIBCXX_MODULE_MAP_FILE "-fmodule-map-file=${RANGES_LIBCXX_MODULE}")
endif()
if (RANGES_ENV_MACOSX)
ranges_append_flag(RANGES_HAS_NO_IMPLICIT_MODULE_MAPS -fno-implicit-module-maps)
endif()
if (RANGES_DEBUG_BUILD)
ranges_append_flag(RANGES_HAS_GMODULES -gmodules)
endif()
endif()
# Sanitizer support: detect incompatible sanitizer combinations
if (RANGES_ASAN AND RANGES_MSAN)
message(FATAL_ERROR "[range-v3 error]: AddressSanitizer and MemorySanitizer are both enabled at the same time!")
endif()
if (RANGES_MSAN AND RANGES_ENV_MACOSX)
message(FATAL_ERROR "[range-v3 error]: MemorySanitizer is not supported on MacOSX!")
endif()
# AddressSanitizer support
if (RANGES_ASAN)
# This policy enables passing the linker flags to the linker when trying to
# test the features, which is required to successfully link ASan binaries
cmake_policy(SET CMP0056 NEW)
set (ASAN_FLAGS "")
if (RANGES_ENV_MACOSX) # LeakSanitizer not supported on MacOSX
set (ASAN_FLAGS "-fsanitize=address,signed-integer-overflow,shift,integer-divide-by-zero,implicit-signed-integer-truncation,implicit-integer-sign-change,undefined,nullability")
else()
if (RANGES_CXX_COMPILER_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.0")
set (ASAN_FLAGS "-fsanitize=address")
else()
set (ASAN_FLAGS "-fsanitize=address,signed-integer-overflow,shift,integer-divide-by-zero,implicit-signed-integer-truncation,implicit-integer-sign-change,leak,nullability")
endif()
endif()
ranges_append_flag(RANGES_HAS_ASAN "${ASAN_FLAGS}")
if (RANGES_HAS_ASAN) #ASAN flags must be passed to the linker:
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${ASAN_FLAGS}")
endif()
ranges_append_flag(RANGES_HAS_SANITIZE_NO_RECOVER "-fno-sanitize-recover=all")
ranges_append_flag(RANGES_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer)
endif()
# MemorySanitizer support
if (RANGES_MSAN)
# This policy enables passing the linker flags to the linker when trying to
# compile the examples, which is required to successfully link MSan binaries
cmake_policy(SET CMP0056 NEW)
ranges_append_flag(RANGES_HAS_MSAN "-fsanitize=memory")
ranges_append_flag(RANGES_HAS_MSAN_TRACK_ORIGINS -fsanitize-memory-track-origins)
ranges_append_flag(RANGES_HAS_SANITIZE_RECOVER_ALL "-fno-sanitize-recover=all")
ranges_append_flag(RANGES_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer)
endif()
# Build types:
if (RANGES_DEBUG_BUILD AND RANGES_RELEASE_BUILD)
message(FATAL_ERROR "[range-v3 error] Cannot simultaneously generate debug and release builds!")
endif()
if (RANGES_DEBUG_BUILD)
ranges_append_flag(RANGES_HAS_O0 -O0)
ranges_append_flag(RANGES_HAS_NO_INLINE -fno-inline)
ranges_append_flag(RANGES_HAS_STACK_PROTECTOR_ALL -fstack-protector-all)
ranges_append_flag(RANGES_HAS_G3 -g3)
# Clang can generate debug info tuned for LLDB or GDB
if (RANGES_CXX_COMPILER_CLANG)
if (RANGES_ENV_MACOSX)
ranges_append_flag(RANGES_HAS_GLLDB -glldb)
elseif(RANGES_ENV_LINUX)
ranges_append_flag(RANGES_HAS_GGDB -ggdb)
endif()
endif()
endif()
if (RANGES_RELEASE_BUILD)
if (NOT RANGES_ASSERTIONS)
ranges_append_flag(RANGES_HAS_DNDEBUG -DNDEBUG)
endif()
if (NOT RANGES_ASAN AND NOT RANGES_MSAN)
# The quality of ASan and MSan error messages suffers if we disable the
# frame pointer, so leave it enabled when compiling with either of them:
ranges_append_flag(RANGES_HAS_OMIT_FRAME_POINTER -fomit-frame-pointer)
endif()
ranges_append_flag(RANGES_HAS_OFAST -Ofast)
if (NOT RANGES_HAS_OFAST)
ranges_append_flag(RANGES_HAS_O2 -O2)
endif()
ranges_append_flag(RANGES_HAS_STRICT_ALIASING -fstrict-aliasing)
ranges_append_flag(RANGES_HAS_STRICT_VTABLE_POINTERS -fstrict-vtable-pointers)
ranges_append_flag(RANGES_HAS_FAST_MATH -ffast-math)
ranges_append_flag(RANGES_HAS_VECTORIZE -fvectorize)
if (NOT RANGES_ENV_MACOSX)
# Sized deallocation is not available in MacOSX:
ranges_append_flag(RANGES_HAS_SIZED_DEALLOCATION -fsized-deallocation)
endif()
if (RANGES_LLVM_POLLY)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly -mllvm -polly-vectorizer=stripmine")
endif()
if (RANGES_CXX_COMPILER_CLANG AND (NOT (RANGES_INLINE_THRESHOLD EQUAL -1)))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -inline-threshold=${RANGES_INLINE_THRESHOLD}")
endif()
endif()
if (RANGES_NATIVE)
ranges_append_flag(RANGES_HAS_MARCH_NATIVE "-march=native")
ranges_append_flag(RANGES_HAS_MTUNE_NATIVE "-mtune=native")
endif()
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS ${RANGES_STD_FLAG})
# Probe for library and compiler support for aligned new
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/aligned_new_probe.cpp" RANGE_V3_PROBE_CODE)
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGE_V3_ALIGNED_NEW_PROBE)
unset(RANGE_V3_PROBE_CODE)
unset(CMAKE_REQUIRED_FLAGS)
if (NOT RANGE_V3_ALIGNED_NEW_PROBE)
add_compile_options("-DRANGES_CXX_ALIGNED_NEW=0")
endif()
# Probe for coroutine TS support
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/coro_test_code.cpp" RANGE_V3_PROBE_CODE)
if(RANGES_CXX_COMPILER_MSVC)
set(CMAKE_REQUIRED_FLAGS "/await")
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGES_HAS_AWAIT)
if(RANGES_HAS_AWAIT)
set(RANGE_V3_COROUTINE_FLAGS "/await")
endif()
elseif(RANGES_CXX_COMPILER_CLANG)
set(CMAKE_REQUIRED_FLAGS "-fcoroutines-ts ${RANGES_STD_FLAG}")
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGES_HAS_FCOROUTINES_TS)
if(RANGES_HAS_FCOROUTINES_TS)
set(RANGE_V3_COROUTINE_FLAGS "-fcoroutines-ts")
endif()
endif()
unset(CMAKE_REQUIRED_FLAGS)
unset(RANGE_V3_PROBE_CODE)
if (RANGE_V3_COROUTINE_FLAGS)
add_compile_options(${RANGE_V3_COROUTINE_FLAGS})
endif()
# Test for concepts support
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/concepts_test_code.cpp" RANGE_V3_PROBE_CODE)
if(RANGES_CXX_COMPILER_GCC OR RANGES_CXX_COMPILER_CLANG)
set(CMAKE_REQUIRED_FLAGS "-fconcepts ${RANGES_STD_FLAG}")
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGE_V3_HAS_FCONCEPTS)
if(RANGE_V3_HAS_FCONCEPTS)
set(RANGE_V3_CONCEPTS_FLAGS "-fconcepts")
endif()
endif()
unset(CMAKE_REQUIRED_FLAGS)
unset(RANGE_V3_PROBE_CODE)
if (RANGE_V3_CONCEPTS_FLAGS AND RANGES_PREFER_REAL_CONCEPTS)
add_compile_options(${RANGE_V3_CONCEPTS_FLAGS})
endif()
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: C++ flags: ${CMAKE_CXX_FLAGS}")
message(STATUS "[range-v3]: C++ debug flags: ${CMAKE_CXX_FLAGS_DEBUG}")
message(STATUS "[range-v3]: C++ Release Flags: ${CMAKE_CXX_FLAGS_RELEASE}")
message(STATUS "[range-v3]: C++ Compile Flags: ${CMAKE_CXX_COMPILE_FLAGS}")
message(STATUS "[range-v3]: Compile options: ${COMPILE_OPTIONS_}")
message(STATUS "[range-v3]: C Flags: ${CMAKE_C_FLAGS}")
message(STATUS "[range-v3]: C Compile Flags: ${CMAKE_C_COMPILE_FLAGS}")
message(STATUS "[range-v3]: EXE Linker flags: ${CMAKE_EXE_LINKER_FLAGS}")
message(STATUS "[range-v3]: C++ Linker flags: ${CMAKE_CXX_LINK_FLAGS}")
message(STATUS "[range-v3]: MODULE Linker flags: ${CMAKE_MODULE_LINKER_FLAGS}")
get_directory_property(CMakeCompDirDefs COMPILE_DEFINITIONS)
message(STATUS "[range-v3]: Compile Definitions: ${CmakeCompDirDefs}")
endif()

View File

@ -1,56 +0,0 @@
# Copyright Gonzalo Brito Gadeschi 2015
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
#
# CMake options
include(CMakeDependentOption)
set(RANGES_CXX_STD 14 CACHE STRING "C++ standard version.")
option(RANGES_BUILD_CALENDAR_EXAMPLE "Builds the calendar example." ON)
option(RANGES_ASAN "Run the tests using AddressSanitizer." OFF)
option(RANGES_MSAN "Run the tests using MemorySanitizer." OFF)
option(RANGES_ASSERTIONS "Enable assertions." ON)
option(RANGES_DEBUG_INFO "Include debug information in the binaries." ON)
option(RANGES_MODULES "Enables use of Clang modules (experimental)." OFF)
option(RANGES_NATIVE "Enables -march/-mtune=native." ON)
option(RANGES_VERBOSE_BUILD "Enables debug output from CMake." OFF)
option(RANGES_LLVM_POLLY "Enables LLVM Polly." OFF)
option(RANGES_PREFER_REAL_CONCEPTS
"Use real concepts instead of emulation if the compiler supports it"
ON)
option(RANGES_DEEP_STL_INTEGRATION
"Hijacks the primary std::iterator_traits template to emulate the C++20 std::ranges:: behavior."
OFF)
option(RANGE_V3_HEADER_CHECKS
"Build the Range-v3 header checks and integrate with ctest"
OFF)
set(RANGES_INLINE_THRESHOLD -1 CACHE STRING "Force a specific inlining threshold.")
# Enable verbose configure when passing -Wdev to CMake
if (DEFINED CMAKE_SUPPRESS_DEVELOPER_WARNINGS AND
NOT CMAKE_SUPPRESS_DEVELOPER_WARNINGS)
set(RANGES_VERBOSE_BUILD ON)
endif()
if (RANGES_VERBOSE_BUILD)
message(STATUS "[range-v3]: verbose build enabled.")
endif()
CMAKE_DEPENDENT_OPTION(RANGE_V3_TESTS
"Build the Range-v3 tests and integrate with ctest"
ON "${is_standalone}" OFF)
CMAKE_DEPENDENT_OPTION(RANGE_V3_EXAMPLES
"Build the Range-v3 examples and integrate with ctest"
ON "${is_standalone}" OFF)
CMAKE_DEPENDENT_OPTION(RANGE_V3_PERF
"Build the Range-v3 performance benchmarks"
ON "${is_standalone}" OFF)
CMAKE_DEPENDENT_OPTION(RANGE_V3_DOCS
"Build the Range-v3 documentation"
ON "${is_standalone}" OFF)
mark_as_advanced(RANGE_V3_PERF)

View File

@ -1,5 +0,0 @@
# CMake files overview:
- `ranges_options.cmake`: All options to configure the library.
- `ranges_env.cmake`: Detects the environment: operating system, compiler, build-type, ...
- `ranges_flags.cmake`: Sets up all compiler flags.

View File

@ -1,35 +0,0 @@
# Range v3 library
#
# Copyright Luis Martinez de Bartolome Izquierdo 2016
#
# Use, modification and distribution is subject to the
# Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
#
# Project home: https://github.com/ericniebler/range-v3
#
from conans import ConanFile, CMake
class Rangev3Conan(ConanFile):
name = "range-v3"
version = "0.9.0"
license = "Boost Software License - Version 1.0 - August 17th, 2003"
url = "https://github.com/ericniebler/range-v3"
description = """Experimental range library for C++11/14/17"""
# No settings/options are necessary, this is header only
exports_sources = "include*", "LICENSE.txt", "CMakeLists.txt", "cmake/*", "Version.cmake", "version.hpp.in"
no_copy_source = True
def package(self):
cmake = CMake(self)
cmake.definitions["RANGE_V3_TESTS"] = "OFF"
cmake.definitions["RANGE_V3_EXAMPLES"] = "OFF"
cmake.definitions["RANGE_V3_PERF"] = "OFF"
cmake.definitions["RANGE_V3_DOCS"] = "OFF"
cmake.definitions["RANGE_V3_HEADER_CHECKS"] = "OFF"
cmake.configure()
cmake.install()
self.copy("LICENSE.txt", dst="licenses", ignore_case=True, keep_path=False)

View File

@ -1,87 +0,0 @@
#=============================================================================
# Setup the documentation
#=============================================================================
if (NOT DOXYGEN_FOUND)
message(STATUS
"Doxygen not found; the 'doc' and 'gh-pages.{clean,copy,update}' targets "
"will be unavailable.")
return()
endif()
set(CMAKE_FOLDER "doc")
configure_file(Doxyfile.in Doxyfile @ONLY)
add_custom_target(doc.check
COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile
COMMENT "Running Doxygen to validate the documentation"
VERBATIM
)
set_target_properties(doc.check
PROPERTIES FOLDER ${CMAKE_FOLDER}
)
# if (NOT TARGET benchmarks)
# message(STATUS
# "The 'benchmarks' target is not available; the 'doc' and "
# "'gh-pages.{clean,copy,update}' targets will be unavailable. "
# "The 'doc.check' target can still be used to generate the "
# "documentation to check for errors/warnings.")
# return()
# endif()
add_custom_target(doc
COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile
COMMENT "Generating API documentation with Doxygen"
# DEPENDS benchmarks
VERBATIM
)
set_target_properties(doc
PROPERTIES FOLDER ${CMAKE_FOLDER}
)
if (NOT GIT_FOUND)
message(STATUS
"Git was not found; the 'gh-pages.{clean,copy,update}' targets "
"will be unavailable.")
return()
endif()
add_custom_target(gh-pages.clean
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/clean-gh-pages.cmake
COMMAND ${CMAKE_COMMAND} -E remove_directory search
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/gh-pages
COMMENT "Cleaning up doc/gh-pages"
VERBATIM
)
set_target_properties(gh-pages.clean
PROPERTIES FOLDER ${CMAKE_FOLDER}
)
add_custom_target(gh-pages.copy
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/html ${CMAKE_CURRENT_LIST_DIR}/gh-pages
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/gh-pages
COMMENT "Copying the documentation from ${CMAKE_CURRENT_BINARY_DIR}/html to doc/gh-pages"
DEPENDS doc gh-pages.clean
VERBATIM
)
set_target_properties(gh-pages.copy
PROPERTIES FOLDER ${CMAKE_FOLDER}
)
execute_process(
COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} rev-parse --short HEAD
OUTPUT_VARIABLE RANGE_V3_GIT_SHORT_SHA
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_custom_target(gh-pages.update
COMMAND ${GIT_EXECUTABLE} add --all .
COMMAND ${GIT_EXECUTABLE} commit -m "Update to ${RANGE_V3_GIT_SHORT_SHA}"
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/gh-pages
COMMENT "Updating the gh-pages branch with freshly built documentation"
DEPENDS gh-pages.copy
VERBATIM
)
set_target_properties(gh-pages.update
PROPERTIES FOLDER ${CMAKE_FOLDER}
)

View File

@ -1,174 +0,0 @@
PROJECT_NAME = "Range-v3"
PROJECT_BRIEF = "Range algorithms, views, and actions for the Standard Library"
PROJECT_LOGO =
PROJECT_NUMBER =
STRIP_FROM_PATH = @Range-v3_SOURCE_DIR@/include
BUILTIN_STL_SUPPORT = YES
STRIP_FROM_INC_PATH = @Range-v3_SOURCE_DIR@/include
ALIASES =
ENABLED_SECTIONS =
# Resources
OUTPUT_DIRECTORY =
INPUT = @Range-v3_SOURCE_DIR@/include \
@Range-v3_SOURCE_DIR@/doc/index.md \
@Range-v3_SOURCE_DIR@/doc/examples.md \
@Range-v3_SOURCE_DIR@/doc/release_notes.md
FILE_PATTERNS = *.hpp *.md
RECURSIVE = YES
EXCLUDE = @Range-v3_SOURCE_DIR@/include/range/v3/detail \
@Range-v3_SOURCE_DIR@/include/range/v3/algorithm/aux_ \
@Range-v3_SOURCE_DIR@/include/range/v3/algorithm/tagspec.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/at.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/back.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/begin_end.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/data.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/distance.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/empty.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/front.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/getlines.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/index.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/istream_range.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/iterator_range.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/range_access.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/range_concepts.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/range_traits.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/size.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/span.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/to_container.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/to_container.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/associated_types.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/basic_iterator.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/common_iterator.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/concepts.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/counted_iterator.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/dangling.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/functional.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/infinity.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/invoke.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/iterator_concepts.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/iterator_traits.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/iterator.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/nullptr_v.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/semiregular.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/tagged_pair.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/tagged_tuple.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/unreachable.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/utility/view_adaptor.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/view_adaptor.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/view_facade.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/view_interface.hpp \
@Range-v3_SOURCE_DIR@/include/range/v3/view/bounded.hpp
EXAMPLE_PATH = @Range-v3_SOURCE_DIR@/example \
@Range-v3_SOURCE_DIR@/test
EXAMPLE_RECURSIVE = YES
IMAGE_PATH = @Range-v3_BINARY_DIR@/image
WARN_IF_UNDOCUMENTED = NO
SHOW_GROUPED_MEMB_INC = YES
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = YES
MULTILINE_CPP_IS_BRIEF = YES
INHERIT_DOCS = NO
SEPARATE_MEMBER_PAGES = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = NO
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
# Generated formats
GENERATE_HTML = YES
GENERATE_LATEX = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST = YES
SHOW_USED_FILES = NO
SHOW_FILES = YES
SHOW_NAMESPACES = YES
LAYOUT_FILE = @Range-v3_SOURCE_DIR@/doc/layout.xml
CLASS_DIAGRAMS = YES
HAVE_DOT = NO
HIDE_UNDOC_RELATIONS = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = NO
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = YES
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = YES
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 1
# Preprocessing
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH = @Range-v3_SOURCE_DIR@/include
INCLUDE_FILE_PATTERNS =
PREDEFINED = RANGES_DOXYGEN_INVOKED=1 \
META_DOXYGEN_INVOKED=1 \
CPP_DOXYGEN_INVOKED=1 \
"RANGES_INLINE_VARIABLE(T,N)=inline constexpr T N{};"
SKIP_FUNCTION_MACROS = NO
# Source browsing
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = NO
# CLANG_ASSISTED_PARSING = NO
# CLANG_OPTIONS =
# HTML output
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 75 # 0 - 359
HTML_COLORSTYLE_SAT = 100 # 0 - 255
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_SECTIONS = YES
HTML_INDEX_NUM_ENTRIES = 0 # Fully expand trees in the Indexes by default
DISABLE_INDEX = YES
GENERATE_TREEVIEW = YES
TREEVIEW_WIDTH = 270
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
SEARCHENGINE = YES
# Mathjax (HTML only)
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =

View File

@ -1,4 +0,0 @@
FILE(GLOB gh_files "*.html" "*.js" "*.css" "*.png")
IF( gh_files )
execute_process( COMMAND ${CMAKE_COMMAND} -E remove ${gh_files} )
ENDIF()

View File

@ -1,62 +0,0 @@
Examples
========
\section example-algorithms Examples: Algorithms
\subsection example-hello Hello, Ranges!
\snippet hello.cpp hello
\subsection example-any-all-none any_of, all_of, none_of
\snippet any_all_none_of.cpp any_all_none_of
\subsection example-count count
\snippet count.cpp count
\subsection example-count_if count_if
\snippet count_if.cpp count_if
\subsection example-find find, find_if, find_if_not on sequence containers
\snippet find.cpp find
\subsection example-for_each-seq for_each on sequence containers
\snippet for_each_sequence.cpp for_each_sequence
\subsection example-for_each-assoc for_each on associative containers
\snippet for_each_assoc.cpp for_each_assoc
\subsection example-is_sorted is_sorted
\snippet is_sorted.cpp is_sorted
\section example-views Examples: Views
\subsection example-filter-transform Filter and transform
\snippet filter_transform.cpp filter_transform
\subsection example-accumulate-ints Generate ints and accumulate
\snippet accumulate_ints.cpp accumulate_ints
\subsection example-comprehension-conversion Convert a range comprehension to a vector
\snippet comprehension_conversion.cpp comprehension_conversion
\section example-actions Examples: Actions
\subsection example-sort-unique Remove non-unique elements from a container
\snippet sort_unique.cpp sort_unique
\section example-gestalt Examples: Putting it all together
\subsection example-calendar Calendar
\snippet calendar.cpp calendar

View File

@ -1,950 +0,0 @@
User Manual {#mainpage}
===========
\tableofcontents
\section tutorial-preface Preface
--------------------------------------------
Range library for C++14/17/20. This code is the basis of [the range support in C++20](http://eel.is/c++draft/#ranges).
**Development Status:**
This code is fairly stable, well-tested, and suitable for casual use, although
currently lacking documentation. No promise is made about support or long-term
stability. This code *will* evolve without regard to backwards compatibility.
A notable exception is anything found within the `ranges::cpp20` namespace.
Those components will change rarely or (preferably) never at all.
\subsection tutorial-installation Installation
--------------------------------------------
This library is header-only. You can get the source code from the
[range-v3 repository](https://github.com/ericniebler/range-v3) on github. To
compile with Range-v3, just `#include` the individual headers you want.
This distribution actually contains three separate header-only libraries:
* <strong><tt>include/concepts/...</tt></strong> contains the Concepts Portability Preprocessor, or
CPP, which is a set of macros for defining and using concept checks,
regardless of whether your compiler happens to support the C++20 concepts
language feature or not.
* <strong><tt>include/meta/...</tt></strong> contains the Meta Library, which is a set of
meta-programming utilities for processing types and lists of types at compile
time.
* <strong><tt>include/range/...</tt></strong> contains the Range-v3 library, as described below.
The Range-v3 library is physically structured in directories by feature group:
* <strong><tt>include/range/v3/actions/...</tt></strong> contains _actions_, or composable
components that operate eagerly on containers and return the mutated container
for further actions.
* <strong><tt>include/range/v3/algorithms/...</tt></strong> contains all the STL _algorithms_ with
overloads that accept ranges, in addition to the familiar overloads that take iterators.
* <strong><tt>include/range/v3/functional/...</tt></strong> contains many generally useful
components that would be familiar to functional programmers.
* <strong><tt>include/range/v3/iterator/...</tt></strong> contains the definitions of many useful
iterators and iterator-related concepts and utilities.
* <strong><tt>include/range/v3/numeric/...</tt></strong> contains numeric algorithms corresponding
to those found in the standard `<numeric>` header.
* <strong><tt>include/range/v3/range/...</tt></strong> contains range-related utilities, such as
`begin`, `end`, and `size`, range traits and concepts, and conversions to
containers.
* <strong><tt>include/range/v3/utility/...</tt></strong> contains a miscellaneous assortment of
reusable code.
* <strong><tt>include/range/v3/view/...</tt></strong> contains _views_, or composable
components that operate lazily on ranges and that themselves return ranges
that can be operated upon with additional view adaptors.
\subsection tutorial-license License
--------------------------------------------
Most of the source code in this project are mine, and those are under the Boost
Software License. Parts are taken from Alex Stepanov's Elements of Programming,
Howard Hinnant's libc++, and from the SGI STL. Please see the attached LICENSE
file and the CREDITS file for the licensing and acknowledgements.
\subsection tutorial-compilers Supported Compilers
--------------------------------------------------------------------------------
The code is known to work on the following compilers:
- clang 3.6.2
- GCC 5.0.2
- MSVC VS2017 15.9 (`_MSC_VER >= 1916`), with `/std:c++17 /permissive-`
\section tutorial-quick-start Quick Start
--------------------------------------------------------------------------------
Range-v3 is a generic library that augments the existing standard library with
facilities for working with *ranges*. A range can be loosely thought of a pair
of iterators, although they need not be implemented that way. Bundling begin/end
iterators into a single object brings several benefits: convenience,
composability, and correctness.
**Convenience**
It's more convenient to pass a single range object to an algorithm than separate
begin/end iterators. Compare:
~~~~~~~{.cpp}
std::vector<int> v{/*...*/};
std::sort( v.begin(), v.end() );
~~~~~~~
with
~~~~~~~{.cpp}
std::vector<int> v{/*...*/};
ranges::sort( v );
~~~~~~~
Range-v3 contains full implementations of all the standard algorithms with
range-based overloads for convenience.
**Composability**
Having a single range object permits *pipelines* of operations. In a pipeline, a
range is lazily adapted or eagerly mutated in some way, with the result
immediately available for further adaptation or mutation. Lazy adaption is
handled by *views*, and eager mutation is handled by *actions*.
For instance, the below uses _views_ to filter a container using a predicate
and transform the resulting range with a function. Note that the underlying
data is `const` and is not mutated by the views.
~~~~~~~{.cpp}
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i){ return i % 2 == 1; })
| views::transform([](int i){ return std::to_string(i); });
// rng == {"2","4","6","8","10"};
~~~~~~~
In the code above, `rng` simply stores a reference to the underlying data and
the filter and transformation functions. No work is done until `rng` is
iterated.
In contrast, _actions_ do their work eagerly, but they also compose. Consider
the code below, which reads some data into a vector, sorts it, and makes it
unique.
~~~~~~~{.cpp}
extern std::vector<int> read_data();
using namespace ranges;
std::vector<int> vi = read_data() | actions::sort | actions::unique;
~~~~~~~
Unlike views, with actions each step in the pipeline (`actions::sort` and
`actions::unique`) accepts a container _by value_, mutates it in place, and
returns it.
**Correctness**
Whether you are using views or actions, you are operating on data in a pure
functional, declarative style. You rarely need to trouble yourself with
iterators, although they are there under the covers should you need them.
By operating declaratively and functionally instead of imperatively, we reduce
the need for overt state manipulation and branches and loops. This brings down
the number of states your program can be in, which brings down your bug counts.
In short, if you can find a way to express your solution as a composition of
functional transformations on your data, you can make your code _correct by
construction_.
\subsection tutorial-views Views
--------------------------------------------------------------------------------
As described above, the big advantage of ranges over iterators is their
composability. They permit a functional style of programming where data is
manipulated by passing it through a series of combinators. In addition, the
combinators can be *lazy*, only doing work when the answer is requested, and
*purely functional*, without mutating the original data. This makes it easier to
reason about your code.
A _view_ is a lightweight wrapper that presents a view of an underlying sequence
of elements in some custom way without mutating or copying it. Views are cheap
to create and copy and have non-owning reference semantics. Below are some
examples that use views:
Filter a container using a predicate and transform it.
~~~~~~~{.cpp}
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i){return i % 2 == 1;})
| views::transform([](int i){return std::to_string(i);});
// rng == {"2","4","6","8","10"};
~~~~~~~
Generate an infinite list of integers starting at 1, square them, take the first
10, and sum them:
~~~~~~~{.cpp}
using namespace ranges;
int sum = accumulate(views::ints(1)
| views::transform([](int i){return i*i;})
| views::take(10), 0);
~~~~~~~
Generate a sequence on the fly with a range comprehension and initialize a
vector with it:
~~~~~~~{.cpp}
using namespace ranges;
auto vi =
views::for_each(views::ints(1, 10), [](int i) {
return yield_from(views::repeat_n(i, i));
})
| to<std::vector>();
// vi == {1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,...}
~~~~~~~
### View const-ness
Logically, a view is a factory for iterators, but in practice a view is often
implemented as a state machine, with the state stored within the view object
itself (to keep iterators small) and mutated as the view is iterated. Because
the view contains mutable state, many views lack a `const`-qualified
`begin()`/`end()`. When `const` versions of `begin()`/`end()` are provided, they
are truly `const`; that is, thread-safe.
Since views present the same interface as containers, the temptation is to think
they behave like containers with regard to `const`-ness. This is not the case.
Their behavior with regards to `const`-ness is similar to iterators and
pointers.
The `const`-ness of a view is not related to the `const`-ness of the underlying
data. A non-`const` view may refer to elements that are themselves `const`, and
_vice versa_. This is analogous to pointers; an `int* const` is a `const`
pointer to a mutable `int`, and a `int const*` is a non-`const` pointer to a
`const` `int`.
Use non-`const` views whenever possible. If you need thread-safety, work with
view copies in threads; don't share.
### View validity
Any operation on the underlying range that invalidates its iterators or
sentinels will also invalidate any view that refers to any part of that range.
Additionally, some views (_e.g._, `views::filter`), are invalidated when the
underlying elements of the range are mutated. It is best to recreate a view
after any operation that may have mutated the underlying range.
### List of range views
Below is a list of the lazy range combinators, or views, that Range-v3
provides, and a blurb about how each is intended to be used.
<DL>
<DT>\link ranges::views::addressof_fn `views::addressof`\endlink</DT>
<DD>Given a source range of lvalue references, return a new view that is the result of taking `std::addressof` of each.</DD>
<DT>\link ranges::views::adjacent_filter_fn `views::adjacent_filter`\endlink</DT>
<DD>For each pair of adjacent elements in a source range, evaluate the specified binary predicate. If the predicate evaluates to false, the second element of the pair is removed from the result range; otherwise, it is included. The first element in the source range is always included. (For instance, `adjacent_filter` with `std::not_equal_to` filters out all the non-unique elements.)</DD>
<DT>\link ranges::views::adjacent_remove_if_fn `views::adjacent_remove_if`\endlink</DT>
<DD>For each pair of adjacent elements in a source range, evaluate the specified binary predicate. If the predicate evaluates to true, the first element of the pair is removed from the result range; otherwise, it is included. The last element in the source range is always included.</DD>
<DT>\link ranges::views::all_fn `views::all`\endlink</DT>
<DD>Return a range containing all the elements in the source. Useful for converting containers to ranges.</DD>
<DT>\link ranges::any_view `any_view<T>(rng)`\endlink</DT>
<DD>Type-erased range of elements with value type `T`; can store _any_ range with this value type.</DD>
<DT>\link ranges::views::c_str_fn `views::c_str`\endlink</DT>
<DD>View a `\0`-terminated C string (e.g. from a `const char*`) as a range.</DD>
<DT>\link ranges::views::cartesian_product_fn `views::cartesian_product`\endlink</DT>
<DD>Enumerates the n-ary cartesian product of `n` ranges, i.e., generates all `n`-tuples `(e1, e2, ... , en)` where `e1` is an element of the first range, `e2` is an element of the second range, etc.</DD>
<DT>\link ranges::views::chunk_fn `views::chunk`\endlink</DT>
<DD>Given a source range and an integer *N*, produce a range of contiguous ranges where each inner range has *N* contiguous elements. The final range may have fewer than *N* elements.</DD>
<DT>\link ranges::views::common_fn `views::common`\endlink</DT>
<DD>Convert the source range to a *common* range, where the type of the `end` is the same as the `begin`. Useful for calling algorithms in the `std::` namespace.</DD>
<DT>\link ranges::views::concat_fn `views::concat`\endlink</DT>
<DD>Given *N* source ranges, produce a result range that is the concatenation of all of them.</DD>
<DT>\link ranges::views::const_fn `views::const_`\endlink</DT>
<DD>Present a `const` view of a source range.</DD>
<DT>\link ranges::views::counted_fn `views::counted`\endlink</DT>
<DD>Given an iterator `it` and a count `n`, create a range that starts at `it` and includes the next `n` elements.</DD>
<DT>\link ranges::views::cycle_fn `views::cycle`\endlink</DT>
<DD>Returns an infinite range that endlessly repeats the source range.</DD>
<DT>\link ranges::views::delimit_fn `views::delimit`\endlink</DT>
<DD>Given a source range and a value, return a new range that ends either at the end of the source or at the first occurrence of the value, whichever comes first. Alternatively, `views::delimit` can be called with an iterator and a value, in which case it returns a range that starts at the specified position and ends at the first occurrence of the value.</DD>
<DT>\link ranges::views::drop_fn `views::drop`\endlink</DT>
<DD>Given a source range and an integral count, return a range consisting of all but the first *count* elements from the source range, or an empty range if it has fewer elements.</DD>
<DT>\link ranges::views::drop_last_fn `views::drop_last`\endlink</DT>
<DD>Given a source range and an integral count, return a range consisting of all but the last *count* elements from the source range, or an empty range if it has fewer elements.</DD>
<DT>\link ranges::views::drop_exactly_fn `views::drop_exactly`\endlink</DT>
<DD>Given a source range and an integral count, return a range consisting of all but the first *count* elements from the source range. The source range must have at least that many elements.</DD>
<DT>\link ranges::views::drop_while_fn `views::drop_while`\endlink</DT>
<DD>Remove elements from the front of a range that satisfy a unary predicate.</DD>
<DT>\link ranges::views::empty() `views::empty`\endlink</DT>
<DD>Create an empty range with a given value type.</DD>
<DT>\link ranges::views::enumerate() `views::enumerate`\endlink</DT>
<DD>Pair each element of a range with its index.</DD>
<DT>\link ranges::views::filter_fn `views::filter`\endlink</DT>
<DD>Given a source range and a unary predicate, filter the elements that satisfy the predicate. (For users of Boost.Range, this is like the `filter` adaptor.)</DD>
<DT>\link ranges::views::for_each_fn `views::for_each`\endlink</DT>
<DD>Lazily applies an unary function to each element in the source range that returns another range (possibly empty), flattening the result.</DD>
<DT>\link ranges::views::generate_fn `views::generate`\endlink</DT>
<DD>Given a nullary function, return an infinite range whose elements are generated with the function.</DD>
<DT>\link ranges::views::generate_n_fn `views::generate_n`\endlink</DT>
<DD>Given a nullary function and a count, return a range that generates the requested number of elements by calling the function.</DD>
<DT>\link ranges::views::group_by_fn `views::group_by`\endlink</DT>
<DD>Given a source range and a binary predicate, return a range of ranges where each range contains contiguous elements from the source range such that the following condition holds: for each element in the range apart from the first, when that element and the first element are passed to the binary predicate, the result is true. In essence, `views::group_by` *groups* contiguous elements together with a binary predicate.</DD>
<DT>\link ranges::views::indirect_fn `views::indirect`\endlink</DT>
<DD>Given a source range of readable values (e.g. pointers or iterators), return a new view that is the result of dereferencing each.</DD>
<DT>\link ranges::views::intersperse_fn `views::intersperse`\endlink</DT>
<DD>Given a source range and a value, return a new range where the value is inserted between contiguous elements from the source.</DD>
<DT>\link ranges::views::ints_fn `views::ints`\endlink</DT>
<DD>Generate a range of monotonically increasing `int`s. When used without arguments, it generates the quasi-infinite range [0,1,2,3...]. It can also be called with a lower bound, or with a lower and upper bound (exclusive). An inclusive version is provided by `closed_ints`.</DD>
<DT>\link ranges::views::iota_fn `views::iota`\endlink</DT>
<DD>A generalization of `views::ints` that generates a sequence of monotonically increasing values of any incrementable type. When specified with a single argument, the result is an infinite range beginning at the specified value. With two arguments, the values are assumed to denote a half-open range.</DD>
<DT>\link ranges::views::join_fn `views::join`\endlink</DT>
<DD>Given a range of ranges, join them into a flattened sequence of elements. Optionally, you can specify a value or a range to be inserted between each source range.</DD>
<DT>\link ranges::views::keys_fn `views::keys`\endlink</DT>
<DD>Given a range of `pair`s (like a `std::map`), return a new range consisting of just the first element of the `pair`.</DD>
<DT>\link ranges::views::linear_distribute_fn `views::linear_distribute`\endlink</DT>
<DD>Distributes `n` values linearly in the closed interval `[from, to]` (the end points are always included). If `from == to`, returns `n`-times `to`, and if `n == 1` it returns `to`.</DD>
<DT>\link ranges::views::move_fn `views::move`\endlink</DT>
<DD>Given a source range, return a new range where each element has been has been cast to an rvalue reference.</DD>
<DT>\link ranges::views::partial_sum_fn `views::partial_sum`\endlink</DT>
<DD>Given a range and a binary function, return a new range where the *N*<SUP>th</SUP> element is the result of applying the function to the *N*<SUP>th</SUP> element from the source range and the (N-1)th element from the result range.</DD>
<DT>\link ranges::views::remove_fn `views::remove`\endlink</DT>
<DD>Given a source range and a value, filter out those elements that do not equal value.</DD>
<DT>\link ranges::views::remove_if_fn `views::remove_if`\endlink</DT>
<DD>Given a source range and a unary predicate, filter out those elements that do not satisfy the predicate. (For users of Boost.Range, this is like the `filter` adaptor with the predicate negated.)</DD>
<DT>\link ranges::views::repeat_fn `views::repeat`\endlink</DT>
<DD>Given a value, create a range that is that value repeated infinitely.</DD>
<DT>\link ranges::views::repeat_n_fn `views::repeat_n`\endlink</DT>
<DD>Given a value and a count, create a range that is that value repeated *count* number of times.</DD>
<DT>\link ranges::views::replace_fn `views::replace`\endlink</DT>
<DD>Given a source range, a source value and a target value, create a new range where all elements equal to the source value are replaced with the target value.</DD>
<DT>\link ranges::views::replace_if_fn `views::replace_if`\endlink</DT>
<DD>Given a source range, a unary predicate and a target value, create a new range where all elements that satisfy the predicate are replaced with the target value.</DD>
<DT>\link ranges::views::reverse_fn `views::reverse`\endlink</DT>
<DD>Create a new range that traverses the source range in reverse order.</DD>
<DT>\link ranges::views::sample_fn `views::sample`\endlink</DT>
<DD>Returns a random sample of a range of length `size(range)`.</DD>
<DT>\link ranges::views::single_fn `views::single`\endlink</DT>
<DD>Given a value, create a range with exactly one element.</DD>
<DT>\link ranges::views::slice_fn `views::slice`\endlink</DT>
<DD>Give a source range a lower bound (inclusive) and an upper bound (exclusive), create a new range that begins and ends at the specified offsets. Both the begin and the end can be integers relative to the front, or relative to the end with "`end-2`" syntax.</DD>
<DT>\link ranges::views::sliding_fn `views::sliding`\endlink</DT>
<DD>Given a range and a count `n`, place a window over the first `n` elements of the underlying range. Return the contents of that window as the first element of the adapted range, then slide the window forward one element at a time until hitting the end of the underlying range.</DD>
<DT>\link ranges::views::split_fn `views::split`\endlink</DT>
<DD>Given a source range and a delimiter specifier, split the source range into a range of ranges using the delimiter specifier to find the boundaries. The delimiter specifier can be an element or a range of elements. The elements matching the delimiter are excluded from the resulting range of ranges.</DD>
<DT>\link ranges::views::split_when_fn `views::split_when`\endlink</DT>
<DD>Given a source range and a delimiter specifier, split the source range into a range of ranges using the delimiter specifier to find the boundaries. The delimiter specifier can be a predicate or a function. The predicate should take a single argument of the range's reference type and return `true` if and only if the element is part of a delimiter. The function should accept an iterator and sentinel indicating the current position and end of the source range and return `std::make_pair(true, iterator_past_the_delimiter)` if the current position is a boundary; otherwise `std::make_pair(false, ignored_iterator_value)`. The elements matching the delimiter are excluded from the resulting range of ranges.</DD>
<DT>\link ranges::views::stride_fn `views::stride`\endlink</DT>
<DD>Given a source range and an integral stride value, return a range consisting of every *N*<SUP>th</SUP> element, starting with the first.</DD>
<DT>\link ranges::views::tail_fn `views::tail`\endlink</DT>
<DD>Given a source range, return a new range without the first element. The range must have at least one element.</DD>
<DT>\link ranges::views::take_fn `views::take`\endlink</DT>
<DD>Given a source range and an integral count, return a range consisting of the first *count* elements from the source range, or the complete range if it has fewer elements. (The result of `views::take` is not a `sized_range`.)</DD>
<DT>\link ranges::views::take_exactly_fn `views::take_exactly`\endlink</DT>
<DD>Given a source range and an integral count, return a range consisting of the first *count* elements from the source range. The source range must have at least that many elements. (The result of `views::take_exactly` is a `sized_range`.)</DD>
<DT>\link ranges::views::take_last_fn `views::take_last`\endlink</DT>
<DD>Given a source range and an integral count, return a range consisting of the last *count* elements from the source range. The source range must be a `sized_range`. If the source range does not have at least *count* elements, the full range is returned.</DD>
<DT>\link ranges::views::take_while_fn `views::take_while`\endlink</DT>
<DD>Given a source range and a unary predicate, return a new range consisting of the elements from the front that satisfy the predicate.</DD>
<DT>\link ranges::views::tokenize_fn `views::tokenize`\endlink</DT>
<DD>Given a source range and optionally a submatch specifier and a `std::regex_constants::match_flag_type`, return a `std::regex_token_iterator` to step through the regex submatches of the source range. The submatch specifier may be either a plain `int`, a `std::vector<int>`, or a `std::initializer_list<int>`.</DD>
<DT>\link ranges::views::transform_fn `views::transform`\endlink</DT>
<DD>Given a source range and a unary function, return a new range where each result element is the result of applying the unary function to a source element.</DD>
<DT>\link ranges::views::trim_fn `views::trim`\endlink</DT>
<DD>Given a source bidirectional range and a unary predicate, return a new range without the front and back elements that satisfy the predicate.</DD>
<DT>\link ranges::views::unbounded_fn `views::unbounded`\endlink</DT>
<DD>Given an iterator, return an infinite range that begins at that position.</DD>
<DT>\link ranges::views::unique_fn `views::unique`\endlink</DT>
<DD>Given a range, return a new range where all consecutive elements that compare equal save the first have been filtered out.</DD>
<DT>\link ranges::views::values_fn `views::values`\endlink</DT>
<DD>Given a range of `pair`s (like a `std::map`), return a new range consisting of just the second element of the `pair`.</DD>
<DT>\link ranges::views::zip_fn `views::zip`\endlink</DT>
<DD>Given *N* ranges, return a new range where *M*<SUP>th</SUP> element is the result of calling `make_tuple` on the *M*<SUP>th</SUP> elements of all *N* ranges.</DD>
<DT>\link ranges::views::zip_with_fn `views::zip_with`\endlink</DT>
<DD>Given *N* ranges and a *N*-ary function, return a new range where *M*<SUP>th</SUP> element is the result of calling the function on the *M*<SUP>th</SUP> elements of all *N* ranges.</DD>
</DL>
\subsection tutorial-actions Actions
--------------------------------------------------------------
When you want to mutate a container in-place, or forward it through a chain of
mutating operations, you can use actions. The following examples should make it
clear.
Read data into a vector, sort it, and make it unique.
~~~~~~~{.cpp}
extern std::vector<int> read_data();
using namespace ranges;
std::vector<int> vi = read_data() | actions::sort | actions::unique;
~~~~~~~
Do the same to a `vector` that already contains some data:
~~~~~~~{.cpp}
vi = std::move(vi) | actions::sort | actions::unique;
~~~~~~~
Mutate the container in-place:
~~~~~~~{.cpp}
vi |= actions::sort | actions::unique;
~~~~~~~
Same as above, but with function-call syntax instead of pipe syntax:
~~~~~~~{.cpp}
actions::unique(actions::sort(vi));
~~~~~~~
### List of range actions
Below is a list of the eager range combinators, or actions, that Range-v3
provides, and a blurb about how each is intended to be used.
<DL>
<DT>\link ranges::actions::drop_fn `actions::drop`\endlink</DT>
<DD>Removes the first `N` elements of the source range.</DD>
<DT>\link ranges::actions::drop_while_fn `actions::drop_while`\endlink</DT>
<DD>Removes the first elements of the source range that satisfy the unary predicate.</DD>
<DT>`actions::erase`</DT>
<DD>Removes all elements in the sub-range of the source (range version) or all elements after position.</DD>
<DT>`actions::insert`</DT>
<DD>Inserts all elements of the range into the source at position.</DD>
<DT>\link ranges::actions::join_fn `actions::join`\endlink</DT>
<DD>Flattens a range of ranges.</DD>
<DT> `actions::push_back`</DT>
<DD>Appends elements to the tail of the source.</DD>
<DT>`actions::push_front`</DT>
<DD>Appends elements before the head of the source.</DD>
<DT>\link ranges::actions::remove_if_fn `actions::remove_if`\endlink</DT>
<DD>Removes all elements from the source that satisfy the predicate.</DD>
<DT>\link ranges::actions::remove_fn `actions::remove`\endlink</DT>
<DD>Removes all elements from the source that are equal to value.</DD>
<DT>\link ranges::actions::reverse_fn `actions::reverse`\endlink</DT>
<DD>Reverses all the elements in the container.</DD>
<DT>\link ranges::actions::shuffle_fn `actions::shuffle`\endlink</DT>
<DD>Shuffles the source range.</DD>
<DT>\link ranges::actions::slice_fn `actions::slice`\endlink</DT>
<DD>Removes all elements from the source that are not part of the sub-range.</DD>
<DT>\link ranges::actions::sort_fn `actions::sort`\endlink</DT>
<DD>Sorts the source range (unstable).</DD>
<DT>\link ranges::actions::split_fn `actions::split`\endlink</DT>
<DD>Split a range into a sequence of subranges using a delimiter (a value, a sequence of values, a predicate, or a binary function returning a `pair<bool, N>`).</DD>
<DT>\link ranges::actions::stable_sort_fn `actions::stable_sort`\endlink</DT>
<DD>Sorts the source range (stable).</DD>
<DT>\link ranges::actions::stride_fn `actions::stride`\endlink</DT>
<DD>Removes all elements whose position does not match the stride.</DD>
<DT>\link ranges::actions::take_fn `actions::take`\endlink</DT>
<DD>Keeps the first `N`-th elements of the range, removes the rest.</DD>
<DT>\link ranges::actions::take_while_fn `actions::take_while`\endlink</DT>
<DD>Keeps the first elements that satisfy the predicate, removes the rest.</DD>
<DT>\link ranges::actions::transform_fn `actions::transform`\endlink</DT>
<DD>Replaces elements of the source with the result of the unary function.</DD>
<DT>\link ranges::actions::unique_fn `actions::unique`\endlink</DT>
<DD>Removes adjacent elements of the source that compare equal. If the source is sorted, removes all duplicate elements.</DD>
<DT>\link ranges::actions::unstable_remove_if_fn `actions::unstable_remove_if`\endlink</DT>
<DD>Much faster (each element remove has constant time complexity), unordered version of `remove_if`. Requires bidirectional container.</DD>
</DL>
\subsection tutorial-utilities Utilities
----------------------------------------------
Below we cover some utilities that range-v3 provides for creating your own
view adaptors and iterators.
### Create Custom Views with view_facade
Range-v3 provides a utility for easily creating your own range types, called
\link ranges::view_facade `ranges::view_facade`\endlink. The code below uses
`view_facade` to create a range that traverses a null-terminated string:
~~~~~~~{.cpp}
#include <range/v3/all.hpp>
// A range that iterates over all the characters in a
// null-terminated string.
class c_string_range
: public ranges::view_facade<c_string_range>
{
friend ranges::range_access;
char const * sz_ = "";
char const & read() const { return *sz_; }
bool equal(ranges::default_sentinel_t) const { return *sz_ == '\0'; }
void next() { ++sz_; }
public:
c_string_range() = default;
explicit c_string_range(char const *sz) : sz_(sz)
{
assert(sz != nullptr);
}
};
~~~~~~~
The `view_facade` class generates an iterator and begin/end member functions
from the minimal interface provided by `c_string_range`. This is an example of a
very simple range for which it is not necessary to separate the range itself
from the thing that iterates the range. Future examples will show examples of
more sophisticated ranges.
With `c_string_range`, you can now use algorithms to operate on null-terminated
strings, as below:
~~~~~~~{.cpp}
#include <iostream>
int main()
{
c_string_range r("hello world");
// Iterate over all the characters and print them out
ranges::for_each(r, [](char ch){
std::cout << ch << ' ';
});
// prints: h e l l o w o r l d
}
~~~~~~~
### Create Custom Views with view_adaptor
Often, a new range type is most easily expressed by adapting an existing range
type. That's the case for many of the range views provided by the Range-v3
library; for example, the `views::remove_if` and `views::transform` views. These
are rich types with many moving parts, but thanks to a helper class called
\link ranges::view_adaptor `ranges::view_adaptor`\endlink, they aren't hard
to write.
Below in roughly 2 dozen lines of code is the `transform` view, which takes one
range and transforms all the elements with a unary function.
~~~~~~~{.cpp}
#include <range/v3/all.hpp>
// A class that adapts an existing range with a function
template<class Rng, class Fun>
class transform_view
: public ranges::view_adaptor<transform_view<Rng, Fun>, Rng>
{
friend ranges::range_access;
ranges::semiregular_box_t<Fun> fun_; // Make Fun model semiregular if it doesn't
class adaptor : public ranges::adaptor_base
{
ranges::semiregular_box_t<Fun> fun_;
public:
adaptor() = default;
adaptor(ranges::semiregular_box_t<Fun> const &fun) : fun_(fun) {}
// Here is where we apply Fun to the elements:
auto read(ranges::iterator_t<Rng> it) const -> decltype(fun_(*it))
{
return fun_(*it);
}
};
adaptor begin_adaptor() const { return {fun_}; }
adaptor end_adaptor() const { return {fun_}; }
public:
transform_view() = default;
transform_view(Rng && rng, Fun fun)
: transform_view::view_adaptor{std::forward<Rng>(rng)}
, fun_(std::move(fun))
{}
};
template<class Rng, class Fun>
transform_view<Rng, Fun> transform(Rng && rng, Fun fun)
{
return {std::forward<Rng>(rng), std::move(fun)};
}
~~~~~~~
Range transformation is achieved by defining a nested `adaptor` class that
handles the transformation, and then defining `begin_adaptor` and `end_adaptor`
members that return adaptors for the begin iterator and the end sentinel,
respectively. The `adaptor` class has a `read` member that performs the
transformation. It is passed an iterator to the current element. Other members
are available for customization: `equal`, `next`, `prev`, `advance`, and
`distance_to`; but the transform adaptor accepts the defaults defined in
\link ranges::adaptor_base `ranges::adaptor_base`\endlink.
With `transform_view`, we can print out the first 20 squares:
~~~~~~~{.cpp}
int main()
{
auto squares = ::transform(views::ints(1), [](int i){return i*i;});
for(int i : squares | views::take(20))
std::cout << i << ' ';
std::cout << '\n';
// prints 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400
}
~~~~~~~
The `transform_view` defined above is an input range when it is wrapping an
input range, a forward range when it's wrapping a forward range, etc. That happens
because of smart defaults defined in the `adaptor_base` class that frees you
from having to deal with a host of niggly detail when implementing iterators.
*(Note: the above `transform_view` always stores a copy of the function in the
sentinel. That is only necessary if the underlying range's sentinel type models
bidirectional_iterator. That's a finer point that you shouldn't worry about right
now.)*
#### view_adaptor in details
Each `view_adaptor` contains `base()` member in view and iterator.
`base()` - allow to access "adapted" range/iterator:
~~~~~~~{.cpp}
std::vector<int> vec;
auto list = vec | views::transfom([](int i){ return i+1; });
assert( vec.begin() == list.begin().base() );
assert( vec.begin() == list.base().begin() );
~~~~~~~
Like `basic_iterator`'s `cursor`, `view_adaptor`'s `adaptor` can contain mixin class too,
to inject things into the public interface of the iterator:
~~~~~~~{.cpp}
class adaptor : public ranges::adaptor_base
{
template<class base_mixin>
struct mixin : base_mixin
{
// everything inside this class will be accessible from iterator
using base_mixin::base_mixin;
auto& base_value() const
{
return *this->base();
}
int get_i() const
{
return this->get().i;
}
};
int i = 100;
};
~~~~~~~
From within mixin you can call:
* `get()` - to access adaptor internals
* `base()` - to access adaptable iterator
Iterator/sentinel adaptor may "override" the following members:
~~~~~~~{.cpp}
class adaptor : public ranges::adaptor_base
{
// !For begin_adaptor only!
template<typename Rng>
constexpr auto begin(Rng &rng)
{
return ranges::begin(rng.base());
}
// !For end_adaptor only!
template<typename Rng>
constexpr auto end(Rng &rng)
{
return ranges::end(rng.base());
}
template<typename I>
bool equal(I const &this_iter, I const &that_iter) const
{
return this_iter == that_iter;
}
// or
template<typename I>
bool equal(I const &this_iter, I const &that_iter, adaptor const &that_adapt) const
{
return
*this.some_value == that_adapt.some_value
&& this_iter == that_iter;
}
// !For end_adaptor only!
// Same as equal, but compare iterator with sentinel.
// Not used, if iterator same as sentinel, and both have the same adaptor.
template<typename I, typename S>
constexpr bool empty(I const &it, S const &end) const
{
return it == end;
}
// or
template<typename I, typename S, typename SA>
constexpr bool empty(I const &it, S const &end, SA const &end_adapt) const
{
return
*this.some_value == end_adapt.some_value
&& it == end;
}
template<typename I>
reference_t<I> read(I const &it)
{
return *it;
}
template<typename I>
void next(I &it)
{
++it;
}
// !For bidirectional iterator only!
template<typename I>
void prev(I &it)
{
--it;
}
// !For random access iterator only!
template<typename I>
void advance(I &it, difference_type_t<I> n)
{
it += n;
}
// !For "sized" iterators only!
template<typename I>
difference_type_t<I> distance_to(I const &this_iter, I const &that_iter)
{
return that_iter - this_iter;
}
// or
template<typename I>
difference_type_t<I> distance_to
(I const &this_iter, I const &that_iter, adaptor const &that_adapt)
{
return that_iter - this_iter;
}
}
~~~~~~~
As you can see, some "overrides" have effect only for `begin_adaptor` or
`end_adaptor`. In order to use full potential of adaptor, you need to have
separate adaptors for begin and end:
~~~~~~~{.cpp}
struct adaptor : adaptor_base
{
int n = 0;
void next(iterator_t<Rng>& it)
{
++n;
++it;
}
};
struct sentinel_adaptor : adaptor_base
{
int stop_at;
bool empty(const iterator_t<Rng>&, const adaptor& ia, const sentinel_t<Rng>& s) const
{
return ia.n == stop_at;
}
};
adaptor begin_adaptor() const { return {}; }
sentinel_adaptor end_adaptor() const { return {100}; }
~~~~~~~
Sometimes, you can use the same adaptor for both `begin_adaptor` and `end_adaptor`:
~~~~~~~{.cpp}
struct adaptor : adaptor_base
{
int n = 0;
void next(iterator_t<Rng>& it)
{
++n;
++it;
}
// pay attention, we use equal, not empty. empty() will never trigger.
template<typename I>
bool equal(I const &this_iter, I const &that_iter, adaptor const &that_adapt) const
{
return *this.n == that_adapt.n;
}
};
adaptor begin_adaptor() const { return {}; }
adaptor end_adaptor() const { return {100}; }
~~~~~~~
Note that all the data you store in the adaptor will become part of the iterator.
If you will not "override" `begin_adaptor()` or/and `end_adaptor()` in your view_adaptor, default ones will be used.
### Create Custom Iterators with basic_iterator
Here is an example of Range-v3 compatible random access proxy iterator.
The iterator returns a key/value pair, like the `zip` view.
~~~~~~~{.cpp}
#include <range/v3/iterator/basic_iterator.hpp>
#include <range/v3/utility/common_tuple.hpp>
using KeyIter = typename std::vector<Key>::iterator;
using ValueIter = typename std::vector<Value>::iterator;
struct cursor
{
// basic_iterator derives from "mixin", if present, so it can be used
// to inject things into the public interface of the iterator
struct mixin;
// This is for dereference operator.
using value_type = std::pair<Key, Value>;
ranges::common_pair<Key&, Value&> read() const
{
return { *key_iterator, *value_iterator };
}
bool equal(const cursor& other) const
{
return key_iterator == other.key_iterator;
}
void next()
{
++key_iterator;
++value_iterator;
}
// prev optional. Required for Bidirectional iterator
void prev()
{
--key_iterator;
--value_iterator;
}
// advance and distance_to are optional. Required for random access iterator
void advance(std::ptrdiff_t n)
{
key_iterator += n;
value_iterator += n;
}
std::ptrdiff_t distance_to(const cursor& other) const
{
return other.key_iterator - this->key_iterator;
}
cursor() = default;
cursor(KeyIter key_iterator, ValueIter value_iterator)
: key_iterator(key_iterator)
, value_iterator(value_iterator)
{}
KeyIter key_iterator;
ValueIter value_iterator;
};
struct cursor::mixin : ranges::basic_mixin<cursor>
{
using ranges::basic_mixin<cursor>::basic_mixin;
// It is necessary to expose constructor in this way
mixin(KeyIter key_iterator, ValueIter value_iterator)
: mixin{ cursor(key_iterator, value_iterator) }
{}
KeyIter key_iterator()
{
return this->get().key_iterator;
}
ValueIter value_iterator()
{
return this->get().value_iterator;
}
};
using iterator = ranges::basic_iterator<cursor>;
void test()
{
std::vector<Key> keys = {1};
std::vector<Value> values = {10};
iterator iter(keys.begin(), values.begin());
ranges::common_pair<Key&, Value&> pair = *iter;
Key& key = pair.first;
Value& value = pair.second;
assert(&key == &keys[0]);
assert(&value == &values[0]);
auto key_iter = iter.key_iterator();
assert(key_iter == keys.begin());
}
~~~~~~~
`read()` returns references. But the default for `value_type`, which is
`decay_t<decltype(read())>`, is `common_pair<Key&, Value&>`. That is not correct
in our case. It should be `pair<Key, Value>`, so we explicitly specify
`value_type`.
`ranges::common_pair` has conversions:
> `ranges::common_pair<Key&, Value&>` &harr; `ranges::common_pair<Key, Value>`.
All `ranges::common_pair`s converts to their `std::pair` equivalents, also.
For more information, see [http://wg21.link/P0186#basic-iterators-iterators.basic](http://wg21.link/P0186#basic-iterators-iterators.basic)
\subsection tutorial-concepts Concept Checking
--------------------------------------------------------------------------------
The Range-v3 library makes heavy use of concepts to constrain functions, control
overloading, and check type constraints at compile-time. It achieves this with
the help of a Concepts emulation layer that works on any standard-conforming
C++14 compiler. The library provides many useful concepts, both for the core
language and for iterators and ranges. You can use the concepts framework to
constrain your own code.
For instance, if you would like to write a function that takes an
iterator/sentinel pair, you can write it like this:
~~~~~~~{.cpp}
CPP_template(class Iter, class Sent, class Comp = /*...some_default..*/)
(requires sentinel_for<Sent, Iter>)
void my_algorithm(Iter first, Sent last, Comp comp = Comp{})
{
// ...
}
~~~~~~~
You can then add an overload that take a Range:
~~~~~~~{.cpp}
CPP_template(class Rng, class Comp = /*...some_default..*/)
(requires range<Rng>)
void my_algorithm(Rng && rng, Comp comp = Comp{})
{
return my_algorithm(ranges::begin(rng), ranges::end(rng));
}
~~~~~~~
With the type constraints expressed with the `CPP_template` macro, these
two overloads are guaranteed to not be ambiguous. When compiling with C++20
concepts support, this uses real concept checks. On legacy compilers, it falls
back to using `std::enable_if`.
\subsection tutorial-future Range-v3 and the Future
--------------------------------------------------------------------------------
Range-v3 formed the basis for the
[Technical Specification on Ranges](https://www.iso.org/standard/70910.html),
which has since been merged into the working draft of C++20.
In addition, a subset of range-v3's views are also a part of the C++20 working
draft, with more slated for eventual inclusion in future versions of C++.
The actions, as well as various utilities, have not yet been reviewed by the
Committee, although the basic direction has already passed an initial review.

View File

@ -1,225 +0,0 @@
<doxygenlayout version="1.0">
<!-- Generated by doxygen 1.8.7 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes"
title="Reference"
intro="
The reference documentation is split into logical modules, as documented in
the user manual."/>
<tab type="usergroup" visible="yes" title="Indexes">
<tab type="classmembers" visible="yes"
title="Methods"
intro="
This is an index of all the methods with links to the
corresponding documentation."/>
<tab type="classlist" visible="yes"
title="Classes"
intro="
This is an index of all the classes in the library with links to the
corresponding documentation."/>
<tab type="filelist" visible="yes"
title="Files"
intro=""/>
</tab>
<!-- We don't use what's below -->
<tab type="namespaces" visible="no" title="">
<tab type="namespacelist" visible="no" title="" intro=""/>
<tab type="namespacemembers" visible="no" title="" intro=""/>
</tab>
<tab type="examples" visible="no" title="" intro=""/>
<tab type="classes" visible="no" title="">
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="no" title="" intro=""/>
</tab>
<tab type="files" visible="no" title="Files">
<tab type="globals" visible="no" title="Globals" intro=""/>
</tab>
</navindex>
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="no"/>
<detaileddescription title="Description"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<memberdecl>
<related title="Synopsis of methods" subtitle=" "/>
<nestedclasses visible="yes" title="Instances and minimal complete definitions"/>
<friends title=""/>
<membergroups visible="yes"/>
<!-- We don't use what's below -->
<publictypes title=""/>
<services title=""/>
<interfaces title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
<publicstaticmethods title=""/>
<publicattributes title=""/>
<publicstaticattributes title=""/>
<protectedtypes title=""/>
<protectedslots title=""/>
<protectedmethods title=""/>
<protectedstaticmethods title=""/>
<protectedattributes title=""/>
<protectedstaticattributes title=""/>
<packagetypes title=""/>
<packagemethods title=""/>
<packagestaticmethods title=""/>
<packageattributes title=""/>
<packagestaticattributes title=""/>
<properties title=""/>
<events title=""/>
<privatetypes title=""/>
<privateslots title=""/>
<privatemethods title=""/>
<privatestaticmethods title=""/>
<privateattributes title=""/>
<privatestaticattributes title=""/>
</memberdecl>
<memberdef>
<related title="Methods"/>
<functions title=""/>
<variables title=""/>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<!-- This is not for C++ -->
<services title=""/>
<interfaces title=""/>
<constructors title=""/>
<properties title=""/>
<events title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="yes"/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<classes visible="yes" title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
<sourcelink visible="yes"/>
<memberdecl>
<classes visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="no"/>
<detaileddescription title="Description"/>
<groupgraph visible="$GROUP_GRAPHS"/>
<memberdecl>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
<membergroups visible="yes"/>
</memberdecl>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
</directory>
</doxygenlayout>

View File

@ -1,308 +0,0 @@
Release Notes {#release_notes}
=============
\section v0-9-1 Version 0.9.1
_Released:_ Sept 1, 2019.
gcc-9.x portability fixes.
\section v0-9-0 Version 0.9.0 "Std::ranger Things"
_Released:_ Aug 26, 2019.
Bring many interfaces into sync with the C++20 draft.
* **NEW:** An improved concepts portability layer with macros that use C++20
concepts when the compiler supports them.
* **NEW:** An improved directory structure that keeps disjoint parts of the
library -- iterators, ranges, algorithms, actions, views, functional
programming support, and general utilities -- physically separate.
* **NEW:** A `RANGES_DEEP_STL_INTEGRATION` configuration option that makes your
STL implementation default to structural conformance to infer iterator
category, as in C++20. Applies to libc++, libstdc++, and MSVC's Standard
Library.
* **NEW:** A `ranges::cpp20` namespace that contains all the functionality of
C++20's `std::ranges` namespace.
* All concept names have been given standard_case (renamed from PascalCase) and
have been harmonized with the C++20 draft.
* The following range access customization points no longer accept rvalue ranges
by default:
- `ranges::begin`
- `ranges::end`
- `ranges::rbegin`
- `ranges::rend`
- `ranges::cbegin`
- `ranges::cend`
- `ranges::crbegin`
- `ranges::crend`
- `ranges::data`
- `ranges::cdata`
* Iterators may specify an `iterator_concept` type alias in addition to
`iterator_category` -- either as a nested type or as a member of a
`std::iterator_traits` specialization -- to denote conformance to the C++20
iterator concepts as distinct from the C++98 iterator requirements.
(See [P1037 "Deep Integration of the Ranges TS"](http://wg21.link/p1037)
for more information.)
* The `ranges::value_type` trait has been renamed to `readable_traits`.
* The `ranges::difference_type` trait has been renamed to `incrementable_traits`.
* The `ranges::iterator_category` trait has been deprecated. Specialize
`std::iterator_traits` to non-intrusively specify an iterator's category
and (optionally) concept.
* Rename the `ranges::view` namespace to `ranges::views` and `ranges::action` to
`ranges::actions` (with deprecated namespace aliases for migration).
* Rename `view::bounded` to `views::common`.
* Rename `unreachable` to `unreachable_sentinel_t`.
* Change `dangling` from a class template that wraps an iterator to a class that
acts as a placeholder for an iterator that would otherwise dangle.
* Implement C++20's `subrange` as a view that wraps an iterator/sentinel pair;
deprecate `iterator_range`.
* Deprecate implicit conversion from view types to containers; rename
`ranges::to_` to `ranges::to` and extend it to support converting a
range-of-ranges to a container-of-containers.
* Deprecate the `ranges::v3` inline versioning namespace.
* The following views have had minor changes to bring them into conformance with
the C++20 working draft:
- `join_view`
- `single_view`
- `empty_view`
- `split_view`
- `reverse_view`
- `all_view`
- `take_view`
- `iota_view`
<p/>`iota_view<std::[u]intmax_t>`, in particular, is given a user-defined
`difference_type` that avoids integer overflow.
* New names for the iterator and range type aliases:
| Old Name | New Name |
|-------------------------------|-----------------------------|
| `value_type_t` | `iter_value_t` |
| `reference_t` | `iter_reference_t` |
| `difference_type_t` | `iter_difference_t` |
| `size_type_t` | _deprecated_ |
| `rvalue_reference_t` | `iter_rvalue_reference_t` |
| `range_value_type_t` | `range_value_t` |
| `range_difference_type_t` | `range_difference_t` |
| `range_size_type_t` | `range_size_t` |
\section v0-5-0 Version 0.5.0
_Released:_ Apr 30, 2019.
* **NEW:** MSVC support, from @CaseyCarter :tada: (See the docs for the list of supported compilers.)
* **NEW:** `view::enumerate`, from @MikeGitb
* **NEW:** `view::addressof`, from @tower120
* **NEW:** `unstable_remove_if` algorithm and action, from @tower120
* **NEW:** `adjacent_remove_if` algorithm and action, from @cjdb
* **NEW:** `ostream_joiner`, from @sv1990
* `view::drop_while` and `view::take_while` get projection support, from @mrpi
* `view::filter` and `view::remove_if` get projection support, from @mrpi
* `view::unique` accepts optional comparison operator, from @tete17
* `action::slice` supports sliding from the end, from @tete17
* Support coroutines on MSVC, from @CaseyCarter
* Faster `view::generate_n`, from GitHub user @tower120
* Improved aligned new detection for libc++ on iOS, from @mtak-
* Various CMake improvements, from @johelegp
* `view_adaptor` supports `basic_iterator`-style mixins, from @tower120
* Fix `ranges::advance` for random-access iterators for `n==0`, from @tower120
* Bugs fixed: [#755](https://github.com/ericniebler/range-v3/issues/755), [#759](https://github.com/ericniebler/range-v3/issues/759), [#942](https://github.com/ericniebler/range-v3/issues/942), [#946](https://github.com/ericniebler/range-v3/issues/946), [#952](https://github.com/ericniebler/range-v3/issues/952), [#975](https://github.com/ericniebler/range-v3/issues/975), [#978](https://github.com/ericniebler/range-v3/issues/978), [#986](https://github.com/ericniebler/range-v3/issues/986), [#996](https://github.com/ericniebler/range-v3/issues/996), [#1041](https://github.com/ericniebler/range-v3/issues/1041), [#1047](https://github.com/ericniebler/range-v3/issues/1047), [#1088](https://github.com/ericniebler/range-v3/issues/1088), [#1094](https://github.com/ericniebler/range-v3/issues/1094), [#1107](https://github.com/ericniebler/range-v3/issues/1107), [#1129](https://github.com/ericniebler/range-v3/issues/1129)
\section v0-4-0 Version 0.4.0
_Released:_ Oct 18, 2018.
- Minor interface-breaking changes:
* `single_view` returns by `const &` (see [#817](https://github.com/ericniebler/range-v3/issues/817)).
* `reverse_view` of a non-Sized, non-Bounded RandomAccess range (eg., a null-terminated string) no longer satisfies SizedRange.
* The `generate` and `generate_n` views now return the generated values by xvalue reference (`T &&`) to the value cached within the view (see [#905](https://github.com/ericniebler/range-v3/issues/905)).
* Views no longer prefer returning constant iterators when they can; some views have different constant and mutable iterators.
- Enhancements:
* Views can successfully adapt other views that have different constant and mutable iterators.
* The `single` and `empty` views are much closer to the versions as specified in [P0896](http://wg21.link/P0896).
- Bug fixes:
* "single_view should not copy the value" [#817](https://github.com/ericniebler/range-v3/issues/817).
* "Calling back() on strided range does not return the correct last value in range" [#901](https://github.com/ericniebler/range-v3/issues/901).
* "generate(foo) | take(n) calls foo n+1 times" [#819](https://github.com/ericniebler/range-v3/issues/819).
* "generate seems broken with move-only return types" [#905](https://github.com/ericniebler/range-v3/issues/905).
* "Unexpected behavior in generate with return by reference" [#807](https://github.com/ericniebler/range-v3/issues/807).
* "Inconsistent behaviour of ranges::distance with ranges::view::zip using infinite views." [#783](https://github.com/ericniebler/range-v3/issues/783).
* "Infinite loop when using ranges::view::cycle with an infinite range" [#780](https://github.com/ericniebler/range-v3/issues/780).
* "Composing ranges::view::cycle with ranges::view::slice" [#778](https://github.com/ericniebler/range-v3/issues/778).
* "cartesian_product view, now with moar bugs." [#919](https://github.com/ericniebler/range-v3/issues/919).
\section v0-3-7 Version 0.3.7
_Released:_ Sept 19, 2018.
- Improved support for clang-cl (thanks to @CaseyCarter).
- Fix for `any_view<T, category::sized | category::input>` (see #869).
- Fix `iter_move` of a `ranges::reverse_iterator` (see #888).
- Fix `move_sentinel` comparisons (see #889).
- Avoid ambiguity created by `boost::advance` and `std::advance` (see #893).
\section v0-3-6 Version 0.3.6
_Released:_ May 15, 2018.
- NEW: `view::exclusive_scan` (thanks to GitHub user @mitsutaka-takeda).
- All views get non-`const` overloads of `.empty()` and `.size()` (see [ericniebler/stl2\#793](https://github.com/ericniebler/stl2/issues/793)).
- Upgrade Conan support for conan 1.0.
- `subspan` interface tweaks.
- Fix bug in `view::split` (see [this stackoverflow question](https://stackoverflow.com/questions/49015671)).
- Fix bug in `view::stride` (see [ericniebler/stl2\#805](https://github.com/ericniebler/stl2/issues/805)).
- Fix `const`-correctness problem in `view::chunk` (see [this stackoverflow question](https://stackoverflow.com/questions/49210190)).
- Replace uses of `ranges::result_of` with `ranges::invoke_result`.
- Fix potential buffer overrun of `view::drop` over RandomAccessRanges.
- Lots of `view::cartesian_product` fixes (see [ericniebler/stl2\#820](https://github.com/ericniebler/stl2/issues/820), [ericniebler/stl2\#823](https://github.com/ericniebler/stl2/issues/823)).
- Work around gcc-8 regression regarding `volatile` `std::initializer_list`s (see [ericniebler/stl2\#826](https://github.com/ericniebler/stl2/issues/826)).
- Fix `const`-correctness problem of `view::take`.
\section v0-3-5 Version 0.3.5
_Released:_ February 17, 2018.
- Rvalues may satisfy `Writable` (see [ericniebler/stl2\#387](https://github.com/ericniebler/stl2/issues/387)).
- `view_interface` gets a bounds-checking `at` method.
- `chunk_view` works on Input ranges.
- Fix bug in `group_by_view`.
- Improved concept checks for `partial_sum` numeric algorithm.
- Define `ContiguousIterator` concept and `contiguous_iterator_tag` iterator
category tag.
- Sundry `span` fixes.
- `action::insert` avoids interfering with `vector`'s exponentional growth
strategy.
- Add an experimental `shared` view for views that need container-like scratch
space to do their work.
- Faster, simpler `reverse_view`.
- Rework `ranges::reference_wrapper` to avoid [LWG\#2993](https://wg21.link/lwg2993).
- Reworked `any_view`, the type-erased view wrapper.
- `equal` algorithm is `constexpr` in C++14.
- `stride_view` no longer needs an `atomic` data member.
- `const`-correct `drop_view`.
- `adjacent_filter_view` supports bidirectional iteration.
- Massive `view_adaptor` cleanup to remove the need for a `mutable` data
member holding the adapted view.
- Fix `counting_iterator` post-increment bug.
- `tail_view` of an empty range is an empty range, not undefined behavior.
- Various portability fixes for gcc and clang trunk.
\section v0-3-0 Version 0.3.0
_Released:_ June 30, 2017.
- Input views may now be move-only (from @CaseyCarter)
- Input `any_view`s are now *much* more efficient (from @CaseyCarter)
- Better support for systems lacking a working `<thread>` header (from @CaseyCarter)
\section v0-2-6 Version 0.2.6
_Released:_ June 21, 2017.
- Experimental coroutines with `ranges::experimental::generator` (from @CaseyCarter)
- `ranges::optional` now behaves like `std::optional` (from @CaseyCarter)
- Extensive bug fixes with Input ranges (from @CaseyCarter)
\section v0-2-5 Version 0.2.5
_Released:_ May 16, 2017.
- `view::chunk` works on Input ranges (from @CaseyCarter)
- `for_each_n` algorithm (from @khlebnikov)
- Portability fixes for MinGW, clang-3.6 and -3.7, and gcc-7; and cmake 3.0
\section v0-2-4 Version 0.2.4
_Released:_ April 12, 2017.
Fix the following bug:
- `action::stable_sort` of `vector` broken on Clang 3.8.1 since ~last Xmas (ericniebler/range-v3#632).
\section v0-2-3 Version 0.2.3
_Released:_ April 4, 2017.
Fix the following bug:
- iterators that return move-only types by value do not satisfy Readable (ericniebler/stl2#399).
\section v0-2-2 Version 0.2.2
_Released:_ March 30, 2017.
New in this release:
- `view::linear_distribute(from,to,n)` - A view of `n` elements between `from` and `to`, distributed evenly.
- `view::indices(n)` - A view of the indices `[0,1,2...n-1]`.
- `view::closed_indices(n)` - A view of the indices `[0,1,2...n]`.
This release deprecates `view::ints(n)` as confusing to new users.
\section v0-2-1 Version 0.2.1
_Released:_ March 22, 2017.
New in this release:
- `view::cartesian_product`
- `action::reverse`
\section v0-2-0 Version 0.2.0
_Released:_ March 13, 2017.
Bring many interfaces into sync with the Ranges TS.
- Many interfaces are simply renamed. The following table shows the old names
and the new. (All names are in the `ranges::v3` namespace.)
| Old Name | New Name |
|-------------------------------|---------------------------|
| `indirect_swap` | `iter_swap` |
| `indirect_move` | `iter_move` |
| `iterator_value_t` | `value_type_t` |
| `iterator_reference_t` | `reference_t` |
| `iterator_difference_t` | `difference_type_t` |
| `iterator_size_t` | `size_type_t` |
| `iterator_rvalue_reference_t` | `rvalue_reference_t` |
| `iterator_common_reference_t` | `iter_common_reference_t` |
| `range_value_t` | `range_value_type_t` |
| `range_difference_t` | `range_difference_type_t` |
| `range_size_t` | `range_size_type_t` |
| `range_iterator_t` | `iterator_t` |
| `range_sentinel_t` | `sentinel_t` |
- `common_iterator` now requires that its two types (`Iterator` and `Sentinel`)
are different. Use `common_iterator_t<I, S>` to get the old behavior (i.e., if the two types are the same, it is an alias for `I`; otherwise, it is
`common_iterator<I, S>`).
- The following iterator adaptors now work with iterators that return proxies
from their postfix increment operator (i.e., `operator++(int)`):
* `common_iterator`
* `counted_iterator`
- The following customization points are now implemented per the Ranges TS
spec and will no longer find the associated unconstrained overload in
namespace `std::`:
* `ranges::begin`
* `ranges::end`
* `ranges::size`
* `ranges::swap`
* `ranges::iter_swap`
(In practice, this has very little effect but it may effect overloading in
rare situations.)
- `ranges::is_swappable` now only takes one template parameter. The new
`ranges::is_swappable_with<T, U>` tests whether `T` and `U` are swappable.
`ranges::is_swappable<T>` is equivalent to `ranges::is_swappable_with<T &, T &>`.
- The following object concepts have changed to conform with the Ranges TS
specification, and approved changes (see [P0547](http://wg21.link/p0547)):
* `Destructible`
* `Constructible`
* `DefaultConstructible`
* `MoveConstructible`
* `MoveConstructible`
* `Movable`
* `Assignable`
- The `View` concept is no longer satisfied by reference types.
- The syntax for defining a concept has changed slightly. See [iterator/concepts.hpp](https://github.com/ericniebler/range-v3/blob/master/include/range/v3/iterator/concepts.hpp) for examples.
\section v0-1-1 Version 0.1.1
- Small tweak to `Writable` concept to fix #537.
\section v0-1-0 Version 0.1.0
- March 8, 2017, Begin semantic versioning

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More