Commit 89ed072f392ab71646631410bddf778aee8ef737
Committed by
GitHub
1 parent
27d120e7
Codecov (#5)
* Adding first try at coverage * Fix for CMakeLists * Fix for wrong shell * Fixes to Coverage * Fix for missing coverage * Adding badge * Using repo token * Adding test files to coverage * Fix typo * Adding codecov * Fix for code coverage * Fix single file tests under CodeCov * Seperating coverage steps * Fix testrunner to be ctest * Fix constant rebuilding of doxygen * Adding coverage, tested locally * Adding compiler version to gcov * Fixing code coverage gcov download and use * Adding Coverage build type with some fixes * Better way of using gcc * Adding coverage badge * Removing tests from calcs
Showing
10 changed files
with
277 additions
and
14 deletions
.ci/build_doxygen.sh
| 1 | 1 | DOXYGEN_URL="ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.13.src.tar.gz" |
| 2 | 2 | cd "${DEPS_DIR}" |
| 3 | 3 | |
| 4 | -if [[ ! -f "${DEPS_DIR}/doxygen/bin/doxygen" ]] ; then | |
| 4 | +if [[ ! -f "${DEPS_DIR}/doxygen/build/bin/doxygen" ]] ; then | |
| 5 | 5 | echo "Downloading Doxygen" |
| 6 | 6 | mkdir -p doxygen |
| 7 | 7 | travis_retry wget --no-check-certificate --quiet -O - "${DOXYGEN_URL}" | tar --strip-components=1 -xz -C doxygen | ... | ... |
.ci/build_lcov.sh
0 → 100644
| 1 | +LCOV_URL="http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz" | |
| 2 | +cd "${DEPS_DIR}" | |
| 3 | + | |
| 4 | +if [[ ! -f "${DEPS_DIR}/lcov/bin/lcov" ]] ; then | |
| 5 | + echo "Downloading lcov" | |
| 6 | + mkdir -p lcov | |
| 7 | + travis_retry wget --no-check-certificate --quiet -O - "${LCOV_URL}" | tar --strip-components=1 -xz -C lcov | |
| 8 | +fi | |
| 9 | + | |
| 10 | +export PATH="${DEPS_DIR}/lcov/bin:${PATH}" | |
| 11 | +cd "${TRAVIS_BUILD_DIR}" | ... | ... |
.ci/prepare_altern.sh
0 → 100644
| 1 | +cd "${DEPS_DIR}" | |
| 2 | +mkdir -p extrabin | |
| 3 | +cd extrabin | |
| 4 | + | |
| 5 | +if [ "$CXX" = "g++" ] ; then | |
| 6 | + ln -s `which gcc-$COMPILER` gcc | |
| 7 | + ln -s `which g++-$COMPILER` g++ | |
| 8 | + ln -s `which gcov-$COMPILER` gcov | |
| 9 | +else | |
| 10 | + ln -s `which clang-$COMPILER` clang | |
| 11 | + ln -s `which clang++-$COMPILER` clang++ | |
| 12 | +fi | |
| 13 | + | |
| 14 | +export PATH="${DEPS_DIR}/extrabin":$PATH | |
| 15 | + | |
| 16 | +cd "${TRAVIS_BUILD_DIR}" | ... | ... |
.ci/run_codecov.sh
0 → 100755
| 1 | +set -evx | |
| 2 | + | |
| 3 | +cd ${TRAVIS_BUILD_DIR} | |
| 4 | +cd build | |
| 5 | +cmake .. -DCLI_SINGLE_FILE_TESTS=OFF -DCMAKE_BUILD_TYPE=Coverage | |
| 6 | +cmake --build . | |
| 7 | +cmake --build . --target CLI_coverage | |
| 8 | + | |
| 9 | +lcov --directory . --capture --output-file coverage.info # capture coverage info | |
| 10 | +lcov --remove coverage.info '*/tests/*' '*gtest*' '*gmock*' '/usr/*' --output-file coverage.info # filter out system | |
| 11 | +lcov --list coverage.info #debug info | |
| 12 | +# Uploading report to CodeCov | |
| 13 | +bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" | ... | ... |
.ci/travis.sh
.codecov.yml
0 → 100644
.travis.yml
| ... | ... | @@ -31,7 +31,11 @@ matrix: |
| 31 | 31 | - ubuntu-toolchain-r-test |
| 32 | 32 | packages: |
| 33 | 33 | - g++-6 |
| 34 | - env: COMPILER=6 | |
| 34 | + - curl | |
| 35 | + - lcov | |
| 36 | + env: | |
| 37 | + - COMPILER=6 | |
| 38 | + - COVERALLS=yes | |
| 35 | 39 | - compiler: gcc |
| 36 | 40 | addons: |
| 37 | 41 | apt: |
| ... | ... | @@ -39,17 +43,17 @@ matrix: |
| 39 | 43 | - ubuntu-toolchain-r-test |
| 40 | 44 | packages: |
| 41 | 45 | - g++-4.7 |
| 42 | - env: COMPILER=4.7 | |
| 46 | + env: | |
| 47 | + - COMPILER=4.7 | |
| 43 | 48 | - os: osx |
| 44 | 49 | |
| 45 | 50 | install: |
| 46 | 51 | - python -c 'import sys; print(sys.version_info[:])' |
| 47 | -- if [ "$CXX" = "g++" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then export CXX="g++-$COMPILER" CC="gcc-$COMPILER"; fi | |
| 48 | -- if [ "$CXX" = "clang++" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then export CXX="clang++-$COMPILER" CC="clang-$COMPILER"; fi | |
| 49 | -- if ["$TRAVIS_OS_NAME" = "osx" ] ; then export CXX="clang++" CC="clang"; fi | |
| 50 | 52 | - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" |
| 53 | +- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd $TRAVIS_BUILD_DIR && . .ci/prepare_altern.sh ; fi | |
| 51 | 54 | - if [ "$TRAVIS_OS_NAME" = "linux" ] ; then cd $TRAVIS_BUILD_DIR && . .ci/build_cmake.sh ; fi |
| 52 | 55 | - if [ "$TRAVIS_OS_NAME" = "linux" ] ; then cd $TRAVIS_BUILD_DIR && . .ci/build_doxygen.sh ; fi |
| 56 | +- if [ -n "$COVERALLS" ] ; then cd $TRAVIS_BUILD_DIR && . .ci/build_lcov.sh ; fi | |
| 53 | 57 | - cd "${DEPS_DIR}" |
| 54 | 58 | - if [ "$(python -c 'import sys; print(sys.version_info[0])')" = "2" ] ; then pip |
| 55 | 59 | install --user pathlib; fi |
| ... | ... | @@ -59,9 +63,10 @@ script: |
| 59 | 63 | - .ci/travis.sh |
| 60 | 64 | |
| 61 | 65 | after_success: |
| 66 | +- if [ -n "$COVERALLS" ] ; then cd $TRAVIS_BUILD_DIR && .ci/run_codecov.sh ; fi | |
| 62 | 67 | - echo "${TRAVIS_BRANCH}" |
| 63 | 68 | - echo "${TRAVIS_PULL_REQUEST}" |
| 64 | -- if [[ "${TRAVIS_BRANCH}" == "master" && "${TRAVIS_PULL_REQUEST}" == "false" && "$DEPLOY_MAT" == "yes" ]] ; then echo "Updating docs" && cd $TRAVIS_BUILD_DIR && .ci/build_docs.sh ; fi | |
| 69 | +- if [ "${TRAVIS_BRANCH}" == "master" && "${TRAVIS_PULL_REQUEST}" == "false" && -n "$DEPLOY_MAT" ] ; then echo "Updating docs" && cd $TRAVIS_BUILD_DIR && .ci/build_docs.sh ; fi | |
| 65 | 70 | |
| 66 | 71 | deploy: |
| 67 | 72 | provider: releases | ... | ... |
CMakeLists.txt
| ... | ... | @@ -22,6 +22,12 @@ if(MSVC) |
| 22 | 22 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) |
| 23 | 23 | endif() |
| 24 | 24 | |
| 25 | + | |
| 26 | +if(CMAKE_BUILD_TYPE STREQUAL Coverage) | |
| 27 | + include(CodeCoverage) | |
| 28 | + setup_target_for_coverage(CLI_coverage ctest coverage) | |
| 29 | +endif() | |
| 30 | + | |
| 25 | 31 | add_library(CLI INTERFACE) |
| 26 | 32 | target_include_directories(CLI INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") |
| 27 | 33 | |
| ... | ... | @@ -58,3 +64,4 @@ option(CLI_EXAMPLES "Build the examples" ${CUR_PROJ}) |
| 58 | 64 | if(CLI_EXAMPLES) |
| 59 | 65 | add_subdirectory(examples) |
| 60 | 66 | endif() |
| 67 | + | ... | ... |
README.md
| 1 | -[](https://travis-ci.org/henryiii/CLI11) | |
| 2 | -[](https://ci.appveyor.com/project/HenrySchreiner/cli11) | |
| 3 | -[](https://gitter.im/CLI11gitter/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | |
| 1 | +[![Build Status Linux and macOS][travis-badge]][travis-link] | |
| 2 | +[![Build Status Windows][appveyor-badge]][appveyor-link] | |
| 3 | +[![Code Coverage][codecov-badge]][codecov-link] | |
| 4 | +[![Join the chat at https://gitter.im/CLI11gitter/Lobby][gitter-badge]][gitter-link] | |
| 4 | 5 | [](./LICENSE) |
| 5 | 6 | |
| 6 | 7 | # CLI11: Command line parser for C++11 |
| ... | ... | @@ -251,3 +252,13 @@ try { |
| 251 | 252 | |
| 252 | 253 | This will print help in blue, errors in red, and will reset before returning the terminal to the user. |
| 253 | 254 | |
| 255 | + | |
| 256 | +[travis-badge]: https://img.shields.io/travis/henryiii/CLI11/master.svg?label=Linux/macOS | |
| 257 | +[travis-link]: https://travis-ci.org/henryiii/CLI11 | |
| 258 | +[appveyor-badge]: https://img.shields.io/appveyor/ci/HenrySchreiner/cli11/master.svg?label=Windows | |
| 259 | +[appveyor-link]: https://ci.appveyor.com/project/HenrySchreiner/cli11 | |
| 260 | +[codecov-badge]: https://codecov.io/gh/henryiii/CLI11/branch/master/graph/badge.svg | |
| 261 | +[codecov-link]: https://codecov.io/gh/henryiii/CLI11 | |
| 262 | +[gitter-badge]: https://badges.gitter.im/CLI11gitter/Lobby.svg | |
| 263 | +[gitter-link]: https://gitter.im/CLI11gitter/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge | |
| 264 | +[license-badge]: https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg | ... | ... |
cmake/CodeCoverage.cmake
0 → 100644
| 1 | +# Copyright (c) 2012 - 2015, Lars Bilke | |
| 2 | +# All rights reserved. | |
| 3 | +# | |
| 4 | +# Redistribution and use in source and binary forms, with or without modification, | |
| 5 | +# are permitted provided that the following conditions are met: | |
| 6 | +# | |
| 7 | +# 1. Redistributions of source code must retain the above copyright notice, this | |
| 8 | +# list of conditions and the following disclaimer. | |
| 9 | +# | |
| 10 | +# 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 11 | +# this list of conditions and the following disclaimer in the documentation | |
| 12 | +# and/or other materials provided with the distribution. | |
| 13 | +# | |
| 14 | +# 3. Neither the name of the copyright holder nor the names of its contributors | |
| 15 | +# may be used to endorse or promote products derived from this software without | |
| 16 | +# specific prior written permission. | |
| 17 | +# | |
| 18 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
| 19 | +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 20 | +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| 21 | +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | |
| 22 | +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 23 | +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 24 | +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
| 25 | +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 26 | +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 27 | +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 28 | +# | |
| 29 | +# | |
| 30 | +# | |
| 31 | +# 2012-01-31, Lars Bilke | |
| 32 | +# - Enable Code Coverage | |
| 33 | +# | |
| 34 | +# 2013-09-17, Joakim Söderberg | |
| 35 | +# - Added support for Clang. | |
| 36 | +# - Some additional usage instructions. | |
| 37 | +# | |
| 38 | +# USAGE: | |
| 39 | + | |
| 40 | +# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here: | |
| 41 | +# http://stackoverflow.com/a/22404544/80480 | |
| 42 | +# | |
| 43 | +# 1. Copy this file into your cmake modules path. | |
| 44 | +# | |
| 45 | +# 2. Add the following line to your CMakeLists.txt: | |
| 46 | +# INCLUDE(CodeCoverage) | |
| 47 | +# | |
| 48 | +# 3. Set compiler flags to turn off optimization and enable coverage: | |
| 49 | +# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") | |
| 50 | +# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") | |
| 51 | +# | |
| 52 | +# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target | |
| 53 | +# which runs your test executable and produces a lcov code coverage report: | |
| 54 | +# Example: | |
| 55 | +# SETUP_TARGET_FOR_COVERAGE( | |
| 56 | +# my_coverage_target # Name for custom target. | |
| 57 | +# test_driver # Name of the test driver executable that runs the tests. | |
| 58 | +# # NOTE! This should always have a ZERO as exit code | |
| 59 | +# # otherwise the coverage generation will not complete. | |
| 60 | +# coverage # Name of output directory. | |
| 61 | +# ) | |
| 62 | +# | |
| 63 | +# 4. Build a Debug build: | |
| 64 | +# cmake -DCMAKE_BUILD_TYPE=Debug .. | |
| 65 | +# make | |
| 66 | +# make my_coverage_target | |
| 67 | +# | |
| 68 | +# | |
| 69 | + | |
| 70 | +# Check prereqs | |
| 71 | + | |
| 72 | +FIND_PROGRAM( GCOV_PATH gcov) | |
| 73 | +FIND_PROGRAM( LCOV_PATH lcov ) | |
| 74 | +FIND_PROGRAM( GENHTML_PATH genhtml ) | |
| 75 | +FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests) | |
| 76 | + | |
| 77 | +IF(NOT GCOV_PATH) | |
| 78 | + MESSAGE(FATAL_ERROR "gcov not found! Aborting...") | |
| 79 | +ENDIF() # NOT GCOV_PATH | |
| 80 | + | |
| 81 | +IF("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") | |
| 82 | + IF("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) | |
| 83 | + MESSAGE(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") | |
| 84 | + ENDIF() | |
| 85 | +ELSEIF(NOT CMAKE_COMPILER_IS_GNUCXX) | |
| 86 | + MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") | |
| 87 | +ENDIF() # CHECK VALID COMPILER | |
| 88 | + | |
| 89 | +SET(CMAKE_CXX_FLAGS_COVERAGE | |
| 90 | + "-g -O0 --coverage" | |
| 91 | + CACHE STRING "Flags used by the C++ compiler during coverage builds." | |
| 92 | + FORCE ) | |
| 93 | +SET(CMAKE_C_FLAGS_COVERAGE | |
| 94 | + "-g -O0 --coverage" | |
| 95 | + CACHE STRING "Flags used by the C compiler during coverage builds." | |
| 96 | + FORCE ) | |
| 97 | +SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE | |
| 98 | + "--coverage" | |
| 99 | + CACHE STRING "Flags used for linking binaries during coverage builds." | |
| 100 | + FORCE ) | |
| 101 | +SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE | |
| 102 | + "--coverage" | |
| 103 | + CACHE STRING "Flags used by the shared libraries linker during coverage builds." | |
| 104 | + FORCE ) | |
| 105 | +MARK_AS_ADVANCED( | |
| 106 | + CMAKE_CXX_FLAGS_COVERAGE | |
| 107 | + CMAKE_C_FLAGS_COVERAGE | |
| 108 | + CMAKE_EXE_LINKER_FLAGS_COVERAGE | |
| 109 | + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) | |
| 110 | + | |
| 111 | +IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage")) | |
| 112 | + MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) | |
| 113 | +ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" | |
| 114 | + | |
| 115 | + | |
| 116 | +# Param _targetname The name of new the custom make target | |
| 117 | +# Param _testrunner The name of the target which runs the tests. | |
| 118 | +# MUST return ZERO always, even on errors. | |
| 119 | +# If not, no coverage report will be created! | |
| 120 | +# Param _outputname lcov output is generated as _outputname.info | |
| 121 | +# HTML report is generated in _outputname/index.html | |
| 122 | +# Optional fourth parameter is passed as arguments to _testrunner | |
| 123 | +# Pass them in list form, e.g.: "-j;2" for -j 2 | |
| 124 | +FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname) | |
| 125 | + | |
| 126 | + IF(NOT LCOV_PATH) | |
| 127 | + MESSAGE(FATAL_ERROR "lcov not found! Aborting...") | |
| 128 | + ENDIF() # NOT LCOV_PATH | |
| 129 | + | |
| 130 | + IF(NOT GENHTML_PATH) | |
| 131 | + MESSAGE(FATAL_ERROR "genhtml not found! Aborting...") | |
| 132 | + ENDIF() # NOT GENHTML_PATH | |
| 133 | + | |
| 134 | + SET(coverage_info "${CMAKE_BINARY_DIR}/${_outputname}.info") | |
| 135 | + SET(coverage_cleaned "${coverage_info}.cleaned") | |
| 136 | + | |
| 137 | + SEPARATE_ARGUMENTS(test_command UNIX_COMMAND "${_testrunner}") | |
| 138 | + | |
| 139 | + # Setup target | |
| 140 | + ADD_CUSTOM_TARGET(${_targetname} | |
| 141 | + | |
| 142 | + # Cleanup lcov | |
| 143 | + ${LCOV_PATH} --directory . --zerocounters | |
| 144 | + | |
| 145 | + # Run tests | |
| 146 | + COMMAND ${test_command} ${ARGV3} | |
| 147 | + | |
| 148 | + # Capturing lcov counters and generating report | |
| 149 | + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${coverage_info} | |
| 150 | + COMMAND ${LCOV_PATH} --remove ${coverage_info} '*/tests/*' '*gtest*' '*gmock*' '/usr/*' --output-file ${coverage_cleaned} | |
| 151 | + COMMAND ${GENHTML_PATH} -o ${_outputname} ${coverage_cleaned} | |
| 152 | + COMMAND ${CMAKE_COMMAND} -E remove ${coverage_info} ${coverage_cleaned} | |
| 153 | + | |
| 154 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} | |
| 155 | + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." | |
| 156 | + ) | |
| 157 | + | |
| 158 | + # Show info where to find the report | |
| 159 | + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD | |
| 160 | + COMMAND ; | |
| 161 | + COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report." | |
| 162 | + ) | |
| 163 | + | |
| 164 | +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE | |
| 165 | + | |
| 166 | +# Param _targetname The name of new the custom make target | |
| 167 | +# Param _testrunner The name of the target which runs the tests | |
| 168 | +# Param _outputname cobertura output is generated as _outputname.xml | |
| 169 | +# Optional fourth parameter is passed as arguments to _testrunner | |
| 170 | +# Pass them in list form, e.g.: "-j;2" for -j 2 | |
| 171 | +FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname) | |
| 172 | + | |
| 173 | + IF(NOT PYTHON_EXECUTABLE) | |
| 174 | + MESSAGE(FATAL_ERROR "Python not found! Aborting...") | |
| 175 | + ENDIF() # NOT PYTHON_EXECUTABLE | |
| 176 | + | |
| 177 | + IF(NOT GCOVR_PATH) | |
| 178 | + MESSAGE(FATAL_ERROR "gcovr not found! Aborting...") | |
| 179 | + ENDIF() # NOT GCOVR_PATH | |
| 180 | + | |
| 181 | + ADD_CUSTOM_TARGET(${_targetname} | |
| 182 | + | |
| 183 | + # Run tests | |
| 184 | + ${_testrunner} ${ARGV3} | |
| 185 | + | |
| 186 | + # Running gcovr | |
| 187 | + COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml | |
| 188 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} | |
| 189 | + COMMENT "Running gcovr to produce Cobertura code coverage report." | |
| 190 | + ) | |
| 191 | + | |
| 192 | + # Show info where to find the report | |
| 193 | + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD | |
| 194 | + COMMAND ; | |
| 195 | + COMMENT "Cobertura code coverage report saved in ${_outputname}.xml." | |
| 196 | + ) | |
| 197 | + | |
| 198 | +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA | ... | ... |