mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-16 12:45:24 +00:00
Compare commits
6 Commits
ripple/was
...
bthomee/up
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
857a4eea9a | ||
|
|
24177fdca0 | ||
|
|
96d17b7f66 | ||
|
|
ec44347ffc | ||
|
|
c9458b72ca | ||
|
|
ebcfd6645d |
@@ -28,6 +28,7 @@ ignoreRegExpList:
|
||||
- /[\['"`]-[DWw][a-zA-Z0-9_-]+['"`\]]/g # compile flags
|
||||
suggestWords:
|
||||
- xprl->xrpl
|
||||
- xprld->xrpld
|
||||
- unsynched->unsynced
|
||||
- synched->synced
|
||||
- synch->sync
|
||||
@@ -61,6 +62,7 @@ words:
|
||||
- compr
|
||||
- conanfile
|
||||
- conanrun
|
||||
- confs
|
||||
- connectability
|
||||
- coro
|
||||
- coros
|
||||
@@ -90,6 +92,7 @@ words:
|
||||
- finalizers
|
||||
- firewalled
|
||||
- fmtdur
|
||||
- fsanitize
|
||||
- funclets
|
||||
- gcov
|
||||
- gcovr
|
||||
@@ -126,6 +129,7 @@ words:
|
||||
- lseq
|
||||
- lsmf
|
||||
- ltype
|
||||
- mcmodel
|
||||
- MEMORYSTATUSEX
|
||||
- Merkle
|
||||
- Metafuncton
|
||||
@@ -235,6 +239,8 @@ words:
|
||||
- txn
|
||||
- txns
|
||||
- txs
|
||||
- UBSAN
|
||||
- ubsan
|
||||
- umant
|
||||
- unacquired
|
||||
- unambiguity
|
||||
@@ -270,6 +276,7 @@ words:
|
||||
- xbridge
|
||||
- xchain
|
||||
- ximinez
|
||||
- EXPECT_STREQ
|
||||
- XMACRO
|
||||
- xrpkuwait
|
||||
- xrpl
|
||||
|
||||
6
.github/actions/build-deps/action.yml
vendored
6
.github/actions/build-deps/action.yml
vendored
@@ -18,6 +18,10 @@ inputs:
|
||||
description: "The logging verbosity."
|
||||
required: false
|
||||
default: "verbose"
|
||||
sanitizers:
|
||||
description: "The sanitizers to enable."
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -29,9 +33,11 @@ runs:
|
||||
BUILD_OPTION: ${{ inputs.force_build == 'true' && '*' || 'missing' }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
|
||||
SANITIZERS: ${{ inputs.sanitizers }}
|
||||
run: |
|
||||
echo 'Installing dependencies.'
|
||||
conan install \
|
||||
--profile ci \
|
||||
--build="${BUILD_OPTION}" \
|
||||
--options:host='&:tests=True' \
|
||||
--options:host='&:xrpld=True' \
|
||||
|
||||
21
.github/actions/extract-version/action.yml
vendored
Normal file
21
.github/actions/extract-version/action.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Extract version
|
||||
description: "Extract version from BuildInfo.cpp"
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "The version extracted from BuildInfo.cpp."
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Extract version
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')"
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo 'Unable to extract version from BuildInfo.cpp.'
|
||||
exit 1
|
||||
fi
|
||||
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
2
.github/actions/setup-conan/action.yml
vendored
2
.github/actions/setup-conan/action.yml
vendored
@@ -28,7 +28,7 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'Installing profile.'
|
||||
conan config install conan/profiles/default -tf $(conan config home)/profiles/
|
||||
conan config install conan/profiles/ -tf $(conan config home)/profiles/
|
||||
|
||||
echo 'Conan profile:'
|
||||
conan profile show
|
||||
|
||||
72
.github/actions/upload-recipe/action.yml
vendored
Normal file
72
.github/actions/upload-recipe/action.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Upload Conan recipe
|
||||
description: "Upload recipe to a Conan remote."
|
||||
|
||||
inputs:
|
||||
conan_recipe_name:
|
||||
description: "The name of the recipe to use."
|
||||
required: false
|
||||
default: xrpl
|
||||
conan_recipe_version:
|
||||
description: "The version of the recipe to use."
|
||||
required: true
|
||||
conan_recipe_channel:
|
||||
description: "The optional Conan channel to use."
|
||||
required: false
|
||||
conan_recipe_user:
|
||||
description: "The optional Conan user to use."
|
||||
required: false
|
||||
conan_remote_name:
|
||||
description: "The name of the Conan remote to use."
|
||||
required: true
|
||||
conan_remote_url:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: true
|
||||
conan_remote_username:
|
||||
description: "The username for logging into the Conan remote."
|
||||
required: true
|
||||
conan_remote_password:
|
||||
description: "The password for logging into the Conan remote."
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
conan_ref: ${{ steps.ref.outputs.ref }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: Calculate Conan reference
|
||||
id: ref
|
||||
shell: bash
|
||||
env:
|
||||
CONAN_RECIPE_NAME: ${{ inputs.conan_recipe_name }}
|
||||
CONAN_RECIPE_VERSION: ${{ inputs.conan_recipe_version }}
|
||||
CONAN_RECIPE_CHANNEL: ${{ inputs.conan_recipe_channel }}
|
||||
CONAN_RECIPE_USER: ${{ inputs.conan_recipe_user }}
|
||||
run: |
|
||||
if [[ -n "${CONAN_RECIPE_USER}" && -n "${CONAN_RECIPE_CHANNEL}" ]]; then
|
||||
echo "ref=${CONAN_RECIPE_NAME}/${CONAN_RECIPE_VERSION}@${CONAN_RECIPE_USER}/${CONAN_RECIPE_CHANNEL}" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo "ref=${CONAN_RECIPE_NAME}/${CONAN_RECIPE_VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
- name: Set up Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
with:
|
||||
conan_remote_name: ${{ inputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ inputs.conan_remote_url }}
|
||||
- name: Log into Conan remote
|
||||
shell: bash
|
||||
env:
|
||||
CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
|
||||
CONAN_REMOTE_USERNAME: ${{ inputs.conan_remote_username }}
|
||||
CONAN_REMOTE_PASSWORD: ${{ inputs.conan_remote_password }}
|
||||
run: conan remote login "${CONAN_REMOTE_NAME}" "${CONAN_REMOTE_USERNAME}" --password "${CONAN_REMOTE_PASSWORD}"
|
||||
- name: Upload package
|
||||
shell: bash
|
||||
env:
|
||||
CONAN_RECIPE_CHANNEL: ${{ inputs.conan_recipe_channel }}
|
||||
CONAN_RECIPE_USER: ${{ inputs.conan_recipe_user }}
|
||||
CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
|
||||
run: |
|
||||
conan export --channel="${CONAN_RECIPE_CHANNEL}" --user="${CONAN_RECIPE_USER}" .
|
||||
conan upload --confirm --check --remote="${CONAN_REMOTE_NAME}" ${{ steps.ref.outputs.ref }}
|
||||
60
.github/scripts/strategy-matrix/generate.py
vendored
60
.github/scripts/strategy-matrix/generate.py
vendored
@@ -229,7 +229,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (n := os["compiler_version"]) != "":
|
||||
config_name += f"-{n}"
|
||||
config_name += (
|
||||
f"-{architecture['platform'][architecture['platform'].find('/') + 1 :]}"
|
||||
f"-{architecture['platform'][architecture['platform'].find('/')+1:]}"
|
||||
)
|
||||
config_name += f"-{build_type.lower()}"
|
||||
if "-Dcoverage=ON" in cmake_args:
|
||||
@@ -240,17 +240,53 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
# Add the configuration to the list, with the most unique fields first,
|
||||
# so that they are easier to identify in the GitHub Actions UI, as long
|
||||
# names get truncated.
|
||||
configurations.append(
|
||||
{
|
||||
"config_name": config_name,
|
||||
"cmake_args": cmake_args,
|
||||
"cmake_target": cmake_target,
|
||||
"build_only": build_only,
|
||||
"build_type": build_type,
|
||||
"os": os,
|
||||
"architecture": architecture,
|
||||
}
|
||||
)
|
||||
# Add Address and Thread (both coupled with UB) sanitizers for specific bookworm distros.
|
||||
# GCC-Asan rippled-embedded tests are failing because of https://github.com/google/sanitizers/issues/856
|
||||
if (
|
||||
os["distro_version"] == "bookworm"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
|
||||
):
|
||||
# Add ASAN + UBSAN configuration.
|
||||
configurations.append(
|
||||
{
|
||||
"config_name": config_name + "-asan-ubsan",
|
||||
"cmake_args": cmake_args,
|
||||
"cmake_target": cmake_target,
|
||||
"build_only": build_only,
|
||||
"build_type": build_type,
|
||||
"os": os,
|
||||
"architecture": architecture,
|
||||
"sanitizers": "address,undefinedbehavior",
|
||||
}
|
||||
)
|
||||
# TSAN is deactivated due to seg faults with latest compilers.
|
||||
activate_tsan = False
|
||||
if activate_tsan:
|
||||
configurations.append(
|
||||
{
|
||||
"config_name": config_name + "-tsan-ubsan",
|
||||
"cmake_args": cmake_args,
|
||||
"cmake_target": cmake_target,
|
||||
"build_only": build_only,
|
||||
"build_type": build_type,
|
||||
"os": os,
|
||||
"architecture": architecture,
|
||||
"sanitizers": "thread,undefinedbehavior",
|
||||
}
|
||||
)
|
||||
else:
|
||||
configurations.append(
|
||||
{
|
||||
"config_name": config_name,
|
||||
"cmake_args": cmake_args,
|
||||
"cmake_target": cmake_target,
|
||||
"build_only": build_only,
|
||||
"build_type": build_type,
|
||||
"os": os,
|
||||
"architecture": architecture,
|
||||
"sanitizers": "",
|
||||
}
|
||||
)
|
||||
|
||||
return configurations
|
||||
|
||||
|
||||
19
.github/workflows/reusable-build-test-config.yml
vendored
19
.github/workflows/reusable-build-test-config.yml
vendored
@@ -51,6 +51,12 @@ on:
|
||||
type: number
|
||||
default: 2
|
||||
|
||||
sanitizers:
|
||||
description: "The sanitizers to enable."
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
secrets:
|
||||
CODECOV_TOKEN:
|
||||
description: "The Codecov token to use for uploading coverage reports."
|
||||
@@ -91,6 +97,7 @@ jobs:
|
||||
# Determine if coverage and voidstar should be enabled.
|
||||
COVERAGE_ENABLED: ${{ contains(inputs.cmake_args, '-Dcoverage=ON') }}
|
||||
VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
||||
SANITIZERS_ENABLED: ${{ inputs.sanitizers != '' }}
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
@@ -128,11 +135,13 @@ jobs:
|
||||
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
||||
# amount of logs. For other OSes, the "verbose" logs are more useful.
|
||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||
sanitizers: ${{ inputs.sanitizers }}
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
SANITIZERS: ${{ inputs.sanitizers }}
|
||||
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
||||
run: |
|
||||
cmake \
|
||||
@@ -174,7 +183,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Check linking (Linux)
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
run: |
|
||||
ldd ./xrpld
|
||||
@@ -191,6 +200,14 @@ jobs:
|
||||
run: |
|
||||
./xrpld --version | grep libvoidstar
|
||||
|
||||
- name: Set sanitizer options
|
||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
||||
run: |
|
||||
echo "ASAN_OPTIONS=print_stacktrace=1:detect_container_overflow=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=second_deadlock_stack=1:halt_on_error=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
|
||||
1
.github/workflows/reusable-build-test.yml
vendored
1
.github/workflows/reusable-build-test.yml
vendored
@@ -57,5 +57,6 @@ jobs:
|
||||
runs_on: ${{ toJSON(matrix.architecture.runner) }}
|
||||
image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }}
|
||||
config_name: ${{ matrix.config_name }}
|
||||
sanitizers: ${{ matrix.sanitizers }}
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
32
.github/workflows/reusable-notify-clio.yml
vendored
32
.github/workflows/reusable-notify-clio.yml
vendored
@@ -44,37 +44,29 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
- name: Extract version
|
||||
id: version
|
||||
uses: ./.github/actions/extract-version
|
||||
- name: Generate outputs
|
||||
id: generate
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
echo 'Generating user and channel.'
|
||||
echo "user=clio" >> "${GITHUB_OUTPUT}"
|
||||
echo "channel=pr_${PR_NUMBER}" >> "${GITHUB_OUTPUT}"
|
||||
echo 'Extracting version.'
|
||||
echo "version=$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}"
|
||||
- name: Calculate conan reference
|
||||
id: conan_ref
|
||||
run: |
|
||||
echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}"
|
||||
- name: Set up Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
- name: Upload recipe
|
||||
uses: ./.github/actions/upload-recipe
|
||||
id: upload
|
||||
with:
|
||||
conan_recipe_version: ${{ steps.version.outputs.version }}
|
||||
conan_recipe_channel: ${{ steps.generate.outputs.channel }}
|
||||
conan_recipe_user: ${{ steps.generate.outputs.user }}
|
||||
conan_remote_name: ${{ inputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ inputs.conan_remote_url }}
|
||||
- name: Log into Conan remote
|
||||
env:
|
||||
CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
|
||||
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}"
|
||||
- name: Upload package
|
||||
env:
|
||||
CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
|
||||
run: |
|
||||
conan export --user=${{ steps.generate.outputs.user }} --channel=${{ steps.generate.outputs.channel }} .
|
||||
conan upload --confirm --check --remote="${CONAN_REMOTE_NAME}" xrpl/${{ steps.conan_ref.outputs.conan_ref }}
|
||||
conan_remote_username: ${{ secrets.conan_remote_username }}
|
||||
conan_remote_password: ${{ secrets.conan_remote_password }}
|
||||
outputs:
|
||||
conan_ref: ${{ steps.conan_ref.outputs.conan_ref }}
|
||||
conan_ref: ${{ steps.upload.outputs.conan_ref }}
|
||||
|
||||
notify:
|
||||
needs: upload
|
||||
|
||||
38
BUILD.md
38
BUILD.md
@@ -1,5 +1,5 @@
|
||||
| :warning: **WARNING** :warning:
|
||||
|---|
|
||||
| :warning: **WARNING** :warning: |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md). |
|
||||
|
||||
> These instructions also assume a basic familiarity with Conan and CMake.
|
||||
@@ -523,18 +523,32 @@ stored inside the build directory, as either of:
|
||||
- file named `coverage.`_extension_, with a suitable extension for the report format, or
|
||||
- directory named `coverage`, with the `index.html` and other files inside, for the `html-details` or `html-nested` report formats.
|
||||
|
||||
## Sanitizers
|
||||
|
||||
To build dependencies and xrpld with sanitizer instrumentation, set the
|
||||
`SANITIZERS` environment variable (only once before running conan and cmake) and use the `sanitizers` profile in conan:
|
||||
|
||||
```bash
|
||||
export SANITIZERS=address,undefinedbehavior
|
||||
|
||||
conan install .. --output-folder . --profile:all sanitizers --build missing --settings build_type=Debug
|
||||
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -Dxrpld=ON -Dtests=ON ..
|
||||
```
|
||||
|
||||
See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Default Value | Description |
|
||||
| ---------- | ------------- | ------------------------------------------------------------------ |
|
||||
| `assert` | OFF | Enable assertions. |
|
||||
| `coverage` | OFF | Prepare the coverage report. |
|
||||
| `san` | N/A | Enable a sanitizer with Clang. Choices are `thread` and `address`. |
|
||||
| `tests` | OFF | Build tests. |
|
||||
| `unity` | OFF | Configure a unity build. |
|
||||
| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. |
|
||||
| `werr` | OFF | Treat compilation warnings as errors |
|
||||
| `wextra` | OFF | Enable additional compilation warnings |
|
||||
| Option | Default Value | Description |
|
||||
| ---------- | ------------- | -------------------------------------------------------------- |
|
||||
| `assert` | OFF | Enable assertions. |
|
||||
| `coverage` | OFF | Prepare the coverage report. |
|
||||
| `tests` | OFF | Build tests. |
|
||||
| `unity` | OFF | Configure a unity build. |
|
||||
| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. |
|
||||
| `werr` | OFF | Treat compilation warnings as errors |
|
||||
| `wextra` | OFF | Enable additional compilation warnings |
|
||||
|
||||
[Unity builds][5] may be faster for the first build
|
||||
(at the cost of much more memory) since they concatenate sources into fewer
|
||||
|
||||
@@ -16,14 +16,16 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
include(CompilationEnv)
|
||||
|
||||
if(is_gcc)
|
||||
# GCC-specific fixes
|
||||
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
|
||||
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
elseif(is_clang)
|
||||
# Clang-specific fixes
|
||||
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
|
||||
elseif(MSVC)
|
||||
elseif(is_msvc)
|
||||
# MSVC-specific fixes
|
||||
add_compile_options(/wd4068) # Ignore unknown pragmas
|
||||
endif()
|
||||
@@ -77,6 +79,7 @@ if (packages_only)
|
||||
return ()
|
||||
endif ()
|
||||
include(XrplCompiler)
|
||||
include(XrplSanitizers)
|
||||
include(XrplInterface)
|
||||
|
||||
option(only_docs "Include only the docs target?" FALSE)
|
||||
|
||||
54
cmake/CompilationEnv.cmake
Normal file
54
cmake/CompilationEnv.cmake
Normal file
@@ -0,0 +1,54 @@
|
||||
# Shared detection of compiler, operating system, and architecture.
|
||||
#
|
||||
# This module centralizes environment detection so that other
|
||||
# CMake modules can use the same variables instead of repeating
|
||||
# checks on CMAKE_* and built-in platform variables.
|
||||
|
||||
# Only run once per configure step.
|
||||
include_guard(GLOBAL)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Compiler detection (C++)
|
||||
# --------------------------------------------------------------------
|
||||
set(is_clang FALSE)
|
||||
set(is_gcc FALSE)
|
||||
set(is_msvc FALSE)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
|
||||
set(is_clang TRUE)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(is_gcc TRUE)
|
||||
elseif(MSVC)
|
||||
set(is_msvc TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Operating system detection
|
||||
# --------------------------------------------------------------------
|
||||
set(is_linux FALSE)
|
||||
set(is_windows FALSE)
|
||||
set(is_macos FALSE)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(is_linux TRUE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(is_windows TRUE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(is_macos TRUE)
|
||||
endif()
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Architecture
|
||||
# --------------------------------------------------------------------
|
||||
set(is_amd64 FALSE)
|
||||
set(is_arm64 FALSE)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(is_amd64 TRUE)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
|
||||
set(is_arm64 TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
@@ -2,16 +2,23 @@
|
||||
setup project-wide compiler settings
|
||||
#]===================================================================]
|
||||
|
||||
include(CompilationEnv)
|
||||
|
||||
#[=========================================================[
|
||||
TODO some/most of these common settings belong in a
|
||||
toolchain file, especially the ABI-impacting ones
|
||||
#]=========================================================]
|
||||
add_library (common INTERFACE)
|
||||
add_library (Xrpl::common ALIAS common)
|
||||
include(XrplSanitizers)
|
||||
# add a single global dependency on this interface lib
|
||||
link_libraries (Xrpl::common)
|
||||
# Respect CMAKE_POSITION_INDEPENDENT_CODE setting (may be set by Conan toolchain)
|
||||
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
set_target_properties (common
|
||||
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON)
|
||||
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
target_compile_definitions (common
|
||||
INTERFACE
|
||||
@@ -116,8 +123,8 @@ else ()
|
||||
# link to static libc/c++ iff:
|
||||
# * static option set and
|
||||
# * NOT APPLE (AppleClang does not support static libc/c++) and
|
||||
# * NOT san (sanitizers typically don't work with static libc/c++)
|
||||
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${san}>>>:
|
||||
# * NOT SANITIZERS (sanitizers typically don't work with static libc/c++)
|
||||
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>:
|
||||
-static-libstdc++
|
||||
-static-libgcc
|
||||
>)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
xrpld compile options/settings via an interface library
|
||||
#]===================================================================]
|
||||
|
||||
include(CompilationEnv)
|
||||
|
||||
add_library (opts INTERFACE)
|
||||
add_library (Xrpl::opts ALIAS opts)
|
||||
target_compile_definitions (opts
|
||||
@@ -42,22 +44,6 @@ if(jemalloc)
|
||||
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
|
||||
endif ()
|
||||
|
||||
if (san)
|
||||
target_compile_options (opts
|
||||
INTERFACE
|
||||
# sanitizers recommend minimum of -O1 for reasonable performance
|
||||
$<$<CONFIG:Debug>:-O1>
|
||||
${SAN_FLAG}
|
||||
-fno-omit-frame-pointer)
|
||||
target_compile_definitions (opts
|
||||
INTERFACE
|
||||
$<$<STREQUAL:${san},address>:SANITIZER=ASAN>
|
||||
$<$<STREQUAL:${san},thread>:SANITIZER=TSAN>
|
||||
$<$<STREQUAL:${san},memory>:SANITIZER=MSAN>
|
||||
$<$<STREQUAL:${san},undefined>:SANITIZER=UBSAN>)
|
||||
target_link_libraries (opts INTERFACE ${SAN_FLAG} ${SAN_LIB})
|
||||
endif ()
|
||||
|
||||
#[===================================================================[
|
||||
xrpld transitive library deps via an interface library
|
||||
#]===================================================================]
|
||||
|
||||
198
cmake/XrplSanitizers.cmake
Normal file
198
cmake/XrplSanitizers.cmake
Normal file
@@ -0,0 +1,198 @@
|
||||
#[===================================================================[
|
||||
Configure sanitizers based on environment variables.
|
||||
|
||||
This module reads the following environment variables:
|
||||
- SANITIZERS: The sanitizers to enable. Possible values:
|
||||
- "address"
|
||||
- "address,undefinedbehavior"
|
||||
- "thread"
|
||||
- "thread,undefinedbehavior"
|
||||
- "undefinedbehavior"
|
||||
|
||||
The compiler type and platform are detected in CompilationEnv.cmake.
|
||||
The sanitizer compile options are applied to the 'common' interface library
|
||||
which is linked to all targets in the project.
|
||||
|
||||
Internal flag variables set by this module:
|
||||
|
||||
- SANITIZER_TYPES: List of sanitizer types to enable (e.g., "address",
|
||||
"thread", "undefined"). And two more flags for undefined behavior sanitizer (e.g., "float-divide-by-zero", "unsigned-integer-overflow").
|
||||
This list is joined with commas and passed to -fsanitize=<list>.
|
||||
|
||||
- SANITIZERS_COMPILE_FLAGS: Compiler flags for sanitizer instrumentation.
|
||||
Includes:
|
||||
* -fno-omit-frame-pointer: Preserves frame pointers for stack traces
|
||||
* -O1: Minimum optimization for reasonable performance
|
||||
* -fsanitize=<types>: Enables sanitizer instrumentation
|
||||
* -fsanitize-ignorelist=<path>: (Clang only) Compile-time ignorelist
|
||||
* -mcmodel=large/medium: (GCC only) Code model for large binaries
|
||||
* -Wno-stringop-overflow: (GCC only) Suppresses false positive warnings
|
||||
* -Wno-tsan: (For GCC TSAN combination only) Suppresses atomic_thread_fence warnings
|
||||
|
||||
- SANITIZERS_LINK_FLAGS: Linker flags for sanitizer runtime libraries.
|
||||
Includes:
|
||||
* -fsanitize=<types>: Links sanitizer runtime libraries
|
||||
* -mcmodel=large/medium: (GCC only) Matches compile-time code model
|
||||
|
||||
- SANITIZERS_RELOCATION_FLAGS: (GCC only) Code model flags for linking.
|
||||
Used to handle large instrumented binaries on x86_64:
|
||||
* -mcmodel=large: For AddressSanitizer (prevents relocation errors)
|
||||
* -mcmodel=medium: For ThreadSanitizer (large model is incompatible)
|
||||
#]===================================================================]
|
||||
|
||||
include(CompilationEnv)
|
||||
|
||||
# Read environment variable
|
||||
set(SANITIZERS $ENV{SANITIZERS})
|
||||
|
||||
# Set SANITIZERS_ENABLED flag for use in other modules
|
||||
if(SANITIZERS MATCHES "address|thread|undefinedbehavior")
|
||||
set(SANITIZERS_ENABLED TRUE)
|
||||
else()
|
||||
set(SANITIZERS_ENABLED FALSE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Sanitizers are not supported on Windows/MSVC
|
||||
if(is_msvc)
|
||||
message(FATAL_ERROR "Sanitizers are not supported on Windows/MSVC. "
|
||||
"Please unset the SANITIZERS environment variable.")
|
||||
endif()
|
||||
|
||||
message(STATUS "Configuring sanitizers: ${SANITIZERS}")
|
||||
|
||||
# Parse SANITIZERS value to determine which sanitizers to enable
|
||||
set(enable_asan FALSE)
|
||||
set(enable_tsan FALSE)
|
||||
set(enable_ubsan FALSE)
|
||||
|
||||
# Normalize SANITIZERS into a list
|
||||
set(san_list "${SANITIZERS}")
|
||||
string(REPLACE "," ";" san_list "${san_list}")
|
||||
separate_arguments(san_list)
|
||||
|
||||
foreach(san IN LISTS san_list)
|
||||
if(san STREQUAL "address")
|
||||
set(enable_asan TRUE)
|
||||
elseif(san STREQUAL "thread")
|
||||
set(enable_tsan TRUE)
|
||||
elseif(san STREQUAL "undefinedbehavior")
|
||||
set(enable_ubsan TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported sanitizer type: ${san}"
|
||||
"Supported: address, thread, undefinedbehavior and their combinations.")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Validate sanitizer compatibility
|
||||
if(enable_asan AND enable_tsan)
|
||||
message(FATAL_ERROR "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously. "
|
||||
"Use 'address' or 'thread', optionally with 'undefinedbehavior'.")
|
||||
endif()
|
||||
|
||||
# Frame pointer is required for meaningful stack traces. Sanitizers recommend minimum of -O1 for reasonable performance
|
||||
set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1")
|
||||
|
||||
# Build the sanitizer flags list
|
||||
set(SANITIZER_TYPES)
|
||||
|
||||
if(enable_asan)
|
||||
list(APPEND SANITIZER_TYPES "address")
|
||||
elseif(enable_tsan)
|
||||
list(APPEND SANITIZER_TYPES "thread")
|
||||
endif()
|
||||
|
||||
if(enable_ubsan)
|
||||
# UB sanitizer flags
|
||||
list(APPEND SANITIZER_TYPES "undefined" "float-divide-by-zero")
|
||||
if(is_clang)
|
||||
# Clang supports additional UB checks. More info here https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
|
||||
list(APPEND SANITIZER_TYPES "unsigned-integer-overflow")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Configure code model for GCC on amd64
|
||||
# Use large code model for ASAN to avoid relocation errors
|
||||
# Use medium code model for TSAN (large is not compatible with TSAN)
|
||||
set(SANITIZERS_RELOCATION_FLAGS)
|
||||
|
||||
# Compiler-specific configuration
|
||||
if(is_gcc)
|
||||
# Disable mold, gold and lld linkers for GCC with sanitizers
|
||||
# Use default linker (bfd/ld) which is more lenient with mixed code models
|
||||
# This is needed since the size of instrumented binary exceeds the limits set by mold, lld and gold linkers
|
||||
set(use_mold OFF CACHE BOOL "Use mold linker" FORCE)
|
||||
set(use_gold OFF CACHE BOOL "Use gold linker" FORCE)
|
||||
set(use_lld OFF CACHE BOOL "Use lld linker" FORCE)
|
||||
message(STATUS " Disabled mold, gold, and lld linkers for GCC with sanitizers")
|
||||
|
||||
# Suppress false positive warnings in GCC with stringop-overflow
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-stringop-overflow")
|
||||
|
||||
if(is_amd64 AND enable_asan)
|
||||
message(STATUS " Using large code model (-mcmodel=large)")
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=large")
|
||||
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=large")
|
||||
elseif(enable_tsan)
|
||||
# GCC doesn't support atomic_thread_fence with tsan. Suppress warnings.
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-tsan")
|
||||
message(STATUS " Using medium code model (-mcmodel=medium)")
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=medium")
|
||||
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=medium")
|
||||
endif()
|
||||
|
||||
# Join sanitizer flags with commas for -fsanitize option
|
||||
list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR)
|
||||
|
||||
# Add sanitizer to compile and link flags
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
set(SANITIZERS_LINK_FLAGS "${SANITIZERS_RELOCATION_FLAGS}" "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
|
||||
elseif(is_clang)
|
||||
# Add ignorelist for Clang (GCC doesn't support this)
|
||||
# Use CMAKE_SOURCE_DIR to get the path to the ignorelist
|
||||
set(IGNORELIST_PATH "${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt")
|
||||
if(NOT EXISTS "${IGNORELIST_PATH}")
|
||||
message(FATAL_ERROR "Sanitizer ignorelist not found: ${IGNORELIST_PATH}")
|
||||
endif()
|
||||
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize-ignorelist=${IGNORELIST_PATH}")
|
||||
message(STATUS " Using sanitizer ignorelist: ${IGNORELIST_PATH}")
|
||||
|
||||
# Join sanitizer flags with commas for -fsanitize option
|
||||
list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR)
|
||||
|
||||
# Add sanitizer to compile and link flags
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
set(SANITIZERS_LINK_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
endif()
|
||||
|
||||
message(STATUS " Compile flags: ${SANITIZERS_COMPILE_FLAGS}")
|
||||
message(STATUS " Link flags: ${SANITIZERS_LINK_FLAGS}")
|
||||
|
||||
# Apply the sanitizer flags to the 'common' interface library
|
||||
# This is the same library used by XrplCompiler.cmake
|
||||
target_compile_options(common INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:${SANITIZERS_COMPILE_FLAGS}>
|
||||
$<$<COMPILE_LANGUAGE:C>:${SANITIZERS_COMPILE_FLAGS}>
|
||||
)
|
||||
|
||||
# Apply linker flags
|
||||
target_link_options(common INTERFACE ${SANITIZERS_LINK_FLAGS})
|
||||
|
||||
# Define SANITIZERS macro for BuildInfo.cpp
|
||||
set(sanitizers_list)
|
||||
if(enable_asan)
|
||||
list(APPEND sanitizers_list "ASAN")
|
||||
endif()
|
||||
if(enable_tsan)
|
||||
list(APPEND sanitizers_list "TSAN")
|
||||
endif()
|
||||
if(enable_ubsan)
|
||||
list(APPEND sanitizers_list "UBSAN")
|
||||
endif()
|
||||
|
||||
if(sanitizers_list)
|
||||
list(JOIN sanitizers_list "." sanitizers_str)
|
||||
target_compile_definitions(common INTERFACE SANITIZERS=${sanitizers_str})
|
||||
endif()
|
||||
@@ -2,6 +2,8 @@
|
||||
sanity checks
|
||||
#]===================================================================]
|
||||
|
||||
include(CompilationEnv)
|
||||
|
||||
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||
@@ -16,14 +18,12 @@ if (NOT is_multiconfig)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") # both Clang and AppleClang
|
||||
set (is_clang TRUE)
|
||||
if (is_clang) # both Clang and AppleClang
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
|
||||
message (FATAL_ERROR "This project requires clang 16 or later")
|
||||
endif ()
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set (is_gcc TRUE)
|
||||
elseif (is_gcc)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
|
||||
message (FATAL_ERROR "This project requires GCC 12 or later")
|
||||
endif ()
|
||||
@@ -40,11 +40,6 @@ if (MSVC AND CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
||||
message (FATAL_ERROR "Visual Studio 32-bit build is not supported.")
|
||||
endif ()
|
||||
|
||||
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message (FATAL_ERROR "Xrpld requires a 64 bit target architecture.\n"
|
||||
"The most likely cause of this warning is trying to build xrpld with a 32-bit OS.")
|
||||
endif ()
|
||||
|
||||
if (APPLE AND NOT HOMEBREW)
|
||||
find_program (HOMEBREW brew)
|
||||
endif ()
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
declare options and variables
|
||||
#]===================================================================]
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set (is_linux TRUE)
|
||||
else()
|
||||
set(is_linux FALSE)
|
||||
endif()
|
||||
include(CompilationEnv)
|
||||
|
||||
if("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
|
||||
set(is_ci TRUE)
|
||||
@@ -62,7 +58,7 @@ else()
|
||||
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
endif()
|
||||
|
||||
if(is_linux)
|
||||
if(is_linux AND NOT SANITIZER)
|
||||
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
|
||||
option(static "link protobuf, openssl, libc++, and boost statically" ON)
|
||||
option(perf "Enables flags that assist with perf recording" OFF)
|
||||
@@ -107,33 +103,6 @@ option(local_protobuf
|
||||
option(local_grpc
|
||||
"Force a local build of gRPC instead of looking for an installed version." OFF)
|
||||
|
||||
# this one is a string and therefore can't be an option
|
||||
set(san "" CACHE STRING "On gcc & clang, add sanitizer instrumentation")
|
||||
set_property(CACHE san PROPERTY STRINGS ";undefined;memory;address;thread")
|
||||
if(san)
|
||||
string(TOLOWER ${san} san)
|
||||
set(SAN_FLAG "-fsanitize=${san}")
|
||||
set(SAN_LIB "")
|
||||
if(is_gcc)
|
||||
if(san STREQUAL "address")
|
||||
set(SAN_LIB "asan")
|
||||
elseif(san STREQUAL "thread")
|
||||
set(SAN_LIB "tsan")
|
||||
elseif(san STREQUAL "memory")
|
||||
set(SAN_LIB "msan")
|
||||
elseif(san STREQUAL "undefined")
|
||||
set(SAN_LIB "ubsan")
|
||||
endif()
|
||||
endif()
|
||||
set(_saved_CRL ${CMAKE_REQUIRED_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_LIBRARIES "${SAN_FLAG};${SAN_LIB}")
|
||||
check_cxx_compiler_flag(${SAN_FLAG} COMPILER_SUPPORTS_SAN)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${_saved_CRL})
|
||||
if(NOT COMPILER_SUPPORTS_SAN)
|
||||
message(FATAL_ERROR "${san} sanitizer does not seem to be supported by your compiler")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# the remaining options are obscure and rarely used
|
||||
option(beast_no_unit_test_inline
|
||||
"Prevents unit test definitions from being inserted into global table"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
include(CompilationEnv)
|
||||
include(XrplSanitizers)
|
||||
|
||||
find_package(Boost REQUIRED
|
||||
COMPONENTS
|
||||
chrono
|
||||
@@ -32,7 +35,7 @@ target_link_libraries(xrpl_boost
|
||||
if(Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif()
|
||||
if(san AND is_clang)
|
||||
if(SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else
|
||||
# for gcc ?
|
||||
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
|
||||
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
|
||||
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
|
||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
|
||||
"grpc/1.72.0#f244a57bff01e708c55a1100b12e1589%1765850193.734",
|
||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
|
||||
"doctest/2.4.12#eb9fb352fb2fdfc8abb17ec270945165%1765850143.95",
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
|
||||
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
|
||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
|
||||
|
||||
1
conan/profiles/ci
Normal file
1
conan/profiles/ci
Normal file
@@ -0,0 +1 @@
|
||||
include(sanitizers)
|
||||
59
conan/profiles/sanitizers
Normal file
59
conan/profiles/sanitizers
Normal file
@@ -0,0 +1,59 @@
|
||||
include(default)
|
||||
{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %}
|
||||
{% set sanitizers = os.getenv("SANITIZERS") %}
|
||||
|
||||
[conf]
|
||||
{% if sanitizers %}
|
||||
{% if compiler == "gcc" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set model_code = "" %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1", "-Wno-stringop-overflow"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set model_code = "-mcmodel=large" %}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set model_code = "-mcmodel=medium" %}
|
||||
{% set _ = extra_cxxflags.append("-Wno-tsan") %}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("undefined") %}
|
||||
{% set _ = sanitizer_list.append("float-divide-by-zero") %}
|
||||
{% endif %}
|
||||
|
||||
{% set sanitizer_flags = "-fsanitize=" ~ ",".join(sanitizer_list) ~ " " ~ model_code %}
|
||||
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
{% endif %}
|
||||
{% elif compiler == "apple-clang" or compiler == "clang" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("undefined") %}
|
||||
{% set _ = sanitizer_list.append("float-divide-by-zero") %}
|
||||
{% set _ = sanitizer_list.append("unsigned-integer-overflow") %}
|
||||
{% endif %}
|
||||
|
||||
{% set sanitizer_flags = "-fsanitize=" ~ ",".join(sanitizer_list) %}
|
||||
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]
|
||||
@@ -39,7 +39,7 @@ class Xrpl(ConanFile):
|
||||
]
|
||||
|
||||
test_requires = [
|
||||
"doctest/2.4.12",
|
||||
"gtest/1.17.0",
|
||||
]
|
||||
|
||||
tool_requires = [
|
||||
|
||||
207
docs/build/sanitizers.md
vendored
Normal file
207
docs/build/sanitizers.md
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
# Sanitizer Configuration for Rippled
|
||||
|
||||
This document explains how to properly configure and run sanitizers (AddressSanitizer, undefinedbehaviorSanitizer, ThreadSanitizer) with the xrpld project.
|
||||
Corresponding suppression files are located in the `sanitizers/suppressions` directory.
|
||||
|
||||
- [Sanitizer Configuration for Rippled](#sanitizer-configuration-for-rippled)
|
||||
- [Building with Sanitizers](#building-with-sanitizers)
|
||||
- [Summary](#summary)
|
||||
- [Build steps:](#build-steps)
|
||||
- [Install dependencies](#install-dependencies)
|
||||
- [Call CMake](#call-cmake)
|
||||
- [Build](#build)
|
||||
- [Running Tests with Sanitizers](#running-tests-with-sanitizers)
|
||||
- [AddressSanitizer (ASAN)](#addresssanitizer-asan)
|
||||
- [ThreadSanitizer (TSan)](#threadsanitizer-tsan)
|
||||
- [LeakSanitizer (LSan)](#leaksanitizer-lsan)
|
||||
- [UndefinedBehaviorSanitizer (UBSan)](#undefinedbehaviorsanitizer-ubsan)
|
||||
- [Suppression Files](#suppression-files)
|
||||
- [`asan.supp`](#asansupp)
|
||||
- [`lsan.supp`](#lsansupp)
|
||||
- [`ubsan.supp`](#ubsansupp)
|
||||
- [`tsan.supp`](#tsansupp)
|
||||
- [`sanitizer-ignorelist.txt`](#sanitizer-ignorelisttxt)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- ["ASAN is ignoring requested \_\_asan_handle_no_return" warnings](#asan-is-ignoring-requested-__asan_handle_no_return-warnings)
|
||||
- [Sanitizer Mismatch Errors](#sanitizer-mismatch-errors)
|
||||
- [References](#references)
|
||||
|
||||
## Building with Sanitizers
|
||||
|
||||
### Summary
|
||||
|
||||
Follow the same instructions as mentioned in [BUILD.md](../../BUILD.md) but with the following changes:
|
||||
|
||||
1. Make sure you have a clean build directory.
|
||||
2. Set the `SANITIZERS` environment variable before calling conan install and cmake. Only set it once. Make sure both conan and cmake read the same values.
|
||||
Example: `export SANITIZERS=address,undefinedbehavior`
|
||||
3. Optionally use `--profile:all sanitizers` with Conan to build dependencies with sanitizer instrumentation. [!NOTE]Building with sanitizer-instrumented dependencies is slower but produces fewer false positives.
|
||||
4. Set `ASAN_OPTIONS`, `LSAN_OPTIONS`, `UBSAN_OPTIONS` and `TSAN_OPTIONS` environment variables to configure sanitizer behavior when running executables. [More details below](#running-tests-with-sanitizers).
|
||||
|
||||
---
|
||||
|
||||
### Build steps:
|
||||
|
||||
```bash
|
||||
cd /path/to/rippled
|
||||
rm -rf .build
|
||||
mkdir .build
|
||||
cd .build
|
||||
```
|
||||
|
||||
#### Install dependencies
|
||||
|
||||
The `SANITIZERS` environment variable is used by both Conan and CMake.
|
||||
|
||||
```bash
|
||||
export SANITIZERS=address,undefinedbehavior
|
||||
# Standard build (without instrumenting dependencies)
|
||||
conan install .. --output-folder . --build missing --settings build_type=Debug
|
||||
|
||||
# Or with sanitizer-instrumented dependencies (takes longer but fewer false positives)
|
||||
conan install .. --output-folder . --profile:all sanitizers --build missing --settings build_type=Debug
|
||||
```
|
||||
|
||||
[!CAUTION]
|
||||
Do not mix Address and Thread sanitizers - they are incompatible.
|
||||
|
||||
Since you already set the `SANITIZERS` environment variable when running Conan, same values will be read for the next part.
|
||||
|
||||
#### Call CMake
|
||||
|
||||
```bash
|
||||
cmake .. -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-Dtests=ON -Dxrpld=ON
|
||||
```
|
||||
|
||||
#### Build
|
||||
|
||||
```bash
|
||||
cmake --build . --parallel 4
|
||||
```
|
||||
|
||||
## Running Tests with Sanitizers
|
||||
|
||||
### AddressSanitizer (ASAN)
|
||||
|
||||
**IMPORTANT**: ASAN with Boost produces many false positives. Use these options:
|
||||
|
||||
```bash
|
||||
export ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=path/to/asan.supp:halt_on_error=0:log_path=asan.log"
|
||||
export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsan.log"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
```
|
||||
|
||||
**Why `detect_container_overflow=0`?**
|
||||
|
||||
- Boost intrusive containers (used in `aged_unordered_container`) trigger false positives
|
||||
- Boost context switching (used in `Workers.cpp`) confuses ASAN's stack tracking
|
||||
- Since we usually don't build Boost (because we don't want to instrument Boost and detect issues in Boost code) with ASAN but use Boost containers in ASAN instrumented rippled code, it generates false positives.
|
||||
- Building dependencies with ASAN instrumentation reduces false positives. But we don't want to instrument dependencies like Boost with ASAN because it is slow (to compile as well as run tests) and not necessary.
|
||||
- See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
||||
- More such flags are detailed [here](https://github.com/google/sanitizers/wiki/AddressSanitizerFlags)
|
||||
|
||||
### ThreadSanitizer (TSan)
|
||||
|
||||
```bash
|
||||
export TSAN_OPTIONS="suppressions=path/to/tsan.supp halt_on_error=0 log_path=tsan.log"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
```
|
||||
|
||||
More details [here](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual).
|
||||
|
||||
### LeakSanitizer (LSan)
|
||||
|
||||
LSan is automatically enabled with ASAN. To disable it:
|
||||
|
||||
```bash
|
||||
export ASAN_OPTIONS="detect_leaks=0"
|
||||
```
|
||||
|
||||
More details [here](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer).
|
||||
|
||||
### UndefinedBehaviorSanitizer (UBSan)
|
||||
|
||||
```bash
|
||||
export UBSAN_OPTIONS="suppressions=path/to/ubsan.supp:print_stacktrace=1:halt_on_error=0:log_path=ubsan.log"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
```
|
||||
|
||||
More details [here](https://clang.llvm.org/docs/undefinedbehaviorSanitizer.html).
|
||||
|
||||
## Suppression Files
|
||||
|
||||
[!NOTE] Attached files contain more details.
|
||||
|
||||
### [`asan.supp`](../../sanitizers/suppressions/asan.supp)
|
||||
|
||||
- **Purpose**: Suppress AddressSanitizer (ASAN) errors only
|
||||
- **Format**: `interceptor_name:<pattern>` where pattern matches file names. Supported suppression types are:
|
||||
- interceptor_name
|
||||
- interceptor_via_fun
|
||||
- interceptor_via_lib
|
||||
- odr_violation
|
||||
- **More info**: [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer)
|
||||
- **Note**: Cannot suppress stack-buffer-overflow, container-overflow, etc.
|
||||
|
||||
### [`lsan.supp`](../../sanitizers/suppressions/lsan.supp)
|
||||
|
||||
- **Purpose**: Suppress LeakSanitizer (LSan) errors only
|
||||
- **Format**: `leak:<pattern>` where pattern matches function/file names
|
||||
- **More info**: [LeakSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer)
|
||||
|
||||
### [`ubsan.supp`](../../sanitizers/suppressions/ubsan.supp)
|
||||
|
||||
- **Purpose**: Suppress undefinedbehaviorSanitizer errors
|
||||
- **Format**: `<error_type>:<pattern>` (e.g., `unsigned-integer-overflow:protobuf`)
|
||||
- **Covers**: Intentional overflows in sanitizers/suppressions libraries (protobuf, gRPC, stdlib)
|
||||
- More info [UBSan suppressions](https://clang.llvm.org/docs/SanitizerSpecialCaseList.html).
|
||||
|
||||
### [`tsan.supp`](../../sanitizers/suppressions/tsan.supp)
|
||||
|
||||
- **Purpose**: Suppress ThreadSanitizer data race warnings
|
||||
- **Format**: `race:<pattern>` where pattern matches function/file names
|
||||
- **More info**: [ThreadSanitizer suppressions](https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions)
|
||||
|
||||
### [`sanitizer-ignorelist.txt`](../../sanitizers/suppressions/sanitizer-ignorelist.txt)
|
||||
|
||||
- **Purpose**: Compile-time ignorelist for all sanitizers
|
||||
- **Usage**: Passed via `-fsanitize-ignorelist=absolute/path/to/sanitizer-ignorelist.txt`
|
||||
- **Format**: `<level>:<pattern>` (e.g., `src:Workers.cpp`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "ASAN is ignoring requested \_\_asan_handle_no_return" warnings
|
||||
|
||||
These warnings appear when using Boost context switching and are harmless. They indicate potential false positives.
|
||||
|
||||
### Sanitizer Mismatch Errors
|
||||
|
||||
If you see undefined symbols like `___tsan_atomic_load` when building with ASAN:
|
||||
|
||||
**Problem**: Dependencies were built with a different sanitizer than the main project.
|
||||
|
||||
**Solution**: Rebuild everything with the same sanitizer:
|
||||
|
||||
```bash
|
||||
rm -rf .build
|
||||
# Then follow the build instructions above
|
||||
```
|
||||
|
||||
Then review the log files: `asan.log.*`, `ubsan.log.*`, `tsan.log.*`
|
||||
|
||||
## References
|
||||
|
||||
- [AddressSanitizer Wiki](https://github.com/google/sanitizers/wiki/AddressSanitizer)
|
||||
- [AddressSanitizer Flags](https://github.com/google/sanitizers/wiki/AddressSanitizerFlags)
|
||||
- [Container Overflow Detection](https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow)
|
||||
- [UndefinedBehavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
|
||||
- [ThreadSanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual)
|
||||
29
sanitizers/suppressions/asan.supp
Normal file
29
sanitizers/suppressions/asan.supp
Normal file
@@ -0,0 +1,29 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppressions.
|
||||
#
|
||||
# ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0"
|
||||
#
|
||||
# The detect_container_overflow=0 option disables false positives from:
|
||||
# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h)
|
||||
# - Boost context/coroutine stack switching (Workers.cpp, thread.h)
|
||||
#
|
||||
# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
||||
|
||||
# Boost
|
||||
interceptor_name:boost/asio
|
||||
|
||||
# Leaks in Doctest tests: xrpl.test.*
|
||||
interceptor_name:src/libxrpl/net/HTTPClient.cpp
|
||||
interceptor_name:src/libxrpl/net/RegisterSSLCerts.cpp
|
||||
interceptor_name:src/tests/libxrpl/net/HTTPClient.cpp
|
||||
interceptor_name:xrpl/net/AutoSocket.h
|
||||
interceptor_name:xrpl/net/HTTPClient.h
|
||||
interceptor_name:xrpl/net/HTTPClientSSLContext.h
|
||||
interceptor_name:xrpl/net/RegisterSSLCerts.h
|
||||
|
||||
# Suppress false positive stack-buffer errors in thread stack allocation
|
||||
# Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189)
|
||||
# These occur during multi-threaded test initialization on macOS
|
||||
interceptor_name:memcpy
|
||||
interceptor_name:__bzero
|
||||
interceptor_name:__asan_memset
|
||||
interceptor_name:__asan_memcpy
|
||||
16
sanitizers/suppressions/lsan.supp
Normal file
16
sanitizers/suppressions/lsan.supp
Normal file
@@ -0,0 +1,16 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
||||
|
||||
# Suppress leaks detected by asan in rippled code.
|
||||
leak:src/libxrpl/net/HTTPClient.cpp
|
||||
leak:src/libxrpl/net/RegisterSSLCerts.cpp
|
||||
leak:src/tests/libxrpl/net/HTTPClient.cpp
|
||||
leak:xrpl/net/AutoSocket.h
|
||||
leak:xrpl/net/HTTPClient.h
|
||||
leak:xrpl/net/HTTPClientSSLContext.h
|
||||
leak:xrpl/net/RegisterSSLCerts.h
|
||||
leak:ripple::HTTPClient
|
||||
leak:ripple::HTTPClientImp
|
||||
|
||||
# Suppress leaks detected by asan in boost code.
|
||||
leak:boost::asio
|
||||
leak:boost/asio
|
||||
29
sanitizers/suppressions/sanitizer-ignorelist.txt
Normal file
29
sanitizers/suppressions/sanitizer-ignorelist.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
# We were seeing some false positives and some repeated errors(since these are library files) in following files.
|
||||
# Clang will skip instrumenting the files added here.
|
||||
# We should fix the underlying issues(if any) and remove these entries.
|
||||
|
||||
deadlock:libxrpl/beast/utility/beast_Journal.cpp
|
||||
deadlock:libxrpl/beast/utility/beast_PropertyStream.cpp
|
||||
deadlock:test/beast/beast_PropertyStream_test.cpp
|
||||
deadlock:xrpld/core/detail/Workers.cpp
|
||||
deadlock:xrpld/core/JobQueue.cpp
|
||||
|
||||
race:libxrpl/beast/utility/beast_Journal.cpp
|
||||
race:libxrpl/beast/utility/beast_PropertyStream.cpp
|
||||
race:test/beast/beast_PropertyStream_test.cpp
|
||||
race:xrpld/core/detail/Workers.cpp
|
||||
race:xrpld/core/JobQueue.cpp
|
||||
|
||||
signal:libxrpl/beast/utility/beast_Journal.cpp
|
||||
signal:libxrpl/beast/utility/beast_PropertyStream.cpp
|
||||
signal:test/beast/beast_PropertyStream_test.cpp
|
||||
signal:xrpld/core/detail/Workers.cpp
|
||||
signal:xrpld/core/JobQueue.cpp
|
||||
|
||||
src:beast/utility/beast_Journal.cpp
|
||||
src:beast/utility/beast_PropertyStream.cpp
|
||||
src:core/detail/Workers.cpp
|
||||
src:core/JobQueue.cpp
|
||||
src:libxrpl/beast/utility/beast_Journal.cpp
|
||||
src:test/beast/beast_PropertyStream_test.cpp
|
||||
src:src/test/app/Invariants_test.cpp
|
||||
102
sanitizers/suppressions/tsan.supp
Normal file
102
sanitizers/suppressions/tsan.supp
Normal file
@@ -0,0 +1,102 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
||||
|
||||
# Suppress race in Boost ASIO scheduler detected by GCC-15
|
||||
# This is a false positive in Boost's internal pipe() synchronization
|
||||
race:boost/asio/
|
||||
race:boost/context/
|
||||
race:boost/asio/executor.hpp
|
||||
race:boost::asio
|
||||
|
||||
# Suppress tsan related issues in rippled code.
|
||||
race:src/libxrpl/basics/make_SSLContext.cpp
|
||||
race:src/libxrpl/basics/Number.cpp
|
||||
race:src/libxrpl/json/json_value.cpp
|
||||
race:src/libxrpl/json/to_string.cpp
|
||||
race:src/libxrpl/ledger/OpenView.cpp
|
||||
race:src/libxrpl/net/HTTPClient.cpp
|
||||
race:src/libxrpl/nodestore/backend/NuDBFactory.cpp
|
||||
race:src/libxrpl/protocol/InnerObjectFormats.cpp
|
||||
race:src/libxrpl/protocol/STParsedJSON.cpp
|
||||
race:src/libxrpl/resource/ResourceManager.cpp
|
||||
race:src/test/app/Flow_test.cpp
|
||||
race:src/test/app/LedgerReplay_test.cpp
|
||||
race:src/test/app/NFToken_test.cpp
|
||||
race:src/test/app/Offer_test.cpp
|
||||
race:src/test/app/ValidatorSite_test.cpp
|
||||
race:src/test/consensus/NegativeUNL_test.cpp
|
||||
race:src/test/jtx/impl/Env.cpp
|
||||
race:src/test/jtx/impl/JSONRPCClient.cpp
|
||||
race:src/test/jtx/impl/pay.cpp
|
||||
race:src/test/jtx/impl/token.cpp
|
||||
race:src/test/rpc/Book_test.cpp
|
||||
race:src/xrpld/app/ledger/detail/InboundTransactions.cpp
|
||||
race:src/xrpld/app/main/Application.cpp
|
||||
race:src/xrpld/app/main/BasicApp.cpp
|
||||
race:src/xrpld/app/main/GRPCServer.cpp
|
||||
race:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
||||
race:src/xrpld/app/misc/FeeVoteImpl.cpp
|
||||
race:src/xrpld/app/rdb/detail/Wallet.cpp
|
||||
race:src/xrpld/overlay/detail/OverlayImpl.cpp
|
||||
race:src/xrpld/peerfinder/detail/PeerfinderManager.cpp
|
||||
race:src/xrpld/peerfinder/detail/SourceStrings.cpp
|
||||
race:src/xrpld/rpc/detail/ServerHandler.cpp
|
||||
race:xrpl/server/detail/Door.h
|
||||
race:xrpl/server/detail/Spawn.h
|
||||
race:xrpl/server/detail/ServerImpl.h
|
||||
race:xrpl/nodestore/detail/DatabaseNodeImp.h
|
||||
race:src/libxrpl/beast/utility/beast_Journal.cpp
|
||||
race:src/test/beast/LexicalCast_test.cpp
|
||||
race:ripple::ServerHandler
|
||||
|
||||
# More suppressions in external library code.
|
||||
race:crtstuff.c
|
||||
race:pipe
|
||||
|
||||
# Deadlock / lock-order-inversion suppressions
|
||||
# Note: GCC's TSAN may not fully support all deadlock suppression patterns
|
||||
deadlock:src/libxrpl/beast/utility/beast_Journal.cpp
|
||||
deadlock:src/libxrpl/beast/utility/beast_PropertyStream.cpp
|
||||
deadlock:src/test/beast/beast_PropertyStream_test.cpp
|
||||
deadlock:src/xrpld/core/detail/Workers.cpp
|
||||
deadlock:src/xrpld/app/misc/detail/Manifest.cpp
|
||||
deadlock:src/xrpld/app/misc/detail/ValidatorList.cpp
|
||||
deadlock:src/xrpld/app/misc/detail/ValidatorSite.cpp
|
||||
|
||||
signal:src/libxrpl/beast/utility/beast_Journal.cpp
|
||||
signal:src/xrpld/core/detail/Workers.cpp
|
||||
signal:src/xrpld/core/JobQueue.cpp
|
||||
signal:ripple::Workers::Worker
|
||||
|
||||
# Aggressive suppressing of deadlock tsan errors
|
||||
deadlock:pthread_create
|
||||
deadlock:pthread_rwlock_rdlock
|
||||
deadlock:boost::asio
|
||||
|
||||
# Suppress SEGV crashes in TSAN itself during stringbuf operations
|
||||
# This appears to be a GCC-15 TSAN instrumentation issue with basic_stringbuf::str()
|
||||
# Commonly triggered in beast::Journal::ScopedStream destructor
|
||||
signal:std::__cxx11::basic_stringbuf
|
||||
signal:basic_stringbuf
|
||||
signal:basic_ostringstream
|
||||
|
||||
called_from_lib:libclang_rt
|
||||
race:ostreambuf_iterator
|
||||
race:basic_ostream
|
||||
|
||||
# Suppress SEGV in Boost ASIO memory allocation with GCC-15 TSAN
|
||||
signal:boost::asio::aligned_new
|
||||
signal:boost::asio::detail::memory
|
||||
|
||||
# Suppress SEGV in execute_native_thread_routine
|
||||
signal:execute_native_thread_routine
|
||||
|
||||
# Suppress data race in Boost Context fiber management
|
||||
# This is a false positive in Boost's exception state management during fiber context switching
|
||||
race:__cxxabiv1::manage_exception_state
|
||||
race:boost::context::fiber::resume
|
||||
race:boost::asio::detail::spawned_fiber_thread
|
||||
race:boost::asio::detail::spawned_fiber_thread::suspend_with
|
||||
race:boost::asio::detail::spawned_fiber_thread::destroy
|
||||
|
||||
# Suppress data race in __tsan_memcpy called from Boost fiber operations
|
||||
race:__tsan_memcpy
|
||||
237
sanitizers/suppressions/ubsan.supp
Normal file
237
sanitizers/suppressions/ubsan.supp
Normal file
@@ -0,0 +1,237 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
||||
|
||||
# Suppress UBSan errors in external code by source file path
|
||||
# This matches any source file under the external/ directory
|
||||
alignment:external
|
||||
bool:external
|
||||
bounds:external
|
||||
cfi:external
|
||||
enum:external
|
||||
float-cast-overflow:external
|
||||
float-divide-by-zero:external
|
||||
function:external
|
||||
implicit-integer-sign-change:external
|
||||
implicit-signed-integer-truncation::external
|
||||
implicit-signed-integer-truncation:external
|
||||
implicit-unsigned-integer-truncation:external
|
||||
integer-divide-by-zero:external
|
||||
invalid-builtin-use:external
|
||||
invalid-objc-cast:external
|
||||
nonnull-attribute:external
|
||||
null:external
|
||||
nullability-arg:external
|
||||
nullability-assign:external
|
||||
nullability-return:external
|
||||
object-size:external
|
||||
pointer-overflow:external
|
||||
return:external
|
||||
returns-nonnull-attribute:external
|
||||
shift-base:external
|
||||
shift-exponent:external
|
||||
signed-integer-overflow:external
|
||||
undefined:external
|
||||
unreachable:external
|
||||
unsigned-integer-overflow:external
|
||||
vla-bound:external
|
||||
vptr_check:external
|
||||
vptr:external
|
||||
|
||||
# Suppress all UBSan errors in Boost libraries
|
||||
# This matches any files containing "boost" in its path or name
|
||||
alignment:boost
|
||||
bool:boost
|
||||
bounds:boost
|
||||
cfi:boost
|
||||
enum:boost
|
||||
float-cast-overflow:boost
|
||||
float-divide-by-zero:boost
|
||||
function:boost
|
||||
implicit-integer-sign-change:boost
|
||||
implicit-signed-integer-truncation:boost
|
||||
implicit-unsigned-integer-truncation:boost
|
||||
integer-divide-by-zero:boost
|
||||
invalid-builtin-use:boost
|
||||
invalid-objc-cast:boost
|
||||
nonnull-attribute:boost
|
||||
null:boost
|
||||
nullability-arg:boost
|
||||
nullability-assign:boost
|
||||
nullability-return:boost
|
||||
object-size:boost
|
||||
pointer-overflow:boost
|
||||
return:boost
|
||||
returns-nonnull-attribute:boost
|
||||
shift-base:boost
|
||||
shift-exponent:boost
|
||||
signed-integer-overflow:boost
|
||||
undefined:boost
|
||||
unreachable:boost
|
||||
unsigned-integer-overflow:boost
|
||||
vla-bound:boost
|
||||
vptr_check:boost
|
||||
vptr:boost
|
||||
|
||||
# Google protobuf
|
||||
undefined:protobuf
|
||||
|
||||
# Suppress UBSan errors in rippled code by source file path
|
||||
undefined:src/libxrpl/basics/base64.cpp
|
||||
undefined:src/libxrpl/basics/Number.cpp
|
||||
undefined:src/libxrpl/beast/utility/beast_Journal.cpp
|
||||
undefined:src/libxrpl/crypto/RFC1751.cpp
|
||||
undefined:src/libxrpl/ledger/ApplyView.cpp
|
||||
undefined:src/libxrpl/ledger/View.cpp
|
||||
undefined:src/libxrpl/protocol/Permissions.cpp
|
||||
undefined:src/libxrpl/protocol/STAmount.cpp
|
||||
undefined:src/libxrpl/protocol/STPathSet.cpp
|
||||
undefined:src/libxrpl/protocol/tokens.cpp
|
||||
undefined:src/libxrpl/shamap/SHAMap.cpp
|
||||
undefined:src/test/app/Batch_test.cpp
|
||||
undefined:src/test/app/Invariants_test.cpp
|
||||
undefined:src/test/app/NFToken_test.cpp
|
||||
undefined:src/test/app/Offer_test.cpp
|
||||
undefined:src/test/app/Path_test.cpp
|
||||
undefined:src/test/basics/XRPAmount_test.cpp
|
||||
undefined:src/test/beast/LexicalCast_test.cpp
|
||||
undefined:src/test/jtx/impl/acctdelete.cpp
|
||||
undefined:src/test/ledger/SkipList_test.cpp
|
||||
undefined:src/test/rpc/Subscribe_test.cpp
|
||||
undefined:src/tests/libxrpl/basics/RangeSet.cpp
|
||||
undefined:src/xrpld/app/main/BasicApp.cpp
|
||||
undefined:src/xrpld/app/main/BasicApp.cpp
|
||||
undefined:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
||||
undefined:src/xrpld/app/misc/NetworkOPs.cpp
|
||||
undefined:src/libxrpl/json/json_value.cpp
|
||||
undefined:src/xrpld/app/paths/detail/StrandFlow.h
|
||||
undefined:src/xrpld/app/tx/detail/NFTokenMint.cpp
|
||||
undefined:src/xrpld/app/tx/detail/SetOracle.cpp
|
||||
undefined:src/xrpld/core/detail/JobQueue.cpp
|
||||
undefined:src/xrpld/core/detail/Workers.cpp
|
||||
undefined:src/xrpld/rpc/detail/Role.cpp
|
||||
undefined:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
|
||||
undefined:xrpl/basics/base_uint.h
|
||||
undefined:xrpl/basics/DecayingSample.h
|
||||
undefined:xrpl/beast/test/yield_to.h
|
||||
undefined:xrpl/beast/xor_shift_engine.h
|
||||
undefined:xrpl/nodestore/detail/varint.h
|
||||
undefined:xrpl/peerfinder/detail/Counts.h
|
||||
undefined:xrpl/protocol/nft.h
|
||||
|
||||
# basic_string.h:483:51: runtime error: unsigned integer overflow
|
||||
unsigned-integer-overflow:basic_string.h
|
||||
unsigned-integer-overflow:bits/chrono.h
|
||||
unsigned-integer-overflow:bits/random.h
|
||||
unsigned-integer-overflow:bits/random.tcc
|
||||
unsigned-integer-overflow:bits/stl_algobase.h
|
||||
unsigned-integer-overflow:bits/uniform_int_dist.h
|
||||
unsigned-integer-overflow:string_view
|
||||
|
||||
# runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'std::size_t' (aka 'unsigned long')
|
||||
unsigned-integer-overflow:src/libxrpl/basics/base64.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/basics/Number.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/crypto/RFC1751.cpp
|
||||
unsigned-integer-overflow:rc/libxrpl/json/json_value.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/ledger/ApplyView.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/ledger/View.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/Permissions.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/STPathSet.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/tokens.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/shamap/SHAMap.cpp
|
||||
unsigned-integer-overflow:src/test/app/Batch_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/Invariants_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/NFToken_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/Offer_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/Path_test.cpp
|
||||
unsigned-integer-overflow:src/test/basics/XRPAmount_test.cpp
|
||||
unsigned-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
||||
unsigned-integer-overflow:src/test/jtx/impl/acctdelete.cpp
|
||||
unsigned-integer-overflow:src/test/ledger/SkipList_test.cpp
|
||||
unsigned-integer-overflow:src/test/rpc/Subscribe_test.cpp
|
||||
unsigned-integer-overflow:src/tests/libxrpl/basics/RangeSet.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/main/BasicApp.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/misc/NetworkOPs.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/paths/detail/StrandFlow.h
|
||||
unsigned-integer-overflow:src/xrpld/app/tx/detail/NFTokenMint.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/tx/detail/SetOracle.cpp
|
||||
unsigned-integer-overflow:src/xrpld/rpc/detail/Role.cpp
|
||||
unsigned-integer-overflow:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
|
||||
unsigned-integer-overflow:xrpl/basics/base_uint.h
|
||||
unsigned-integer-overflow:xrpl/basics/DecayingSample.h
|
||||
unsigned-integer-overflow:xrpl/beast/test/yield_to.h
|
||||
unsigned-integer-overflow:xrpl/beast/xor_shift_engine.h
|
||||
unsigned-integer-overflow:xrpl/nodestore/detail/varint.h
|
||||
unsigned-integer-overflow:xrpl/peerfinder/detail/Counts.h
|
||||
unsigned-integer-overflow:xrpl/protocol/nft.h
|
||||
|
||||
# Rippled intentional overflows and operations
|
||||
# STAmount uses intentional negation of INT64_MIN and overflow in arithmetic
|
||||
signed-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
||||
|
||||
# XRPAmount test intentional overflows
|
||||
signed-integer-overflow:src/test/basics/XRPAmount_test.cpp
|
||||
|
||||
# Peerfinder intentional overflow in counter arithmetic
|
||||
unsigned-integer-overflow:src/xrpld/peerfinder/detail/Counts.h
|
||||
|
||||
# Signed integer overflow suppressions
|
||||
signed-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
||||
|
||||
# External library suppressions
|
||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
||||
|
||||
# Protobuf intentional overflows in hash functions
|
||||
# Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393)
|
||||
unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h
|
||||
|
||||
# gRPC intentional overflows
|
||||
# gRPC uses intentional overflow in timer calculations
|
||||
unsigned-integer-overflow:grpc
|
||||
unsigned-integer-overflow:timer_manager.cc
|
||||
|
||||
# Standard library intentional overflows
|
||||
# These are intentional overflows in random number generation and character conversion
|
||||
unsigned-integer-overflow:__random/seed_seq.h
|
||||
unsigned-integer-overflow:__charconv/traits.h
|
||||
|
||||
|
||||
# Suppress errors in RocksDB
|
||||
# RocksDB uses intentional unsigned integer overflows in hash functions and CRC calculations
|
||||
unsigned-integer-overflow:rocks*/*/util/xxhash.h
|
||||
unsigned-integer-overflow:rocks*/*/util/xxph3.h
|
||||
unsigned-integer-overflow:rocks*/*/util/hash.cc
|
||||
unsigned-integer-overflow:rocks*/*/util/crc32c.cc
|
||||
unsigned-integer-overflow:rocks*/*/util/crc32c.h
|
||||
unsigned-integer-overflow:rocks*/*/include/rocksdb/utilities/options_type.h
|
||||
unsigned-integer-overflow:rocks*/*/table/format.h
|
||||
unsigned-integer-overflow:rocks*/*/table/format.cc
|
||||
unsigned-integer-overflow:rocks*/*/table/block_based/block_based_table_builder.cc
|
||||
unsigned-integer-overflow:rocks*/*/table/block_based/reader_common.cc
|
||||
unsigned-integer-overflow:rocks*/*/db/version_set.cc
|
||||
|
||||
# RocksDB misaligned loads (intentional for performance on ARM64)
|
||||
alignment:rocks*/*/util/crc32c_arm64.cc
|
||||
|
||||
# nudb intentional overflows in hash functions
|
||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
||||
alignment:nudb/detail/xxhash.hpp
|
||||
|
||||
# Snappy compression library intentional overflows
|
||||
unsigned-integer-overflow:snappy.cc
|
||||
|
||||
# Abseil intentional overflows
|
||||
unsigned-integer-overflow:absl/strings/numbers.cc
|
||||
unsigned-integer-overflow:absl/strings/internal/cord_rep_flat.h
|
||||
unsigned-integer-overflow:absl/base/internal/low_level_alloc.cc
|
||||
unsigned-integer-overflow:absl/hash/internal/hash.h
|
||||
unsigned-integer-overflow:absl/container/internal/raw_hash_set.h
|
||||
|
||||
# Standard library intentional overflows in chrono duration arithmetic
|
||||
unsigned-integer-overflow:__chrono/duration.h
|
||||
|
||||
# Suppress undefined errors in RocksDB and nudb
|
||||
undefined:rocks.*/*/util/crc32c_arm64.cc
|
||||
undefined:rocks.*/*/util/xxhash.h
|
||||
undefined:nudb
|
||||
@@ -451,9 +451,8 @@ getTrustLineBalance(
|
||||
amount.clear(Issue{currency, issuer});
|
||||
}
|
||||
|
||||
JLOG(j.trace()) << "getTrustLineBalance:"
|
||||
<< " account=" << to_string(account)
|
||||
<< " amount=" << amount.getFullText();
|
||||
JLOG(j.trace()) << "getTrustLineBalance:" << " account="
|
||||
<< to_string(account) << " amount=" << amount.getFullText();
|
||||
|
||||
return view.balanceHook(account, issuer, amount);
|
||||
}
|
||||
@@ -700,8 +699,7 @@ xrpLiquid(
|
||||
STAmount const amount =
|
||||
(balance < reserve) ? STAmount{0} : balance - reserve;
|
||||
|
||||
JLOG(j.trace()) << "accountHolds:"
|
||||
<< " account=" << to_string(id)
|
||||
JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
|
||||
<< " amount=" << amount.getFullText()
|
||||
<< " fullBalance=" << fullBalance.getFullText()
|
||||
<< " balance=" << balance.getFullText()
|
||||
@@ -1107,7 +1105,7 @@ adjustOwnerCount(
|
||||
std::function<void(SLE::ref)>
|
||||
describeOwnerDir(AccountID const& account)
|
||||
{
|
||||
return [&account](std::shared_ptr<SLE> const& sle) {
|
||||
return [account](std::shared_ptr<SLE> const& sle) {
|
||||
(*sle)[sfOwner] = account;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <xrpl/beast/core/SemanticVersion.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -20,7 +22,7 @@ namespace BuildInfo {
|
||||
char const* const versionString = "3.2.0-b0"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZER)
|
||||
#if defined(DEBUG) || defined(SANITIZERS)
|
||||
"+"
|
||||
#ifdef GIT_COMMIT_HASH
|
||||
GIT_COMMIT_HASH
|
||||
@@ -28,13 +30,13 @@ char const* const versionString = "3.2.0-b0"
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
"DEBUG"
|
||||
#ifdef SANITIZER
|
||||
#ifdef SANITIZERS
|
||||
"."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SANITIZER
|
||||
BOOST_PP_STRINGIZE(SANITIZER) // cspell: disable-line
|
||||
#ifdef SANITIZERS
|
||||
BOOST_PP_STRINGIZE(SANITIZERS) // cspell: disable-line
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -427,6 +427,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||
auto tx1 = batch::inner(pay(alice, bob, XRP(1)), seq + 1);
|
||||
tx1[jss::Fee] = "1.5";
|
||||
env.set_parse_failure_expected(true);
|
||||
try
|
||||
{
|
||||
env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||
@@ -438,6 +439,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
{
|
||||
BEAST_EXPECT(true);
|
||||
}
|
||||
env.set_parse_failure_expected(false);
|
||||
}
|
||||
|
||||
// temSEQ_AND_TICKET: Batch: inner txn cannot have both Sequence
|
||||
|
||||
@@ -2076,7 +2076,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
PrettyAsset const& asset,
|
||||
Vault& vault,
|
||||
MPTTester& mptt) {
|
||||
testcase("MPT failed reserve to re-create MPToken");
|
||||
testcase("MPT fail reserve to re-create MPToken");
|
||||
|
||||
auto [tx, keylet] =
|
||||
vault.create({.owner = owner, .asset = asset});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Unit tests
|
||||
|
||||
This directory contains unit tests for the project. The difference from existing `src/test` folder
|
||||
is that we switch to 3rd party testing framework (doctest). We intend to gradually move existing tests
|
||||
from our own framework to doctest and such tests will be moved to this new folder.
|
||||
is that we switch to 3rd party testing framework (`gtest`). We intend to gradually move existing tests
|
||||
from our own framework to `gtest` and such tests will be moved to this new folder.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
include(XrplAddTest)
|
||||
|
||||
# Test requirements.
|
||||
find_package(doctest REQUIRED)
|
||||
find_package(GTest REQUIRED)
|
||||
|
||||
# Custom target for all tests defined in this file
|
||||
add_custom_target(xrpl.tests)
|
||||
|
||||
# Common library dependencies for the rest of the tests.
|
||||
add_library(xrpl.imports.test INTERFACE)
|
||||
target_link_libraries(xrpl.imports.test INTERFACE doctest::doctest xrpl.libxrpl)
|
||||
target_link_libraries(xrpl.imports.test INTERFACE gtest::gtest xrpl.libxrpl)
|
||||
|
||||
# One test for each module.
|
||||
xrpl_add_test(basics)
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#include <xrpl/basics/RangeSet.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("RangeSet");
|
||||
|
||||
TEST_CASE("prevMissing")
|
||||
TEST(RangeSet, prevMissing)
|
||||
{
|
||||
// Set will include:
|
||||
// [ 0, 5]
|
||||
@@ -31,80 +29,78 @@ TEST_CASE("prevMissing")
|
||||
|
||||
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
||||
}
|
||||
CHECK(prevMissing(set, i) == expected);
|
||||
EXPECT_EQ(prevMissing(set, i), expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("toString")
|
||||
TEST(RangeSet, toString)
|
||||
{
|
||||
RangeSet<std::uint32_t> set;
|
||||
CHECK(to_string(set) == "empty");
|
||||
EXPECT_EQ(to_string(set), "empty");
|
||||
|
||||
set.insert(1);
|
||||
CHECK(to_string(set) == "1");
|
||||
EXPECT_EQ(to_string(set), "1");
|
||||
|
||||
set.insert(range(4u, 6u));
|
||||
CHECK(to_string(set) == "1,4-6");
|
||||
EXPECT_EQ(to_string(set), "1,4-6");
|
||||
|
||||
set.insert(2);
|
||||
CHECK(to_string(set) == "1-2,4-6");
|
||||
EXPECT_EQ(to_string(set), "1-2,4-6");
|
||||
|
||||
set.erase(range(4u, 5u));
|
||||
CHECK(to_string(set) == "1-2,6");
|
||||
EXPECT_EQ(to_string(set), "1-2,6");
|
||||
}
|
||||
|
||||
TEST_CASE("fromString")
|
||||
TEST(RangeSet, fromString)
|
||||
{
|
||||
RangeSet<std::uint32_t> set;
|
||||
|
||||
CHECK(!from_string(set, ""));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
EXPECT_FALSE(from_string(set, ""));
|
||||
EXPECT_EQ(boost::icl::length(set), 0);
|
||||
|
||||
CHECK(!from_string(set, "#"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
EXPECT_FALSE(from_string(set, "#"));
|
||||
EXPECT_EQ(boost::icl::length(set), 0);
|
||||
|
||||
CHECK(!from_string(set, ","));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
EXPECT_FALSE(from_string(set, ","));
|
||||
EXPECT_EQ(boost::icl::length(set), 0);
|
||||
|
||||
CHECK(!from_string(set, ",-"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
EXPECT_FALSE(from_string(set, ",-"));
|
||||
EXPECT_EQ(boost::icl::length(set), 0);
|
||||
|
||||
CHECK(!from_string(set, "1,,2"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
EXPECT_FALSE(from_string(set, "1,,2"));
|
||||
EXPECT_EQ(boost::icl::length(set), 0);
|
||||
|
||||
CHECK(from_string(set, "1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
EXPECT_TRUE(from_string(set, "1"));
|
||||
EXPECT_EQ(boost::icl::length(set), 1);
|
||||
EXPECT_EQ(boost::icl::first(set), 1);
|
||||
|
||||
CHECK(from_string(set, "1,1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
EXPECT_TRUE(from_string(set, "1,1"));
|
||||
EXPECT_EQ(boost::icl::length(set), 1);
|
||||
EXPECT_EQ(boost::icl::first(set), 1);
|
||||
|
||||
CHECK(from_string(set, "1-1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
EXPECT_TRUE(from_string(set, "1-1"));
|
||||
EXPECT_EQ(boost::icl::length(set), 1);
|
||||
EXPECT_EQ(boost::icl::first(set), 1);
|
||||
|
||||
CHECK(from_string(set, "1,4-6"));
|
||||
CHECK(boost::icl::length(set) == 4);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(!boost::icl::contains(set, 2));
|
||||
CHECK(!boost::icl::contains(set, 3));
|
||||
CHECK(boost::icl::contains(set, 4));
|
||||
CHECK(boost::icl::contains(set, 5));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
EXPECT_TRUE(from_string(set, "1,4-6"));
|
||||
EXPECT_EQ(boost::icl::length(set), 4);
|
||||
EXPECT_EQ(boost::icl::first(set), 1);
|
||||
EXPECT_FALSE(boost::icl::contains(set, 2));
|
||||
EXPECT_FALSE(boost::icl::contains(set, 3));
|
||||
EXPECT_TRUE(boost::icl::contains(set, 4));
|
||||
EXPECT_TRUE(boost::icl::contains(set, 5));
|
||||
EXPECT_EQ(boost::icl::last(set), 6);
|
||||
|
||||
CHECK(from_string(set, "1-2,4-6"));
|
||||
CHECK(boost::icl::length(set) == 5);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(boost::icl::contains(set, 2));
|
||||
CHECK(boost::icl::contains(set, 4));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
EXPECT_TRUE(from_string(set, "1-2,4-6"));
|
||||
EXPECT_EQ(boost::icl::length(set), 5);
|
||||
EXPECT_EQ(boost::icl::first(set), 1);
|
||||
EXPECT_TRUE(boost::icl::contains(set, 2));
|
||||
EXPECT_TRUE(boost::icl::contains(set, 4));
|
||||
EXPECT_EQ(boost::icl::last(set), 6);
|
||||
|
||||
CHECK(from_string(set, "1-2,6"));
|
||||
CHECK(boost::icl::length(set) == 3);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(boost::icl::contains(set, 2));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
EXPECT_TRUE(from_string(set, "1-2,6"));
|
||||
EXPECT_EQ(boost::icl::length(set), 3);
|
||||
EXPECT_EQ(boost::icl::first(set), 1);
|
||||
EXPECT_TRUE(boost::icl::contains(set, 2));
|
||||
EXPECT_EQ(boost::icl::last(set), 6);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <xrpl/basics/Slice.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -12,37 +12,35 @@ static std::uint8_t const data[] = {
|
||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||
|
||||
TEST_SUITE_BEGIN("Slice");
|
||||
|
||||
TEST_CASE("equality & inequality")
|
||||
TEST(Slice, equality_and_inequality)
|
||||
{
|
||||
Slice const s0{};
|
||||
|
||||
CHECK(s0.size() == 0);
|
||||
CHECK(s0.data() == nullptr);
|
||||
CHECK(s0 == s0);
|
||||
EXPECT_EQ(s0.size(), 0);
|
||||
EXPECT_EQ(s0.data(), nullptr);
|
||||
EXPECT_EQ(s0, s0);
|
||||
|
||||
// Test slices of equal and unequal size pointing to same data:
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
{
|
||||
Slice const s1{data, i};
|
||||
|
||||
CHECK(s1.size() == i);
|
||||
CHECK(s1.data() != nullptr);
|
||||
EXPECT_EQ(s1.size(), i);
|
||||
EXPECT_NE(s1.data(), nullptr);
|
||||
|
||||
if (i == 0)
|
||||
CHECK(s1 == s0);
|
||||
EXPECT_EQ(s1, s0);
|
||||
else
|
||||
CHECK(s1 != s0);
|
||||
EXPECT_NE(s1, s0);
|
||||
|
||||
for (std::size_t j = 0; j != sizeof(data); ++j)
|
||||
{
|
||||
Slice const s2{data, j};
|
||||
|
||||
if (i == j)
|
||||
CHECK(s1 == s2);
|
||||
EXPECT_EQ(s1, s2);
|
||||
else
|
||||
CHECK(s1 != s2);
|
||||
EXPECT_NE(s1, s2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,22 +51,22 @@ TEST_CASE("equality & inequality")
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
a[i] = b[i] = data[i];
|
||||
|
||||
CHECK(makeSlice(a) == makeSlice(b));
|
||||
EXPECT_EQ(makeSlice(a), makeSlice(b));
|
||||
b[7]++;
|
||||
CHECK(makeSlice(a) != makeSlice(b));
|
||||
EXPECT_NE(makeSlice(a), makeSlice(b));
|
||||
a[7]++;
|
||||
CHECK(makeSlice(a) == makeSlice(b));
|
||||
EXPECT_EQ(makeSlice(a), makeSlice(b));
|
||||
}
|
||||
|
||||
TEST_CASE("indexing")
|
||||
TEST(Slice, indexing)
|
||||
{
|
||||
Slice const s{data, sizeof(data)};
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
CHECK(s[i] == data[i]);
|
||||
EXPECT_EQ(s[i], data[i]);
|
||||
}
|
||||
|
||||
TEST_CASE("advancing")
|
||||
TEST(Slice, advancing)
|
||||
{
|
||||
for (std::size_t i = 0; i < sizeof(data); ++i)
|
||||
{
|
||||
@@ -77,10 +75,8 @@ TEST_CASE("advancing")
|
||||
Slice s(data + i, sizeof(data) - i);
|
||||
s += j;
|
||||
|
||||
CHECK(s.data() == data + i + j);
|
||||
CHECK(s.size() == sizeof(data) - i - j);
|
||||
EXPECT_EQ(s.data(), data + i + j);
|
||||
EXPECT_EQ(s.size(), sizeof(data) - i - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <xrpl/basics/base64.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -10,11 +10,11 @@ static void
|
||||
check(std::string const& in, std::string const& out)
|
||||
{
|
||||
auto const encoded = base64_encode(in);
|
||||
CHECK(encoded == out);
|
||||
CHECK(base64_decode(encoded) == in);
|
||||
EXPECT_EQ(encoded, out);
|
||||
EXPECT_EQ(base64_decode(encoded), in);
|
||||
}
|
||||
|
||||
TEST_CASE("base64")
|
||||
TEST(base64, base64)
|
||||
{
|
||||
// cspell: disable
|
||||
check("", "");
|
||||
@@ -46,5 +46,5 @@ TEST_CASE("base64")
|
||||
|
||||
std::string const notBase64 = "not_base64!!";
|
||||
std::string const truncated = "not";
|
||||
CHECK(base64_decode(notBase64) == base64_decode(truncated));
|
||||
EXPECT_EQ(base64_decode(notBase64), base64_decode(truncated));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_CASE("contract")
|
||||
TEST(contract, contract)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -15,7 +15,7 @@ TEST_CASE("contract")
|
||||
}
|
||||
catch (std::runtime_error const& e1)
|
||||
{
|
||||
CHECK(std::string(e1.what()) == "Throw test");
|
||||
EXPECT_STREQ(e1.what(), "Throw test");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -23,15 +23,15 @@ TEST_CASE("contract")
|
||||
}
|
||||
catch (std::runtime_error const& e2)
|
||||
{
|
||||
CHECK(std::string(e2.what()) == "Throw test");
|
||||
EXPECT_STREQ(e2.what(), "Throw test");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CHECK(false);
|
||||
FAIL() << "std::runtime_error should have been re-caught";
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CHECK(false);
|
||||
FAIL() << "std::runtime_error should have been caught the first time";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
#include <xrpl/basics/mulDiv.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_CASE("mulDiv")
|
||||
TEST(mulDiv, mulDiv)
|
||||
{
|
||||
auto const max = std::numeric_limits<std::uint64_t>::max();
|
||||
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
auto result = mulDiv(85, 20, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 340);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 340);
|
||||
result = mulDiv(20, 85, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 340);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 340);
|
||||
|
||||
result = mulDiv(0, max - 1, max - 3);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 0);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 0);
|
||||
result = mulDiv(max - 1, 0, max - 3);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 0);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 0);
|
||||
|
||||
result = mulDiv(max, 2, max / 2);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 4);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 4);
|
||||
result = mulDiv(max, 1000, max / 1000);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 1000000);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 1000000);
|
||||
result = mulDiv(max, 1000, max / 1001);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 1001000);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 1001000);
|
||||
result = mulDiv(max32 + 1, max32 + 1, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 3689348814741910323);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(*result, 3689348814741910323);
|
||||
|
||||
// Overflow
|
||||
result = mulDiv(max - 1, max - 2, 5);
|
||||
CHECK(!result);
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <xrpl/basics/scope.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_CASE("scope_exit")
|
||||
TEST(scope, scope_exit)
|
||||
{
|
||||
// scope_exit always executes the functor on destruction,
|
||||
// unless release() is called
|
||||
@@ -12,23 +12,23 @@ TEST_CASE("scope_exit")
|
||||
{
|
||||
scope_exit x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 1);
|
||||
EXPECT_EQ(i, 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 1);
|
||||
EXPECT_EQ(i, 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
EXPECT_EQ(i, 3);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
EXPECT_EQ(i, 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -39,7 +39,7 @@ TEST_CASE("scope_exit")
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
EXPECT_EQ(i, 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -51,10 +51,10 @@ TEST_CASE("scope_exit")
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
EXPECT_EQ(i, 5);
|
||||
}
|
||||
|
||||
TEST_CASE("scope_fail")
|
||||
TEST(scope, scope_fail)
|
||||
{
|
||||
// scope_fail executes the functor on destruction only
|
||||
// if an exception is unwinding, unless release() is called
|
||||
@@ -62,23 +62,23 @@ TEST_CASE("scope_fail")
|
||||
{
|
||||
scope_fail x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 0);
|
||||
EXPECT_EQ(i, 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 0);
|
||||
EXPECT_EQ(i, 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 3; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 0);
|
||||
EXPECT_EQ(i, 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 0);
|
||||
EXPECT_EQ(i, 0);
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -89,7 +89,7 @@ TEST_CASE("scope_fail")
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
EXPECT_EQ(i, 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -101,10 +101,10 @@ TEST_CASE("scope_fail")
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
EXPECT_EQ(i, 5);
|
||||
}
|
||||
|
||||
TEST_CASE("scope_success")
|
||||
TEST(scope, scope_success)
|
||||
{
|
||||
// scope_success executes the functor on destruction only
|
||||
// if an exception is not unwinding, unless release() is called
|
||||
@@ -112,23 +112,23 @@ TEST_CASE("scope_success")
|
||||
{
|
||||
scope_success x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 1);
|
||||
EXPECT_EQ(i, 1);
|
||||
{
|
||||
scope_success x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 1);
|
||||
EXPECT_EQ(i, 1);
|
||||
{
|
||||
scope_success x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
EXPECT_EQ(i, 3);
|
||||
{
|
||||
scope_success x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
EXPECT_EQ(i, 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -139,7 +139,7 @@ TEST_CASE("scope_success")
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 3);
|
||||
EXPECT_EQ(i, 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -151,5 +151,5 @@ TEST_CASE("scope_success")
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 3);
|
||||
EXPECT_EQ(i, 3);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <xrpl/basics/tagged_integer.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@@ -102,127 +102,123 @@ static_assert(
|
||||
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
||||
"TagUInt2 should not be convertible to a TagUInt3");
|
||||
|
||||
TEST_SUITE_BEGIN("tagged_integer");
|
||||
|
||||
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
||||
|
||||
TEST_CASE("comparison operators")
|
||||
TEST(tagged_integer, comparison_operators)
|
||||
{
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
|
||||
CHECK(one == one);
|
||||
CHECK(!(one == zero));
|
||||
EXPECT_TRUE(one == one);
|
||||
EXPECT_FALSE(one == zero);
|
||||
|
||||
CHECK(one != zero);
|
||||
CHECK(!(one != one));
|
||||
EXPECT_TRUE(one != zero);
|
||||
EXPECT_FALSE(one != one);
|
||||
|
||||
CHECK(zero < one);
|
||||
CHECK(!(one < zero));
|
||||
EXPECT_TRUE(zero < one);
|
||||
EXPECT_FALSE(one < zero);
|
||||
|
||||
CHECK(one > zero);
|
||||
CHECK(!(zero > one));
|
||||
EXPECT_TRUE(one > zero);
|
||||
EXPECT_FALSE(zero > one);
|
||||
|
||||
CHECK(one >= one);
|
||||
CHECK(one >= zero);
|
||||
CHECK(!(zero >= one));
|
||||
EXPECT_TRUE(one >= one);
|
||||
EXPECT_TRUE(one >= zero);
|
||||
EXPECT_FALSE(zero >= one);
|
||||
|
||||
CHECK(zero <= one);
|
||||
CHECK(zero <= zero);
|
||||
CHECK(!(one <= zero));
|
||||
EXPECT_TRUE(zero <= one);
|
||||
EXPECT_TRUE(zero <= zero);
|
||||
EXPECT_FALSE(one <= zero);
|
||||
}
|
||||
|
||||
TEST_CASE("increment / decrement operators")
|
||||
TEST(tagged_integer, increment_decrement_operators)
|
||||
{
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
TagInt a{0};
|
||||
++a;
|
||||
CHECK(a == one);
|
||||
EXPECT_EQ(a, one);
|
||||
--a;
|
||||
CHECK(a == zero);
|
||||
EXPECT_EQ(a, zero);
|
||||
a++;
|
||||
CHECK(a == one);
|
||||
EXPECT_EQ(a, one);
|
||||
a--;
|
||||
CHECK(a == zero);
|
||||
EXPECT_EQ(a, zero);
|
||||
}
|
||||
|
||||
TEST_CASE("arithmetic operators")
|
||||
TEST(tagged_integer, arithmetic_operators)
|
||||
{
|
||||
TagInt a{-2};
|
||||
CHECK(+a == TagInt{-2});
|
||||
CHECK(-a == TagInt{2});
|
||||
CHECK(TagInt{-3} + TagInt{4} == TagInt{1});
|
||||
CHECK(TagInt{-3} - TagInt{4} == TagInt{-7});
|
||||
CHECK(TagInt{-3} * TagInt{4} == TagInt{-12});
|
||||
CHECK(TagInt{8} / TagInt{4} == TagInt{2});
|
||||
CHECK(TagInt{7} % TagInt{4} == TagInt{3});
|
||||
EXPECT_EQ(+a, TagInt{-2});
|
||||
EXPECT_EQ(-a, TagInt{2});
|
||||
EXPECT_EQ(TagInt{-3} + TagInt{4}, TagInt{1});
|
||||
EXPECT_EQ(TagInt{-3} - TagInt{4}, TagInt{-7});
|
||||
EXPECT_EQ(TagInt{-3} * TagInt{4}, TagInt{-12});
|
||||
EXPECT_EQ(TagInt{8} / TagInt{4}, TagInt{2});
|
||||
EXPECT_EQ(TagInt{7} % TagInt{4}, TagInt{3});
|
||||
|
||||
CHECK(~TagInt{8} == TagInt{~TagInt::value_type{8}});
|
||||
CHECK((TagInt{6} & TagInt{3}) == TagInt{2});
|
||||
CHECK((TagInt{6} | TagInt{3}) == TagInt{7});
|
||||
CHECK((TagInt{6} ^ TagInt{3}) == TagInt{5});
|
||||
EXPECT_EQ(~TagInt{8}, TagInt{~TagInt::value_type{8}});
|
||||
EXPECT_EQ((TagInt{6} & TagInt{3}), TagInt{2});
|
||||
EXPECT_EQ((TagInt{6} | TagInt{3}), TagInt{7});
|
||||
EXPECT_EQ((TagInt{6} ^ TagInt{3}), TagInt{5});
|
||||
|
||||
CHECK((TagInt{4} << TagInt{2}) == TagInt{16});
|
||||
CHECK((TagInt{16} >> TagInt{2}) == TagInt{4});
|
||||
EXPECT_EQ((TagInt{4} << TagInt{2}), TagInt{16});
|
||||
EXPECT_EQ((TagInt{16} >> TagInt{2}), TagInt{4});
|
||||
}
|
||||
|
||||
TEST_CASE("assignment operators")
|
||||
TEST(tagged_integer, assignment_operators)
|
||||
{
|
||||
TagInt a{-2};
|
||||
TagInt b{0};
|
||||
b = a;
|
||||
CHECK(b == TagInt{-2});
|
||||
EXPECT_EQ(b, TagInt{-2});
|
||||
|
||||
// -3 + 4 == 1
|
||||
a = TagInt{-3};
|
||||
a += TagInt{4};
|
||||
CHECK(a == TagInt{1});
|
||||
EXPECT_EQ(a, TagInt{1});
|
||||
|
||||
// -3 - 4 == -7
|
||||
a = TagInt{-3};
|
||||
a -= TagInt{4};
|
||||
CHECK(a == TagInt{-7});
|
||||
EXPECT_EQ(a, TagInt{-7});
|
||||
|
||||
// -3 * 4 == -12
|
||||
a = TagInt{-3};
|
||||
a *= TagInt{4};
|
||||
CHECK(a == TagInt{-12});
|
||||
EXPECT_EQ(a, TagInt{-12});
|
||||
|
||||
// 8/4 == 2
|
||||
a = TagInt{8};
|
||||
a /= TagInt{4};
|
||||
CHECK(a == TagInt{2});
|
||||
EXPECT_EQ(a, TagInt{2});
|
||||
|
||||
// 7 % 4 == 3
|
||||
a = TagInt{7};
|
||||
a %= TagInt{4};
|
||||
CHECK(a == TagInt{3});
|
||||
EXPECT_EQ(a, TagInt{3});
|
||||
|
||||
// 6 & 3 == 2
|
||||
a = TagInt{6};
|
||||
a /= TagInt{3};
|
||||
CHECK(a == TagInt{2});
|
||||
EXPECT_EQ(a, TagInt{2});
|
||||
|
||||
// 6 | 3 == 7
|
||||
a = TagInt{6};
|
||||
a |= TagInt{3};
|
||||
CHECK(a == TagInt{7});
|
||||
EXPECT_EQ(a, TagInt{7});
|
||||
|
||||
// 6 ^ 3 == 5
|
||||
a = TagInt{6};
|
||||
a ^= TagInt{3};
|
||||
CHECK(a == TagInt{5});
|
||||
EXPECT_EQ(a, TagInt{5});
|
||||
|
||||
// 4 << 2 == 16
|
||||
a = TagInt{4};
|
||||
a <<= TagInt{2};
|
||||
CHECK(a == TagInt{16});
|
||||
EXPECT_EQ(a, TagInt{16});
|
||||
|
||||
// 16 >> 2 == 4
|
||||
a = TagInt{16};
|
||||
a >>= TagInt{2};
|
||||
CHECK(a == TagInt{4});
|
||||
EXPECT_EQ(a, TagInt{4});
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#include <xrpl/crypto/csprng.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_CASE("get values")
|
||||
TEST(csprng, get_values)
|
||||
{
|
||||
auto& engine = crypto_prng();
|
||||
auto rand_val = engine();
|
||||
CHECK(rand_val >= engine.min());
|
||||
CHECK(rand_val <= engine.max());
|
||||
EXPECT_GE(rand_val, engine.min());
|
||||
EXPECT_LE(rand_val, engine.max());
|
||||
uint16_t twoByte{0};
|
||||
engine(&twoByte, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -2,31 +2,29 @@
|
||||
#include <xrpl/json/json_reader.h>
|
||||
#include <xrpl/json/json_writer.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace xrpl;
|
||||
using namespace Json;
|
||||
|
||||
TEST_SUITE_BEGIN("JsonOutput");
|
||||
|
||||
static void
|
||||
checkOutput(std::string const& valueDesc)
|
||||
{
|
||||
std::string output;
|
||||
Json::Value value;
|
||||
REQUIRE(Json::Reader().parse(valueDesc, value));
|
||||
ASSERT_TRUE(Json::Reader().parse(valueDesc, value));
|
||||
auto out = stringOutput(output);
|
||||
outputJson(value, out);
|
||||
|
||||
auto expected = Json::FastWriter().write(value);
|
||||
CHECK(output == expected);
|
||||
CHECK(output == valueDesc);
|
||||
CHECK(output == jsonAsString(value));
|
||||
EXPECT_EQ(output, expected);
|
||||
EXPECT_EQ(output, valueDesc);
|
||||
EXPECT_EQ(output, jsonAsString(value));
|
||||
}
|
||||
|
||||
TEST_CASE("output cases")
|
||||
TEST(JsonOutput, output_cases)
|
||||
{
|
||||
checkOutput("{}");
|
||||
checkOutput("[]");
|
||||
@@ -36,5 +34,3 @@ TEST_CASE("output cases")
|
||||
checkOutput("[[]]");
|
||||
checkOutput(R"({"array":[{"12":23},{},null,false,0.5]})");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#include <xrpl/json/Writer.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <google/protobuf/stubs/port.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -9,14 +9,14 @@
|
||||
using namespace xrpl;
|
||||
using namespace Json;
|
||||
|
||||
TEST_SUITE_BEGIN("JsonWriter");
|
||||
|
||||
struct WriterFixture
|
||||
class WriterFixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
std::string output;
|
||||
std::unique_ptr<Writer> writer;
|
||||
|
||||
WriterFixture()
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
writer = std::make_unique<Writer>(stringOutput(output));
|
||||
}
|
||||
@@ -31,7 +31,7 @@ struct WriterFixture
|
||||
void
|
||||
expectOutput(std::string const& expected) const
|
||||
{
|
||||
CHECK(output == expected);
|
||||
EXPECT_EQ(output, expected);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -42,20 +42,20 @@ struct WriterFixture
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "trivial")
|
||||
TEST_F(WriterFixture, trivial)
|
||||
{
|
||||
CHECK(output.empty());
|
||||
EXPECT_TRUE(output.empty());
|
||||
checkOutputAndReset("");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "near trivial")
|
||||
TEST_F(WriterFixture, near_trivial)
|
||||
{
|
||||
CHECK(output.empty());
|
||||
EXPECT_TRUE(output.empty());
|
||||
writer->output(0);
|
||||
checkOutputAndReset("0");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "primitives")
|
||||
TEST_F(WriterFixture, primitives)
|
||||
{
|
||||
writer->output(true);
|
||||
checkOutputAndReset("true");
|
||||
@@ -79,7 +79,7 @@ TEST_CASE_FIXTURE(WriterFixture, "primitives")
|
||||
checkOutputAndReset("null");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "empty")
|
||||
TEST_F(WriterFixture, empty)
|
||||
{
|
||||
writer->startRoot(Writer::array);
|
||||
writer->finish();
|
||||
@@ -90,7 +90,7 @@ TEST_CASE_FIXTURE(WriterFixture, "empty")
|
||||
checkOutputAndReset("{}");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "escaping")
|
||||
TEST_F(WriterFixture, escaping)
|
||||
{
|
||||
writer->output("\\");
|
||||
checkOutputAndReset(R"("\\")");
|
||||
@@ -108,7 +108,7 @@ TEST_CASE_FIXTURE(WriterFixture, "escaping")
|
||||
checkOutputAndReset(R"("\b\f\n\r\t")");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "array")
|
||||
TEST_F(WriterFixture, array)
|
||||
{
|
||||
writer->startRoot(Writer::array);
|
||||
writer->append(12);
|
||||
@@ -116,7 +116,7 @@ TEST_CASE_FIXTURE(WriterFixture, "array")
|
||||
checkOutputAndReset("[12]");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "long array")
|
||||
TEST_F(WriterFixture, long_array)
|
||||
{
|
||||
writer->startRoot(Writer::array);
|
||||
writer->append(12);
|
||||
@@ -126,7 +126,7 @@ TEST_CASE_FIXTURE(WriterFixture, "long array")
|
||||
checkOutputAndReset(R"([12,true,"hello"])");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "embedded array simple")
|
||||
TEST_F(WriterFixture, embedded_array_simple)
|
||||
{
|
||||
writer->startRoot(Writer::array);
|
||||
writer->startAppend(Writer::array);
|
||||
@@ -135,7 +135,7 @@ TEST_CASE_FIXTURE(WriterFixture, "embedded array simple")
|
||||
checkOutputAndReset("[[]]");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "object")
|
||||
TEST_F(WriterFixture, object)
|
||||
{
|
||||
writer->startRoot(Writer::object);
|
||||
writer->set("hello", "world");
|
||||
@@ -143,7 +143,7 @@ TEST_CASE_FIXTURE(WriterFixture, "object")
|
||||
checkOutputAndReset(R"({"hello":"world"})");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "complex object")
|
||||
TEST_F(WriterFixture, complex_object)
|
||||
{
|
||||
writer->startRoot(Writer::object);
|
||||
writer->set("hello", "world");
|
||||
@@ -160,7 +160,7 @@ TEST_CASE_FIXTURE(WriterFixture, "complex object")
|
||||
R"({"hello":"world","array":[true,12,[{"goodbye":"cruel world.","subarray":[23.5]}]]})");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(WriterFixture, "json value")
|
||||
TEST_F(WriterFixture, json_value)
|
||||
{
|
||||
Json::Value value(Json::objectValue);
|
||||
value["foo"] = 23;
|
||||
@@ -169,5 +169,3 @@ TEST_CASE_FIXTURE(WriterFixture, "json value")
|
||||
writer->finish();
|
||||
checkOutputAndReset(R"({"hello":{"foo":23}})");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
@@ -217,7 +217,7 @@ runHTTPTest(
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_CASE("HTTPClient case insensitive Content-Length")
|
||||
TEST(HTTPClient, case_insensitive_content_length)
|
||||
{
|
||||
// Test different cases of Content-Length header
|
||||
std::vector<std::string> header_cases = {
|
||||
@@ -249,14 +249,14 @@ TEST_CASE("HTTPClient case insensitive Content-Length")
|
||||
result_error);
|
||||
|
||||
// Verify results
|
||||
CHECK(test_completed);
|
||||
CHECK(!result_error);
|
||||
CHECK(result_status == 200);
|
||||
CHECK(result_data == test_body);
|
||||
EXPECT_TRUE(test_completed);
|
||||
EXPECT_FALSE(result_error);
|
||||
EXPECT_EQ(result_status, 200);
|
||||
EXPECT_EQ(result_data, test_body);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("HTTPClient basic HTTP request")
|
||||
TEST(HTTPClient, basic_http_request)
|
||||
{
|
||||
TestHTTPServer server;
|
||||
std::string test_body = "Test response body";
|
||||
@@ -271,13 +271,13 @@ TEST_CASE("HTTPClient basic HTTP request")
|
||||
bool test_completed = runHTTPTest(
|
||||
server, "/basic", completed, result_status, result_data, result_error);
|
||||
|
||||
CHECK(test_completed);
|
||||
CHECK(!result_error);
|
||||
CHECK(result_status == 200);
|
||||
CHECK(result_data == test_body);
|
||||
EXPECT_TRUE(test_completed);
|
||||
EXPECT_FALSE(result_error);
|
||||
EXPECT_EQ(result_status, 200);
|
||||
EXPECT_EQ(result_data, test_body);
|
||||
}
|
||||
|
||||
TEST_CASE("HTTPClient empty response")
|
||||
TEST(HTTPClient, empty_response)
|
||||
{
|
||||
TestHTTPServer server;
|
||||
server.setResponseBody(""); // Empty body
|
||||
@@ -291,13 +291,13 @@ TEST_CASE("HTTPClient empty response")
|
||||
bool test_completed = runHTTPTest(
|
||||
server, "/empty", completed, result_status, result_data, result_error);
|
||||
|
||||
CHECK(test_completed);
|
||||
CHECK(!result_error);
|
||||
CHECK(result_status == 200);
|
||||
CHECK(result_data.empty());
|
||||
EXPECT_TRUE(test_completed);
|
||||
EXPECT_FALSE(result_error);
|
||||
EXPECT_EQ(result_status, 200);
|
||||
EXPECT_TRUE(result_data.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("HTTPClient different status codes")
|
||||
TEST(HTTPClient, different_status_codes)
|
||||
{
|
||||
std::vector<unsigned int> status_codes = {200, 404, 500};
|
||||
|
||||
@@ -320,8 +320,8 @@ TEST_CASE("HTTPClient different status codes")
|
||||
result_data,
|
||||
result_error);
|
||||
|
||||
CHECK(test_completed);
|
||||
CHECK(!result_error);
|
||||
CHECK(result_status == static_cast<int>(status));
|
||||
EXPECT_TRUE(test_completed);
|
||||
EXPECT_FALSE(result_error);
|
||||
EXPECT_EQ(result_status, static_cast<int>(status));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user