mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-11 10:15:24 +00:00
Compare commits
3 Commits
vlntb/mall
...
pratik/Mig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61be075ff8 | ||
|
|
db73390eff | ||
|
|
adc6ca6d11 |
@@ -1,272 +0,0 @@
|
||||
ignorePaths:
|
||||
- build/**
|
||||
- src/libxrpl/crypto
|
||||
- src/test/** # Will be removed in the future
|
||||
- CMakeUserPresets.json
|
||||
- Doxyfile
|
||||
- docs/**/*.puml
|
||||
- cmake/**
|
||||
- LICENSE.md
|
||||
language: en
|
||||
allowCompoundWords: true
|
||||
ignoreRandomStrings: true
|
||||
minWordLength: 5
|
||||
dictionaries:
|
||||
- cpp
|
||||
- en_US
|
||||
- en_GB
|
||||
ignoreRegExpList:
|
||||
- /[rs][1-9A-HJ-NP-Za-km-z]{25,34}/g # addresses and seeds
|
||||
- /(XRPL|BEAST)_[A-Z_0-9]+_H_INCLUDED+/g # include guards
|
||||
- /(XRPL|BEAST)_[A-Z_0-9]+_H+/g # include guards
|
||||
- /::[a-z:_]+/g # things from other namespaces
|
||||
- /lib[a-z]+/g # libraries
|
||||
- /[0-9]{4}-[0-9]{2}-[0-9]{2}[,:][A-Za-zÀ-ÖØ-öø-ÿ.\s]+/g # copyright dates
|
||||
- /[0-9]{4}[,:]?\s*[A-Za-zÀ-ÖØ-öø-ÿ.\s]+/g # copyright years
|
||||
- /\[[A-Za-z0-9-]+\]\(https:\/\/github.com\/[A-Za-z0-9-]+\)/g # Github usernames
|
||||
- /-[DWw][a-zA-Z0-9_-]+=/g # compile flags
|
||||
- /[\['"`]-[DWw][a-zA-Z0-9_-]+['"`\]]/g # compile flags
|
||||
suggestWords:
|
||||
- xprl->xrpl
|
||||
- unsynched->unsynced
|
||||
- synched->synced
|
||||
- synch->sync
|
||||
words:
|
||||
- abempty
|
||||
- AMMID
|
||||
- amt
|
||||
- amts
|
||||
- asnode
|
||||
- asynchrony
|
||||
- attestation
|
||||
- authorises
|
||||
- autobridge
|
||||
- autobridged
|
||||
- autobridging
|
||||
- bimap
|
||||
- bindir
|
||||
- bookdir
|
||||
- Bougalis
|
||||
- Britto
|
||||
- Btrfs
|
||||
- canonicality
|
||||
- checkme
|
||||
- chrono
|
||||
- citardauq
|
||||
- clawback
|
||||
- clawbacks
|
||||
- coeffs
|
||||
- coldwallet
|
||||
- compr
|
||||
- conanfile
|
||||
- conanrun
|
||||
- connectability
|
||||
- coro
|
||||
- coros
|
||||
- cowid
|
||||
- cryptocondition
|
||||
- cryptoconditional
|
||||
- cryptoconditions
|
||||
- csprng
|
||||
- ctid
|
||||
- currenttxhash
|
||||
- daria
|
||||
- dcmake
|
||||
- dearmor
|
||||
- deleteme
|
||||
- demultiplexer
|
||||
- deserializaton
|
||||
- desync
|
||||
- desynced
|
||||
- determ
|
||||
- distro
|
||||
- doxyfile
|
||||
- dxrpl
|
||||
- endmacro
|
||||
- endpointv
|
||||
- exceptioned
|
||||
- Falco
|
||||
- finalizers
|
||||
- firewalled
|
||||
- fmtdur
|
||||
- funclets
|
||||
- gcov
|
||||
- gcovr
|
||||
- Gnutella
|
||||
- gpgcheck
|
||||
- gpgkey
|
||||
- hotwallet
|
||||
- ifndef
|
||||
- inequation
|
||||
- insuf
|
||||
- insuff
|
||||
- iou
|
||||
- ious
|
||||
- isrdc
|
||||
- jemalloc
|
||||
- jlog
|
||||
- keylet
|
||||
- keylets
|
||||
- keyvadb
|
||||
- ledgerentry
|
||||
- ledgerhash
|
||||
- ledgerindex
|
||||
- leftw
|
||||
- legleux
|
||||
- levelization
|
||||
- levelized
|
||||
- libpb
|
||||
- libxrpl
|
||||
- llection
|
||||
- LOCALGOOD
|
||||
- logwstream
|
||||
- lseq
|
||||
- lsmf
|
||||
- ltype
|
||||
- MEMORYSTATUSEX
|
||||
- Merkle
|
||||
- Metafuncton
|
||||
- misprediction
|
||||
- mptbalance
|
||||
- mptflags
|
||||
- mptid
|
||||
- mptissuance
|
||||
- mptissuanceid
|
||||
- mptoken
|
||||
- mptokenid
|
||||
- mptokenissuance
|
||||
- mptokens
|
||||
- mpts
|
||||
- multisig
|
||||
- multisign
|
||||
- multisigned
|
||||
- Nakamoto
|
||||
- nftid
|
||||
- nftoffer
|
||||
- nftoken
|
||||
- nftokenid
|
||||
- nftokenpages
|
||||
- nftokens
|
||||
- nftpage
|
||||
- nikb
|
||||
- nonxrp
|
||||
- noripple
|
||||
- nudb
|
||||
- nullptr
|
||||
- nunl
|
||||
- Nyffenegger
|
||||
- ostr
|
||||
- partitioner
|
||||
- paychan
|
||||
- paychans
|
||||
- permdex
|
||||
- perminute
|
||||
- permissioned
|
||||
- pointee
|
||||
- preauth
|
||||
- preauthorization
|
||||
- preauthorize
|
||||
- preauthorizes
|
||||
- preclaim
|
||||
- protobuf
|
||||
- protos
|
||||
- ptrs
|
||||
- pyenv
|
||||
- qalloc
|
||||
- queuable
|
||||
- Raphson
|
||||
- replayer
|
||||
- rerere
|
||||
- retriable
|
||||
- RIPD
|
||||
- ripdtop
|
||||
- rippleci
|
||||
- rippled
|
||||
- ripplerpc
|
||||
- rippletest
|
||||
- RLUSD
|
||||
- rngfill
|
||||
- rocksdb
|
||||
- Rohrs
|
||||
- roundings
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- secp
|
||||
- sendq
|
||||
- seqit
|
||||
- sf
|
||||
- shamap
|
||||
- shamapitem
|
||||
- sidechain
|
||||
- SIGGOOD
|
||||
- sle
|
||||
- sles
|
||||
- soci
|
||||
- socidb
|
||||
- sslws
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
- stnum
|
||||
- stobj
|
||||
- stobject
|
||||
- stpath
|
||||
- stpathset
|
||||
- sttx
|
||||
- stvar
|
||||
- stvector
|
||||
- stxchainattestations
|
||||
- superpeer
|
||||
- superpeers
|
||||
- takergets
|
||||
- takerpays
|
||||
- ters
|
||||
- tx
|
||||
- txid
|
||||
- txids
|
||||
- txjson
|
||||
- txn
|
||||
- txns
|
||||
- txs
|
||||
- umant
|
||||
- unacquired
|
||||
- unambiguity
|
||||
- unauthorizes
|
||||
- unauthorizing
|
||||
- unergonomic
|
||||
- unfetched
|
||||
- unflatten
|
||||
- unfund
|
||||
- unimpair
|
||||
- unroutable
|
||||
- unscalable
|
||||
- unserviced
|
||||
- unshareable
|
||||
- unshares
|
||||
- unsquelch
|
||||
- unsquelched
|
||||
- unsquelching
|
||||
- unvalidated
|
||||
- unveto
|
||||
- unvetoed
|
||||
- upvotes
|
||||
- USDB
|
||||
- variadics
|
||||
- venv
|
||||
- vfalco
|
||||
- vinnie
|
||||
- wextra
|
||||
- wptr
|
||||
- writeme
|
||||
- wsrch
|
||||
- wthread
|
||||
- xbridge
|
||||
- xchain
|
||||
- ximinez
|
||||
- XMACRO
|
||||
- xrpkuwait
|
||||
- xrpl
|
||||
- xrpld
|
||||
- xrplf
|
||||
- xxhash
|
||||
- xxhasher
|
||||
9
.github/actions/build-deps/action.yml
vendored
9
.github/actions/build-deps/action.yml
vendored
@@ -4,6 +4,9 @@ description: "Install Conan dependencies, optionally forcing a rebuild of all de
|
||||
# Note that actions do not support 'type' and all inputs are strings, see
|
||||
# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs.
|
||||
inputs:
|
||||
build_dir:
|
||||
description: "The directory where to build."
|
||||
required: true
|
||||
build_type:
|
||||
description: 'The build type to use ("Debug", "Release").'
|
||||
required: true
|
||||
@@ -25,13 +28,17 @@ runs:
|
||||
- name: Install Conan dependencies
|
||||
shell: bash
|
||||
env:
|
||||
BUILD_DIR: ${{ inputs.build_dir }}
|
||||
BUILD_NPROC: ${{ inputs.build_nproc }}
|
||||
BUILD_OPTION: ${{ inputs.force_build == 'true' && '*' || 'missing' }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
|
||||
run: |
|
||||
echo 'Installing dependencies.'
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
cd "${BUILD_DIR}"
|
||||
conan install \
|
||||
--output-folder . \
|
||||
--build="${BUILD_OPTION}" \
|
||||
--options:host='&:tests=True' \
|
||||
--options:host='&:xrpld=True' \
|
||||
@@ -39,4 +46,4 @@ runs:
|
||||
--conf:all tools.build:jobs=${BUILD_NPROC} \
|
||||
--conf:all tools.build:verbosity="${LOG_VERBOSITY}" \
|
||||
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \
|
||||
.
|
||||
..
|
||||
|
||||
24
.github/actions/print-env/action.yml
vendored
24
.github/actions/print-env/action.yml
vendored
@@ -11,6 +11,12 @@ runs:
|
||||
echo 'Checking environment variables.'
|
||||
set
|
||||
|
||||
echo 'Checking CMake version.'
|
||||
cmake --version
|
||||
|
||||
echo 'Checking Conan version.'
|
||||
conan --version
|
||||
|
||||
- name: Check configuration (Linux and macOS)
|
||||
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}
|
||||
shell: bash
|
||||
@@ -21,23 +27,17 @@ runs:
|
||||
echo 'Checking environment variables.'
|
||||
env | sort
|
||||
|
||||
echo 'Checking CMake version.'
|
||||
cmake --version
|
||||
|
||||
echo 'Checking compiler version.'
|
||||
${{ runner.os == 'Linux' && '${CC}' || 'clang' }} --version
|
||||
|
||||
echo 'Checking Conan version.'
|
||||
conan --version
|
||||
|
||||
echo 'Checking Ninja version.'
|
||||
ninja --version
|
||||
|
||||
echo 'Checking nproc version.'
|
||||
nproc --version
|
||||
|
||||
- name: Check configuration (all)
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'Checking Ccache version.'
|
||||
ccache --version
|
||||
|
||||
echo 'Checking CMake version.'
|
||||
cmake --version
|
||||
|
||||
echo 'Checking Conan version.'
|
||||
conan --version
|
||||
|
||||
2
.github/scripts/levelization/README.md
vendored
2
.github/scripts/levelization/README.md
vendored
@@ -81,7 +81,7 @@ It generates many files of [results](results):
|
||||
|
||||
- `rawincludes.txt`: The raw dump of the `#includes`
|
||||
- `paths.txt`: A second dump grouping the source module
|
||||
to the destination module, de-duped, and with frequency counts.
|
||||
to the destination module, deduped, and with frequency counts.
|
||||
- `includes/`: A directory where each file represents a module and
|
||||
contains a list of modules and counts that the module _includes_.
|
||||
- `includedby/`: Similar to `includes/`, but the other way around. Each
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
doctest.basics > xrpl.basics
|
||||
doctest.basics > xrpl.protocol
|
||||
doctest.beast > xrpl.basics
|
||||
doctest.core > xrpl.core
|
||||
doctest.core > xrpl.json
|
||||
doctest.csf > test.csf
|
||||
doctest.nodestore > xrpl.nodestore
|
||||
doctest.protocol > xrpl.basics
|
||||
doctest.protocol > xrpl.json
|
||||
doctest.protocol > xrpl.protocol
|
||||
libxrpl.basics > xrpl.basics
|
||||
libxrpl.core > xrpl.basics
|
||||
libxrpl.core > xrpl.core
|
||||
|
||||
4
.github/scripts/rename/README.md
vendored
4
.github/scripts/rename/README.md
vendored
@@ -31,9 +31,6 @@ run from the repository root.
|
||||
the `xrpld` binary.
|
||||
5. `.github/scripts/rename/namespace.sh`: This script will rename the C++
|
||||
namespaces from `ripple` to `xrpl`.
|
||||
6. `.github/scripts/rename/config.sh`: This script will rename the config from
|
||||
`rippled.cfg` to `xrpld.cfg`, and updating the code accordingly. The old
|
||||
filename will still be accepted.
|
||||
|
||||
You can run all these scripts from the repository root as follows:
|
||||
|
||||
@@ -43,5 +40,4 @@ You can run all these scripts from the repository root as follows:
|
||||
./.github/scripts/rename/cmake.sh .
|
||||
./.github/scripts/rename/binary.sh .
|
||||
./.github/scripts/rename/namespace.sh .
|
||||
./.github/scripts/rename/config.sh .
|
||||
```
|
||||
|
||||
72
.github/scripts/rename/config.sh
vendored
72
.github/scripts/rename/config.sh
vendored
@@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -e
|
||||
|
||||
# On MacOS, ensure that GNU sed is installed and available as `gsed`.
|
||||
SED_COMMAND=sed
|
||||
if [[ "${OSTYPE}" == 'darwin'* ]]; then
|
||||
if ! command -v gsed &> /dev/null; then
|
||||
echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'."
|
||||
exit 1
|
||||
fi
|
||||
SED_COMMAND=gsed
|
||||
fi
|
||||
|
||||
# This script renames the config from `rippled.cfg` to `xrpld.cfg`, and updates
|
||||
# the code accordingly. The old filename will still be accepted.
|
||||
# Usage: .github/scripts/rename/config.sh <repository directory>
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <repository directory>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DIRECTORY=$1
|
||||
echo "Processing directory: ${DIRECTORY}"
|
||||
if [ ! -d "${DIRECTORY}" ]; then
|
||||
echo "Error: Directory '${DIRECTORY}' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
pushd ${DIRECTORY}
|
||||
|
||||
# Add the xrpld.cfg to the .gitignore.
|
||||
if ! grep -q 'xrpld.cfg' .gitignore; then
|
||||
${SED_COMMAND} -i '/rippled.cfg/a\
|
||||
/xrpld.cfg' .gitignore
|
||||
fi
|
||||
|
||||
# Rename the files.
|
||||
if [ -e rippled.cfg ]; then
|
||||
mv rippled.cfg xrpld.cfg
|
||||
fi
|
||||
if [ -e cfg/rippled-example.cfg ]; then
|
||||
mv cfg/rippled-example.cfg cfg/xrpld-example.cfg
|
||||
fi
|
||||
|
||||
# Rename inside the files.
|
||||
DIRECTORIES=("cfg" "cmake" "include" "src")
|
||||
for DIRECTORY in "${DIRECTORIES[@]}"; do
|
||||
echo "Processing directory: ${DIRECTORY}"
|
||||
|
||||
find "${DIRECTORY}" -type f \( -name "*.h" -o -name "*.hpp" -o -name "*.ipp" -o -name "*.cpp" -o -name "*.cmake" -o -name "*.txt" -o -name "*.cfg" -o -name "*.md" \) | while read -r FILE; do
|
||||
echo "Processing file: ${FILE}"
|
||||
${SED_COMMAND} -i -E 's/rippled(-example)?[ .]cfg/xrpld\1.cfg/g' "${FILE}"
|
||||
done
|
||||
done
|
||||
${SED_COMMAND} -i 's/rippled/xrpld/g' cfg/xrpld-example.cfg
|
||||
${SED_COMMAND} -i 's/rippled/xrpld/g' src/test/core/Config_test.cpp
|
||||
${SED_COMMAND} -i 's/ripplevalidators/xrplvalidators/g' src/test/core/Config_test.cpp
|
||||
${SED_COMMAND} -i 's/rippleConfig/xrpldConfig/g' src/test/core/Config_test.cpp
|
||||
${SED_COMMAND} -i 's@ripple/@xrpld/@g' src/test/core/Config_test.cpp
|
||||
${SED_COMMAND} -i 's/Rippled/File/g' src/test/core/Config_test.cpp
|
||||
|
||||
|
||||
# Restore the old config file name in the code that maintains support for now.
|
||||
${SED_COMMAND} -i 's/configLegacyName = "xrpld.cfg"/configLegacyName = "rippled.cfg"/g' src/xrpld/core/detail/Config.cpp
|
||||
|
||||
# Restore an URL.
|
||||
${SED_COMMAND} -i 's/connect-your-xrpld-to-the-xrp-test-net.html/connect-your-rippled-to-the-xrp-test-net.html/g' cfg/xrpld-example.cfg
|
||||
|
||||
popd
|
||||
echo "Renaming complete."
|
||||
2
.github/scripts/strategy-matrix/generate.py
vendored
2
.github/scripts/strategy-matrix/generate.py
vendored
@@ -232,8 +232,6 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
f"-{architecture['platform'][architecture['platform'].find('/') + 1 :]}"
|
||||
)
|
||||
config_name += f"-{build_type.lower()}"
|
||||
if "-Dcoverage=ON" in cmake_args:
|
||||
config_name += "-coverage"
|
||||
if "-Dunity=ON" in cmake_args:
|
||||
config_name += "-unity"
|
||||
|
||||
|
||||
56
.github/scripts/strategy-matrix/linux.json
vendored
56
.github/scripts/strategy-matrix/linux.json
vendored
@@ -15,196 +15,196 @@
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "12",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "13",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "14",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "15",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "16",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "17",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "18",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "19",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "bookworm",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "20",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "trixie",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "14",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "trixie",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "15",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "trixie",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "20",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "debian",
|
||||
"distro_version": "trixie",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "21",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "0525eae"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "8",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "14",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "8",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "any",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "9",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "12",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "9",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "13",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "9",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "14",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "9",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "any",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "10",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "14",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "rhel",
|
||||
"distro_version": "10",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "any",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "jammy",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "12",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "noble",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "13",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "noble",
|
||||
"compiler_name": "gcc",
|
||||
"compiler_version": "14",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "noble",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "16",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "noble",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "17",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "noble",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "18",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
},
|
||||
{
|
||||
"distro_name": "ubuntu",
|
||||
"distro_version": "noble",
|
||||
"compiler_name": "clang",
|
||||
"compiler_version": "19",
|
||||
"image_sha": "cc09fd3"
|
||||
"image_sha": "e1782cd"
|
||||
}
|
||||
],
|
||||
"build_type": ["Debug", "Release"],
|
||||
|
||||
3
.github/workflows/on-pr.yml
vendored
3
.github/workflows/on-pr.yml
vendored
@@ -114,9 +114,6 @@ jobs:
|
||||
matrix:
|
||||
os: [linux, macos, windows]
|
||||
with:
|
||||
# Enable ccache only for events targeting the XRPLF repository, since
|
||||
# other accounts will not have access to our remote cache storage.
|
||||
ccache_enabled: ${{ github.repository_owner == 'XRPLF' }}
|
||||
os: ${{ matrix.os }}
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
6
.github/workflows/on-trigger.yml
vendored
6
.github/workflows/on-trigger.yml
vendored
@@ -68,12 +68,6 @@ jobs:
|
||||
matrix:
|
||||
os: [linux, macos, windows]
|
||||
with:
|
||||
# Enable ccache only for events targeting the XRPLF repository, since
|
||||
# other accounts will not have access to our remote cache storage.
|
||||
# However, we do not enable ccache for events targeting the master or a
|
||||
# release branch, to protect against the rare case that the output
|
||||
# produced by ccache is not identical to a regular compilation.
|
||||
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !(github.base_ref == 'master' || startsWith(github.base_ref, 'release')) }}
|
||||
os: ${{ matrix.os }}
|
||||
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
|
||||
secrets:
|
||||
|
||||
4
.github/workflows/publish-docs.yml
vendored
4
.github/workflows/publish-docs.yml
vendored
@@ -22,7 +22,7 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
BUILD_DIR: .build
|
||||
NPROC_SUBTRACT: 2
|
||||
|
||||
jobs:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/get-nproc@046b1620f6bfd6cd0985dc82c3df02786801fe0a
|
||||
id: nproc
|
||||
with:
|
||||
subtract: ${{ env.NPROC_SUBTRACT }}
|
||||
|
||||
87
.github/workflows/reusable-build-test-config.yml
vendored
87
.github/workflows/reusable-build-test-config.yml
vendored
@@ -3,6 +3,11 @@ name: Build and test configuration
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_dir:
|
||||
description: "The directory where to build."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
build_only:
|
||||
description: 'Whether to only build or to build and test the code ("true", "false").'
|
||||
required: true
|
||||
@@ -10,14 +15,8 @@ on:
|
||||
|
||||
build_type:
|
||||
description: 'The build type to use ("Debug", "Release").'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
ccache_enabled:
|
||||
description: "Whether to enable ccache."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
required: true
|
||||
|
||||
cmake_args:
|
||||
description: "Additional arguments to pass to CMake."
|
||||
@@ -27,8 +26,8 @@ on:
|
||||
|
||||
cmake_target:
|
||||
description: "The CMake target to build."
|
||||
required: true
|
||||
type: string
|
||||
required: true
|
||||
|
||||
runs_on:
|
||||
description: Runner to run the job on as a JSON string
|
||||
@@ -60,11 +59,6 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
# Conan installs the generators in the build/generators directory, see the
|
||||
# layout() method in conanfile.py. We then run CMake from the build directory.
|
||||
BUILD_DIR: build
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: ${{ inputs.config_name }}
|
||||
@@ -72,47 +66,26 @@ jobs:
|
||||
container: ${{ inputs.image != '' && inputs.image || null }}
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
# Use a namespace to keep the objects separate for each configuration.
|
||||
CCACHE_NAMESPACE: ${{ inputs.config_name }}
|
||||
# Ccache supports both Redis and HTTP endpoints.
|
||||
# * For Redis, use the following format: redis://ip:port, see
|
||||
# https://github.com/ccache/ccache/wiki/Redis-storage. Note that TLS is
|
||||
# not directly supported by ccache, and requires use of a proxy.
|
||||
# * For HTTP use the following format: http://ip:port/cache when using
|
||||
# nginx as backend or http://ip:port|layout=bazel when using Bazel
|
||||
# Remote Cache, see https://github.com/ccache/ccache/wiki/HTTP-storage.
|
||||
# Note that HTTPS is not directly supported by ccache.
|
||||
CCACHE_REMOTE_ONLY: true
|
||||
CCACHE_REMOTE_STORAGE: http://cache.dev.ripplex.io:8080|layout=bazel
|
||||
# Ignore the creation and modification timestamps on files, since the
|
||||
# header files are copied into separate directories by CMake, which will
|
||||
# otherwise result in cache misses.
|
||||
CCACHE_SLOPPINESS: include_file_ctime,include_file_mtime
|
||||
# Determine if coverage and voidstar should be enabled.
|
||||
COVERAGE_ENABLED: ${{ contains(inputs.cmake_args, '-Dcoverage=ON') }}
|
||||
VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
||||
ENABLED_VOIDSTAR: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
||||
ENABLED_COVERAGE: ${{ contains(inputs.cmake_args, '-Dcoverage=ON') }}
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
uses: XRPLF/actions/cleanup-workspace@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/cleanup-workspace@01b244d2718865d427b499822fbd3f15e7197fcc
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/prepare-runner@99685816bb60a95a66852f212f382580e180df3a
|
||||
with:
|
||||
disable_ccache: ${{ !inputs.ccache_enabled }}
|
||||
|
||||
- name: Set ccache log file
|
||||
if: ${{ inputs.ccache_enabled && runner.debug == '1' }}
|
||||
run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >> "${GITHUB_ENV}"
|
||||
disable_ccache: false
|
||||
|
||||
- name: Print build environment
|
||||
uses: ./.github/actions/print-env
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/get-nproc@046b1620f6bfd6cd0985dc82c3df02786801fe0a
|
||||
id: nproc
|
||||
with:
|
||||
subtract: ${{ inputs.nproc_subtract }}
|
||||
@@ -123,6 +96,7 @@ jobs:
|
||||
- name: Build dependencies
|
||||
uses: ./.github/actions/build-deps
|
||||
with:
|
||||
build_dir: ${{ inputs.build_dir }}
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ inputs.build_type }}
|
||||
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
||||
@@ -130,7 +104,7 @@ jobs:
|
||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
||||
@@ -143,7 +117,7 @@ jobs:
|
||||
..
|
||||
|
||||
- name: Build the binary
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -155,18 +129,11 @@ jobs:
|
||||
--parallel "${BUILD_NPROC}" \
|
||||
--target "${CMAKE_TARGET}"
|
||||
|
||||
- name: Show ccache statistics
|
||||
if: ${{ inputs.ccache_enabled }}
|
||||
run: |
|
||||
ccache --show-stats -vv
|
||||
if [ '${{ runner.debug }}' = '1' ]; then
|
||||
cat "${CCACHE_LOGFILE}"
|
||||
curl ${CCACHE_REMOTE_STORAGE%|*}/status || true
|
||||
fi
|
||||
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
env:
|
||||
BUILD_DIR: ${{ inputs.build_dir }}
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
path: ${{ env.BUILD_DIR }}/xrpld
|
||||
@@ -175,7 +142,7 @@ jobs:
|
||||
|
||||
- name: Check linking (Linux)
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
run: |
|
||||
ldd ./xrpld
|
||||
if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then
|
||||
@@ -186,14 +153,14 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Verify presence of instrumentation (Linux)
|
||||
if: ${{ runner.os == 'Linux' && env.VOIDSTAR_ENABLED == 'true' }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
if: ${{ runner.os == 'Linux' && env.ENABLED_VOIDSTAR == 'true' }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
run: |
|
||||
./xrpld --version | grep libvoidstar
|
||||
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
# Windows locks some of the build files while running tests, and parallel jobs can collide
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -206,7 +173,7 @@ jobs:
|
||||
|
||||
- name: Run the embedded tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', inputs.build_dir, inputs.build_type) || inputs.build_dir }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
run: |
|
||||
@@ -221,8 +188,8 @@ jobs:
|
||||
netstat -an
|
||||
|
||||
- name: Prepare coverage report
|
||||
if: ${{ !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
if: ${{ !inputs.build_only && env.ENABLED_COVERAGE == 'true' }}
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -234,13 +201,13 @@ jobs:
|
||||
--target coverage
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.ENABLED_COVERAGE == 'true' }}
|
||||
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
fail_ci_if_error: true
|
||||
files: ${{ env.BUILD_DIR }}/coverage.xml
|
||||
files: ${{ inputs.build_dir }}/coverage.xml
|
||||
plugins: noop
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true
|
||||
|
||||
13
.github/workflows/reusable-build-test.yml
vendored
13
.github/workflows/reusable-build-test.yml
vendored
@@ -8,24 +8,21 @@ name: Build and test
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ccache_enabled:
|
||||
description: "Whether to enable ccache."
|
||||
build_dir:
|
||||
description: "The directory where to build."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
type: string
|
||||
default: ".build"
|
||||
os:
|
||||
description: 'The operating system to use for the build ("linux", "macos", "windows").'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
strategy_matrix:
|
||||
# TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations.
|
||||
description: 'The strategy matrix to use for generating the configurations ("minimal", "all").'
|
||||
required: false
|
||||
type: string
|
||||
default: "minimal"
|
||||
|
||||
secrets:
|
||||
CODECOV_TOKEN:
|
||||
description: "The Codecov token to use for uploading coverage reports."
|
||||
@@ -49,9 +46,9 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
max-parallel: 10
|
||||
with:
|
||||
build_dir: ${{ inputs.build_dir }}
|
||||
build_only: ${{ matrix.build_only }}
|
||||
build_type: ${{ matrix.build_type }}
|
||||
ccache_enabled: ${{ inputs.ccache_enabled }}
|
||||
cmake_args: ${{ matrix.cmake_args }}
|
||||
cmake_target: ${{ matrix.cmake_target }}
|
||||
runs_on: ${{ toJSON(matrix.architecture.runner) }}
|
||||
|
||||
2
.github/workflows/reusable-check-rename.yml
vendored
2
.github/workflows/reusable-check-rename.yml
vendored
@@ -29,8 +29,6 @@ jobs:
|
||||
run: .github/scripts/rename/binary.sh .
|
||||
- name: Check namespaces
|
||||
run: .github/scripts/rename/namespace.sh .
|
||||
- name: Check config name
|
||||
run: .github/scripts/rename/config.sh .
|
||||
- name: Check for differences
|
||||
env:
|
||||
MESSAGE: |
|
||||
|
||||
9
.github/workflows/upload-conan-deps.yml
vendored
9
.github/workflows/upload-conan-deps.yml
vendored
@@ -64,21 +64,21 @@ jobs:
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
uses: XRPLF/actions/cleanup-workspace@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/cleanup-workspace@01b244d2718865d427b499822fbd3f15e7197fcc
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/prepare-runner@99685816bb60a95a66852f212f382580e180df3a
|
||||
with:
|
||||
disable_ccache: true
|
||||
disable_ccache: false
|
||||
|
||||
- name: Print build environment
|
||||
uses: ./.github/actions/print-env
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/.github/actions/get-nproc@046b1620f6bfd6cd0985dc82c3df02786801fe0a
|
||||
id: nproc
|
||||
with:
|
||||
subtract: ${{ env.NPROC_SUBTRACT }}
|
||||
@@ -92,6 +92,7 @@ jobs:
|
||||
- name: Build dependencies
|
||||
uses: ./.github/actions/build-deps
|
||||
with:
|
||||
build_dir: .build
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ matrix.build_type }}
|
||||
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,7 +35,6 @@ gmon.out
|
||||
|
||||
# Customized configs.
|
||||
/rippled.cfg
|
||||
/xrpld.cfg
|
||||
/validators.txt
|
||||
|
||||
# Locally patched Conan recipes
|
||||
|
||||
@@ -36,21 +36,6 @@ repos:
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
# - repo: https://github.com/streetsidesoftware/cspell-cli
|
||||
# rev: v9.2.0
|
||||
# hooks:
|
||||
# - id: cspell # Spell check changed files
|
||||
# - id: cspell # Spell check the commit message
|
||||
# name: check commit message spelling
|
||||
# args:
|
||||
# - --no-must-find-files
|
||||
# - --no-progress
|
||||
# - --no-summary
|
||||
# - --files
|
||||
# - .git/COMMIT_EDITMSG
|
||||
# stages: [commit-msg]
|
||||
# always_run: true # This might not be necessary.
|
||||
|
||||
exclude: |
|
||||
(?x)^(
|
||||
external/.*|
|
||||
|
||||
@@ -28,9 +28,6 @@ elseif(MSVC)
|
||||
add_compile_options(/wd4068) # Ignore unknown pragmas
|
||||
endif()
|
||||
|
||||
# Enable ccache to speed up builds.
|
||||
include(Ccache)
|
||||
|
||||
# make GIT_COMMIT_HASH define available to all sources
|
||||
find_package(Git)
|
||||
if(Git_FOUND)
|
||||
@@ -150,4 +147,5 @@ include(XrplValidatorKeys)
|
||||
if(tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
add_subdirectory(src/doctest)
|
||||
endif()
|
||||
|
||||
@@ -555,16 +555,16 @@ Rippled uses a linear workflow model that can be summarized as:
|
||||
git fetch --multiple upstreams user1 user2 user3 [...]
|
||||
git checkout -B release-next --no-track upstream/develop
|
||||
|
||||
# Only do an ff-only merge if pr-branch1 is either already
|
||||
# Only do an ff-only merge if prbranch1 is either already
|
||||
# squashed, or needs to be merged with separate commits,
|
||||
# and has no merge commits.
|
||||
# Use -S on the ff-only merge if pr-branch1 isn't signed.
|
||||
git merge [-S] --ff-only user1/pr-branch1
|
||||
# Use -S on the ff-only merge if prbranch1 isn't signed.
|
||||
git merge [-S] --ff-only user1/prbranch1
|
||||
|
||||
git merge --squash user2/pr-branch2
|
||||
git merge --squash user2/prbranch2
|
||||
git commit -S # Use the commit message provided on the PR
|
||||
|
||||
git merge --squash user3/pr-branch3
|
||||
git merge --squash user3/prbranch3
|
||||
git commit -S # Use the commit message provided on the PR
|
||||
|
||||
[...]
|
||||
@@ -876,7 +876,7 @@ git push --delete upstream-push master-next
|
||||
|
||||
#### Special cases: point releases, hotfixes, etc.
|
||||
|
||||
On occasion, a bug or issue is discovered in a version that already
|
||||
On occassion, a bug or issue is discovered in a version that already
|
||||
had a final release. Most of the time, development will have started
|
||||
on the next version, and will usually have changes in `develop`
|
||||
and often in `release`.
|
||||
|
||||
@@ -42,7 +42,7 @@ For more information on responsible disclosure, please read this [Wikipedia arti
|
||||
|
||||
## Report Handling Process
|
||||
|
||||
Please report the bug directly to us and limit further disclosure. If you want to prove that you knew the bug as of a given time, consider using a cryptographic pre-commitment: hash the content of your report and publish the hash on a medium of your choice (e.g. on Twitter or as a memo in a transaction) as "proof" that you had written the text at a given point in time.
|
||||
Please report the bug directly to us and limit further disclosure. If you want to prove that you knew the bug as of a given time, consider using a cryptographic precommitment: hash the content of your report and publish the hash on a medium of your choice (e.g. on Twitter or as a memo in a transaction) as "proof" that you had written the text at a given point in time.
|
||||
|
||||
Once we receive a report, we:
|
||||
|
||||
|
||||
@@ -29,18 +29,18 @@
|
||||
#
|
||||
# Purpose
|
||||
#
|
||||
# This file documents and provides examples of all xrpld server process
|
||||
# configuration options. When the xrpld server instance is launched, it
|
||||
# This file documents and provides examples of all rippled server process
|
||||
# configuration options. When the rippled server instance is launched, it
|
||||
# looks for a file with the following name:
|
||||
#
|
||||
# xrpld.cfg
|
||||
# rippled.cfg
|
||||
#
|
||||
# For more information on where the xrpld server instance searches for the
|
||||
# For more information on where the rippled server instance searches for the
|
||||
# file, visit:
|
||||
#
|
||||
# https://xrpl.org/commandline-usage.html#generic-options
|
||||
#
|
||||
# This file should be named xrpld.cfg. This file is UTF-8 with DOS, UNIX,
|
||||
# This file should be named rippled.cfg. This file is UTF-8 with DOS, UNIX,
|
||||
# or Mac style end of lines. Blank lines and lines beginning with '#' are
|
||||
# ignored. Undefined sections are reserved. No escapes are currently defined.
|
||||
#
|
||||
@@ -89,8 +89,8 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
# xrpld offers various server protocols to clients making inbound
|
||||
# connections. The listening ports xrpld uses are "universal" ports
|
||||
# rippled offers various server protocols to clients making inbound
|
||||
# connections. The listening ports rippled uses are "universal" ports
|
||||
# which may be configured to handshake in one or more of the available
|
||||
# supported protocols. These universal ports simplify administration:
|
||||
# A single open port can be used for multiple protocols.
|
||||
@@ -103,7 +103,7 @@
|
||||
#
|
||||
# A list of port names and key/value pairs. A port name must start with a
|
||||
# letter and contain only letters and numbers. The name is not case-sensitive.
|
||||
# For each name in this list, xrpld will look for a configuration file
|
||||
# For each name in this list, rippled will look for a configuration file
|
||||
# section with the same name and use it to create a listening port. The
|
||||
# name is informational only; the choice of name does not affect the function
|
||||
# of the listening port.
|
||||
@@ -134,7 +134,7 @@
|
||||
# ip = 127.0.0.1
|
||||
# protocol = http
|
||||
#
|
||||
# When xrpld is used as a command line client (for example, issuing a
|
||||
# When rippled is used as a command line client (for example, issuing a
|
||||
# server stop command), the first port advertising the http or https
|
||||
# protocol will be used to make the connection.
|
||||
#
|
||||
@@ -175,7 +175,7 @@
|
||||
# same time. It is possible have both Websockets and Secure Websockets
|
||||
# together in one port.
|
||||
#
|
||||
# NOTE If no ports support the peer protocol, xrpld cannot
|
||||
# NOTE If no ports support the peer protocol, rippled cannot
|
||||
# receive incoming peer connections or become a superpeer.
|
||||
#
|
||||
# limit = <number>
|
||||
@@ -194,7 +194,7 @@
|
||||
# required. IP address restrictions, if any, will be checked in addition
|
||||
# to the credentials specified here.
|
||||
#
|
||||
# When acting in the client role, xrpld will supply these credentials
|
||||
# When acting in the client role, rippled will supply these credentials
|
||||
# using HTTP's Basic Authentication headers when making outbound HTTP/S
|
||||
# requests.
|
||||
#
|
||||
@@ -218,7 +218,7 @@
|
||||
# administrative commands.
|
||||
#
|
||||
# NOTE A common configuration value for the admin field is "localhost".
|
||||
# If you are listening on all IPv4/IPv6 addresses by specifying
|
||||
# If you are listening on all IPv4/IPv6 addresses by specifing
|
||||
# ip = :: then you can use admin = ::ffff:127.0.0.1,::1 to allow
|
||||
# administrative access from both IPv4 and IPv6 localhost
|
||||
# connections.
|
||||
@@ -237,7 +237,7 @@
|
||||
# WS, or WSS protocol interfaces. If administrative commands are
|
||||
# disabled for a port, these credentials have no effect.
|
||||
#
|
||||
# When acting in the client role, xrpld will supply these credentials
|
||||
# When acting in the client role, rippled will supply these credentials
|
||||
# in the submitted JSON for any administrative command requests when
|
||||
# invoking JSON-RPC commands on remote servers.
|
||||
#
|
||||
@@ -258,7 +258,7 @@
|
||||
# resource controls will default to those for non-administrative users.
|
||||
#
|
||||
# The secure_gateway IP addresses are intended to represent
|
||||
# proxies. Since xrpld trusts these hosts, they must be
|
||||
# proxies. Since rippled trusts these hosts, they must be
|
||||
# responsible for properly authenticating the remote user.
|
||||
#
|
||||
# If some IP addresses are included for both "admin" and
|
||||
@@ -272,7 +272,7 @@
|
||||
# Use the specified files when configuring SSL on the port.
|
||||
#
|
||||
# NOTE If no files are specified and secure protocols are selected,
|
||||
# xrpld will generate an internal self-signed certificate.
|
||||
# rippled will generate an internal self-signed certificate.
|
||||
#
|
||||
# The files have these meanings:
|
||||
#
|
||||
@@ -297,12 +297,12 @@
|
||||
# Control the ciphers which the server will support over SSL on the port,
|
||||
# specified using the OpenSSL "cipher list format".
|
||||
#
|
||||
# NOTE If unspecified, xrpld will automatically configure a modern
|
||||
# NOTE If unspecified, rippled will automatically configure a modern
|
||||
# cipher suite. This default suite should be widely supported.
|
||||
#
|
||||
# You should not modify this string unless you have a specific
|
||||
# reason and cryptographic expertise. Incorrect modification may
|
||||
# keep xrpld from connecting to other instances of xrpld or
|
||||
# keep rippled from connecting to other instances of rippled or
|
||||
# prevent RPC and WebSocket clients from connecting.
|
||||
#
|
||||
# send_queue_limit = [1..65535]
|
||||
@@ -382,7 +382,7 @@
|
||||
#-----------------
|
||||
#
|
||||
# These settings control security and access attributes of the Peer to Peer
|
||||
# server section of the xrpld process. Peer Protocol implements the
|
||||
# server section of the rippled process. Peer Protocol implements the
|
||||
# Ripple Payment protocol. It is over peer connections that transactions
|
||||
# and validations are passed from to machine to machine, to determine the
|
||||
# contents of validated ledgers.
|
||||
@@ -396,7 +396,7 @@
|
||||
# true - enables compression
|
||||
# false - disables compression [default].
|
||||
#
|
||||
# The xrpld server can save bandwidth by compressing its peer-to-peer communications,
|
||||
# The rippled server can save bandwidth by compressing its peer-to-peer communications,
|
||||
# at a cost of greater CPU usage. If you enable link compression,
|
||||
# the server automatically compresses communications with peer servers
|
||||
# that also have link compression enabled.
|
||||
@@ -432,7 +432,7 @@
|
||||
#
|
||||
# [ips_fixed]
|
||||
#
|
||||
# List of IP addresses or hostnames to which xrpld should always attempt to
|
||||
# List of IP addresses or hostnames to which rippled should always attempt to
|
||||
# maintain peer connections with. This is useful for manually forming private
|
||||
# networks, for example to configure a validation server that connects to the
|
||||
# Ripple network through a public-facing server, or for building a set
|
||||
@@ -573,7 +573,7 @@
|
||||
#
|
||||
# minimum_txn_in_ledger_standalone = <number>
|
||||
#
|
||||
# Like minimum_txn_in_ledger when xrpld is running in standalone
|
||||
# Like minimum_txn_in_ledger when rippled is running in standalone
|
||||
# mode. Default: 1000.
|
||||
#
|
||||
# target_txn_in_ledger = <number>
|
||||
@@ -710,7 +710,7 @@
|
||||
#
|
||||
# [validator_token]
|
||||
#
|
||||
# This is an alternative to [validation_seed] that allows xrpld to perform
|
||||
# This is an alternative to [validation_seed] that allows rippled to perform
|
||||
# validation without having to store the validator keys on the network
|
||||
# connected server. The field should contain a single token in the form of a
|
||||
# base64-encoded blob.
|
||||
@@ -745,7 +745,7 @@
|
||||
#
|
||||
# Specify the file by its name or path.
|
||||
# Unless an absolute path is specified, it will be considered relative to
|
||||
# the folder in which the xrpld.cfg file is located.
|
||||
# the folder in which the rippled.cfg file is located.
|
||||
#
|
||||
# Examples:
|
||||
# /home/ripple/validators.txt
|
||||
@@ -840,7 +840,7 @@
|
||||
#
|
||||
# 0: Disable the ledger replay feature [default]
|
||||
# 1: Enable the ledger replay feature. With this feature enabled, when
|
||||
# acquiring a ledger from the network, a xrpld node only downloads
|
||||
# acquiring a ledger from the network, a rippled node only downloads
|
||||
# the ledger header and the transactions instead of the whole ledger.
|
||||
# And the ledger is built by applying the transactions to the parent
|
||||
# ledger.
|
||||
@@ -851,7 +851,7 @@
|
||||
#
|
||||
#----------------
|
||||
#
|
||||
# The xrpld server instance uses HTTPS GET requests in a variety of
|
||||
# The rippled server instance uses HTTPS GET requests in a variety of
|
||||
# circumstances, including but not limited to contacting trusted domains to
|
||||
# fetch information such as mapping an email address to a Ripple Payment
|
||||
# Network address.
|
||||
@@ -891,7 +891,7 @@
|
||||
#
|
||||
#------------
|
||||
#
|
||||
# xrpld creates 4 SQLite database to hold bookkeeping information
|
||||
# rippled creates 4 SQLite database to hold bookkeeping information
|
||||
# about transactions, local credentials, and various other things.
|
||||
# It also creates the NodeDB, which holds all the objects that
|
||||
# make up the current and historical ledgers.
|
||||
@@ -902,7 +902,7 @@
|
||||
# the performance of the server.
|
||||
#
|
||||
# Partial pathnames will be considered relative to the location of
|
||||
# the xrpld.cfg file.
|
||||
# the rippled.cfg file.
|
||||
#
|
||||
# [node_db] Settings for the Node Database (required)
|
||||
#
|
||||
@@ -920,11 +920,11 @@
|
||||
# type = NuDB
|
||||
#
|
||||
# NuDB is a high-performance database written by Ripple Labs and optimized
|
||||
# for xrpld and solid-state drives.
|
||||
# for rippled and solid-state drives.
|
||||
#
|
||||
# NuDB maintains its high speed regardless of the amount of history
|
||||
# stored. Online delete may be selected, but is not required. NuDB is
|
||||
# available on all platforms that xrpld runs on.
|
||||
# available on all platforms that rippled runs on.
|
||||
#
|
||||
# type = RocksDB
|
||||
#
|
||||
@@ -1049,7 +1049,7 @@
|
||||
#
|
||||
# recovery_wait_seconds
|
||||
# The online delete process checks periodically
|
||||
# that xrpld is still in sync with the network,
|
||||
# that rippled is still in sync with the network,
|
||||
# and that the validated ledger is less than
|
||||
# 'age_threshold_seconds' old. If not, then continue
|
||||
# sleeping for this number of seconds and
|
||||
@@ -1069,8 +1069,8 @@
|
||||
# The server creates and maintains 4 to 5 bookkeeping SQLite databases in
|
||||
# the 'database_path' location. If you omit this configuration setting,
|
||||
# the server creates a directory called "db" located in the same place as
|
||||
# your xrpld.cfg file.
|
||||
# Partial pathnames are relative to the location of the xrpld executable.
|
||||
# your rippled.cfg file.
|
||||
# Partial pathnames are relative to the location of the rippled executable.
|
||||
#
|
||||
# [sqlite] Tuning settings for the SQLite databases (optional)
|
||||
#
|
||||
@@ -1120,7 +1120,7 @@
|
||||
# The default is "wal", which uses a write-ahead
|
||||
# log to implement database transactions.
|
||||
# Alternately, "memory" saves disk I/O, but if
|
||||
# xrpld crashes during a transaction, the
|
||||
# rippled crashes during a transaction, the
|
||||
# database is likely to be corrupted.
|
||||
# See https://www.sqlite.org/pragma.html#pragma_journal_mode
|
||||
# for more details about the available options.
|
||||
@@ -1130,7 +1130,7 @@
|
||||
# synchronous Valid values: off, normal, full, extra
|
||||
# The default is "normal", which works well with
|
||||
# the "wal" journal mode. Alternatively, "off"
|
||||
# allows xrpld to continue as soon as data is
|
||||
# allows rippled to continue as soon as data is
|
||||
# passed to the OS, which can significantly
|
||||
# increase speed, but risks data corruption if
|
||||
# the host computer crashes before writing that
|
||||
@@ -1144,7 +1144,7 @@
|
||||
# The default is "file", which will use files
|
||||
# for temporary database tables and indices.
|
||||
# Alternatively, "memory" may save I/O, but
|
||||
# xrpld does not currently use many, if any,
|
||||
# rippled does not currently use many, if any,
|
||||
# of these temporary objects.
|
||||
# See https://www.sqlite.org/pragma.html#pragma_temp_store
|
||||
# for more details about the available options.
|
||||
@@ -1173,7 +1173,7 @@
|
||||
#
|
||||
# These settings are designed to help server administrators diagnose
|
||||
# problems, and obtain detailed information about the activities being
|
||||
# performed by the xrpld process.
|
||||
# performed by the rippled process.
|
||||
#
|
||||
#
|
||||
#
|
||||
@@ -1190,7 +1190,7 @@
|
||||
#
|
||||
# Configuration parameters for the Beast. Insight stats collection module.
|
||||
#
|
||||
# Insight is a module that collects information from the areas of xrpld
|
||||
# Insight is a module that collects information from the areas of rippled
|
||||
# that have instrumentation. The configuration parameters control where the
|
||||
# collection metrics are sent. The parameters are expressed as key = value
|
||||
# pairs with no white space. The main parameter is the choice of server:
|
||||
@@ -1199,7 +1199,7 @@
|
||||
#
|
||||
# Choice of server to send metrics to. Currently the only choice is
|
||||
# "statsd" which sends UDP packets to a StatsD daemon, which must be
|
||||
# running while xrpld is running. More information on StatsD is
|
||||
# running while rippled is running. More information on StatsD is
|
||||
# available here:
|
||||
# https://github.com/b/statsd_spec
|
||||
#
|
||||
@@ -1209,7 +1209,7 @@
|
||||
# in the format, n.n.n.n:port.
|
||||
#
|
||||
# "prefix" A string prepended to each collected metric. This is used
|
||||
# to distinguish between different running instances of xrpld.
|
||||
# to distinguish between different running instances of rippled.
|
||||
#
|
||||
# If this section is missing, or the server type is unspecified or unknown,
|
||||
# statistics are not collected or reported.
|
||||
@@ -1236,7 +1236,7 @@
|
||||
#
|
||||
# Example:
|
||||
# [perf]
|
||||
# perf_log=/var/log/xrpld/perf.log
|
||||
# perf_log=/var/log/rippled/perf.log
|
||||
# log_interval=2
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
@@ -1246,7 +1246,7 @@
|
||||
#----------
|
||||
#
|
||||
# The vote settings configure settings for the entire Ripple network.
|
||||
# While a single instance of xrpld cannot unilaterally enforce network-wide
|
||||
# While a single instance of rippled cannot unilaterally enforce network-wide
|
||||
# settings, these choices become part of the instance's vote during the
|
||||
# consensus process for each voting ledger.
|
||||
#
|
||||
@@ -1260,7 +1260,7 @@
|
||||
# The reference transaction is the simplest form of transaction.
|
||||
# It represents an XRP payment between two parties.
|
||||
#
|
||||
# If this parameter is unspecified, xrpld will use an internal
|
||||
# If this parameter is unspecified, rippled will use an internal
|
||||
# default. Don't change this without understanding the consequences.
|
||||
#
|
||||
# Example:
|
||||
@@ -1272,7 +1272,7 @@
|
||||
# account's XRP balance that is at or below the reserve may only be
|
||||
# spent on transaction fees, and not transferred out of the account.
|
||||
#
|
||||
# If this parameter is unspecified, xrpld will use an internal
|
||||
# If this parameter is unspecified, rippled will use an internal
|
||||
# default. Don't change this without understanding the consequences.
|
||||
#
|
||||
# Example:
|
||||
@@ -1284,7 +1284,7 @@
|
||||
# each ledger item owned by the account. Ledger items an account may
|
||||
# own include trust lines, open orders, and tickets.
|
||||
#
|
||||
# If this parameter is unspecified, xrpld will use an internal
|
||||
# If this parameter is unspecified, rippled will use an internal
|
||||
# default. Don't change this without understanding the consequences.
|
||||
#
|
||||
# Example:
|
||||
@@ -1326,7 +1326,7 @@
|
||||
# tool instead.
|
||||
#
|
||||
# This flag has no effect on the "sign" and "sign_for" command line options
|
||||
# that xrpld makes available.
|
||||
# that rippled makes available.
|
||||
#
|
||||
# The default value of this field is "false"
|
||||
#
|
||||
@@ -1405,7 +1405,7 @@
|
||||
#--------------------
|
||||
#
|
||||
# Administrators can use these values as a starting point for configuring
|
||||
# their instance of xrpld, but each value should be checked to make sure
|
||||
# their instance of rippled, but each value should be checked to make sure
|
||||
# it meets the business requirements for the organization.
|
||||
#
|
||||
# Server
|
||||
@@ -1415,7 +1415,7 @@
|
||||
# "peer"
|
||||
#
|
||||
# Peer protocol open to everyone. This is required to accept
|
||||
# incoming xrpld connections. This does not affect automatic
|
||||
# incoming rippled connections. This does not affect automatic
|
||||
# or manual outgoing Peer protocol connections.
|
||||
#
|
||||
# "rpc"
|
||||
@@ -1432,7 +1432,7 @@
|
||||
#
|
||||
# ETL commands for Clio. We recommend setting secure_gateway
|
||||
# in this section to a comma-separated list of the addresses
|
||||
# of your Clio servers, in order to bypass xrpld's rate limiting.
|
||||
# of your Clio servers, in order to bypass rippled's rate limiting.
|
||||
#
|
||||
# This port is commented out but can be enabled by removing
|
||||
# the '#' from each corresponding line including the entry under [server]
|
||||
@@ -1449,8 +1449,8 @@
|
||||
# NOTE
|
||||
#
|
||||
# To accept connections on well known ports such as 80 (HTTP) or
|
||||
# 443 (HTTPS), most operating systems will require xrpld to
|
||||
# run with administrator privileges, or else xrpld will not start.
|
||||
# 443 (HTTPS), most operating systems will require rippled to
|
||||
# run with administrator privileges, or else rippled will not start.
|
||||
|
||||
[server]
|
||||
port_rpc_admin_local
|
||||
@@ -1496,7 +1496,7 @@ secure_gateway = 127.0.0.1
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# This is primary persistent datastore for xrpld. This includes transaction
|
||||
# This is primary persistent datastore for rippled. This includes transaction
|
||||
# metadata, account states, and ledger headers. Helpful information can be
|
||||
# found at https://xrpl.org/capacity-planning.html#node-db-type
|
||||
# type=NuDB is recommended for non-validators with fast SSDs. Validators or
|
||||
@@ -1511,19 +1511,19 @@ secure_gateway = 127.0.0.1
|
||||
# deletion.
|
||||
[node_db]
|
||||
type=NuDB
|
||||
path=/var/lib/xrpld/db/nudb
|
||||
path=/var/lib/rippled/db/nudb
|
||||
nudb_block_size=4096
|
||||
online_delete=512
|
||||
advisory_delete=0
|
||||
|
||||
[database_path]
|
||||
/var/lib/xrpld/db
|
||||
/var/lib/rippled/db
|
||||
|
||||
|
||||
# This needs to be an absolute directory reference, not a relative one.
|
||||
# Modify this value as required.
|
||||
[debug_logfile]
|
||||
/var/log/xrpld/debug.log
|
||||
/var/log/rippled/debug.log
|
||||
|
||||
# To use the XRP test network
|
||||
# (see https://xrpl.org/connect-your-rippled-to-the-xrp-test-net.html),
|
||||
@@ -1533,7 +1533,7 @@ advisory_delete=0
|
||||
|
||||
# File containing trusted validator keys or validator list publishers.
|
||||
# Unless an absolute path is specified, it will be considered relative to the
|
||||
# folder in which the xrpld.cfg file is located.
|
||||
# folder in which the rippled.cfg file is located.
|
||||
[validators_file]
|
||||
validators.txt
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Default validators.txt
|
||||
#
|
||||
# This file is located in the same folder as your xrpld.cfg file
|
||||
# This file is located in the same folder as your rippled.cfg file
|
||||
# and defines which validators your server trusts not to collude.
|
||||
#
|
||||
# This file is UTF-8 with DOS, UNIX, or Mac style line endings.
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
find_program(CCACHE_PATH "ccache")
|
||||
if (NOT CCACHE_PATH)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
# For Linux and macOS we can use the ccache binary directly.
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PATH}")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PATH}")
|
||||
message(STATUS "Found ccache: ${CCACHE_PATH}")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
# For Windows more effort is required. The code below is a modified version of
|
||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio#usage-with-cmake.
|
||||
if ("${CCACHE_PATH}" MATCHES "chocolatey")
|
||||
message(DEBUG "Ccache path: ${CCACHE_PATH}")
|
||||
# Chocolatey uses a shim executable that we cannot use directly, in which
|
||||
# case we have to find the executable it points to. If we cannot find the
|
||||
# target executable then we cannot use ccache.
|
||||
find_program(BASH_PATH "bash")
|
||||
if (NOT BASH_PATH)
|
||||
message(WARNING "Could not find bash.")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "export LC_ALL='en_US.UTF-8'; ${CCACHE_PATH} --shimgen-noop | grep -oP 'path to executable: \\K.+' | head -c -1"
|
||||
OUTPUT_VARIABLE CCACHE_PATH)
|
||||
|
||||
if (NOT CCACHE_PATH)
|
||||
message(WARNING "Could not find ccache target.")
|
||||
return()
|
||||
endif ()
|
||||
file(TO_CMAKE_PATH "${CCACHE_PATH}" CCACHE_PATH)
|
||||
endif ()
|
||||
message(STATUS "Found ccache: ${CCACHE_PATH}")
|
||||
|
||||
# Tell cmake to use ccache for compiling with Visual Studio.
|
||||
file(COPY_FILE
|
||||
${CCACHE_PATH} ${CMAKE_BINARY_DIR}/cl.exe
|
||||
ONLY_IF_DIFFERENT)
|
||||
set(CMAKE_VS_GLOBALS
|
||||
"CLToolExe=cl.exe"
|
||||
"CLToolPath=${CMAKE_BINARY_DIR}"
|
||||
"TrackFileAccess=false"
|
||||
"UseMultiToolTask=true")
|
||||
|
||||
# By default Visual Studio generators will use /Zi, which is not compatible with
|
||||
# ccache, so tell it to use /Z7 instead.
|
||||
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
|
||||
@@ -149,7 +149,7 @@ elseif (use_gold AND is_gcc)
|
||||
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
#[=========================================================[
|
||||
NOTE: THE gold linker inserts -rpath as DT_RUNPATH by
|
||||
default instead of DT_RPATH, so you might have slightly
|
||||
default intead of DT_RPATH, so you might have slightly
|
||||
unexpected runtime ld behavior if you were expecting
|
||||
DT_RPATH. Specify --disable-new-dtags to gold if you do
|
||||
not want the default DT_RUNPATH behavior. This rpath
|
||||
|
||||
@@ -206,7 +206,7 @@ if(xrpld)
|
||||
)
|
||||
exclude_if_included(xrpld)
|
||||
# define a macro for tests that might need to
|
||||
# be excluded or run differently in CI environment
|
||||
# be exluded or run differently in CI environment
|
||||
if(is_ci)
|
||||
target_compile_definitions(xrpld PRIVATE XRPL_RUNNING_IN_CI)
|
||||
endif ()
|
||||
|
||||
@@ -62,7 +62,7 @@ if (is_root_project AND TARGET xrpld)
|
||||
message (\"-- Skipping : \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\")
|
||||
endif ()
|
||||
endmacro()
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg\" etc xrpld.cfg)
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/rippled-example.cfg\" etc rippled.cfg)
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt\" etc validators.txt)
|
||||
")
|
||||
install(CODE "
|
||||
|
||||
@@ -182,10 +182,12 @@ class Xrpl(ConanFile):
|
||||
libxrpl.libs = [
|
||||
"xrpl",
|
||||
"xrpl.libpb",
|
||||
"ed25519",
|
||||
"secp256k1",
|
||||
]
|
||||
# TODO: Fix the protobufs to include each other relative to
|
||||
# `include/`, not `include/xrpl/proto/`.
|
||||
libxrpl.includedirs = ["include", "include/xrpl/proto"]
|
||||
# `include/`, not `include/ripple/proto/`.
|
||||
libxrpl.includedirs = ["include", "include/ripple/proto"]
|
||||
libxrpl.requires = [
|
||||
"boost::headers",
|
||||
"boost::chrono",
|
||||
|
||||
@@ -134,7 +134,7 @@ validation messages (_PAV_) received from each validator on the node's UNL. Note
|
||||
that the node will only count the validation messages that agree with its own
|
||||
validations.
|
||||
|
||||
We define the **PAV** as the Percentage of Agreed Validation
|
||||
We define the **PAV** as the **P**ercentage of **A**greed **V**alidation
|
||||
messages received for the last N ledgers, where N = 256 by default.
|
||||
|
||||
When the PAV drops below the **_low-water mark_**, the validator is considered
|
||||
|
||||
@@ -43,14 +43,14 @@ alt phase == OPEN
|
||||
alt sqn%256==0
|
||||
CA -[#green]> RM: <font color=green>getValidations
|
||||
CA -[#green]> CA: <font color=green>create UNLModify Tx
|
||||
hnote over CA#lightgreen: use validations of the last 256 ledgers\nto figure out UNLModify Tx candidates.\nIf any, create UNLModify Tx, and add to TxSet.
|
||||
hnote over CA#lightgreen: use validatations of the last 256 ledgers\nto figure out UNLModify Tx candidates.\nIf any, create UNLModify Tx, and add to TxSet.
|
||||
end
|
||||
CA -> GC
|
||||
GC -> CA: propose
|
||||
deactivate CA
|
||||
end
|
||||
else phase == ESTABLISH
|
||||
hnote over GC: receive peer positions
|
||||
hnote over GC: receive peer postions
|
||||
GC -> GC : update our position
|
||||
GC -> CA : propose \n(if position changed)
|
||||
GC -> GC : check if have consensus
|
||||
|
||||
@@ -189,7 +189,7 @@ validations. It checks this on every call to `timerEntry`.
|
||||
- _Wrong Ledger_ indicates the node is not working on the correct prior ledger
|
||||
and does not have it available. It requests that ledger from the network, but
|
||||
continues to work towards consensus this round while waiting. If it had been
|
||||
_proposing_, it will send a special "bow-out" proposal to its peers to indicate
|
||||
_proposing_, it will send a special "bowout" proposal to its peers to indicate
|
||||
its change in mode for the rest of this round. For the duration of the round,
|
||||
it defers to peer positions for determining the consensus outcome as if it
|
||||
were just _observing_.
|
||||
@@ -515,7 +515,7 @@ are excerpts of the generic consensus implementation and of helper types that wi
|
||||
interact with the concrete implementing class.
|
||||
|
||||
```{.cpp}
|
||||
// Represents a transaction under dispute this round
|
||||
// Represents a transction under dispute this round
|
||||
template <class Tx_t, class NodeID_t> class DisputedTx;
|
||||
|
||||
// Represents how the node participates in Consensus this round
|
||||
|
||||
@@ -58,7 +58,7 @@ concept CAdoptTag = std::is_same_v<T, SharedIntrusiveAdoptIncrementStrongTag> ||
|
||||
When the strong pointer count goes to zero, the "partialDestructor" is
|
||||
called. This can be used to destroy as much of the object as possible while
|
||||
still retaining the reference counts. For example, for SHAMapInnerNodes the
|
||||
children may be reset in that function. Note that std::shared_pointer WILL
|
||||
children may be reset in that function. Note that std::shared_poiner WILL
|
||||
run the destructor when the strong count reaches zero, but may not free the
|
||||
memory used by the object until the weak count reaches zero. In rippled, we
|
||||
typically allocate shared pointers with the `make_shared` function. When
|
||||
|
||||
@@ -301,7 +301,7 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
|
||||
// change the counts and flags (the count could be atomically changed, but
|
||||
// the flags depend on the current value of the counts).
|
||||
//
|
||||
// Note: If this becomes a perf bottleneck, the `partialDestroyStartedMask`
|
||||
// Note: If this becomes a perf bottleneck, the `partialDestoryStartedMask`
|
||||
// may be able to be set non-atomically. But it is easier to reason about
|
||||
// the code if the flag is set atomically.
|
||||
while (1)
|
||||
|
||||
@@ -221,8 +221,7 @@ public:
|
||||
private:
|
||||
enum {
|
||||
// Maximum line length for log messages.
|
||||
// If the message exceeds this length it will be truncated with
|
||||
// ellipses.
|
||||
// If the message exceeds this length it will be truncated with elipses.
|
||||
maximumMessageCharacters = 12 * 1024
|
||||
};
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#ifndef XRPL_BASICS_MALLOCTRIM_H_INCLUDED
|
||||
#define XRPL_BASICS_MALLOCTRIM_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Allocator interaction note:
|
||||
// - This facility invokes glibc's malloc_trim(0) on Linux/glibc to request that
|
||||
// ptmalloc return free heap pages to the OS.
|
||||
// - If an alternative allocator (e.g. jemalloc or tcmalloc) is linked or
|
||||
// preloaded (LD_PRELOAD), calling glibc's malloc_trim typically has no effect
|
||||
// on the *active* heap. The call is harmless but may not reclaim memory
|
||||
// because those allocators manage their own arenas.
|
||||
// - Only glibc sbrk/arena space is eligible for trimming; large mmap-backed
|
||||
// allocations are usually returned to the OS on free regardless of trimming.
|
||||
// - Call at known reclamation points (e.g., after cache sweeps / online delete)
|
||||
// and consider rate limiting to avoid churn.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct MallocTrimReport
|
||||
{
|
||||
bool supported{false};
|
||||
int trimResult{-1};
|
||||
long rssBeforeKB{-1};
|
||||
long rssAfterKB{-1};
|
||||
|
||||
[[nodiscard]] long
|
||||
deltaKB() const noexcept
|
||||
{
|
||||
if (rssBeforeKB < 0 || rssAfterKB < 0)
|
||||
return 0;
|
||||
return rssAfterKB - rssBeforeKB;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Attempt to return freed memory to the operating system.
|
||||
*
|
||||
* On Linux with glibc malloc, this issues ::malloc_trim(0), which may release
|
||||
* free space from ptmalloc arenas back to the kernel. On other platforms, or if
|
||||
* a different allocator is in use, this function is a no-op and the report will
|
||||
* indicate that trimming is unsupported or had no effect.
|
||||
*
|
||||
* @param tag Optional identifier for logging/debugging purposes.
|
||||
* @param journal Journal for diagnostic logging.
|
||||
* @return Report containing before/after metrics and the trim result.
|
||||
*
|
||||
* @note If an alternative allocator (jemalloc/tcmalloc) is linked or preloaded,
|
||||
* calling glibc's malloc_trim may have no effect on the active heap. The
|
||||
* call is harmless but typically does not reclaim memory under those
|
||||
* allocators.
|
||||
*
|
||||
* @note Only memory served from glibc's sbrk/arena heaps is eligible for trim.
|
||||
* Large allocations satisfied via mmap are usually returned on free
|
||||
* independently of trimming.
|
||||
*
|
||||
* @note Intended for use after operations that free significant memory (e.g.,
|
||||
* cache sweeps, ledger cleanup, online delete). Consider rate limiting.
|
||||
*/
|
||||
MallocTrimReport
|
||||
mallocTrim(std::optional<std::string> const& tag, beast::Journal journal);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -152,8 +152,8 @@ public:
|
||||
|
||||
/** Return a "sub slice" of given length starting at the given position
|
||||
|
||||
Note that the subslice encompasses the range [pos, pos + rCount),
|
||||
where rCount is the smaller of count and size() - pos.
|
||||
Note that the subslice encompasses the range [pos, pos + rcount),
|
||||
where rcount is the smaller of count and size() - pos.
|
||||
|
||||
@param pos position of the first character
|
||||
@count requested length
|
||||
|
||||
@@ -31,7 +31,7 @@ template <class Iterator>
|
||||
std::optional<Blob>
|
||||
strUnHex(std::size_t strSize, Iterator begin, Iterator end)
|
||||
{
|
||||
static constexpr std::array<int, 256> const digitLookupTable = []() {
|
||||
static constexpr std::array<int, 256> const unxtab = []() {
|
||||
std::array<int, 256> t{};
|
||||
|
||||
for (auto& x : t)
|
||||
@@ -57,7 +57,7 @@ strUnHex(std::size_t strSize, Iterator begin, Iterator end)
|
||||
|
||||
if (strSize & 1)
|
||||
{
|
||||
int c = digitLookupTable[*iter++];
|
||||
int c = unxtab[*iter++];
|
||||
|
||||
if (c < 0)
|
||||
return {};
|
||||
@@ -67,12 +67,12 @@ strUnHex(std::size_t strSize, Iterator begin, Iterator end)
|
||||
|
||||
while (iter != end)
|
||||
{
|
||||
int cHigh = digitLookupTable[*iter++];
|
||||
int cHigh = unxtab[*iter++];
|
||||
|
||||
if (cHigh < 0)
|
||||
return {};
|
||||
|
||||
int cLow = digitLookupTable[*iter++];
|
||||
int cLow = unxtab[*iter++];
|
||||
|
||||
if (cLow < 0)
|
||||
return {};
|
||||
|
||||
@@ -3189,12 +3189,11 @@ operator==(aged_unordered_container<
|
||||
{
|
||||
if (size() != other.size())
|
||||
return false;
|
||||
for (auto iter(cbegin()), last(cend()), otherLast(other.cend());
|
||||
iter != last;
|
||||
for (auto iter(cbegin()), last(cend()), olast(other.cend()); iter != last;
|
||||
++iter)
|
||||
{
|
||||
auto otherIter(other.find(extract(*iter)));
|
||||
if (otherIter == otherLast)
|
||||
auto oiter(other.find(extract(*iter)));
|
||||
if (oiter == olast)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// These specializations get called by the non-member functions to do the work
|
||||
// These specializatons get called by the non-member functions to do the work
|
||||
template <class Out, class In>
|
||||
struct LexicalCast;
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ struct is_contiguously_hashable<T[N], HashAlgorithm>
|
||||
Throws:
|
||||
Never
|
||||
Effect:
|
||||
Returns the resulting hash of all the input data.
|
||||
Returns the reslting hash of all the input data.
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ public:
|
||||
print statement examples
|
||||
"parent.child" prints child and all of its children
|
||||
"parent.child." start at the parent and print down to child
|
||||
"parent.grandchild" prints nothing- grandchild not direct descendent
|
||||
"parent.grandchild" prints nothing- grandchild not direct discendent
|
||||
"parent.grandchild." starts at the parent and prints down to grandchild
|
||||
"parent.grandchild.*" starts at parent, print through grandchild
|
||||
children
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
using microseconds = std::chrono::microseconds;
|
||||
|
||||
/**
|
||||
* Configuration from [perf] section of xrpld.cfg.
|
||||
* Configuration from [perf] section of rippled.cfg.
|
||||
*/
|
||||
struct Setup
|
||||
{
|
||||
|
||||
445
include/xrpl/json/Object.h
Normal file
445
include/xrpl/json/Object.h
Normal file
@@ -0,0 +1,445 @@
|
||||
#ifndef XRPL_JSON_OBJECT_H_INCLUDED
|
||||
#define XRPL_JSON_OBJECT_H_INCLUDED
|
||||
|
||||
#include <xrpl/json/Writer.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Json {
|
||||
|
||||
/**
|
||||
Collection is a base class for Array and Object, classes which provide the
|
||||
facade of JSON collections for the O(1) JSON writer, while still using no
|
||||
heap memory and only a very small amount of stack.
|
||||
|
||||
From http://json.org, JSON has two types of collection: array, and object.
|
||||
Everything else is a *scalar* - a number, a string, a boolean, the special
|
||||
value null, or a legacy Json::Value.
|
||||
|
||||
Collections must write JSON "as-it-goes" in order to get the strong
|
||||
performance guarantees. This puts restrictions upon API users:
|
||||
|
||||
1. Only one collection can be open for change at any one time.
|
||||
|
||||
This condition is enforced automatically and a std::logic_error thrown if
|
||||
it is violated.
|
||||
|
||||
2. A tag may only be used once in an Object.
|
||||
|
||||
Some objects have many tags, so this condition might be a little
|
||||
expensive. Enforcement of this condition is turned on in debug builds and
|
||||
a std::logic_error is thrown when the tag is added for a second time.
|
||||
|
||||
Code samples:
|
||||
|
||||
Writer writer;
|
||||
|
||||
// An empty object.
|
||||
{
|
||||
Object::Root (writer);
|
||||
}
|
||||
// Outputs {}
|
||||
|
||||
// An object with one scalar value.
|
||||
{
|
||||
Object::Root root (writer);
|
||||
write["hello"] = "world";
|
||||
}
|
||||
// Outputs {"hello":"world"}
|
||||
|
||||
// Same, using chaining.
|
||||
{
|
||||
Object::Root (writer)["hello"] = "world";
|
||||
}
|
||||
// Output is the same.
|
||||
|
||||
// Add several scalars, with chaining.
|
||||
{
|
||||
Object::Root (writer)
|
||||
.set ("hello", "world")
|
||||
.set ("flag", false)
|
||||
.set ("x", 42);
|
||||
}
|
||||
// Outputs {"hello":"world","flag":false,"x":42}
|
||||
|
||||
// Add an array.
|
||||
{
|
||||
Object::Root root (writer);
|
||||
{
|
||||
auto array = root.setArray ("hands");
|
||||
array.append ("left");
|
||||
array.append ("right");
|
||||
}
|
||||
}
|
||||
// Outputs {"hands":["left", "right"]}
|
||||
|
||||
// Same, using chaining.
|
||||
{
|
||||
Object::Root (writer)
|
||||
.setArray ("hands")
|
||||
.append ("left")
|
||||
.append ("right");
|
||||
}
|
||||
// Output is the same.
|
||||
|
||||
// Add an object.
|
||||
{
|
||||
Object::Root root (writer);
|
||||
{
|
||||
auto object = root.setObject ("hands");
|
||||
object["left"] = false;
|
||||
object["right"] = true;
|
||||
}
|
||||
}
|
||||
// Outputs {"hands":{"left":false,"right":true}}
|
||||
|
||||
// Same, using chaining.
|
||||
{
|
||||
Object::Root (writer)
|
||||
.setObject ("hands")
|
||||
.set ("left", false)
|
||||
.set ("right", true);
|
||||
}
|
||||
}
|
||||
// Outputs {"hands":{"left":false,"right":true}}
|
||||
|
||||
|
||||
Typical ways to make mistakes and get a std::logic_error:
|
||||
|
||||
Writer writer;
|
||||
Object::Root root (writer);
|
||||
|
||||
// Repeat a tag.
|
||||
{
|
||||
root ["hello"] = "world";
|
||||
root ["hello"] = "there"; // THROWS! in a debug build.
|
||||
}
|
||||
|
||||
// Open a subcollection, then set something else.
|
||||
{
|
||||
auto object = root.setObject ("foo");
|
||||
root ["hello"] = "world"; // THROWS!
|
||||
}
|
||||
|
||||
// Open two subcollections at a time.
|
||||
{
|
||||
auto object = root.setObject ("foo");
|
||||
auto array = root.setArray ("bar"); // THROWS!!
|
||||
}
|
||||
|
||||
For more examples, check the unit tests.
|
||||
*/
|
||||
|
||||
class Collection
|
||||
{
|
||||
public:
|
||||
Collection(Collection&& c) noexcept;
|
||||
Collection&
|
||||
operator=(Collection&& c) noexcept;
|
||||
Collection() = delete;
|
||||
|
||||
~Collection();
|
||||
|
||||
protected:
|
||||
// A null parent means "no parent at all".
|
||||
// Writers cannot be null.
|
||||
Collection(Collection* parent, Writer*);
|
||||
void
|
||||
checkWritable(std::string const& label);
|
||||
|
||||
Collection* parent_;
|
||||
Writer* writer_;
|
||||
bool enabled_;
|
||||
};
|
||||
|
||||
class Array;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Represents a JSON object being written to a Writer. */
|
||||
class Object : protected Collection
|
||||
{
|
||||
public:
|
||||
/** Object::Root is the only Collection that has a public constructor. */
|
||||
class Root;
|
||||
|
||||
/** Set a scalar value in the Object for a key.
|
||||
|
||||
A JSON scalar is a single value - a number, string, boolean, nullptr or
|
||||
a Json::Value.
|
||||
|
||||
`set()` throws an exception if this object is disabled (which means that
|
||||
one of its children is enabled).
|
||||
|
||||
In a debug build, `set()` also throws an exception if the key has
|
||||
already been set() before.
|
||||
|
||||
An operator[] is provided to allow writing `object["key"] = scalar;`.
|
||||
*/
|
||||
template <typename Scalar>
|
||||
void
|
||||
set(std::string const& key, Scalar const&);
|
||||
|
||||
void
|
||||
set(std::string const& key, Json::Value const&);
|
||||
|
||||
// Detail class and method used to implement operator[].
|
||||
class Proxy;
|
||||
|
||||
Proxy
|
||||
operator[](std::string const& key);
|
||||
Proxy
|
||||
operator[](Json::StaticString const& key);
|
||||
|
||||
/** Make a new Object at a key and return it.
|
||||
|
||||
This Object is disabled until that sub-object is destroyed.
|
||||
Throws an exception if this Object was already disabled.
|
||||
*/
|
||||
Object
|
||||
setObject(std::string const& key);
|
||||
|
||||
/** Make a new Array at a key and return it.
|
||||
|
||||
This Object is disabled until that sub-array is destroyed.
|
||||
Throws an exception if this Object was already disabled.
|
||||
*/
|
||||
Array
|
||||
setArray(std::string const& key);
|
||||
|
||||
protected:
|
||||
friend class Array;
|
||||
Object(Collection* parent, Writer* w) : Collection(parent, w)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Object::Root : public Object
|
||||
{
|
||||
public:
|
||||
/** Each Object::Root must be constructed with its own unique Writer. */
|
||||
Root(Writer&);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Represents a JSON array being written to a Writer. */
|
||||
class Array : private Collection
|
||||
{
|
||||
public:
|
||||
/** Append a scalar to the Arrary.
|
||||
|
||||
Throws an exception if this array is disabled (which means that one of
|
||||
its sub-collections is enabled).
|
||||
*/
|
||||
template <typename Scalar>
|
||||
void
|
||||
append(Scalar const&);
|
||||
|
||||
/**
|
||||
Appends a Json::Value to an array.
|
||||
Throws an exception if this Array was disabled.
|
||||
*/
|
||||
void
|
||||
append(Json::Value const&);
|
||||
|
||||
/** Append a new Object and return it.
|
||||
|
||||
This Array is disabled until that sub-object is destroyed.
|
||||
Throws an exception if this Array was disabled.
|
||||
*/
|
||||
Object
|
||||
appendObject();
|
||||
|
||||
/** Append a new Array and return it.
|
||||
|
||||
This Array is disabled until that sub-array is destroyed.
|
||||
Throws an exception if this Array was already disabled.
|
||||
*/
|
||||
Array
|
||||
appendArray();
|
||||
|
||||
protected:
|
||||
friend class Object;
|
||||
Array(Collection* parent, Writer* w) : Collection(parent, w)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Generic accessor functions to allow Json::Value and Collection to
|
||||
// interoperate.
|
||||
|
||||
/** Add a new subarray at a named key in a Json object. */
|
||||
Json::Value&
|
||||
setArray(Json::Value&, Json::StaticString const& key);
|
||||
|
||||
/** Add a new subarray at a named key in a Json object. */
|
||||
Array
|
||||
setArray(Object&, Json::StaticString const& key);
|
||||
|
||||
/** Add a new subobject at a named key in a Json object. */
|
||||
Json::Value&
|
||||
addObject(Json::Value&, Json::StaticString const& key);
|
||||
|
||||
/** Add a new subobject at a named key in a Json object. */
|
||||
Object
|
||||
addObject(Object&, Json::StaticString const& key);
|
||||
|
||||
/** Append a new subarray to a Json array. */
|
||||
Json::Value&
|
||||
appendArray(Json::Value&);
|
||||
|
||||
/** Append a new subarray to a Json array. */
|
||||
Array
|
||||
appendArray(Array&);
|
||||
|
||||
/** Append a new subobject to a Json object. */
|
||||
Json::Value&
|
||||
appendObject(Json::Value&);
|
||||
|
||||
/** Append a new subobject to a Json object. */
|
||||
Object
|
||||
appendObject(Array&);
|
||||
|
||||
/** Copy all the keys and values from one object into another. */
|
||||
void
|
||||
copyFrom(Json::Value& to, Json::Value const& from);
|
||||
|
||||
/** Copy all the keys and values from one object into another. */
|
||||
void
|
||||
copyFrom(Object& to, Json::Value const& from);
|
||||
|
||||
/** An Object that contains its own Writer. */
|
||||
class WriterObject
|
||||
{
|
||||
public:
|
||||
WriterObject(Output const& output)
|
||||
: writer_(std::make_unique<Writer>(output))
|
||||
, object_(std::make_unique<Object::Root>(*writer_))
|
||||
{
|
||||
}
|
||||
|
||||
WriterObject(WriterObject&& other) = default;
|
||||
|
||||
Object*
|
||||
operator->()
|
||||
{
|
||||
return object_.get();
|
||||
}
|
||||
|
||||
Object&
|
||||
operator*()
|
||||
{
|
||||
return *object_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Writer> writer_;
|
||||
std::unique_ptr<Object::Root> object_;
|
||||
};
|
||||
|
||||
WriterObject
|
||||
stringWriterObject(std::string&);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Implementation details.
|
||||
|
||||
// Detail class for Object::operator[].
|
||||
class Object::Proxy
|
||||
{
|
||||
private:
|
||||
Object& object_;
|
||||
std::string const key_;
|
||||
|
||||
public:
|
||||
Proxy(Object& object, std::string const& key);
|
||||
|
||||
template <class T>
|
||||
void
|
||||
operator=(T const& t)
|
||||
{
|
||||
object_.set(key_, t);
|
||||
// Note: This function shouldn't return *this, because it's a trap.
|
||||
//
|
||||
// In Json::Value, foo[jss::key] returns a reference to a
|
||||
// mutable Json::Value contained _inside_ foo. But in the case of
|
||||
// Json::Object, where we write once only, there isn't any such
|
||||
// reference that can be returned. Returning *this would return an
|
||||
// object "a level higher" than in Json::Value, leading to obscure bugs,
|
||||
// particularly in generic code.
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
Array::append(Scalar const& value)
|
||||
{
|
||||
checkWritable("append");
|
||||
if (writer_)
|
||||
writer_->append(value);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
Object::set(std::string const& key, Scalar const& value)
|
||||
{
|
||||
checkWritable("set");
|
||||
if (writer_)
|
||||
writer_->set(key, value);
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
setArray(Json::Value& json, Json::StaticString const& key)
|
||||
{
|
||||
return (json[key] = Json::arrayValue);
|
||||
}
|
||||
|
||||
inline Array
|
||||
setArray(Object& json, Json::StaticString const& key)
|
||||
{
|
||||
return json.setArray(std::string(key));
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
addObject(Json::Value& json, Json::StaticString const& key)
|
||||
{
|
||||
return (json[key] = Json::objectValue);
|
||||
}
|
||||
|
||||
inline Object
|
||||
addObject(Object& object, Json::StaticString const& key)
|
||||
{
|
||||
return object.setObject(std::string(key));
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
appendArray(Json::Value& json)
|
||||
{
|
||||
return json.append(Json::arrayValue);
|
||||
}
|
||||
|
||||
inline Array
|
||||
appendArray(Array& json)
|
||||
{
|
||||
return json.appendArray();
|
||||
}
|
||||
|
||||
inline Json::Value&
|
||||
appendObject(Json::Value& json)
|
||||
{
|
||||
return json.append(Json::objectValue);
|
||||
}
|
||||
|
||||
inline Object
|
||||
appendObject(Array& json)
|
||||
{
|
||||
return json.appendObject();
|
||||
}
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef XRPL_JSON_JSON_READER_H_INCLUDED
|
||||
#define XRPL_JSON_JSON_READER_H_INCLUDED
|
||||
|
||||
#define CPPTL_JSON_READER_H_INCLUDED
|
||||
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
@@ -66,7 +68,7 @@ public:
|
||||
* error occurred during parsing.
|
||||
*/
|
||||
std::string
|
||||
getFormattedErrorMessages() const;
|
||||
getFormatedErrorMessages() const;
|
||||
|
||||
static constexpr unsigned nest_limit{25};
|
||||
|
||||
@@ -229,4 +231,4 @@ operator>>(std::istream&, Value&);
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif // XRPL_JSON_JSON_READER_H_INCLUDED
|
||||
#endif // CPPTL_JSON_READER_H_INCLUDED
|
||||
|
||||
@@ -44,7 +44,7 @@ enum ValueType {
|
||||
class StaticString
|
||||
{
|
||||
public:
|
||||
constexpr explicit StaticString(char const* czString) : str_(czString)
|
||||
constexpr explicit StaticString(char const* czstring) : str_(czstring)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -682,4 +682,4 @@ public:
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif // XRPL_JSON_JSON_VALUE_H_INCLUDED
|
||||
#endif // CPPTL_JSON_H_INCLUDED
|
||||
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
void
|
||||
writeArrayValue(Value const& value);
|
||||
bool
|
||||
isMultilineArray(Value const& value);
|
||||
isMultineArray(Value const& value);
|
||||
void
|
||||
pushValue(std::string const& value);
|
||||
void
|
||||
@@ -157,7 +157,7 @@ private:
|
||||
void
|
||||
writeArrayValue(Value const& value);
|
||||
bool
|
||||
isMultilineArray(Value const& value);
|
||||
isMultineArray(Value const& value);
|
||||
void
|
||||
pushValue(std::string const& value);
|
||||
void
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace xrpl {
|
||||
namespace credentials {
|
||||
|
||||
// These function will be used by the code that use DepositPreauth / Credentials
|
||||
// (and any future pre-authorization modes) as part of authorization (all the
|
||||
// (and any future preauthorization modes) as part of authorization (all the
|
||||
// transfer funds transactions)
|
||||
|
||||
// Check if credential sfExpiration field has passed ledger's parentCloseTime
|
||||
@@ -41,8 +41,7 @@ checkFields(STTx const& tx, beast::Journal j);
|
||||
|
||||
// Accessing the ledger to check if provided credentials are valid. Do not use
|
||||
// in doApply (only in preclaim) since it does not remove expired credentials.
|
||||
// If you call it in preclaim, you also must call verifyDepositPreauth in
|
||||
// doApply
|
||||
// If you call it in prelaim, you also must call verifyDepositPreauth in doApply
|
||||
TER
|
||||
valid(
|
||||
STTx const& tx,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace xrpl {
|
||||
namespace NodeStore {
|
||||
|
||||
/** Simple NodeStore Scheduler that just performs the tasks synchronously. */
|
||||
/** Simple NodeStore Scheduler that just peforms the tasks synchronously. */
|
||||
class DummyScheduler : public Scheduler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
HyperLevelDB, LevelDBFactory, SQLite, MDB
|
||||
|
||||
If the fastBackendParameter is omitted or empty, no ephemeral database
|
||||
is used. If the scheduler parameter is omitted or unspecified, a
|
||||
is used. If the scheduler parameter is omited or unspecified, a
|
||||
synchronous scheduler is used which performs all tasks immediately on
|
||||
the caller's thread.
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ Facebook's RocksDB database, builds on LevelDB.
|
||||
|
||||
Use SQLite.
|
||||
|
||||
'path' specifies where the backend will store its data files.
|
||||
'path' speficies where the backend will store its data files.
|
||||
|
||||
Choices for 'compression'
|
||||
|
||||
@@ -130,7 +130,7 @@ newer versions of RocksDB (TBD).
|
||||
## Discussion
|
||||
|
||||
RocksDBQuickFactory is intended to provide a testbed for comparing potential
|
||||
rocksdb performance with the existing recommended configuration in xrpld.cfg.
|
||||
rocksdb performance with the existing recommended configuration in rippled.cfg.
|
||||
Through various executions and profiling some conclusions are presented below.
|
||||
|
||||
- If the write ahead log is enabled, insert speed soon clogs up under load. The
|
||||
@@ -161,7 +161,7 @@ Through various executions and profiling some conclusions are presented below.
|
||||
|
||||
- Multiple runs of the benchmarks can yield surprisingly different results. This
|
||||
can perhaps be attributed to the asynchronous nature of rocksdb's compaction
|
||||
process. The benchmarks are artificial and create highly unlikely write load to
|
||||
process. The benchmarks are artifical and create highly unlikely write load to
|
||||
create the dataset to measure different read access patterns. Therefore multiple
|
||||
runs of the benchmarks are required to get a feel for the effectiveness of the
|
||||
changes. This contrasts sharply with the keyvadb benchmarking were highly
|
||||
|
||||
@@ -9,7 +9,7 @@ import "org/xrpl/rpc/v1/get_ledger_entry.proto";
|
||||
import "org/xrpl/rpc/v1/get_ledger_data.proto";
|
||||
import "org/xrpl/rpc/v1/get_ledger_diff.proto";
|
||||
|
||||
// These methods are binary only methods for retrieving arbitrary ledger state
|
||||
// These methods are binary only methods for retrieiving arbitrary ledger state
|
||||
// via gRPC. These methods are used by clio, but can also be
|
||||
// used by any client that wants to extract ledger state in an efficient manner.
|
||||
// They do not directly mimic the JSON equivalent methods.
|
||||
|
||||
@@ -58,14 +58,14 @@ static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion);
|
||||
static_assert(apiBetaVersion >= apiMaximumSupportedVersion);
|
||||
static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion);
|
||||
|
||||
inline void
|
||||
setVersion(Json::Value& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
template <class JsonObject>
|
||||
void
|
||||
setVersion(JsonObject& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
apiVersion != apiInvalidVersion,
|
||||
"xrpl::RPC::setVersion : input is valid");
|
||||
|
||||
auto& retObj = parent[jss::version] = Json::objectValue;
|
||||
auto& retObj = addObject(parent, jss::version);
|
||||
|
||||
if (apiVersion == apiVersionIfUnspecified)
|
||||
{
|
||||
|
||||
@@ -209,11 +209,33 @@ get_error_info(error_code_i code);
|
||||
|
||||
/** Add or update the json update to reflect the error code. */
|
||||
/** @{ */
|
||||
template <class JsonValue>
|
||||
void
|
||||
inject_error(error_code_i code, Json::Value& json);
|
||||
inject_error(error_code_i code, JsonValue& json)
|
||||
{
|
||||
ErrorInfo const& info(get_error_info(code));
|
||||
json[jss::error] = info.token;
|
||||
json[jss::error_code] = info.code;
|
||||
json[jss::error_message] = info.message;
|
||||
}
|
||||
|
||||
template <class JsonValue>
|
||||
void
|
||||
inject_error(error_code_i code, std::string const& message, Json::Value& json);
|
||||
inject_error(int code, JsonValue& json)
|
||||
{
|
||||
inject_error(error_code_i(code), json);
|
||||
}
|
||||
|
||||
template <class JsonValue>
|
||||
void
|
||||
inject_error(error_code_i code, std::string const& message, JsonValue& json)
|
||||
{
|
||||
ErrorInfo const& info(get_error_info(code));
|
||||
json[jss::error] = info.token;
|
||||
json[jss::error_code] = info.code;
|
||||
json[jss::error_message] = message;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/** Returns a new json object that reflects the error code. */
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace xrpl {
|
||||
|
||||
Arithmetic operations can throw std::overflow_error during normalization
|
||||
if the amount exceeds the largest representable amount, but underflows
|
||||
will silently truncate to zero.
|
||||
will silently trunctate to zero.
|
||||
*/
|
||||
class IOUAmount : private boost::totally_ordered<IOUAmount>,
|
||||
private boost::additive<IOUAmount>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
namespace xrpl {
|
||||
|
||||
class SeqProxy;
|
||||
/** Keylet computation functions.
|
||||
/** Keylet computation funclets.
|
||||
|
||||
Entries in the ledger are located using 256-bit locators. The locators are
|
||||
calculated using a wide range of parameters specific to the entry whose
|
||||
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
|
||||
std::unordered_map<std::uint16_t, uint256> txFeatureMap_;
|
||||
|
||||
std::unordered_map<std::uint16_t, Delegation> delegableTx_;
|
||||
std::unordered_map<std::uint16_t, Delegation> delegatableTx_;
|
||||
|
||||
std::unordered_map<std::string, GranularPermissionType>
|
||||
granularPermissionMap_;
|
||||
@@ -71,7 +71,8 @@ public:
|
||||
getTxFeature(TxType txType) const;
|
||||
|
||||
bool
|
||||
isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const;
|
||||
isDelegatable(std::uint32_t const& permissionValue, Rules const& rules)
|
||||
const;
|
||||
|
||||
// for tx level permission, permission value is equal to tx type plus one
|
||||
uint32_t
|
||||
|
||||
@@ -179,7 +179,7 @@ static constexpr int loanPaymentsPerFeeIncrement = 5;
|
||||
*
|
||||
* This limit is enforced during the loan payment process, and thus is not
|
||||
* estimated. If the limit is hit, no further payments or overpayments will be
|
||||
* processed, no matter how much of the transaction Amount is left, but the
|
||||
* processed, no matter how much of the transation Amount is left, but the
|
||||
* transaction will succeed with the payments that have been processed up to
|
||||
* that point.
|
||||
*
|
||||
|
||||
@@ -210,7 +210,7 @@ public:
|
||||
|
||||
private:
|
||||
// The ceil_in and ceil_out methods that deal in TAmount all convert
|
||||
// their arguments to STAmount and convert the result back to TAmount.
|
||||
// their arguments to STAoumout and convert the result back to TAmount.
|
||||
// This helper function takes care of all the conversion operations.
|
||||
template <
|
||||
class In,
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace xrpl {
|
||||
bool
|
||||
isRpcError(Json::Value jvResult);
|
||||
Json::Value
|
||||
rpcError(error_code_i iError);
|
||||
rpcError(int iError);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
|
||||
// interest and fees, or it will fail. False: Not a full payment.
|
||||
constexpr std::uint32_t const tfLoanFullPayment = 0x00020000;
|
||||
// tfLoanLatePayment: True, indicates that the payment is late,
|
||||
// and includes late interest and fees. If the loan is not late,
|
||||
// and includes late iterest and fees. If the loan is not late,
|
||||
// it will fail. False: not a late payment. If the current payment
|
||||
// is overdue, the transaction will fail.
|
||||
constexpr std::uint32_t const tfLoanLatePayment = 0x00040000;
|
||||
|
||||
@@ -33,12 +33,12 @@ struct AttestationBase
|
||||
// Account on the sending chain that triggered the event (sent the
|
||||
// transaction)
|
||||
AccountID sendingAccount;
|
||||
// Amount transferred on the sending chain
|
||||
// Amount transfered on the sending chain
|
||||
STAmount sendingAmount;
|
||||
// Account on the destination chain that collects a share of the attestation
|
||||
// reward
|
||||
AccountID rewardAccount;
|
||||
// Amount was transferred on the locking chain
|
||||
// Amount was transfered on the locking chain
|
||||
bool wasLockingChainSend;
|
||||
|
||||
explicit AttestationBase(
|
||||
@@ -354,7 +354,7 @@ struct XChainCreateAccountAttestation
|
||||
XChainCreateAccountAttestation const& rhs);
|
||||
};
|
||||
|
||||
// Attestations from witness servers for a particular claim ID and bridge.
|
||||
// Attestations from witness servers for a particular claimid and bridge.
|
||||
// Only one attestation per signature is allowed.
|
||||
template <class TAttestation>
|
||||
class XChainAttestationsBase
|
||||
|
||||
@@ -42,7 +42,7 @@ concept ValidConstructSTArgs =
|
||||
class STVar
|
||||
{
|
||||
private:
|
||||
// The largest "small object" we can accommodate
|
||||
// The largest "small object" we can accomodate
|
||||
static std::size_t constexpr max_size = 72;
|
||||
|
||||
std::aligned_storage<max_size>::type d_;
|
||||
|
||||
@@ -237,7 +237,7 @@ LEDGER_ENTRY(ltOFFER, 0x006f, Offer, offer, ({
|
||||
{sfAdditionalBooks, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** A ledger object which describes a deposit pre-authorization.
|
||||
/** A ledger object which describes a deposit preauthorization.
|
||||
|
||||
\sa keylet::depositPreauth
|
||||
*/
|
||||
@@ -577,7 +577,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
||||
// - TrueTotalLoanValue = PaymentRemaining * PeriodicPayment
|
||||
// The unrounded true total value of the loan.
|
||||
//
|
||||
// - TrueTotalPrincipalOutstanding can be computed using the algorithm
|
||||
// - TrueTotalPrincialOutstanding can be computed using the algorithm
|
||||
// in the ripple::detail::loanPrincipalFromPeriodicPayment function.
|
||||
//
|
||||
// - TrueTotalInterestOutstanding = TrueTotalLoanValue -
|
||||
|
||||
@@ -66,7 +66,7 @@ values over time: this is implemented by the DecayingSample class.
|
||||
Each server in a cluster creates a list of IP addresses of end points
|
||||
that are imposing a significant load. This list is called Gossip, which
|
||||
is passed to other nodes in that cluster. Gossip helps individual
|
||||
servers in the cluster identify IP addresses that might be unduly loading
|
||||
servers in the cluster identify IP addreses that might be unduly loading
|
||||
the entire cluster. Again the recourse of the individual servers is to
|
||||
drop connections to those IP addresses that occur commonly in the gossip.
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ private:
|
||||
// List of all active admin entries
|
||||
EntryIntrusiveList admin_;
|
||||
|
||||
// List of all inactive entries
|
||||
// List of all inactve entries
|
||||
EntryIntrusiveList inactive_;
|
||||
|
||||
// All imported gossip data
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Before boost 1.70, get_lowest_layer required an explicit template parameter
|
||||
// Before boost 1.70, get_lowest_layer required an explicit templat parameter
|
||||
template <class T>
|
||||
decltype(auto)
|
||||
get_lowest_layer(T& t) noexcept
|
||||
|
||||
@@ -226,7 +226,7 @@ The `fetchNodeNT()` method goes through three phases:
|
||||
|
||||
Any SHAMapLeafNode that is immutable has a sequence number of zero
|
||||
(sharable). When a mutable `SHAMap` is created then its SHAMapTreeNodes are
|
||||
given non-zero sequence numbers (unshareable). But all nodes in the
|
||||
given non-zero sequence numbers (unsharable). But all nodes in the
|
||||
TreeNodeCache are immutable, so if one is found here, its sequence number
|
||||
will be 0.
|
||||
|
||||
|
||||
@@ -125,13 +125,13 @@ intrusive_ptr_release(SHAMapItem const* x)
|
||||
{
|
||||
auto p = reinterpret_cast<std::uint8_t const*>(x);
|
||||
|
||||
// The SHAMapItem constructor isn't trivial (because the destructor
|
||||
// The SHAMapItem constuctor isn't trivial (because the destructor
|
||||
// for CountedObject isn't) so we can't avoid calling it here, but
|
||||
// plan for a future where we might not need to.
|
||||
if constexpr (!std::is_trivially_destructible_v<SHAMapItem>)
|
||||
std::destroy_at(x);
|
||||
|
||||
// If the slabber doesn't claim this pointer, it was allocated
|
||||
// If the slabber doens't claim this pointer, it was allocated
|
||||
// manually, so we free it manually.
|
||||
if (!detail::slabber.deallocate(const_cast<std::uint8_t*>(p)))
|
||||
delete[] p;
|
||||
|
||||
73
src/doctest/CMakeLists.txt
Normal file
73
src/doctest/CMakeLists.txt
Normal file
@@ -0,0 +1,73 @@
|
||||
# CMake configuration for doctest-based tests
|
||||
# These are converted from the beast unit_test framework
|
||||
|
||||
include(XrplAddTest)
|
||||
|
||||
find_package(doctest REQUIRED)
|
||||
|
||||
# Custom target for all doctest tests defined in this file
|
||||
add_custom_target(xrpl.doctests)
|
||||
|
||||
# Common library dependencies
|
||||
add_library(xrpl.imports.doctest INTERFACE)
|
||||
target_link_libraries(xrpl.imports.doctest INTERFACE
|
||||
doctest::doctest
|
||||
xrpl.libxrpl
|
||||
)
|
||||
|
||||
# Include xrpld sources for tests that need app-level functionality
|
||||
target_include_directories(xrpl.imports.doctest INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
||||
)
|
||||
|
||||
# Link against xrpld libraries for tests that need app-level functionality
|
||||
target_link_libraries(xrpl.imports.doctest INTERFACE
|
||||
Xrpl::boost
|
||||
Xrpl::opts
|
||||
Xrpl::libs
|
||||
)
|
||||
|
||||
# Compiler flags for strict checking
|
||||
set(DOCTEST_COMPILE_FLAGS
|
||||
-m64
|
||||
-g
|
||||
-fPIE
|
||||
-Wno-unknown-warning-option
|
||||
-Wall
|
||||
-Wdeprecated
|
||||
-Wno-deprecated-declarations
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-Werror
|
||||
-fstack-protector
|
||||
-Wno-sign-compare
|
||||
-Wno-unused-but-set-variable
|
||||
)
|
||||
|
||||
# Helper function to add a doctest module
|
||||
function(xrpl_add_doctest name)
|
||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
|
||||
)
|
||||
|
||||
set(target xrpl.doctest.${name})
|
||||
add_executable(${target} ${sources})
|
||||
target_link_libraries(${target} PRIVATE xrpl.imports.doctest)
|
||||
target_compile_options(${target} PRIVATE ${DOCTEST_COMPILE_FLAGS})
|
||||
|
||||
set_target_properties(${target} PROPERTIES
|
||||
UNITY_BUILD_MODE GROUP
|
||||
UNITY_BUILD_BATCH_SIZE 0
|
||||
)
|
||||
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
add_dependencies(xrpl.doctests ${target})
|
||||
endfunction()
|
||||
|
||||
# One test executable for each module
|
||||
xrpl_add_doctest(basics)
|
||||
xrpl_add_doctest(beast)
|
||||
xrpl_add_doctest(core)
|
||||
xrpl_add_doctest(csf)
|
||||
xrpl_add_doctest(nodestore)
|
||||
xrpl_add_doctest(protocol)
|
||||
171
src/doctest/MIGRATION.md
Normal file
171
src/doctest/MIGRATION.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Doctest Migration Documentation
|
||||
|
||||
This document describes the migration of unit tests from the beast `unit_test` framework to the doctest framework.
|
||||
|
||||
## Overview
|
||||
|
||||
Tests were migrated from `src/test/` (beast unit_test format) to `src/doctest/` (doctest format), following the pattern established in `src/tests/libxrpl/`.
|
||||
|
||||
## Build Configuration
|
||||
|
||||
### CMakeLists.txt Structure
|
||||
|
||||
Created `src/doctest/CMakeLists.txt` with:
|
||||
- Helper function `xrpl_add_doctest(name)` that creates per-module executables
|
||||
- Compiler flags: `-m64 -g -std=c++20 -fPIE -Wno-unknown-warning-option -Wall -Wdeprecated -Wno-deprecated-declarations -Wextra -Wno-unused-parameter -Werror -fstack-protector -Wno-sign-compare -Wno-unused-but-set-variable -MD -MT -MF`
|
||||
- Six module targets: `xrpl.doctest.basics`, `xrpl.doctest.beast`, `xrpl.doctest.core`, `xrpl.doctest.csf`, `xrpl.doctest.nodestore`, `xrpl.doctest.protocol`
|
||||
|
||||
### Module Structure
|
||||
|
||||
Each module has its own `main.cpp` following the pre-migrated test pattern:
|
||||
```
|
||||
src/doctest/
|
||||
├── basics/main.cpp
|
||||
├── beast/main.cpp
|
||||
├── core/main.cpp
|
||||
├── csf/main.cpp
|
||||
├── nodestore/main.cpp
|
||||
└── protocol/main.cpp
|
||||
```
|
||||
|
||||
## Framework Conversion Patterns
|
||||
|
||||
| Beast Unit Test | Doctest Equivalent |
|
||||
|-----------------|-------------------|
|
||||
| `#include <xrpl/beast/unit_test.h>` | `#include <doctest/doctest.h>` |
|
||||
| `BEAST_EXPECT(expr)` | `CHECK(expr)` |
|
||||
| `BEAST_EXPECTS(expr, msg)` | `CHECK_MESSAGE(expr, msg)` |
|
||||
| `testcase("name")` | `SUBCASE("name")` |
|
||||
| `class X : public unit_test::suite { ... }` | Free functions with `TEST_CASE` |
|
||||
| `BEAST_DEFINE_TESTSUITE(Name, Module, Lib)` | `TEST_CASE("Name")` |
|
||||
| `pass()` / `fail()` | `CHECK(true)` / `CHECK(false)` |
|
||||
|
||||
## Namespace Changes
|
||||
|
||||
- Changed from `namespace ripple` to `namespace xrpl` where applicable
|
||||
- Header paths changed from `xrpld/` to `xrpl/` (e.g., `xrpld/app/` → `xrpl/protocol/`)
|
||||
|
||||
## Common Migration Issues and Solutions
|
||||
|
||||
### 1. CHECK Macro with Complex Expressions
|
||||
|
||||
**Problem**: Doctest's CHECK macro doesn't support `&&` or `||` in expressions.
|
||||
|
||||
```cpp
|
||||
// Doesn't work
|
||||
CHECK(a && b);
|
||||
|
||||
// Solution: Split into separate checks
|
||||
CHECK(a);
|
||||
CHECK(b);
|
||||
```
|
||||
|
||||
### 2. CHECK Macro with Custom Iterator Comparisons
|
||||
|
||||
**Problem**: CHECK wraps expressions in `Expression_lhs<>` which breaks template argument deduction for custom comparison operators (especially boost::intrusive iterators).
|
||||
|
||||
```cpp
|
||||
// Doesn't compile
|
||||
CHECK(iter != container.end());
|
||||
|
||||
// Solution: Store result in bool first
|
||||
bool notEnd = (iter != container.end());
|
||||
CHECK(notEnd);
|
||||
```
|
||||
|
||||
### 3. Constructor Argument Order
|
||||
|
||||
Some container constructors have different argument order than initially assumed:
|
||||
|
||||
```cpp
|
||||
// Wrong order
|
||||
Container c(clock, first, last);
|
||||
|
||||
// Correct order (iterators before clock)
|
||||
Container c(first, last, clock);
|
||||
```
|
||||
|
||||
### 4. Return Type Differences
|
||||
|
||||
For map types with `P&&` insert overloads, return types differ:
|
||||
|
||||
```cpp
|
||||
// Use if constexpr to handle both cases
|
||||
if constexpr (!IsMulti && IsMap)
|
||||
{
|
||||
auto result = c.insert(c.end(), value); // returns pair<iterator, bool>
|
||||
CHECK(result.first != c.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = c.insert(c.end(), value); // returns iterator
|
||||
CHECK(it != c.end());
|
||||
}
|
||||
```
|
||||
|
||||
## Files Migrated
|
||||
|
||||
### basics/ (13 files)
|
||||
- Buffer.cpp, Expected.cpp, IOUAmount.cpp, KeyCache.cpp, Number.cpp
|
||||
- StringUtilities.cpp, TaggedCache.cpp, Units.cpp, XRPAmount.cpp
|
||||
- base58.cpp, base_uint.cpp, hardened_hash.cpp, join.cpp
|
||||
|
||||
### beast/ (11 files)
|
||||
- CurrentThreadName.cpp, IPEndpoint.cpp, Journal.cpp, LexicalCast.cpp
|
||||
- PropertyStream.cpp, SemanticVersion.cpp, aged_associative_container.cpp
|
||||
- basic_seconds_clock.cpp, beast_Zero.cpp, xxhasher.cpp
|
||||
|
||||
### core/ (1 file)
|
||||
- Workers.cpp
|
||||
|
||||
### csf/ (4 files)
|
||||
- BasicNetwork.cpp, Digraph.cpp, Histogram.cpp, Scheduler.cpp
|
||||
|
||||
### nodestore/ (1 file)
|
||||
- varint.cpp
|
||||
|
||||
### protocol/ (14 files)
|
||||
- ApiVersion.cpp, BuildInfo.cpp, Issue.cpp, MultiApiJson.cpp
|
||||
- PublicKey.cpp, Quality.cpp, STAccount.cpp, STInteger.cpp
|
||||
- STNumber.cpp, SecretKey.cpp, Seed.cpp, SeqProxy.cpp
|
||||
- Serializer.cpp, TER.cpp
|
||||
|
||||
## Test Results Summary
|
||||
|
||||
| Module | Test Cases | Assertions |
|
||||
|--------|------------|------------|
|
||||
| basics | 61 | 2,638,582 |
|
||||
| beast | 48 | 162,715 |
|
||||
| core | 6 | 66 |
|
||||
| csf | 8 | 101 |
|
||||
| nodestore | 1 | 68 |
|
||||
| protocol | 73 | 20,372 |
|
||||
| **Total** | **197** | **2,821,904** |
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Build all doctest targets
|
||||
cd .build
|
||||
cmake --build . --target xrpl.doctests
|
||||
|
||||
# Run individual module
|
||||
./src/doctest/xrpl.doctest.basics
|
||||
./src/doctest/xrpl.doctest.beast
|
||||
./src/doctest/xrpl.doctest.core
|
||||
./src/doctest/xrpl.doctest.csf
|
||||
./src/doctest/xrpl.doctest.nodestore
|
||||
./src/doctest/xrpl.doctest.protocol
|
||||
|
||||
# Run all tests
|
||||
for test in src/doctest/xrpl.doctest.*; do ./$test; done
|
||||
```
|
||||
|
||||
## Tests Not Migrated
|
||||
|
||||
Some tests were not migrated due to dependencies:
|
||||
- **Manual tests** requiring user interaction (e.g., `DetectCrash_test`)
|
||||
- **Tests using xrpld infrastructure** (`test/jtx.h`, `unit_test/SuiteJournal.h`)
|
||||
- **Complex async tests** using boost coroutines
|
||||
- **Tests with FileDirGuard** or other test-specific utilities
|
||||
|
||||
257
src/doctest/basics/Buffer.cpp
Normal file
257
src/doctest/basics/Buffer.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
namespace {
|
||||
bool
|
||||
sane(Buffer const& b)
|
||||
{
|
||||
if (b.size() == 0)
|
||||
return b.data() == nullptr;
|
||||
|
||||
return b.data() != nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_SUITE_BEGIN("Buffer");
|
||||
|
||||
TEST_CASE("basic operations")
|
||||
{
|
||||
std::uint8_t const data[] = {
|
||||
0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a,
|
||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||
|
||||
Buffer b0;
|
||||
CHECK(sane(b0));
|
||||
CHECK(b0.empty());
|
||||
|
||||
Buffer b1{0};
|
||||
CHECK(sane(b1));
|
||||
CHECK(b1.empty());
|
||||
std::memcpy(b1.alloc(16), data, 16);
|
||||
CHECK(sane(b1));
|
||||
CHECK(!b1.empty());
|
||||
CHECK(b1.size() == 16);
|
||||
|
||||
Buffer b2{b1.size()};
|
||||
CHECK(sane(b2));
|
||||
CHECK(!b2.empty());
|
||||
CHECK(b2.size() == b1.size());
|
||||
std::memcpy(b2.data(), data + 16, 16);
|
||||
|
||||
Buffer b3{data, sizeof(data)};
|
||||
CHECK(sane(b3));
|
||||
CHECK(!b3.empty());
|
||||
CHECK(b3.size() == sizeof(data));
|
||||
CHECK(std::memcmp(b3.data(), data, b3.size()) == 0);
|
||||
|
||||
// Check equality and inequality comparisons
|
||||
CHECK(b0 == b0);
|
||||
CHECK(b0 != b1);
|
||||
CHECK(b1 == b1);
|
||||
CHECK(b1 != b2);
|
||||
CHECK(b2 != b3);
|
||||
|
||||
SUBCASE("Copy Construction / Assignment")
|
||||
{
|
||||
Buffer x{b0};
|
||||
CHECK(x == b0);
|
||||
CHECK(sane(x));
|
||||
Buffer y{b1};
|
||||
CHECK(y == b1);
|
||||
CHECK(sane(y));
|
||||
x = b2;
|
||||
CHECK(x == b2);
|
||||
CHECK(sane(x));
|
||||
x = y;
|
||||
CHECK(x == y);
|
||||
CHECK(sane(x));
|
||||
y = b3;
|
||||
CHECK(y == b3);
|
||||
CHECK(sane(y));
|
||||
x = b0;
|
||||
CHECK(x == b0);
|
||||
CHECK(sane(x));
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
|
||||
x = x;
|
||||
CHECK(x == b0);
|
||||
CHECK(sane(x));
|
||||
y = y;
|
||||
CHECK(y == b3);
|
||||
CHECK(sane(y));
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
SUBCASE("Move Construction / Assignment")
|
||||
{
|
||||
static_assert(std::is_nothrow_move_constructible<Buffer>::value, "");
|
||||
static_assert(std::is_nothrow_move_assignable<Buffer>::value, "");
|
||||
|
||||
{ // Move-construct from empty buf
|
||||
Buffer x;
|
||||
Buffer y{std::move(x)};
|
||||
CHECK(sane(x));
|
||||
CHECK(x.empty());
|
||||
CHECK(sane(y));
|
||||
CHECK(y.empty());
|
||||
CHECK(x == y);
|
||||
}
|
||||
|
||||
{ // Move-construct from non-empty buf
|
||||
Buffer x{b1};
|
||||
Buffer y{std::move(x)};
|
||||
CHECK(sane(x));
|
||||
CHECK(x.empty());
|
||||
CHECK(sane(y));
|
||||
CHECK(y == b1);
|
||||
}
|
||||
|
||||
{ // Move assign empty buf to empty buf
|
||||
Buffer x;
|
||||
Buffer y;
|
||||
|
||||
x = std::move(y);
|
||||
CHECK(sane(x));
|
||||
CHECK(x.empty());
|
||||
CHECK(sane(y));
|
||||
CHECK(y.empty());
|
||||
}
|
||||
|
||||
{ // Move assign non-empty buf to empty buf
|
||||
Buffer x;
|
||||
Buffer y{b1};
|
||||
|
||||
x = std::move(y);
|
||||
CHECK(sane(x));
|
||||
CHECK(x == b1);
|
||||
CHECK(sane(y));
|
||||
CHECK(y.empty());
|
||||
}
|
||||
|
||||
{ // Move assign empty buf to non-empty buf
|
||||
Buffer x{b1};
|
||||
Buffer y;
|
||||
|
||||
x = std::move(y);
|
||||
CHECK(sane(x));
|
||||
CHECK(x.empty());
|
||||
CHECK(sane(y));
|
||||
CHECK(y.empty());
|
||||
}
|
||||
|
||||
{ // Move assign non-empty buf to non-empty buf
|
||||
Buffer x{b1};
|
||||
Buffer y{b2};
|
||||
Buffer z{b3};
|
||||
|
||||
x = std::move(y);
|
||||
CHECK(sane(x));
|
||||
CHECK(!x.empty());
|
||||
CHECK(sane(y));
|
||||
CHECK(y.empty());
|
||||
|
||||
x = std::move(z);
|
||||
CHECK(sane(x));
|
||||
CHECK(!x.empty());
|
||||
CHECK(sane(z));
|
||||
CHECK(z.empty());
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Slice Conversion / Construction / Assignment")
|
||||
{
|
||||
Buffer w{static_cast<Slice>(b0)};
|
||||
CHECK(sane(w));
|
||||
CHECK(w == b0);
|
||||
|
||||
Buffer x{static_cast<Slice>(b1)};
|
||||
CHECK(sane(x));
|
||||
CHECK(x == b1);
|
||||
|
||||
Buffer y{static_cast<Slice>(b2)};
|
||||
CHECK(sane(y));
|
||||
CHECK(y == b2);
|
||||
|
||||
Buffer z{static_cast<Slice>(b3)};
|
||||
CHECK(sane(z));
|
||||
CHECK(z == b3);
|
||||
|
||||
// Assign empty slice to empty buffer
|
||||
w = static_cast<Slice>(b0);
|
||||
CHECK(sane(w));
|
||||
CHECK(w == b0);
|
||||
|
||||
// Assign non-empty slice to empty buffer
|
||||
w = static_cast<Slice>(b1);
|
||||
CHECK(sane(w));
|
||||
CHECK(w == b1);
|
||||
|
||||
// Assign non-empty slice to non-empty buffer
|
||||
x = static_cast<Slice>(b2);
|
||||
CHECK(sane(x));
|
||||
CHECK(x == b2);
|
||||
|
||||
// Assign non-empty slice to non-empty buffer
|
||||
y = static_cast<Slice>(z);
|
||||
CHECK(sane(y));
|
||||
CHECK(y == z);
|
||||
|
||||
// Assign empty slice to non-empty buffer:
|
||||
z = static_cast<Slice>(b0);
|
||||
CHECK(sane(z));
|
||||
CHECK(z == b0);
|
||||
}
|
||||
|
||||
SUBCASE("Allocation, Deallocation and Clearing")
|
||||
{
|
||||
auto test = [](Buffer const& b, std::size_t i) {
|
||||
Buffer x{b};
|
||||
|
||||
// Try to allocate some number of bytes, possibly
|
||||
// zero (which means clear) and sanity check
|
||||
x(i);
|
||||
CHECK(sane(x));
|
||||
CHECK(x.size() == i);
|
||||
CHECK((x.data() == nullptr) == (i == 0));
|
||||
|
||||
// Try to allocate some more data (always non-zero)
|
||||
x(i + 1);
|
||||
CHECK(sane(x));
|
||||
CHECK(x.size() == i + 1);
|
||||
CHECK(x.data() != nullptr);
|
||||
|
||||
// Try to clear:
|
||||
x.clear();
|
||||
CHECK(sane(x));
|
||||
CHECK(x.size() == 0);
|
||||
CHECK(x.data() == nullptr);
|
||||
|
||||
// Try to clear again:
|
||||
x.clear();
|
||||
CHECK(sane(x));
|
||||
CHECK(x.size() == 0);
|
||||
CHECK(x.data() == nullptr);
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
test(b0, i);
|
||||
test(b1, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
228
src/doctest/basics/Expected.cpp
Normal file
228
src/doctest/basics/Expected.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#if BOOST_VERSION >= 107500
|
||||
#include <boost/json.hpp> // Not part of boost before version 1.75
|
||||
#endif // BOOST_VERSION
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("Expected");
|
||||
|
||||
TEST_CASE("non-error const construction")
|
||||
{
|
||||
auto const expected = []() -> Expected<std::string, TER> {
|
||||
return "Valid value";
|
||||
}();
|
||||
CHECK(expected);
|
||||
CHECK(expected.has_value());
|
||||
CHECK(expected.value() == "Valid value");
|
||||
CHECK(*expected == "Valid value");
|
||||
CHECK(expected->at(0) == 'V');
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] TER const t = expected.error();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("non-error non-const construction")
|
||||
{
|
||||
auto expected = []() -> Expected<std::string, TER> {
|
||||
return "Valid value";
|
||||
}();
|
||||
CHECK(expected);
|
||||
CHECK(expected.has_value());
|
||||
CHECK(expected.value() == "Valid value");
|
||||
CHECK(*expected == "Valid value");
|
||||
CHECK(expected->at(0) == 'V');
|
||||
std::string mv = std::move(*expected);
|
||||
CHECK(mv == "Valid value");
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] TER const t = expected.error();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("non-error overlapping type construction")
|
||||
{
|
||||
auto expected = []() -> Expected<std::uint32_t, std::uint16_t> {
|
||||
return 1;
|
||||
}();
|
||||
CHECK(expected);
|
||||
CHECK(expected.has_value());
|
||||
CHECK(expected.value() == 1);
|
||||
CHECK(*expected == 1);
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] std::uint16_t const t = expected.error();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("error construction from rvalue")
|
||||
{
|
||||
auto const expected = []() -> Expected<std::string, TER> {
|
||||
return Unexpected(telLOCAL_ERROR);
|
||||
}();
|
||||
CHECK(!expected);
|
||||
CHECK(!expected.has_value());
|
||||
CHECK(expected.error() == telLOCAL_ERROR);
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no result, so should throw.
|
||||
[[maybe_unused]] std::string const s = *expected;
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("error construction from lvalue")
|
||||
{
|
||||
auto const err(telLOCAL_ERROR);
|
||||
auto expected = [&err]() -> Expected<std::string, TER> {
|
||||
return Unexpected(err);
|
||||
}();
|
||||
CHECK(!expected);
|
||||
CHECK(!expected.has_value());
|
||||
CHECK(expected.error() == telLOCAL_ERROR);
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no result, so should throw.
|
||||
[[maybe_unused]] std::size_t const s = expected->size();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("error construction from const char*")
|
||||
{
|
||||
auto const expected = []() -> Expected<int, char const*> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
CHECK(!expected);
|
||||
CHECK(!expected.has_value());
|
||||
CHECK(expected.error() == std::string("Not what is expected!"));
|
||||
}
|
||||
|
||||
TEST_CASE("error construction of string from const char*")
|
||||
{
|
||||
auto expected = []() -> Expected<int, std::string> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
CHECK(!expected);
|
||||
CHECK(!expected.has_value());
|
||||
CHECK(expected.error() == "Not what is expected!");
|
||||
std::string const s(std::move(expected.error()));
|
||||
CHECK(s == "Not what is expected!");
|
||||
}
|
||||
|
||||
TEST_CASE("non-error const construction of Expected<void, T>")
|
||||
{
|
||||
auto const expected = []() -> Expected<void, std::string> { return {}; }();
|
||||
CHECK(expected);
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] std::size_t const s = expected.error().size();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("non-error non-const construction of Expected<void, T>")
|
||||
{
|
||||
auto expected = []() -> Expected<void, std::string> { return {}; }();
|
||||
CHECK(expected);
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] std::size_t const s = expected.error().size();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
CHECK(throwOccurred);
|
||||
}
|
||||
|
||||
TEST_CASE("error const construction of Expected<void, T>")
|
||||
{
|
||||
auto const expected = []() -> Expected<void, std::string> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
CHECK(!expected);
|
||||
CHECK(expected.error() == "Not what is expected!");
|
||||
}
|
||||
|
||||
TEST_CASE("error non-const construction of Expected<void, T>")
|
||||
{
|
||||
auto expected = []() -> Expected<void, std::string> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
CHECK(!expected);
|
||||
CHECK(expected.error() == "Not what is expected!");
|
||||
std::string const s(std::move(expected.error()));
|
||||
CHECK(s == "Not what is expected!");
|
||||
}
|
||||
|
||||
#if BOOST_VERSION >= 107500
|
||||
TEST_CASE("boost::json::value construction")
|
||||
{
|
||||
auto expected = []() -> Expected<boost::json::value, std::string> {
|
||||
return boost::json::object{{"oops", "me array now"}};
|
||||
}();
|
||||
CHECK(expected);
|
||||
CHECK(!expected.value().is_array());
|
||||
}
|
||||
#endif // BOOST_VERSION
|
||||
|
||||
TEST_SUITE_END();
|
||||
220
src/doctest/basics/IOUAmount.cpp
Normal file
220
src/doctest/basics/IOUAmount.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("IOUAmount");
|
||||
|
||||
TEST_CASE("zero")
|
||||
{
|
||||
IOUAmount const z(0, 0);
|
||||
|
||||
CHECK(z.mantissa() == 0);
|
||||
CHECK(z.exponent() == -100);
|
||||
CHECK(!z);
|
||||
CHECK(z.signum() == 0);
|
||||
CHECK(z == beast::zero);
|
||||
|
||||
CHECK((z + z) == z);
|
||||
CHECK((z - z) == z);
|
||||
CHECK(z == -z);
|
||||
|
||||
IOUAmount const zz(beast::zero);
|
||||
CHECK(z == zz);
|
||||
|
||||
// https://github.com/XRPLF/rippled/issues/5170
|
||||
IOUAmount const zzz{};
|
||||
CHECK(zzz == beast::zero);
|
||||
}
|
||||
|
||||
TEST_CASE("signum")
|
||||
{
|
||||
IOUAmount const neg(-1, 0);
|
||||
CHECK(neg.signum() < 0);
|
||||
|
||||
IOUAmount const zer(0, 0);
|
||||
CHECK(zer.signum() == 0);
|
||||
|
||||
IOUAmount const pos(1, 0);
|
||||
CHECK(pos.signum() > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("beast::Zero Comparisons")
|
||||
{
|
||||
using beast::zero;
|
||||
|
||||
{
|
||||
IOUAmount z(zero);
|
||||
CHECK(z == zero);
|
||||
CHECK(z >= zero);
|
||||
CHECK(z <= zero);
|
||||
CHECK(!(z != zero));
|
||||
CHECK(!(z > zero));
|
||||
CHECK(!(z < zero));
|
||||
}
|
||||
|
||||
{
|
||||
IOUAmount const neg(-2, 0);
|
||||
CHECK(neg < zero);
|
||||
CHECK(neg <= zero);
|
||||
CHECK(neg != zero);
|
||||
CHECK(!(neg == zero));
|
||||
}
|
||||
|
||||
{
|
||||
IOUAmount const pos(2, 0);
|
||||
CHECK(pos > zero);
|
||||
CHECK(pos >= zero);
|
||||
CHECK(pos != zero);
|
||||
CHECK(!(pos == zero));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("IOU Comparisons")
|
||||
{
|
||||
IOUAmount const n(-2, 0);
|
||||
IOUAmount const z(0, 0);
|
||||
IOUAmount const p(2, 0);
|
||||
|
||||
CHECK(z == z);
|
||||
CHECK(z >= z);
|
||||
CHECK(z <= z);
|
||||
CHECK(z == -z);
|
||||
CHECK(!(z > z));
|
||||
CHECK(!(z < z));
|
||||
CHECK(!(z != z));
|
||||
CHECK(!(z != -z));
|
||||
|
||||
CHECK(n < z);
|
||||
CHECK(n <= z);
|
||||
CHECK(n != z);
|
||||
CHECK(!(n > z));
|
||||
CHECK(!(n >= z));
|
||||
CHECK(!(n == z));
|
||||
|
||||
CHECK(p > z);
|
||||
CHECK(p >= z);
|
||||
CHECK(p != z);
|
||||
CHECK(!(p < z));
|
||||
CHECK(!(p <= z));
|
||||
CHECK(!(p == z));
|
||||
|
||||
CHECK(n < p);
|
||||
CHECK(n <= p);
|
||||
CHECK(n != p);
|
||||
CHECK(!(n > p));
|
||||
CHECK(!(n >= p));
|
||||
CHECK(!(n == p));
|
||||
|
||||
CHECK(p > n);
|
||||
CHECK(p >= n);
|
||||
CHECK(p != n);
|
||||
CHECK(!(p < n));
|
||||
CHECK(!(p <= n));
|
||||
CHECK(!(p == n));
|
||||
|
||||
CHECK(p > -p);
|
||||
CHECK(p >= -p);
|
||||
CHECK(p != -p);
|
||||
|
||||
CHECK(n < -n);
|
||||
CHECK(n <= -n);
|
||||
CHECK(n != -n);
|
||||
}
|
||||
|
||||
TEST_CASE("IOU strings")
|
||||
{
|
||||
CHECK(to_string(IOUAmount(-2, 0)) == "-2");
|
||||
CHECK(to_string(IOUAmount(0, 0)) == "0");
|
||||
CHECK(to_string(IOUAmount(2, 0)) == "2");
|
||||
CHECK(to_string(IOUAmount(25, -3)) == "0.025");
|
||||
CHECK(to_string(IOUAmount(-25, -3)) == "-0.025");
|
||||
CHECK(to_string(IOUAmount(25, 1)) == "250");
|
||||
CHECK(to_string(IOUAmount(-25, 1)) == "-250");
|
||||
CHECK(to_string(IOUAmount(2, 20)) == "2000000000000000e5");
|
||||
CHECK(to_string(IOUAmount(-2, -20)) == "-2000000000000000e-35");
|
||||
}
|
||||
|
||||
TEST_CASE("mulRatio")
|
||||
{
|
||||
/* The range for the mantissa when normalized */
|
||||
constexpr std::int64_t minMantissa = 1000000000000000ull;
|
||||
constexpr std::int64_t maxMantissa = 9999999999999999ull;
|
||||
/* The range for the exponent when normalized */
|
||||
constexpr int minExponent = -96;
|
||||
constexpr int maxExponent = 80;
|
||||
constexpr auto maxUInt = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
{
|
||||
// multiply by a number that would overflow the mantissa, then
|
||||
// divide by the same number, and check we didn't lose any value
|
||||
IOUAmount bigMan(maxMantissa, 0);
|
||||
CHECK(bigMan == mulRatio(bigMan, maxUInt, maxUInt, true));
|
||||
// rounding mode shouldn't matter as the result is exact
|
||||
CHECK(bigMan == mulRatio(bigMan, maxUInt, maxUInt, false));
|
||||
}
|
||||
{
|
||||
// Similar test as above, but for negative values
|
||||
IOUAmount bigMan(-maxMantissa, 0);
|
||||
CHECK(bigMan == mulRatio(bigMan, maxUInt, maxUInt, true));
|
||||
// rounding mode shouldn't matter as the result is exact
|
||||
CHECK(bigMan == mulRatio(bigMan, maxUInt, maxUInt, false));
|
||||
}
|
||||
|
||||
{
|
||||
// small amounts
|
||||
IOUAmount tiny(minMantissa, minExponent);
|
||||
// Round up should give the smallest allowable number
|
||||
CHECK(tiny == mulRatio(tiny, 1, maxUInt, true));
|
||||
CHECK(tiny == mulRatio(tiny, maxUInt - 1, maxUInt, true));
|
||||
// rounding down should be zero
|
||||
CHECK(beast::zero == mulRatio(tiny, 1, maxUInt, false));
|
||||
CHECK(beast::zero == mulRatio(tiny, maxUInt - 1, maxUInt, false));
|
||||
|
||||
// tiny negative numbers
|
||||
IOUAmount tinyNeg(-minMantissa, minExponent);
|
||||
// Round up should give zero
|
||||
CHECK(beast::zero == mulRatio(tinyNeg, 1, maxUInt, true));
|
||||
CHECK(beast::zero == mulRatio(tinyNeg, maxUInt - 1, maxUInt, true));
|
||||
// rounding down should be tiny
|
||||
CHECK(tinyNeg == mulRatio(tinyNeg, 1, maxUInt, false));
|
||||
CHECK(tinyNeg == mulRatio(tinyNeg, maxUInt - 1, maxUInt, false));
|
||||
}
|
||||
|
||||
{ // rounding
|
||||
{
|
||||
IOUAmount one(1, 0);
|
||||
auto const rup = mulRatio(one, maxUInt - 1, maxUInt, true);
|
||||
auto const rdown = mulRatio(one, maxUInt - 1, maxUInt, false);
|
||||
CHECK(rup.mantissa() - rdown.mantissa() == 1);
|
||||
}
|
||||
{
|
||||
IOUAmount big(maxMantissa, maxExponent);
|
||||
auto const rup = mulRatio(big, maxUInt - 1, maxUInt, true);
|
||||
auto const rdown = mulRatio(big, maxUInt - 1, maxUInt, false);
|
||||
CHECK(rup.mantissa() - rdown.mantissa() == 1);
|
||||
}
|
||||
|
||||
{
|
||||
IOUAmount negOne(-1, 0);
|
||||
auto const rup = mulRatio(negOne, maxUInt - 1, maxUInt, true);
|
||||
auto const rdown = mulRatio(negOne, maxUInt - 1, maxUInt, false);
|
||||
CHECK(rup.mantissa() - rdown.mantissa() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// division by zero
|
||||
IOUAmount one(1, 0);
|
||||
CHECK_THROWS([&] { mulRatio(one, 1, 0, true); }());
|
||||
}
|
||||
|
||||
{
|
||||
// overflow
|
||||
IOUAmount big(maxMantissa, maxExponent);
|
||||
CHECK_THROWS([&] { mulRatio(big, 2, 0, true); }());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
74
src/doctest/basics/KeyCache.cpp
Normal file
74
src/doctest/basics/KeyCache.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/basics/TaggedCache.ipp>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("KeyCache");
|
||||
|
||||
TEST_CASE("KeyCache operations")
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
TestStopwatch clock;
|
||||
clock.set(0);
|
||||
|
||||
using Key = std::string;
|
||||
using Cache = TaggedCache<Key, int, true>;
|
||||
|
||||
beast::Journal j{beast::Journal::getNullSink()};
|
||||
|
||||
SUBCASE("Insert, retrieve, and age item")
|
||||
{
|
||||
Cache c("test", LedgerIndex(1), 2s, clock, j);
|
||||
|
||||
CHECK(c.size() == 0);
|
||||
CHECK(c.insert("one"));
|
||||
CHECK(!c.insert("one"));
|
||||
CHECK(c.size() == 1);
|
||||
CHECK(c.touch_if_exists("one"));
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.size() == 1);
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.size() == 0);
|
||||
CHECK(!c.touch_if_exists("one"));
|
||||
}
|
||||
|
||||
SUBCASE("Insert two items, have one expire")
|
||||
{
|
||||
Cache c("test", LedgerIndex(2), 2s, clock, j);
|
||||
|
||||
CHECK(c.insert("one"));
|
||||
CHECK(c.size() == 1);
|
||||
CHECK(c.insert("two"));
|
||||
CHECK(c.size() == 2);
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.size() == 2);
|
||||
CHECK(c.touch_if_exists("two"));
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.size() == 1);
|
||||
}
|
||||
|
||||
SUBCASE("Insert three items (1 over limit), sweep")
|
||||
{
|
||||
Cache c("test", LedgerIndex(2), 3s, clock, j);
|
||||
|
||||
CHECK(c.insert("one"));
|
||||
++clock;
|
||||
CHECK(c.insert("two"));
|
||||
++clock;
|
||||
CHECK(c.insert("three"));
|
||||
++clock;
|
||||
CHECK(c.size() == 3);
|
||||
c.sweep();
|
||||
CHECK(c.size() < 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
416
src/doctest/basics/Number.cpp
Normal file
416
src/doctest/basics/Number.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("Number");
|
||||
|
||||
TEST_CASE("zero")
|
||||
{
|
||||
Number const z{0, 0};
|
||||
|
||||
CHECK(z.mantissa() == 0);
|
||||
CHECK(z.exponent() == Number{}.exponent());
|
||||
|
||||
CHECK((z + z) == z);
|
||||
CHECK((z - z) == z);
|
||||
CHECK(z == -z);
|
||||
}
|
||||
|
||||
TEST_CASE("limits")
|
||||
{
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number x{10'000'000'000'000'000, 32768};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
Number x{10'000'000'000'000'000, 32767};
|
||||
CHECK((x == Number{1'000'000'000'000'000, 32768}));
|
||||
Number z{1'000'000'000'000'000, -32769};
|
||||
CHECK(z == Number{});
|
||||
Number y{1'000'000'000'000'001'500, 32000};
|
||||
CHECK((y == Number{1'000'000'000'000'002, 32003}));
|
||||
Number m{std::numeric_limits<std::int64_t>::min()};
|
||||
CHECK((m == Number{-9'223'372'036'854'776, 3}));
|
||||
Number M{std::numeric_limits<std::int64_t>::max()};
|
||||
CHECK((M == Number{9'223'372'036'854'776, 3}));
|
||||
caught = false;
|
||||
try
|
||||
{
|
||||
Number q{99'999'999'999'999'999, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
TEST_CASE("add")
|
||||
{
|
||||
using Case = std::tuple<Number, Number, Number>;
|
||||
Case c[]{
|
||||
{Number{1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'066, -15}},
|
||||
{Number{-1'000'000'000'000'000, -15},
|
||||
Number{-6'555'555'555'555'555, -29},
|
||||
Number{-1'000'000'000'000'066, -15}},
|
||||
{Number{-1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{-9'999'999'999'999'344, -16}},
|
||||
{Number{-6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{9'999'999'999'999'344, -16}},
|
||||
{Number{}, Number{5}, Number{5}},
|
||||
{Number{5'555'555'555'555'555, -32768},
|
||||
Number{-5'555'555'555'555'554, -32768},
|
||||
Number{0}},
|
||||
{Number{-9'999'999'999'999'999, -31},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{9'999'999'999'999'990, -16}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK(x + y == z);
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{9'999'999'999'999'999, 32768} +
|
||||
Number{5'000'000'000'000'000, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
TEST_CASE("sub")
|
||||
{
|
||||
using Case = std::tuple<Number, Number, Number>;
|
||||
Case c[]{
|
||||
{Number{1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{9'999'999'999'999'344, -16}},
|
||||
{Number{6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{-9'999'999'999'999'344, -16}},
|
||||
{Number{1'000'000'000'000'000, -15},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{0}},
|
||||
{Number{1'000'000'000'000'000, -15},
|
||||
Number{1'000'000'000'000'001, -15},
|
||||
Number{-1'000'000'000'000'000, -30}},
|
||||
{Number{1'000'000'000'000'001, -15},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{1'000'000'000'000'000, -30}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK(x - y == z);
|
||||
}
|
||||
|
||||
TEST_CASE("mul")
|
||||
{
|
||||
using Case = std::tuple<Number, Number, Number>;
|
||||
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
||||
{
|
||||
Case c[]{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{2000000000000000, -15}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-2000000000000000, -15}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{2000000000000000, -15}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{1000000000000000, -14}},
|
||||
{Number{1000000000000000, -32768},
|
||||
Number{1000000000000000, -32768},
|
||||
Number{0}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK(x * y == z);
|
||||
}
|
||||
Number::setround(Number::towards_zero);
|
||||
{
|
||||
Case c[]{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999, -15}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK(x * y == z);
|
||||
}
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{9'999'999'999'999'999, 32768} *
|
||||
Number{5'000'000'000'000'000, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
TEST_CASE("div")
|
||||
{
|
||||
using Case = std::tuple<Number, Number, Number>;
|
||||
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
||||
{
|
||||
Case c[]{
|
||||
{Number{1}, Number{2}, Number{5, -1}},
|
||||
{Number{1}, Number{10}, Number{1, -1}},
|
||||
{Number{1}, Number{-10}, Number{-1, -1}},
|
||||
{Number{0}, Number{100}, Number{0}},
|
||||
{Number{1414213562373095, -10},
|
||||
Number{1414213562373095, -10},
|
||||
Number{1}},
|
||||
{Number{9'999'999'999'999'999},
|
||||
Number{1'000'000'000'000'000},
|
||||
Number{9'999'999'999'999'999, -15}},
|
||||
{Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}},
|
||||
{Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK(x / y == z);
|
||||
}
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{1000000000000000, -15} / Number{0};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
TEST_CASE("root")
|
||||
{
|
||||
using Case = std::tuple<Number, unsigned, Number>;
|
||||
Case c[]{
|
||||
{Number{2}, 2, Number{1414213562373095, -15}},
|
||||
{Number{2'000'000}, 2, Number{1414213562373095, -12}},
|
||||
{Number{2, -30}, 2, Number{1414213562373095, -30}},
|
||||
{Number{-27}, 3, Number{-3}},
|
||||
{Number{1}, 5, Number{1}},
|
||||
{Number{-1}, 0, Number{1}},
|
||||
{Number{5, -1}, 0, Number{0}},
|
||||
{Number{0}, 5, Number{0}},
|
||||
{Number{5625, -4}, 2, Number{75, -2}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK((root(x, y) == z));
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
(void)root(Number{-2}, 0);
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
caught = false;
|
||||
try
|
||||
{
|
||||
(void)root(Number{-2}, 4);
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
TEST_CASE("power1")
|
||||
{
|
||||
using Case = std::tuple<Number, unsigned, Number>;
|
||||
Case c[]{
|
||||
{Number{64}, 0, Number{1}},
|
||||
{Number{64}, 1, Number{64}},
|
||||
{Number{64}, 2, Number{4096}},
|
||||
{Number{-64}, 2, Number{4096}},
|
||||
{Number{64}, 3, Number{262144}},
|
||||
{Number{-64}, 3, Number{-262144}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
CHECK((power(x, y) == z));
|
||||
}
|
||||
|
||||
TEST_CASE("power2")
|
||||
{
|
||||
using Case = std::tuple<Number, unsigned, unsigned, Number>;
|
||||
Case c[]{
|
||||
{Number{1}, 3, 7, Number{1}},
|
||||
{Number{-1}, 1, 0, Number{1}},
|
||||
{Number{-1, -1}, 1, 0, Number{0}},
|
||||
{Number{16}, 0, 5, Number{1}},
|
||||
{Number{34}, 3, 3, Number{34}},
|
||||
{Number{4}, 3, 2, Number{8}}};
|
||||
for (auto const& [x, n, d, z] : c)
|
||||
CHECK((power(x, n, d) == z));
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
(void)power(Number{7}, 0, 0);
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
caught = false;
|
||||
try
|
||||
{
|
||||
(void)power(Number{7}, 1, 0);
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
caught = false;
|
||||
try
|
||||
{
|
||||
(void)power(Number{-1, -1}, 3, 2);
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
TEST_CASE("conversions")
|
||||
{
|
||||
IOUAmount x{5, 6};
|
||||
Number y = x;
|
||||
CHECK((y == Number{5, 6}));
|
||||
IOUAmount z{y};
|
||||
CHECK(x == z);
|
||||
XRPAmount xrp{500};
|
||||
STAmount st = xrp;
|
||||
Number n = st;
|
||||
CHECK(XRPAmount{n} == xrp);
|
||||
IOUAmount x0{0, 0};
|
||||
Number y0 = x0;
|
||||
CHECK((y0 == Number{0}));
|
||||
IOUAmount z0{y0};
|
||||
CHECK(x0 == z0);
|
||||
XRPAmount xrp0{0};
|
||||
Number n0 = xrp0;
|
||||
CHECK(n0 == Number{0});
|
||||
XRPAmount xrp1{n0};
|
||||
CHECK(xrp1 == xrp0);
|
||||
}
|
||||
|
||||
TEST_CASE("squelch")
|
||||
{
|
||||
Number limit{1, -6};
|
||||
CHECK((squelch(Number{2, -6}, limit) == Number{2, -6}));
|
||||
CHECK((squelch(Number{1, -6}, limit) == Number{1, -6}));
|
||||
CHECK((squelch(Number{9, -7}, limit) == Number{0}));
|
||||
CHECK((squelch(Number{-2, -6}, limit) == Number{-2, -6}));
|
||||
CHECK((squelch(Number{-1, -6}, limit) == Number{-1, -6}));
|
||||
CHECK((squelch(Number{-9, -7}, limit) == Number{0}));
|
||||
}
|
||||
|
||||
TEST_CASE("toString")
|
||||
{
|
||||
CHECK(to_string(Number(-2, 0)) == "-2");
|
||||
CHECK(to_string(Number(0, 0)) == "0");
|
||||
CHECK(to_string(Number(2, 0)) == "2");
|
||||
CHECK(to_string(Number(25, -3)) == "0.025");
|
||||
CHECK(to_string(Number(-25, -3)) == "-0.025");
|
||||
CHECK(to_string(Number(25, 1)) == "250");
|
||||
CHECK(to_string(Number(-25, 1)) == "-250");
|
||||
CHECK(to_string(Number(2, 20)) == "2000000000000000e5");
|
||||
CHECK(to_string(Number(-2, -20)) == "-2000000000000000e-35");
|
||||
}
|
||||
|
||||
TEST_CASE("relationals")
|
||||
{
|
||||
CHECK(!(Number{100} < Number{10}));
|
||||
CHECK(Number{100} > Number{10});
|
||||
CHECK(Number{100} >= Number{10});
|
||||
CHECK(!(Number{100} <= Number{10}));
|
||||
}
|
||||
|
||||
TEST_CASE("stream")
|
||||
{
|
||||
Number x{100};
|
||||
std::ostringstream os;
|
||||
os << x;
|
||||
CHECK(os.str() == to_string(x));
|
||||
}
|
||||
|
||||
TEST_CASE("inc_dec")
|
||||
{
|
||||
Number x{100};
|
||||
Number y = +x;
|
||||
CHECK(x == y);
|
||||
CHECK(x++ == y);
|
||||
CHECK(x == Number{101});
|
||||
CHECK(x-- == Number{101});
|
||||
CHECK(x == y);
|
||||
}
|
||||
|
||||
TEST_CASE("toSTAmount")
|
||||
{
|
||||
NumberSO stNumberSO{true};
|
||||
Issue const issue;
|
||||
Number const n{7'518'783'80596, -5};
|
||||
saveNumberRoundMode const save{Number::setround(Number::to_nearest)};
|
||||
auto res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
CHECK(res2 == STAmount{7518784});
|
||||
|
||||
Number::setround(Number::towards_zero);
|
||||
res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
CHECK(res2 == STAmount{7518783});
|
||||
|
||||
Number::setround(Number::downward);
|
||||
res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
CHECK(res2 == STAmount{7518783});
|
||||
|
||||
Number::setround(Number::upward);
|
||||
res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
CHECK(res2 == STAmount{7518784});
|
||||
}
|
||||
|
||||
TEST_CASE("truncate")
|
||||
{
|
||||
CHECK(Number(25, +1).truncate() == Number(250, 0));
|
||||
CHECK(Number(25, 0).truncate() == Number(25, 0));
|
||||
CHECK(Number(25, -1).truncate() == Number(2, 0));
|
||||
CHECK(Number(25, -2).truncate() == Number(0, 0));
|
||||
CHECK(Number(99, -2).truncate() == Number(0, 0));
|
||||
|
||||
CHECK(Number(-25, +1).truncate() == Number(-250, 0));
|
||||
CHECK(Number(-25, 0).truncate() == Number(-25, 0));
|
||||
CHECK(Number(-25, -1).truncate() == Number(-2, 0));
|
||||
CHECK(Number(-25, -2).truncate() == Number(0, 0));
|
||||
CHECK(Number(-99, -2).truncate() == Number(0, 0));
|
||||
|
||||
CHECK(Number(0, 0).truncate() == Number(0, 0));
|
||||
CHECK(Number(0, 30000).truncate() == Number(0, 0));
|
||||
CHECK(Number(0, -30000).truncate() == Number(0, 0));
|
||||
CHECK(Number(100, -30000).truncate() == Number(0, 0));
|
||||
CHECK(Number(100, -30000).truncate() == Number(0, 0));
|
||||
CHECK(Number(-100, -30000).truncate() == Number(0, 0));
|
||||
CHECK(Number(-100, -30000).truncate() == Number(0, 0));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
284
src/doctest/basics/StringUtilities.cpp
Normal file
284
src/doctest/basics/StringUtilities.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/basics/ToString.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
namespace {
|
||||
void
|
||||
testUnHexSuccess(std::string const& strIn, std::string const& strExpected)
|
||||
{
|
||||
auto rv = strUnHex(strIn);
|
||||
CHECK(rv);
|
||||
CHECK(makeSlice(*rv) == makeSlice(strExpected));
|
||||
}
|
||||
|
||||
void
|
||||
testUnHexFailure(std::string const& strIn)
|
||||
{
|
||||
auto rv = strUnHex(strIn);
|
||||
CHECK(!rv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_SUITE_BEGIN("StringUtilities");
|
||||
|
||||
TEST_CASE("strUnHex")
|
||||
{
|
||||
testUnHexSuccess("526970706c6544", "RippleD");
|
||||
testUnHexSuccess("A", "\n");
|
||||
testUnHexSuccess("0A", "\n");
|
||||
testUnHexSuccess("D0A", "\r\n");
|
||||
testUnHexSuccess("0D0A", "\r\n");
|
||||
testUnHexSuccess("200D0A", " \r\n");
|
||||
testUnHexSuccess("282A2B2C2D2E2F29", "(*+,-./)");
|
||||
|
||||
// Check for things which contain some or only invalid characters
|
||||
testUnHexFailure("123X");
|
||||
testUnHexFailure("V");
|
||||
testUnHexFailure("XRP");
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrl")
|
||||
{
|
||||
// Expected passes.
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain.empty());
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path.empty());
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme:///"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain.empty());
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "lower://domain"));
|
||||
CHECK(pUrl.scheme == "lower");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path.empty());
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "UPPER://domain:234/"));
|
||||
CHECK(pUrl.scheme == "upper");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(*pUrl.port == 234);
|
||||
CHECK(pUrl.path == "/");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "Mixed://domain/path"));
|
||||
CHECK(pUrl.scheme == "mixed");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/path");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://[::1]:123/path"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "::1");
|
||||
CHECK(*pUrl.port == 123);
|
||||
CHECK(pUrl.path == "/path");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://user:pass@domain:123/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username == "user");
|
||||
CHECK(pUrl.password == "pass");
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(*pUrl.port == 123);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://user@domain:123/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username == "user");
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(*pUrl.port == 123);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://:pass@domain:123/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password == "pass");
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(*pUrl.port == 123);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://domain:123/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(*pUrl.port == 123);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://user:pass@domain/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username == "user");
|
||||
CHECK(pUrl.password == "pass");
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://user@domain/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username == "user");
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://:pass@domain/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password == "pass");
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://domain/abc:321"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/abc:321");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme:///path/to/file"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain.empty());
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/path/to/file");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://user:pass@domain/path/with/an@sign"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username == "user");
|
||||
CHECK(pUrl.password == "pass");
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/path/with/an@sign");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://domain/path/with/an@sign"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "domain");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/path/with/an@sign");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "scheme://:999/"));
|
||||
CHECK(pUrl.scheme == "scheme");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == ":999");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/");
|
||||
}
|
||||
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(parseUrl(pUrl, "http://::1:1234/validators"));
|
||||
CHECK(pUrl.scheme == "http");
|
||||
CHECK(pUrl.username.empty());
|
||||
CHECK(pUrl.password.empty());
|
||||
CHECK(pUrl.domain == "::0.1.18.52");
|
||||
CHECK(!pUrl.port);
|
||||
CHECK(pUrl.path == "/validators");
|
||||
}
|
||||
|
||||
// Expected fails.
|
||||
{
|
||||
parsedURL pUrl;
|
||||
CHECK(!parseUrl(pUrl, ""));
|
||||
CHECK(!parseUrl(pUrl, "nonsense"));
|
||||
CHECK(!parseUrl(pUrl, "://"));
|
||||
CHECK(!parseUrl(pUrl, ":///"));
|
||||
CHECK(!parseUrl(pUrl, "scheme://user:pass@domain:65536/abc:321"));
|
||||
CHECK(!parseUrl(pUrl, "UPPER://domain:23498765/"));
|
||||
CHECK(!parseUrl(pUrl, "UPPER://domain:0/"));
|
||||
CHECK(!parseUrl(pUrl, "UPPER://domain:+7/"));
|
||||
CHECK(!parseUrl(pUrl, "UPPER://domain:-7234/"));
|
||||
CHECK(!parseUrl(pUrl, "UPPER://domain:@#$56!/"));
|
||||
}
|
||||
|
||||
{
|
||||
std::string strUrl("s://" + std::string(8192, ':'));
|
||||
parsedURL pUrl;
|
||||
CHECK(!parseUrl(pUrl, strUrl));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("toString")
|
||||
{
|
||||
auto result = to_string("hello");
|
||||
CHECK(result == "hello");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
119
src/doctest/basics/TaggedCache.cpp
Normal file
119
src/doctest/basics/TaggedCache.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/basics/TaggedCache.ipp>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("TaggedCache");
|
||||
|
||||
TEST_CASE("TaggedCache operations")
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
TestStopwatch clock;
|
||||
clock.set(0);
|
||||
|
||||
using Key = LedgerIndex;
|
||||
using Value = std::string;
|
||||
using Cache = TaggedCache<Key, Value>;
|
||||
|
||||
beast::Journal j{beast::Journal::getNullSink()};
|
||||
|
||||
Cache c("test", 1, 1s, clock, j);
|
||||
|
||||
SUBCASE("Insert, retrieve, and age item")
|
||||
{
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 0);
|
||||
CHECK(!c.insert(1, "one"));
|
||||
CHECK(c.getCacheSize() == 1);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
|
||||
{
|
||||
std::string s;
|
||||
CHECK(c.retrieve(1, s));
|
||||
CHECK(s == "one");
|
||||
}
|
||||
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Insert item, maintain strong pointer, age it")
|
||||
{
|
||||
CHECK(!c.insert(2, "two"));
|
||||
CHECK(c.getCacheSize() == 1);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
|
||||
{
|
||||
auto p = c.fetch(2);
|
||||
CHECK(p != nullptr);
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
}
|
||||
|
||||
// Make sure its gone now that our reference is gone
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Insert same key/value pair and canonicalize")
|
||||
{
|
||||
CHECK(!c.insert(3, "three"));
|
||||
|
||||
{
|
||||
auto const p1 = c.fetch(3);
|
||||
auto p2 = std::make_shared<Value>("three");
|
||||
c.canonicalize_replace_client(3, p2);
|
||||
CHECK(p1.get() == p2.get());
|
||||
}
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Put object, keep strong pointer, advance clock, canonicalize")
|
||||
{
|
||||
// Put an object in
|
||||
CHECK(!c.insert(4, "four"));
|
||||
CHECK(c.getCacheSize() == 1);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
|
||||
{
|
||||
// Keep a strong pointer to it
|
||||
auto const p1 = c.fetch(4);
|
||||
CHECK(p1 != nullptr);
|
||||
CHECK(c.getCacheSize() == 1);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
// Advance the clock a lot
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
// Canonicalize a new object with the same key
|
||||
auto p2 = std::make_shared<std::string>("four");
|
||||
CHECK(c.canonicalize_replace_client(4, p2));
|
||||
CHECK(c.getCacheSize() == 1);
|
||||
CHECK(c.getTrackSize() == 1);
|
||||
// Make sure we get the original object
|
||||
CHECK(p1.get() == p2.get());
|
||||
}
|
||||
|
||||
++clock;
|
||||
c.sweep();
|
||||
CHECK(c.getCacheSize() == 0);
|
||||
CHECK(c.getTrackSize() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
332
src/doctest/basics/Units.cpp
Normal file
332
src/doctest/basics/Units.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("Units");
|
||||
|
||||
TEST_CASE("Initial XRP")
|
||||
{
|
||||
CHECK(INITIAL_XRP.drops() == 100'000'000'000'000'000);
|
||||
CHECK(INITIAL_XRP == XRPAmount{100'000'000'000'000'000});
|
||||
}
|
||||
|
||||
TEST_CASE("Types")
|
||||
{
|
||||
using FeeLevel32 = FeeLevel<std::uint32_t>;
|
||||
|
||||
SUBCASE("XRPAmount with uint32 FeeLevel")
|
||||
{
|
||||
XRPAmount x{100};
|
||||
CHECK(x.drops() == 100);
|
||||
CHECK((std::is_same_v<decltype(x)::unit_type, unit::dropTag>));
|
||||
auto y = 4u * x;
|
||||
CHECK(y.value() == 400);
|
||||
CHECK((std::is_same_v<decltype(y)::unit_type, unit::dropTag>));
|
||||
|
||||
auto z = 4 * y;
|
||||
CHECK(z.value() == 1600);
|
||||
CHECK((std::is_same_v<decltype(z)::unit_type, unit::dropTag>));
|
||||
|
||||
FeeLevel32 f{10};
|
||||
FeeLevel32 baseFee{100};
|
||||
|
||||
auto drops = mulDiv(baseFee, x, f);
|
||||
|
||||
CHECK(drops);
|
||||
CHECK(drops.value() == 1000);
|
||||
CHECK((std::is_same_v<
|
||||
std::remove_reference_t<decltype(*drops)>::unit_type,
|
||||
unit::dropTag>));
|
||||
|
||||
CHECK((std::is_same_v<
|
||||
std::remove_reference_t<decltype(*drops)>,
|
||||
XRPAmount>));
|
||||
}
|
||||
|
||||
SUBCASE("XRPAmount with uint64 FeeLevel")
|
||||
{
|
||||
XRPAmount x{100};
|
||||
CHECK(x.value() == 100);
|
||||
CHECK((std::is_same_v<decltype(x)::unit_type, unit::dropTag>));
|
||||
auto y = 4u * x;
|
||||
CHECK(y.value() == 400);
|
||||
CHECK((std::is_same_v<decltype(y)::unit_type, unit::dropTag>));
|
||||
|
||||
FeeLevel64 f{10};
|
||||
FeeLevel64 baseFee{100};
|
||||
|
||||
auto drops = mulDiv(baseFee, x, f);
|
||||
|
||||
CHECK(drops);
|
||||
CHECK(drops.value() == 1000);
|
||||
CHECK((std::is_same_v<
|
||||
std::remove_reference_t<decltype(*drops)>::unit_type,
|
||||
unit::dropTag>));
|
||||
CHECK((std::is_same_v<
|
||||
std::remove_reference_t<decltype(*drops)>,
|
||||
XRPAmount>));
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevel64 operations")
|
||||
{
|
||||
FeeLevel64 x{1024};
|
||||
CHECK(x.value() == 1024);
|
||||
CHECK((std::is_same_v<decltype(x)::unit_type, unit::feelevelTag>));
|
||||
std::uint64_t m = 4;
|
||||
auto y = m * x;
|
||||
CHECK(y.value() == 4096);
|
||||
CHECK((std::is_same_v<decltype(y)::unit_type, unit::feelevelTag>));
|
||||
|
||||
XRPAmount basefee{10};
|
||||
FeeLevel64 referencefee{256};
|
||||
|
||||
auto drops = mulDiv(x, basefee, referencefee);
|
||||
|
||||
CHECK(drops);
|
||||
CHECK(drops.value() == 40);
|
||||
CHECK((std::is_same_v<
|
||||
std::remove_reference_t<decltype(*drops)>::unit_type,
|
||||
unit::dropTag>));
|
||||
CHECK((std::is_same_v<
|
||||
std::remove_reference_t<decltype(*drops)>,
|
||||
XRPAmount>));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Json")
|
||||
{
|
||||
using FeeLevel32 = FeeLevel<std::uint32_t>;
|
||||
|
||||
SUBCASE("FeeLevel32 max")
|
||||
{
|
||||
FeeLevel32 x{std::numeric_limits<std::uint32_t>::max()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::uintValue);
|
||||
CHECK(y == Json::Value{x.fee()});
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevel32 min")
|
||||
{
|
||||
FeeLevel32 x{std::numeric_limits<std::uint32_t>::min()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::uintValue);
|
||||
CHECK(y == Json::Value{x.fee()});
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevel64 max")
|
||||
{
|
||||
FeeLevel64 x{std::numeric_limits<std::uint64_t>::max()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::uintValue);
|
||||
CHECK(y == Json::Value{std::numeric_limits<std::uint32_t>::max()});
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevel64 min")
|
||||
{
|
||||
FeeLevel64 x{std::numeric_limits<std::uint64_t>::min()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::uintValue);
|
||||
CHECK(y == Json::Value{0});
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevelDouble max")
|
||||
{
|
||||
FeeLevelDouble x{std::numeric_limits<double>::max()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::realValue);
|
||||
CHECK(y == Json::Value{std::numeric_limits<double>::max()});
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevelDouble min")
|
||||
{
|
||||
FeeLevelDouble x{std::numeric_limits<double>::min()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::realValue);
|
||||
CHECK(y == Json::Value{std::numeric_limits<double>::min()});
|
||||
}
|
||||
|
||||
SUBCASE("XRPAmount max")
|
||||
{
|
||||
XRPAmount x{std::numeric_limits<std::int64_t>::max()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::intValue);
|
||||
CHECK(y == Json::Value{std::numeric_limits<std::int32_t>::max()});
|
||||
}
|
||||
|
||||
SUBCASE("XRPAmount min")
|
||||
{
|
||||
XRPAmount x{std::numeric_limits<std::int64_t>::min()};
|
||||
auto y = x.jsonClipped();
|
||||
CHECK(y.type() == Json::intValue);
|
||||
CHECK(y == Json::Value{std::numeric_limits<std::int32_t>::min()});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Functions")
|
||||
{
|
||||
using FeeLevel32 = FeeLevel<std::uint32_t>;
|
||||
|
||||
SUBCASE("FeeLevel64 functions")
|
||||
{
|
||||
auto make = [&](auto x) -> FeeLevel64 { return x; };
|
||||
auto explicitmake = [&](auto x) -> FeeLevel64 { return FeeLevel64{x}; };
|
||||
|
||||
[[maybe_unused]] FeeLevel64 defaulted;
|
||||
FeeLevel64 test{0};
|
||||
CHECK(test.fee() == 0);
|
||||
|
||||
test = explicitmake(beast::zero);
|
||||
CHECK(test.fee() == 0);
|
||||
|
||||
test = beast::zero;
|
||||
CHECK(test.fee() == 0);
|
||||
|
||||
test = explicitmake(100u);
|
||||
CHECK(test.fee() == 100);
|
||||
|
||||
FeeLevel64 const targetSame{200u};
|
||||
FeeLevel32 const targetOther{300u};
|
||||
test = make(targetSame);
|
||||
CHECK(test.fee() == 200);
|
||||
CHECK(test == targetSame);
|
||||
CHECK(test < FeeLevel64{1000});
|
||||
CHECK(test > FeeLevel64{100});
|
||||
test = make(targetOther);
|
||||
CHECK(test.fee() == 300);
|
||||
CHECK(test == targetOther);
|
||||
|
||||
test = std::uint64_t(200);
|
||||
CHECK(test.fee() == 200);
|
||||
test = std::uint32_t(300);
|
||||
CHECK(test.fee() == 300);
|
||||
|
||||
test = targetSame;
|
||||
CHECK(test.fee() == 200);
|
||||
test = targetOther.fee();
|
||||
CHECK(test.fee() == 300);
|
||||
CHECK(test == targetOther);
|
||||
|
||||
test = targetSame * 2;
|
||||
CHECK(test.fee() == 400);
|
||||
test = 3 * targetSame;
|
||||
CHECK(test.fee() == 600);
|
||||
test = targetSame / 10;
|
||||
CHECK(test.fee() == 20);
|
||||
|
||||
test += targetSame;
|
||||
CHECK(test.fee() == 220);
|
||||
|
||||
test -= targetSame;
|
||||
CHECK(test.fee() == 20);
|
||||
|
||||
test++;
|
||||
CHECK(test.fee() == 21);
|
||||
++test;
|
||||
CHECK(test.fee() == 22);
|
||||
test--;
|
||||
CHECK(test.fee() == 21);
|
||||
--test;
|
||||
CHECK(test.fee() == 20);
|
||||
|
||||
test *= 5;
|
||||
CHECK(test.fee() == 100);
|
||||
test /= 2;
|
||||
CHECK(test.fee() == 50);
|
||||
test %= 13;
|
||||
CHECK(test.fee() == 11);
|
||||
|
||||
CHECK(test);
|
||||
test = 0;
|
||||
CHECK(!test);
|
||||
CHECK(test.signum() == 0);
|
||||
test = targetSame;
|
||||
CHECK(test.signum() == 1);
|
||||
CHECK(to_string(test) == "200");
|
||||
}
|
||||
|
||||
SUBCASE("FeeLevelDouble functions")
|
||||
{
|
||||
auto make = [&](auto x) -> FeeLevelDouble { return x; };
|
||||
auto explicitmake = [&](auto x) -> FeeLevelDouble {
|
||||
return FeeLevelDouble{x};
|
||||
};
|
||||
|
||||
[[maybe_unused]] FeeLevelDouble defaulted;
|
||||
FeeLevelDouble test{0};
|
||||
CHECK(test.fee() == 0);
|
||||
|
||||
test = explicitmake(beast::zero);
|
||||
CHECK(test.fee() == 0);
|
||||
|
||||
test = beast::zero;
|
||||
CHECK(test.fee() == 0);
|
||||
|
||||
test = explicitmake(100.0);
|
||||
CHECK(test.fee() == 100);
|
||||
|
||||
FeeLevelDouble const targetSame{200.0};
|
||||
FeeLevel64 const targetOther{300};
|
||||
test = make(targetSame);
|
||||
CHECK(test.fee() == 200);
|
||||
CHECK(test == targetSame);
|
||||
CHECK(test < FeeLevelDouble{1000.0});
|
||||
CHECK(test > FeeLevelDouble{100.0});
|
||||
test = targetOther.fee();
|
||||
CHECK(test.fee() == 300);
|
||||
CHECK(test == targetOther);
|
||||
|
||||
test = 200.0;
|
||||
CHECK(test.fee() == 200);
|
||||
test = std::uint64_t(300);
|
||||
CHECK(test.fee() == 300);
|
||||
|
||||
test = targetSame;
|
||||
CHECK(test.fee() == 200);
|
||||
|
||||
test = targetSame * 2;
|
||||
CHECK(test.fee() == 400);
|
||||
test = 3 * targetSame;
|
||||
CHECK(test.fee() == 600);
|
||||
test = targetSame / 10;
|
||||
CHECK(test.fee() == 20);
|
||||
|
||||
test += targetSame;
|
||||
CHECK(test.fee() == 220);
|
||||
|
||||
test -= targetSame;
|
||||
CHECK(test.fee() == 20);
|
||||
|
||||
test++;
|
||||
CHECK(test.fee() == 21);
|
||||
++test;
|
||||
CHECK(test.fee() == 22);
|
||||
test--;
|
||||
CHECK(test.fee() == 21);
|
||||
--test;
|
||||
CHECK(test.fee() == 20);
|
||||
|
||||
test *= 5;
|
||||
CHECK(test.fee() == 100);
|
||||
test /= 2;
|
||||
CHECK(test.fee() == 50);
|
||||
|
||||
// legal with signed
|
||||
test = -test;
|
||||
CHECK(test.fee() == -50);
|
||||
CHECK(test.signum() == -1);
|
||||
CHECK(to_string(test) == "-50.000000");
|
||||
|
||||
CHECK(test);
|
||||
test = 0;
|
||||
CHECK(!test);
|
||||
CHECK(test.signum() == 0);
|
||||
test = targetSame;
|
||||
CHECK(test.signum() == 1);
|
||||
CHECK(to_string(test) == "200.000000");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
283
src/doctest/basics/XRPAmount.cpp
Normal file
283
src/doctest/basics/XRPAmount.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("XRPAmount");
|
||||
|
||||
TEST_CASE("signum")
|
||||
{
|
||||
for (auto i : {-1, 0, 1})
|
||||
{
|
||||
XRPAmount const x(i);
|
||||
|
||||
if (i < 0)
|
||||
CHECK(x.signum() < 0);
|
||||
else if (i > 0)
|
||||
CHECK(x.signum() > 0);
|
||||
else
|
||||
CHECK(x.signum() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("beast::Zero Comparisons")
|
||||
{
|
||||
using beast::zero;
|
||||
|
||||
for (auto i : {-1, 0, 1})
|
||||
{
|
||||
XRPAmount const x(i);
|
||||
|
||||
CHECK((i == 0) == (x == zero));
|
||||
CHECK((i != 0) == (x != zero));
|
||||
CHECK((i < 0) == (x < zero));
|
||||
CHECK((i > 0) == (x > zero));
|
||||
CHECK((i <= 0) == (x <= zero));
|
||||
CHECK((i >= 0) == (x >= zero));
|
||||
|
||||
CHECK((0 == i) == (zero == x));
|
||||
CHECK((0 != i) == (zero != x));
|
||||
CHECK((0 < i) == (zero < x));
|
||||
CHECK((0 > i) == (zero > x));
|
||||
CHECK((0 <= i) == (zero <= x));
|
||||
CHECK((0 >= i) == (zero >= x));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("XRP Comparisons")
|
||||
{
|
||||
for (auto i : {-1, 0, 1})
|
||||
{
|
||||
XRPAmount const x(i);
|
||||
|
||||
for (auto j : {-1, 0, 1})
|
||||
{
|
||||
XRPAmount const y(j);
|
||||
|
||||
CHECK((i == j) == (x == y));
|
||||
CHECK((i != j) == (x != y));
|
||||
CHECK((i < j) == (x < y));
|
||||
CHECK((i > j) == (x > y));
|
||||
CHECK((i <= j) == (x <= y));
|
||||
CHECK((i >= j) == (x >= y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Addition & Subtraction")
|
||||
{
|
||||
for (auto i : {-1, 0, 1})
|
||||
{
|
||||
XRPAmount const x(i);
|
||||
|
||||
for (auto j : {-1, 0, 1})
|
||||
{
|
||||
XRPAmount const y(j);
|
||||
|
||||
CHECK(XRPAmount(i + j) == (x + y));
|
||||
CHECK(XRPAmount(i - j) == (x - y));
|
||||
|
||||
CHECK((x + y) == (y + x)); // addition is commutative
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("decimalXRP")
|
||||
{
|
||||
// Tautology
|
||||
CHECK(DROPS_PER_XRP.decimalXRP() == 1);
|
||||
|
||||
XRPAmount test{1};
|
||||
CHECK(test.decimalXRP() == 0.000001);
|
||||
|
||||
test = -test;
|
||||
CHECK(test.decimalXRP() == -0.000001);
|
||||
|
||||
test = 100'000'000;
|
||||
CHECK(test.decimalXRP() == 100);
|
||||
|
||||
test = -test;
|
||||
CHECK(test.decimalXRP() == -100);
|
||||
}
|
||||
|
||||
TEST_CASE("functions")
|
||||
{
|
||||
// Explicitly test every defined function for the XRPAmount class
|
||||
// since some of them are templated, but not used anywhere else.
|
||||
auto make = [&](auto x) -> XRPAmount { return XRPAmount{x}; };
|
||||
|
||||
XRPAmount defaulted;
|
||||
(void)defaulted;
|
||||
XRPAmount test{0};
|
||||
CHECK(test.drops() == 0);
|
||||
|
||||
test = make(beast::zero);
|
||||
CHECK(test.drops() == 0);
|
||||
|
||||
test = beast::zero;
|
||||
CHECK(test.drops() == 0);
|
||||
|
||||
test = make(100);
|
||||
CHECK(test.drops() == 100);
|
||||
|
||||
test = make(100u);
|
||||
CHECK(test.drops() == 100);
|
||||
|
||||
XRPAmount const targetSame{200u};
|
||||
test = make(targetSame);
|
||||
CHECK(test.drops() == 200);
|
||||
CHECK(test == targetSame);
|
||||
CHECK(test < XRPAmount{1000});
|
||||
CHECK(test > XRPAmount{100});
|
||||
|
||||
test = std::int64_t(200);
|
||||
CHECK(test.drops() == 200);
|
||||
test = std::uint32_t(300);
|
||||
CHECK(test.drops() == 300);
|
||||
|
||||
test = targetSame;
|
||||
CHECK(test.drops() == 200);
|
||||
auto testOther = test.dropsAs<std::uint32_t>();
|
||||
CHECK(testOther);
|
||||
CHECK(*testOther == 200);
|
||||
test = std::numeric_limits<std::uint64_t>::max();
|
||||
testOther = test.dropsAs<std::uint32_t>();
|
||||
CHECK(!testOther);
|
||||
test = -1;
|
||||
testOther = test.dropsAs<std::uint32_t>();
|
||||
CHECK(!testOther);
|
||||
|
||||
test = targetSame * 2;
|
||||
CHECK(test.drops() == 400);
|
||||
test = 3 * targetSame;
|
||||
CHECK(test.drops() == 600);
|
||||
test = 20;
|
||||
CHECK(test.drops() == 20);
|
||||
|
||||
test += targetSame;
|
||||
CHECK(test.drops() == 220);
|
||||
|
||||
test -= targetSame;
|
||||
CHECK(test.drops() == 20);
|
||||
|
||||
test *= 5;
|
||||
CHECK(test.drops() == 100);
|
||||
test = 50;
|
||||
CHECK(test.drops() == 50);
|
||||
test -= 39;
|
||||
CHECK(test.drops() == 11);
|
||||
|
||||
// legal with signed
|
||||
test = -test;
|
||||
CHECK(test.drops() == -11);
|
||||
CHECK(test.signum() == -1);
|
||||
CHECK(to_string(test) == "-11");
|
||||
|
||||
CHECK(test);
|
||||
test = 0;
|
||||
CHECK(!test);
|
||||
CHECK(test.signum() == 0);
|
||||
test = targetSame;
|
||||
CHECK(test.signum() == 1);
|
||||
CHECK(to_string(test) == "200");
|
||||
}
|
||||
|
||||
TEST_CASE("mulRatio")
|
||||
{
|
||||
constexpr auto maxUInt32 = std::numeric_limits<std::uint32_t>::max();
|
||||
constexpr auto maxXRP = std::numeric_limits<XRPAmount::value_type>::max();
|
||||
constexpr auto minXRP = std::numeric_limits<XRPAmount::value_type>::min();
|
||||
|
||||
{
|
||||
// multiply by a number that would overflow then divide by the same
|
||||
// number, and check we didn't lose any value
|
||||
XRPAmount big(maxXRP);
|
||||
CHECK(big == mulRatio(big, maxUInt32, maxUInt32, true));
|
||||
// rounding mode shouldn't matter as the result is exact
|
||||
CHECK(big == mulRatio(big, maxUInt32, maxUInt32, false));
|
||||
|
||||
// multiply and divide by values that would overflow if done
|
||||
// naively, and check that it gives the correct answer
|
||||
big -= 0xf; // Subtract a little so it's divisable by 4
|
||||
CHECK(mulRatio(big, 3, 4, false).value() == (big.value() / 4) * 3);
|
||||
CHECK(mulRatio(big, 3, 4, true).value() == (big.value() / 4) * 3);
|
||||
CHECK((big.value() * 3) / 4 != (big.value() / 4) * 3);
|
||||
}
|
||||
|
||||
{
|
||||
// Similar test as above, but for negative values
|
||||
XRPAmount big(minXRP);
|
||||
CHECK(big == mulRatio(big, maxUInt32, maxUInt32, true));
|
||||
// rounding mode shouldn't matter as the result is exact
|
||||
CHECK(big == mulRatio(big, maxUInt32, maxUInt32, false));
|
||||
|
||||
// multiply and divide by values that would overflow if done
|
||||
// naively, and check that it gives the correct answer
|
||||
CHECK(mulRatio(big, 3, 4, false).value() == (big.value() / 4) * 3);
|
||||
CHECK(mulRatio(big, 3, 4, true).value() == (big.value() / 4) * 3);
|
||||
CHECK((big.value() * 3) / 4 != (big.value() / 4) * 3);
|
||||
}
|
||||
|
||||
{
|
||||
// small amounts
|
||||
XRPAmount tiny(1);
|
||||
// Round up should give the smallest allowable number
|
||||
CHECK(tiny == mulRatio(tiny, 1, maxUInt32, true));
|
||||
// rounding down should be zero
|
||||
CHECK(beast::zero == mulRatio(tiny, 1, maxUInt32, false));
|
||||
CHECK(beast::zero == mulRatio(tiny, maxUInt32 - 1, maxUInt32, false));
|
||||
|
||||
// tiny negative numbers
|
||||
XRPAmount tinyNeg(-1);
|
||||
// Round up should give zero
|
||||
CHECK(beast::zero == mulRatio(tinyNeg, 1, maxUInt32, true));
|
||||
CHECK(beast::zero == mulRatio(tinyNeg, maxUInt32 - 1, maxUInt32, true));
|
||||
// rounding down should be tiny
|
||||
CHECK(tinyNeg == mulRatio(tinyNeg, maxUInt32 - 1, maxUInt32, false));
|
||||
}
|
||||
|
||||
{ // rounding
|
||||
{
|
||||
XRPAmount one(1);
|
||||
auto const rup = mulRatio(one, maxUInt32 - 1, maxUInt32, true);
|
||||
auto const rdown = mulRatio(one, maxUInt32 - 1, maxUInt32, false);
|
||||
CHECK(rup.drops() - rdown.drops() == 1);
|
||||
}
|
||||
|
||||
{
|
||||
XRPAmount big(maxXRP);
|
||||
auto const rup = mulRatio(big, maxUInt32 - 1, maxUInt32, true);
|
||||
auto const rdown = mulRatio(big, maxUInt32 - 1, maxUInt32, false);
|
||||
CHECK(rup.drops() - rdown.drops() == 1);
|
||||
}
|
||||
|
||||
{
|
||||
XRPAmount negOne(-1);
|
||||
auto const rup = mulRatio(negOne, maxUInt32 - 1, maxUInt32, true);
|
||||
auto const rdown =
|
||||
mulRatio(negOne, maxUInt32 - 1, maxUInt32, false);
|
||||
CHECK(rup.drops() - rdown.drops() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// division by zero
|
||||
XRPAmount one(1);
|
||||
CHECK_THROWS([&] { mulRatio(one, 1, 0, true); }());
|
||||
}
|
||||
|
||||
{
|
||||
// overflow
|
||||
XRPAmount big(maxXRP);
|
||||
CHECK_THROWS([&] { mulRatio(big, 2, 1, true); }());
|
||||
}
|
||||
|
||||
{
|
||||
// underflow
|
||||
XRPAmount bigNegative(minXRP + 10);
|
||||
CHECK(mulRatio(bigNegative, 2, 1, true) == minXRP);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
432
src/doctest/basics/base58.cpp
Normal file
432
src/doctest/basics/base58.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
// base58 doctest - converted from src/test/basics/base58_test.cpp
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#include <xrpl/protocol/detail/b58_utils.h>
|
||||
#include <xrpl/protocol/tokens.h>
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/random.hpp>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <random>
|
||||
#include <span>
|
||||
#include <sstream>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
randEngine() -> std::mt19937&
|
||||
{
|
||||
static std::mt19937 r = [] {
|
||||
std::random_device rd;
|
||||
return std::mt19937{rd()};
|
||||
}();
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr int numTokenTypeIndexes = 9;
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
tokenTypeAndSize(int i) -> std::tuple<xrpl::TokenType, std::size_t>
|
||||
{
|
||||
assert(i < numTokenTypeIndexes);
|
||||
|
||||
switch (i)
|
||||
{
|
||||
using enum xrpl::TokenType;
|
||||
case 0:
|
||||
return {None, 20};
|
||||
case 1:
|
||||
return {NodePublic, 32};
|
||||
case 2:
|
||||
return {NodePublic, 33};
|
||||
case 3:
|
||||
return {NodePrivate, 32};
|
||||
case 4:
|
||||
return {AccountID, 20};
|
||||
case 5:
|
||||
return {AccountPublic, 32};
|
||||
case 6:
|
||||
return {AccountPublic, 33};
|
||||
case 7:
|
||||
return {AccountSecret, 32};
|
||||
case 8:
|
||||
return {FamilySeed, 16};
|
||||
default:
|
||||
throw std::invalid_argument(
|
||||
"Invalid token selection passed to tokenTypeAndSize() "
|
||||
"in " __FILE__);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
randomTokenTypeAndSize() -> std::tuple<xrpl::TokenType, std::size_t>
|
||||
{
|
||||
using namespace xrpl;
|
||||
auto& rng = randEngine();
|
||||
std::uniform_int_distribution<> d(0, 8);
|
||||
return tokenTypeAndSize(d(rng));
|
||||
}
|
||||
|
||||
// Return the token type and subspan of `d` to use as test data.
|
||||
[[nodiscard]] inline auto
|
||||
randomB256TestData(std::span<std::uint8_t> d)
|
||||
-> std::tuple<xrpl::TokenType, std::span<std::uint8_t>>
|
||||
{
|
||||
auto& rng = randEngine();
|
||||
std::uniform_int_distribution<std::uint8_t> dist(0, 255);
|
||||
auto [tokType, tokSize] = randomTokenTypeAndSize();
|
||||
std::generate(d.begin(), d.begin() + tokSize, [&] { return dist(rng); });
|
||||
return {tokType, d.subspan(0, tokSize)};
|
||||
}
|
||||
|
||||
inline void
|
||||
printAsChar(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
|
||||
{
|
||||
auto asString = [](std::span<std::uint8_t> s) {
|
||||
std::string r;
|
||||
r.resize(s.size());
|
||||
std::copy(s.begin(), s.end(), r.begin());
|
||||
return r;
|
||||
};
|
||||
auto sa = asString(a);
|
||||
auto sb = asString(b);
|
||||
std::cerr << "\n\n" << sa << "\n" << sb << "\n";
|
||||
}
|
||||
|
||||
inline void
|
||||
printAsInt(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
|
||||
{
|
||||
auto asString = [](std::span<std::uint8_t> s) -> std::string {
|
||||
std::stringstream sstr;
|
||||
for (auto i : s)
|
||||
{
|
||||
sstr << std::setw(3) << int(i) << ',';
|
||||
}
|
||||
return sstr.str();
|
||||
};
|
||||
auto sa = asString(a);
|
||||
auto sb = asString(b);
|
||||
std::cerr << "\n\n" << sa << "\n" << sb << "\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace multiprecision_utils {
|
||||
|
||||
boost::multiprecision::checked_uint512_t
|
||||
toBoostMP(std::span<std::uint64_t> in)
|
||||
{
|
||||
boost::multiprecision::checked_uint512_t mbp = 0;
|
||||
for (auto i = in.rbegin(); i != in.rend(); ++i)
|
||||
{
|
||||
mbp <<= 64;
|
||||
mbp += *i;
|
||||
}
|
||||
return mbp;
|
||||
}
|
||||
|
||||
std::vector<std::uint64_t>
|
||||
randomBigInt(std::uint8_t minSize = 1, std::uint8_t maxSize = 5)
|
||||
{
|
||||
auto eng = randEngine();
|
||||
std::uniform_int_distribution<std::uint8_t> numCoeffDist(minSize, maxSize);
|
||||
std::uniform_int_distribution<std::uint64_t> dist;
|
||||
auto const numCoeff = numCoeffDist(eng);
|
||||
std::vector<std::uint64_t> coeffs;
|
||||
coeffs.reserve(numCoeff);
|
||||
for (int i = 0; i < numCoeff; ++i)
|
||||
{
|
||||
coeffs.push_back(dist(eng));
|
||||
}
|
||||
return coeffs;
|
||||
}
|
||||
} // namespace multiprecision_utils
|
||||
|
||||
} // namespace test
|
||||
} // namespace xrpl
|
||||
|
||||
TEST_SUITE_BEGIN("base58");
|
||||
|
||||
TEST_CASE("b58_multiprecision")
|
||||
{
|
||||
using namespace boost::multiprecision;
|
||||
using namespace xrpl::test;
|
||||
using namespace xrpl;
|
||||
|
||||
constexpr std::size_t iters = 100000;
|
||||
auto eng = randEngine();
|
||||
std::uniform_int_distribution<std::uint64_t> dist;
|
||||
std::uniform_int_distribution<std::uint64_t> dist1(1);
|
||||
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
std::uint64_t const d = dist(eng);
|
||||
if (!d)
|
||||
continue;
|
||||
auto bigInt = multiprecision_utils::randomBigInt();
|
||||
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
||||
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
||||
|
||||
auto const refDiv = boostBigInt / d;
|
||||
auto const refMod = boostBigInt % d;
|
||||
|
||||
auto const mod = b58_fast::detail::inplace_bigint_div_rem(
|
||||
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
||||
auto const foundDiv = multiprecision_utils::toBoostMP(bigInt);
|
||||
CHECK(refMod.convert_to<std::uint64_t>() == mod);
|
||||
CHECK(foundDiv == refDiv);
|
||||
}
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
std::uint64_t const d = dist(eng);
|
||||
auto bigInt = multiprecision_utils::randomBigInt(/*minSize*/ 2);
|
||||
if (bigInt[bigInt.size() - 1] ==
|
||||
std::numeric_limits<std::uint64_t>::max())
|
||||
{
|
||||
bigInt[bigInt.size() - 1] -= 1; // Prevent overflow
|
||||
}
|
||||
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
||||
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
||||
|
||||
auto const refAdd = boostBigInt + d;
|
||||
|
||||
auto const result = b58_fast::detail::inplace_bigint_add(
|
||||
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
||||
CHECK(result == TokenCodecErrc::success);
|
||||
auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
|
||||
CHECK(refAdd == foundAdd);
|
||||
}
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
std::uint64_t const d = dist1(eng);
|
||||
// Force overflow
|
||||
std::vector<std::uint64_t> bigInt(
|
||||
5, std::numeric_limits<std::uint64_t>::max());
|
||||
|
||||
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
||||
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
||||
|
||||
auto const refAdd = boostBigInt + d;
|
||||
|
||||
auto const result = b58_fast::detail::inplace_bigint_add(
|
||||
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
||||
CHECK(result == TokenCodecErrc::overflowAdd);
|
||||
auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
|
||||
CHECK(refAdd != foundAdd);
|
||||
}
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
std::uint64_t const d = dist(eng);
|
||||
auto bigInt = multiprecision_utils::randomBigInt(/* minSize */ 2);
|
||||
// inplace mul requires the most significant coeff to be zero to
|
||||
// hold the result.
|
||||
bigInt[bigInt.size() - 1] = 0;
|
||||
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
||||
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
||||
|
||||
auto const refMul = boostBigInt * d;
|
||||
|
||||
auto const result = b58_fast::detail::inplace_bigint_mul(
|
||||
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
||||
CHECK(result == TokenCodecErrc::success);
|
||||
auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
|
||||
CHECK(refMul == foundMul);
|
||||
}
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
std::uint64_t const d = dist1(eng);
|
||||
// Force overflow
|
||||
std::vector<std::uint64_t> bigInt(
|
||||
5, std::numeric_limits<std::uint64_t>::max());
|
||||
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
||||
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
||||
|
||||
auto const refMul = boostBigInt * d;
|
||||
|
||||
auto const result = b58_fast::detail::inplace_bigint_mul(
|
||||
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
||||
CHECK(result == TokenCodecErrc::inputTooLarge);
|
||||
auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
|
||||
CHECK(refMul != foundMul);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("fast_matches_ref")
|
||||
{
|
||||
using namespace xrpl::test;
|
||||
using namespace xrpl;
|
||||
|
||||
auto testRawEncode = [&](std::span<std::uint8_t> const& b256Data) {
|
||||
std::array<std::uint8_t, 64> b58ResultBuf[2];
|
||||
std::array<std::span<std::uint8_t>, 2> b58Result;
|
||||
|
||||
std::array<std::uint8_t, 64> b256ResultBuf[2];
|
||||
std::array<std::span<std::uint8_t>, 2> b256Result;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
std::span const outBuf{b58ResultBuf[i]};
|
||||
if (i == 0)
|
||||
{
|
||||
auto const r =
|
||||
xrpl::b58_fast::detail::b256_to_b58_be(b256Data, outBuf);
|
||||
REQUIRE(r);
|
||||
b58Result[i] = r.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::array<std::uint8_t, 128> tmpBuf;
|
||||
std::string const s = xrpl::b58_ref::detail::encodeBase58(
|
||||
b256Data.data(),
|
||||
b256Data.size(),
|
||||
tmpBuf.data(),
|
||||
tmpBuf.size());
|
||||
REQUIRE(s.size());
|
||||
b58Result[i] = outBuf.subspan(0, s.size());
|
||||
std::copy(s.begin(), s.end(), b58Result[i].begin());
|
||||
}
|
||||
}
|
||||
REQUIRE(b58Result[0].size() == b58Result[1].size());
|
||||
CHECK(
|
||||
memcmp(
|
||||
b58Result[0].data(),
|
||||
b58Result[1].data(),
|
||||
b58Result[0].size()) == 0);
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
std::span const outBuf{
|
||||
b256ResultBuf[i].data(), b256ResultBuf[i].size()};
|
||||
if (i == 0)
|
||||
{
|
||||
std::string const in(
|
||||
b58Result[i].data(),
|
||||
b58Result[i].data() + b58Result[i].size());
|
||||
auto const r =
|
||||
xrpl::b58_fast::detail::b58_to_b256_be(in, outBuf);
|
||||
REQUIRE(r);
|
||||
b256Result[i] = r.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string const st(b58Result[i].begin(), b58Result[i].end());
|
||||
std::string const s = xrpl::b58_ref::detail::decodeBase58(st);
|
||||
REQUIRE(s.size());
|
||||
b256Result[i] = outBuf.subspan(0, s.size());
|
||||
std::copy(s.begin(), s.end(), b256Result[i].begin());
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(b256Result[0].size() == b256Result[1].size());
|
||||
CHECK(
|
||||
memcmp(
|
||||
b256Result[0].data(),
|
||||
b256Result[1].data(),
|
||||
b256Result[0].size()) == 0);
|
||||
};
|
||||
|
||||
auto testTokenEncode = [&](xrpl::TokenType const tokType,
|
||||
std::span<std::uint8_t> const& b256Data) {
|
||||
std::array<std::uint8_t, 64> b58ResultBuf[2];
|
||||
std::array<std::span<std::uint8_t>, 2> b58Result;
|
||||
|
||||
std::array<std::uint8_t, 64> b256ResultBuf[2];
|
||||
std::array<std::span<std::uint8_t>, 2> b256Result;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
std::span const outBuf{
|
||||
b58ResultBuf[i].data(), b58ResultBuf[i].size()};
|
||||
if (i == 0)
|
||||
{
|
||||
auto const r = xrpl::b58_fast::encodeBase58Token(
|
||||
tokType, b256Data, outBuf);
|
||||
REQUIRE(r);
|
||||
b58Result[i] = r.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string const s = xrpl::b58_ref::encodeBase58Token(
|
||||
tokType, b256Data.data(), b256Data.size());
|
||||
REQUIRE(s.size());
|
||||
b58Result[i] = outBuf.subspan(0, s.size());
|
||||
std::copy(s.begin(), s.end(), b58Result[i].begin());
|
||||
}
|
||||
}
|
||||
REQUIRE(b58Result[0].size() == b58Result[1].size());
|
||||
CHECK(
|
||||
memcmp(
|
||||
b58Result[0].data(),
|
||||
b58Result[1].data(),
|
||||
b58Result[0].size()) == 0);
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
std::span const outBuf{
|
||||
b256ResultBuf[i].data(), b256ResultBuf[i].size()};
|
||||
if (i == 0)
|
||||
{
|
||||
std::string const in(
|
||||
b58Result[i].data(),
|
||||
b58Result[i].data() + b58Result[i].size());
|
||||
auto const r =
|
||||
xrpl::b58_fast::decodeBase58Token(tokType, in, outBuf);
|
||||
REQUIRE(r);
|
||||
b256Result[i] = r.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string const st(b58Result[i].begin(), b58Result[i].end());
|
||||
std::string const s =
|
||||
xrpl::b58_ref::decodeBase58Token(st, tokType);
|
||||
REQUIRE(s.size());
|
||||
b256Result[i] = outBuf.subspan(0, s.size());
|
||||
std::copy(s.begin(), s.end(), b256Result[i].begin());
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(b256Result[0].size() == b256Result[1].size());
|
||||
CHECK(
|
||||
memcmp(
|
||||
b256Result[0].data(),
|
||||
b256Result[1].data(),
|
||||
b256Result[0].size()) == 0);
|
||||
};
|
||||
|
||||
auto testIt = [&](xrpl::TokenType const tokType,
|
||||
std::span<std::uint8_t> const& b256Data) {
|
||||
testRawEncode(b256Data);
|
||||
testTokenEncode(tokType, b256Data);
|
||||
};
|
||||
|
||||
// test every token type with data where every byte is the same and the
|
||||
// bytes range from 0-255
|
||||
for (int i = 0; i < numTokenTypeIndexes; ++i)
|
||||
{
|
||||
std::array<std::uint8_t, 128> b256DataBuf;
|
||||
auto const [tokType, tokSize] = tokenTypeAndSize(i);
|
||||
for (int d = 0; d <= 255; ++d)
|
||||
{
|
||||
memset(b256DataBuf.data(), d, tokSize);
|
||||
testIt(tokType, std::span(b256DataBuf.data(), tokSize));
|
||||
}
|
||||
}
|
||||
|
||||
// test with random data
|
||||
constexpr std::size_t iters = 100000;
|
||||
for (int i = 0; i < iters; ++i)
|
||||
{
|
||||
std::array<std::uint8_t, 128> b256DataBuf;
|
||||
auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
|
||||
testIt(tokType, b256Data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
#endif // _MSC_VER
|
||||
323
src/doctest/basics/base_uint.cpp
Normal file
323
src/doctest/basics/base_uint.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/hardened_hash.h>
|
||||
|
||||
#include <boost/endian/conversion.hpp>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <complex>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
// a non-hashing Hasher that just copies the bytes.
|
||||
// Used to test hash_append in base_uint
|
||||
template <std::size_t Bits>
|
||||
struct nonhash
|
||||
{
|
||||
static constexpr auto const endian = boost::endian::order::big;
|
||||
static constexpr std::size_t WIDTH = Bits / 8;
|
||||
|
||||
std::array<std::uint8_t, WIDTH> data_;
|
||||
|
||||
nonhash() = default;
|
||||
|
||||
void
|
||||
operator()(void const* key, std::size_t len) noexcept
|
||||
{
|
||||
assert(len == WIDTH);
|
||||
memcpy(data_.data(), key, len);
|
||||
}
|
||||
|
||||
explicit
|
||||
operator std::size_t() noexcept
|
||||
{
|
||||
return WIDTH;
|
||||
}
|
||||
};
|
||||
|
||||
using test96 = base_uint<96>;
|
||||
static_assert(std::is_copy_constructible<test96>::value);
|
||||
static_assert(std::is_copy_assignable<test96>::value);
|
||||
|
||||
TEST_SUITE_BEGIN("base_uint");
|
||||
|
||||
TEST_CASE("comparisons 64-bit")
|
||||
{
|
||||
static constexpr std::
|
||||
array<std::pair<std::string_view, std::string_view>, 6>
|
||||
test_args{
|
||||
{{"0000000000000000", "0000000000000001"},
|
||||
{"0000000000000000", "ffffffffffffffff"},
|
||||
{"1234567812345678", "2345678923456789"},
|
||||
{"8000000000000000", "8000000000000001"},
|
||||
{"aaaaaaaaaaaaaaa9", "aaaaaaaaaaaaaaaa"},
|
||||
{"fffffffffffffffe", "ffffffffffffffff"}}};
|
||||
|
||||
for (auto const& arg : test_args)
|
||||
{
|
||||
xrpl::base_uint<64> const u{arg.first}, v{arg.second};
|
||||
CHECK(u < v);
|
||||
CHECK(u <= v);
|
||||
CHECK(u != v);
|
||||
CHECK(!(u == v));
|
||||
CHECK(!(u > v));
|
||||
CHECK(!(u >= v));
|
||||
CHECK(!(v < u));
|
||||
CHECK(!(v <= u));
|
||||
CHECK(v != u);
|
||||
CHECK(!(v == u));
|
||||
CHECK(v > u);
|
||||
CHECK(v >= u);
|
||||
CHECK(u == u);
|
||||
CHECK(v == v);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("comparisons 96-bit")
|
||||
{
|
||||
static constexpr std::
|
||||
array<std::pair<std::string_view, std::string_view>, 6>
|
||||
test_args{{
|
||||
{"000000000000000000000000", "000000000000000000000001"},
|
||||
{"000000000000000000000000", "ffffffffffffffffffffffff"},
|
||||
{"0123456789ab0123456789ab", "123456789abc123456789abc"},
|
||||
{"555555555555555555555555", "55555555555a555555555555"},
|
||||
{"aaaaaaaaaaaaaaa9aaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaa"},
|
||||
{"fffffffffffffffffffffffe", "ffffffffffffffffffffffff"},
|
||||
}};
|
||||
|
||||
for (auto const& arg : test_args)
|
||||
{
|
||||
xrpl::base_uint<96> const u{arg.first}, v{arg.second};
|
||||
CHECK(u < v);
|
||||
CHECK(u <= v);
|
||||
CHECK(u != v);
|
||||
CHECK(!(u == v));
|
||||
CHECK(!(u > v));
|
||||
CHECK(!(u >= v));
|
||||
CHECK(!(v < u));
|
||||
CHECK(!(v <= u));
|
||||
CHECK(v != u);
|
||||
CHECK(!(v == u));
|
||||
CHECK(v > u);
|
||||
CHECK(v >= u);
|
||||
CHECK(u == u);
|
||||
CHECK(v == v);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("general purpose tests")
|
||||
{
|
||||
static_assert(!std::is_constructible<test96, std::complex<double>>::value);
|
||||
static_assert(!std::is_assignable<test96&, std::complex<double>>::value);
|
||||
|
||||
// used to verify set insertion (hashing required)
|
||||
std::unordered_set<test96, hardened_hash<>> uset;
|
||||
|
||||
Blob raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
CHECK(test96::bytes == raw.size());
|
||||
|
||||
test96 u{raw};
|
||||
uset.insert(u);
|
||||
CHECK(raw.size() == u.size());
|
||||
CHECK(to_string(u) == "0102030405060708090A0B0C");
|
||||
CHECK(to_short_string(u) == "01020304...");
|
||||
CHECK(*u.data() == 1);
|
||||
CHECK(u.signum() == 1);
|
||||
CHECK(!!u);
|
||||
CHECK(!u.isZero());
|
||||
CHECK(u.isNonZero());
|
||||
unsigned char t = 0;
|
||||
for (auto& d : u)
|
||||
{
|
||||
CHECK(d == ++t);
|
||||
}
|
||||
|
||||
// Test hash_append by "hashing" with a no-op hasher (h)
|
||||
// and then extracting the bytes that were written during hashing
|
||||
// back into another base_uint (w) for comparison with the original
|
||||
nonhash<96> h;
|
||||
hash_append(h, u);
|
||||
test96 w{std::vector<std::uint8_t>(h.data_.begin(), h.data_.end())};
|
||||
CHECK(w == u);
|
||||
|
||||
test96 v{~u};
|
||||
uset.insert(v);
|
||||
CHECK(to_string(v) == "FEFDFCFBFAF9F8F7F6F5F4F3");
|
||||
CHECK(to_short_string(v) == "FEFDFCFB...");
|
||||
CHECK(*v.data() == 0xfe);
|
||||
CHECK(v.signum() == 1);
|
||||
CHECK(!!v);
|
||||
CHECK(!v.isZero());
|
||||
CHECK(v.isNonZero());
|
||||
t = 0xff;
|
||||
for (auto& d : v)
|
||||
{
|
||||
CHECK(d == --t);
|
||||
}
|
||||
|
||||
CHECK(u < v);
|
||||
CHECK(v > u);
|
||||
|
||||
v = u;
|
||||
CHECK(v == u);
|
||||
|
||||
test96 z{beast::zero};
|
||||
uset.insert(z);
|
||||
CHECK(to_string(z) == "000000000000000000000000");
|
||||
CHECK(to_short_string(z) == "00000000...");
|
||||
CHECK(*z.data() == 0);
|
||||
CHECK(*z.begin() == 0);
|
||||
CHECK(*std::prev(z.end(), 1) == 0);
|
||||
CHECK(z.signum() == 0);
|
||||
CHECK(!z);
|
||||
CHECK(z.isZero());
|
||||
CHECK(!z.isNonZero());
|
||||
for (auto& d : z)
|
||||
{
|
||||
CHECK(d == 0);
|
||||
}
|
||||
|
||||
test96 n{z};
|
||||
n++;
|
||||
CHECK(n == test96(1));
|
||||
n--;
|
||||
CHECK(n == beast::zero);
|
||||
CHECK(n == z);
|
||||
n--;
|
||||
CHECK(to_string(n) == "FFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
CHECK(to_short_string(n) == "FFFFFFFF...");
|
||||
n = beast::zero;
|
||||
CHECK(n == z);
|
||||
|
||||
test96 zp1{z};
|
||||
zp1++;
|
||||
test96 zm1{z};
|
||||
zm1--;
|
||||
test96 x{zm1 ^ zp1};
|
||||
uset.insert(x);
|
||||
CHECK(to_string(x) == "FFFFFFFFFFFFFFFFFFFFFFFE");
|
||||
CHECK(to_short_string(x) == "FFFFFFFF...");
|
||||
|
||||
CHECK(uset.size() == 4);
|
||||
|
||||
test96 tmp;
|
||||
CHECK(tmp.parseHex(to_string(u)));
|
||||
CHECK(tmp == u);
|
||||
tmp = z;
|
||||
|
||||
// fails with extra char
|
||||
CHECK(!tmp.parseHex("A" + to_string(u)));
|
||||
tmp = z;
|
||||
|
||||
// fails with extra char at end
|
||||
CHECK(!tmp.parseHex(to_string(u) + "A"));
|
||||
|
||||
// fails with a non-hex character at some point in the string:
|
||||
tmp = z;
|
||||
|
||||
for (std::size_t i = 0; i != 24; ++i)
|
||||
{
|
||||
std::string x = to_string(z);
|
||||
x[i] = ('G' + (i % 10));
|
||||
CHECK(!tmp.parseHex(x));
|
||||
}
|
||||
|
||||
// Walking 1s:
|
||||
for (std::size_t i = 0; i != 24; ++i)
|
||||
{
|
||||
std::string s1 = "000000000000000000000000";
|
||||
s1[i] = '1';
|
||||
|
||||
CHECK(tmp.parseHex(s1));
|
||||
CHECK(to_string(tmp) == s1);
|
||||
}
|
||||
|
||||
// Walking 0s:
|
||||
for (std::size_t i = 0; i != 24; ++i)
|
||||
{
|
||||
std::string s1 = "111111111111111111111111";
|
||||
s1[i] = '0';
|
||||
|
||||
CHECK(tmp.parseHex(s1));
|
||||
CHECK(to_string(tmp) == s1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("constexpr constructors")
|
||||
{
|
||||
static_assert(test96{}.signum() == 0);
|
||||
static_assert(test96("0").signum() == 0);
|
||||
static_assert(test96("000000000000000000000000").signum() == 0);
|
||||
static_assert(test96("000000000000000000000001").signum() == 1);
|
||||
static_assert(test96("800000000000000000000000").signum() == 1);
|
||||
|
||||
// Using the constexpr constructor in a non-constexpr context
|
||||
// with an error in the parsing throws an exception.
|
||||
{
|
||||
// Invalid length for string.
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
// Try to prevent constant evaluation.
|
||||
std::vector<char> str(23, '7');
|
||||
std::string_view sView(str.data(), str.size());
|
||||
[[maybe_unused]] test96 t96(sView);
|
||||
}
|
||||
catch (std::invalid_argument const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("invalid length for hex string"));
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
{
|
||||
// Invalid character in string.
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
// Try to prevent constant evaluation.
|
||||
std::vector<char> str(23, '7');
|
||||
str.push_back('G');
|
||||
std::string_view sView(str.data(), str.size());
|
||||
[[maybe_unused]] test96 t96(sView);
|
||||
}
|
||||
catch (std::range_error const& e)
|
||||
{
|
||||
CHECK(e.what() == std::string("invalid hex character"));
|
||||
caught = true;
|
||||
}
|
||||
CHECK(caught);
|
||||
}
|
||||
|
||||
// Verify that constexpr base_uints interpret a string the same
|
||||
// way parseHex() does.
|
||||
struct StrBaseUint
|
||||
{
|
||||
char const* const str;
|
||||
test96 tst;
|
||||
|
||||
constexpr StrBaseUint(char const* s) : str(s), tst(s)
|
||||
{
|
||||
}
|
||||
};
|
||||
constexpr StrBaseUint testCases[] = {
|
||||
"000000000000000000000000",
|
||||
"000000000000000000000001",
|
||||
"fedcba9876543210ABCDEF91",
|
||||
"19FEDCBA0123456789abcdef",
|
||||
"800000000000000000000000",
|
||||
"fFfFfFfFfFfFfFfFfFfFfFfF"};
|
||||
|
||||
for (StrBaseUint const& t : testCases)
|
||||
{
|
||||
test96 t96;
|
||||
CHECK(t96.parseHex(t.str));
|
||||
CHECK(t96 == t.tst);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
132
src/doctest/basics/hardened_hash.cpp
Normal file
132
src/doctest/basics/hardened_hash.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include <xrpl/basics/hardened_hash.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
namespace {
|
||||
|
||||
template <class T>
|
||||
class test_user_type_member
|
||||
{
|
||||
private:
|
||||
T t;
|
||||
|
||||
public:
|
||||
explicit test_user_type_member(T const& t_ = T()) : t(t_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
friend void
|
||||
hash_append(Hasher& h, test_user_type_member const& a) noexcept
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, a.t);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class test_user_type_free
|
||||
{
|
||||
private:
|
||||
T t;
|
||||
|
||||
public:
|
||||
explicit test_user_type_free(T const& t_ = T()) : t(t_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
friend void
|
||||
hash_append(Hasher& h, test_user_type_free const& a) noexcept
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, a.t);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using test_hardened_unordered_set = std::unordered_set<T, hardened_hash<>>;
|
||||
|
||||
template <class T>
|
||||
using test_hardened_unordered_map = std::unordered_map<T, int, hardened_hash<>>;
|
||||
|
||||
template <class T>
|
||||
using test_hardened_unordered_multiset =
|
||||
std::unordered_multiset<T, hardened_hash<>>;
|
||||
|
||||
template <class T>
|
||||
using test_hardened_unordered_multimap =
|
||||
std::unordered_multimap<T, int, hardened_hash<>>;
|
||||
|
||||
template <class T>
|
||||
void
|
||||
check()
|
||||
{
|
||||
T t{};
|
||||
hardened_hash<>()(t);
|
||||
}
|
||||
|
||||
template <template <class T> class U>
|
||||
void
|
||||
check_user_type()
|
||||
{
|
||||
check<U<bool>>();
|
||||
check<U<char>>();
|
||||
check<U<signed char>>();
|
||||
check<U<unsigned char>>();
|
||||
check<U<wchar_t>>();
|
||||
check<U<short>>();
|
||||
check<U<unsigned short>>();
|
||||
check<U<int>>();
|
||||
check<U<unsigned int>>();
|
||||
check<U<long>>();
|
||||
check<U<long long>>();
|
||||
check<U<unsigned long>>();
|
||||
check<U<unsigned long long>>();
|
||||
check<U<float>>();
|
||||
check<U<double>>();
|
||||
check<U<long double>>();
|
||||
}
|
||||
|
||||
template <template <class T> class C>
|
||||
void
|
||||
check_container()
|
||||
{
|
||||
{
|
||||
C<test_user_type_member<std::string>> c;
|
||||
(void)c;
|
||||
}
|
||||
|
||||
{
|
||||
C<test_user_type_free<std::string>> c;
|
||||
(void)c;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_SUITE_BEGIN("hardened_hash");
|
||||
|
||||
TEST_CASE("user types")
|
||||
{
|
||||
check_user_type<test_user_type_member>();
|
||||
check_user_type<test_user_type_free>();
|
||||
}
|
||||
|
||||
TEST_CASE("containers")
|
||||
{
|
||||
check_container<test_hardened_unordered_set>();
|
||||
check_container<test_hardened_unordered_map>();
|
||||
check_container<test_hardened_unordered_multiset>();
|
||||
check_container<test_hardened_unordered_multimap>();
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
77
src/doctest/basics/join.cpp
Normal file
77
src/doctest/basics/join.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/join.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
TEST_SUITE_BEGIN("join");
|
||||
|
||||
TEST_CASE("CollectionAndDelimiter")
|
||||
{
|
||||
auto test = [](auto collectionanddelimiter, std::string expected) {
|
||||
std::stringstream ss;
|
||||
// Put something else in the buffer before and after to ensure that
|
||||
// the << operator returns the stream correctly.
|
||||
ss << "(" << collectionanddelimiter << ")";
|
||||
auto const str = ss.str();
|
||||
CHECK(str.substr(1, str.length() - 2) == expected);
|
||||
CHECK(str.front() == '(');
|
||||
CHECK(str.back() == ')');
|
||||
};
|
||||
|
||||
// C++ array
|
||||
test(
|
||||
CollectionAndDelimiter(std::array<int, 4>{2, -1, 5, 10}, "/"),
|
||||
"2/-1/5/10");
|
||||
// One item C++ array edge case
|
||||
test(
|
||||
CollectionAndDelimiter(std::array<std::string, 1>{"test"}, " & "),
|
||||
"test");
|
||||
// Empty C++ array edge case
|
||||
test(CollectionAndDelimiter(std::array<int, 0>{}, ","), "");
|
||||
{
|
||||
// C-style array
|
||||
char letters[4]{'w', 'a', 's', 'd'};
|
||||
test(CollectionAndDelimiter(letters, std::to_string(0)), "w0a0s0d");
|
||||
}
|
||||
{
|
||||
// Auto sized C-style array
|
||||
std::string words[]{"one", "two", "three", "four"};
|
||||
test(CollectionAndDelimiter(words, "\n"), "one\ntwo\nthree\nfour");
|
||||
}
|
||||
{
|
||||
// One item C-style array edge case
|
||||
std::string words[]{"thing"};
|
||||
test(CollectionAndDelimiter(words, "\n"), "thing");
|
||||
}
|
||||
// Initializer list
|
||||
test(
|
||||
CollectionAndDelimiter(std::initializer_list<size_t>{19, 25}, "+"),
|
||||
"19+25");
|
||||
// vector
|
||||
test(
|
||||
CollectionAndDelimiter(std::vector<int>{0, 42}, std::to_string(99)),
|
||||
"09942");
|
||||
// empty vector edge case
|
||||
test(CollectionAndDelimiter(std::vector<uint256>{}, ","), "");
|
||||
// C-style string
|
||||
test(CollectionAndDelimiter("string", " "), "s t r i n g");
|
||||
// Empty C-style string edge case
|
||||
test(CollectionAndDelimiter("", "*"), "");
|
||||
// Single char C-style string edge case
|
||||
test(CollectionAndDelimiter("x", "*"), "x");
|
||||
// std::string
|
||||
test(CollectionAndDelimiter(std::string{"string"}, "-"), "s-t-r-i-n-g");
|
||||
// Empty std::string edge case
|
||||
test(CollectionAndDelimiter(std::string{""}, "*"), "");
|
||||
// Single char std::string edge case
|
||||
test(CollectionAndDelimiter(std::string{"y"}, "*"), "y");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
2
src/doctest/basics/main.cpp
Normal file
2
src/doctest/basics/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
69
src/doctest/beast/CurrentThreadName.cpp
Normal file
69
src/doctest/beast/CurrentThreadName.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
TEST_SUITE_BEGIN("CurrentThreadName");
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
exerciseName(
|
||||
std::string myName,
|
||||
std::atomic<bool>* stop,
|
||||
std::atomic<int>* state)
|
||||
{
|
||||
// Verify that upon creation a thread has no name.
|
||||
auto const initialThreadName = beast::getCurrentThreadName();
|
||||
|
||||
// Set the new name.
|
||||
beast::setCurrentThreadName(myName);
|
||||
|
||||
// Indicate to caller that the name is set.
|
||||
*state = 1;
|
||||
|
||||
// If there is an initial thread name then we failed.
|
||||
if (!initialThreadName.empty())
|
||||
return;
|
||||
|
||||
// Wait until all threads have their names.
|
||||
while (!*stop)
|
||||
;
|
||||
|
||||
// Make sure the thread name that we set before is still there
|
||||
// (not overwritten by, for instance, another thread).
|
||||
if (beast::getCurrentThreadName() == myName)
|
||||
*state = 2;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Thread names are preserved")
|
||||
{
|
||||
// Make two different threads with two different names. Make sure
|
||||
// that the expected thread names are still there when the thread
|
||||
// exits.
|
||||
std::atomic<bool> stop{false};
|
||||
|
||||
std::atomic<int> stateA{0};
|
||||
std::thread tA(exerciseName, "tA", &stop, &stateA);
|
||||
|
||||
std::atomic<int> stateB{0};
|
||||
std::thread tB(exerciseName, "tB", &stop, &stateB);
|
||||
|
||||
// Wait until both threads have set their names.
|
||||
while (stateA == 0 || stateB == 0)
|
||||
;
|
||||
|
||||
stop = true;
|
||||
tA.join();
|
||||
tB.join();
|
||||
|
||||
// Both threads should still have the expected name when they exit.
|
||||
CHECK(stateA == 2);
|
||||
CHECK(stateB == 2);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
458
src/doctest/beast/IPEndpoint.cpp
Normal file
458
src/doctest/beast/IPEndpoint.cpp
Normal file
@@ -0,0 +1,458 @@
|
||||
// IPEndpoint doctest - converted from src/test/beast/IPEndpoint_test.cpp
|
||||
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/predef.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
TEST_SUITE_BEGIN("IPEndpoint");
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace beast::IP;
|
||||
|
||||
Endpoint
|
||||
randomEP(bool v4 = true)
|
||||
{
|
||||
using namespace xrpl;
|
||||
auto dv4 = []() -> AddressV4::bytes_type {
|
||||
return {
|
||||
{static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX))}};
|
||||
};
|
||||
auto dv6 = []() -> AddressV6::bytes_type {
|
||||
return {
|
||||
{static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX)),
|
||||
static_cast<std::uint8_t>(rand_int<int>(1, UINT8_MAX))}};
|
||||
};
|
||||
return Endpoint{
|
||||
v4 ? Address{AddressV4{dv4()}} : Address{AddressV6{dv6()}},
|
||||
rand_int<std::uint16_t>(1, UINT16_MAX)};
|
||||
}
|
||||
|
||||
void
|
||||
shouldParseAddrV4(
|
||||
std::string const& s,
|
||||
std::uint32_t value,
|
||||
std::string const& normal = "")
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
Address const result{boost::asio::ip::make_address(s, ec)};
|
||||
REQUIRE_MESSAGE(!ec, ec.message());
|
||||
REQUIRE_MESSAGE(result.is_v4(), s.c_str());
|
||||
REQUIRE_MESSAGE(result.to_v4().to_uint() == value, s.c_str());
|
||||
CHECK_MESSAGE(
|
||||
result.to_string() == (normal.empty() ? s : normal), s.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
failParseAddr(std::string const& s)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto a = boost::asio::ip::make_address(s, ec);
|
||||
CHECK_MESSAGE(ec, s.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
shouldParseEPV4(
|
||||
std::string const& s,
|
||||
AddressV4::bytes_type const& value,
|
||||
std::uint16_t p,
|
||||
std::string const& normal = "")
|
||||
{
|
||||
auto const result = Endpoint::from_string_checked(s);
|
||||
REQUIRE(result);
|
||||
REQUIRE(result->address().is_v4());
|
||||
REQUIRE(result->address().to_v4() == AddressV4{value});
|
||||
CHECK(result->port() == p);
|
||||
CHECK(to_string(*result) == (normal.empty() ? s : normal));
|
||||
}
|
||||
|
||||
void
|
||||
shouldParseEPV6(
|
||||
std::string const& s,
|
||||
AddressV6::bytes_type const& value,
|
||||
std::uint16_t p,
|
||||
std::string const& normal = "")
|
||||
{
|
||||
auto result = Endpoint::from_string_checked(s);
|
||||
REQUIRE(result);
|
||||
REQUIRE(result->address().is_v6());
|
||||
REQUIRE(result->address().to_v6() == AddressV6{value});
|
||||
CHECK(result->port() == p);
|
||||
CHECK(to_string(*result) == (normal.empty() ? s : normal));
|
||||
}
|
||||
|
||||
void
|
||||
failParseEP(std::string s)
|
||||
{
|
||||
auto a1 = Endpoint::from_string(s);
|
||||
CHECK_MESSAGE(is_unspecified(a1), s.c_str());
|
||||
|
||||
auto a2 = Endpoint::from_string(s);
|
||||
CHECK_MESSAGE(is_unspecified(a2), s.c_str());
|
||||
|
||||
boost::replace_last(s, ":", " ");
|
||||
auto a3 = Endpoint::from_string(s);
|
||||
CHECK_MESSAGE(is_unspecified(a3), s.c_str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
parse(std::string const& text, T& t)
|
||||
{
|
||||
std::istringstream stream{text};
|
||||
stream >> t;
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
shouldPass(std::string const& text, std::string const& normal = "")
|
||||
{
|
||||
T t;
|
||||
CHECK(parse(text, t));
|
||||
CHECK_MESSAGE(
|
||||
to_string(t) == (normal.empty() ? text : normal), text.c_str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
shouldFail(std::string const& text)
|
||||
{
|
||||
T t;
|
||||
CHECK_FALSE_MESSAGE(parse(text, t), text.c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("AddressV4")
|
||||
{
|
||||
CHECK(AddressV4{}.to_uint() == 0);
|
||||
CHECK(is_unspecified(AddressV4{}));
|
||||
CHECK(AddressV4{0x01020304}.to_uint() == 0x01020304);
|
||||
|
||||
{
|
||||
AddressV4::bytes_type d = {{1, 2, 3, 4}};
|
||||
CHECK(AddressV4{d}.to_uint() == 0x01020304);
|
||||
CHECK_FALSE(is_unspecified(AddressV4{d}));
|
||||
}
|
||||
|
||||
AddressV4 const v1{1};
|
||||
CHECK(AddressV4{v1}.to_uint() == 1);
|
||||
|
||||
{
|
||||
AddressV4 v;
|
||||
v = v1;
|
||||
CHECK(v.to_uint() == v1.to_uint());
|
||||
}
|
||||
|
||||
{
|
||||
AddressV4 v;
|
||||
auto d = v.to_bytes();
|
||||
d[0] = 1;
|
||||
d[1] = 2;
|
||||
d[2] = 3;
|
||||
d[3] = 4;
|
||||
v = AddressV4{d};
|
||||
CHECK(v.to_uint() == 0x01020304);
|
||||
}
|
||||
|
||||
CHECK(AddressV4(0x01020304).to_string() == "1.2.3.4");
|
||||
|
||||
shouldParseAddrV4("1.2.3.4", 0x01020304);
|
||||
shouldParseAddrV4("255.255.255.255", 0xffffffff);
|
||||
shouldParseAddrV4("0.0.0.0", 0);
|
||||
|
||||
failParseAddr(".");
|
||||
failParseAddr("..");
|
||||
failParseAddr("...");
|
||||
failParseAddr("....");
|
||||
#if BOOST_OS_WINDOWS
|
||||
// WINDOWS bug in asio - I don't think these should parse
|
||||
// at all, and in-fact they do not on mac/linux
|
||||
shouldParseAddrV4("1", 0x00000001, "0.0.0.1");
|
||||
shouldParseAddrV4("1.2", 0x01000002, "1.0.0.2");
|
||||
shouldParseAddrV4("1.2.3", 0x01020003, "1.2.0.3");
|
||||
#else
|
||||
failParseAddr("1");
|
||||
failParseAddr("1.2");
|
||||
failParseAddr("1.2.3");
|
||||
#endif
|
||||
failParseAddr("1.");
|
||||
failParseAddr("1.2.");
|
||||
failParseAddr("1.2.3.");
|
||||
failParseAddr("256.0.0.0");
|
||||
failParseAddr("-1.2.3.4");
|
||||
}
|
||||
|
||||
TEST_CASE("AddressV4::Bytes")
|
||||
{
|
||||
AddressV4::bytes_type d1 = {{10, 0, 0, 1}};
|
||||
AddressV4 v4{d1};
|
||||
CHECK(v4.to_bytes()[0] == 10);
|
||||
CHECK(v4.to_bytes()[1] == 0);
|
||||
CHECK(v4.to_bytes()[2] == 0);
|
||||
CHECK(v4.to_bytes()[3] == 1);
|
||||
|
||||
CHECK((~((0xff) << 16)) == 0xff00ffff);
|
||||
|
||||
auto d2 = v4.to_bytes();
|
||||
d2[1] = 10;
|
||||
v4 = AddressV4{d2};
|
||||
CHECK(v4.to_bytes()[0] == 10);
|
||||
CHECK(v4.to_bytes()[1] == 10);
|
||||
CHECK(v4.to_bytes()[2] == 0);
|
||||
CHECK(v4.to_bytes()[3] == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Address")
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
Address result{boost::asio::ip::make_address("1.2.3.4", ec)};
|
||||
AddressV4::bytes_type d = {{1, 2, 3, 4}};
|
||||
CHECK(!ec);
|
||||
CHECK(result.is_v4());
|
||||
CHECK(result.to_v4() == AddressV4{d});
|
||||
}
|
||||
|
||||
TEST_CASE("Endpoint")
|
||||
{
|
||||
shouldParseEPV4("1.2.3.4", {{1, 2, 3, 4}}, 0);
|
||||
shouldParseEPV4("1.2.3.4:5", {{1, 2, 3, 4}}, 5);
|
||||
shouldParseEPV4("1.2.3.4 5", {{1, 2, 3, 4}}, 5, "1.2.3.4:5");
|
||||
// leading, trailing space
|
||||
shouldParseEPV4(" 1.2.3.4:5", {{1, 2, 3, 4}}, 5, "1.2.3.4:5");
|
||||
shouldParseEPV4("1.2.3.4:5 ", {{1, 2, 3, 4}}, 5, "1.2.3.4:5");
|
||||
shouldParseEPV4("1.2.3.4 ", {{1, 2, 3, 4}}, 0, "1.2.3.4");
|
||||
shouldParseEPV4(" 1.2.3.4", {{1, 2, 3, 4}}, 0, "1.2.3.4");
|
||||
shouldParseEPV6(
|
||||
"2001:db8:a0b:12f0::1",
|
||||
{{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}},
|
||||
0);
|
||||
shouldParseEPV6(
|
||||
"[2001:db8:a0b:12f0::1]:8",
|
||||
{{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}},
|
||||
8);
|
||||
shouldParseEPV6(
|
||||
"[2001:2002:2003:2004:2005:2006:2007:2008]:65535",
|
||||
{{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}},
|
||||
65535);
|
||||
shouldParseEPV6(
|
||||
"2001:2002:2003:2004:2005:2006:2007:2008 65535",
|
||||
{{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}},
|
||||
65535,
|
||||
"[2001:2002:2003:2004:2005:2006:2007:2008]:65535");
|
||||
|
||||
Endpoint ep;
|
||||
|
||||
AddressV4::bytes_type d = {{127, 0, 0, 1}};
|
||||
ep = Endpoint(AddressV4{d}, 80);
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(!is_public(ep));
|
||||
CHECK(is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(is_loopback(ep));
|
||||
CHECK(to_string(ep) == "127.0.0.1:80");
|
||||
// same address as v4 mapped in ipv6
|
||||
ep = Endpoint(
|
||||
boost::asio::ip::make_address_v6(
|
||||
boost::asio::ip::v4_mapped, AddressV4{d}),
|
||||
80);
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(!is_public(ep));
|
||||
CHECK(is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(!is_loopback(ep)); // mapped loopback is not a loopback
|
||||
CHECK(to_string(ep) == "[::ffff:127.0.0.1]:80");
|
||||
|
||||
d = {{10, 0, 0, 1}};
|
||||
ep = Endpoint(AddressV4{d});
|
||||
CHECK(get_class(ep.to_v4()) == 'A');
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(!is_public(ep));
|
||||
CHECK(is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(!is_loopback(ep));
|
||||
CHECK(to_string(ep) == "10.0.0.1");
|
||||
// same address as v4 mapped in ipv6
|
||||
ep = Endpoint(boost::asio::ip::make_address_v6(
|
||||
boost::asio::ip::v4_mapped, AddressV4{d}));
|
||||
CHECK(
|
||||
get_class(boost::asio::ip::make_address_v4(
|
||||
boost::asio::ip::v4_mapped, ep.to_v6())) == 'A');
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(!is_public(ep));
|
||||
CHECK(is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(!is_loopback(ep));
|
||||
CHECK(to_string(ep) == "::ffff:10.0.0.1");
|
||||
|
||||
d = {{166, 78, 151, 147}};
|
||||
ep = Endpoint(AddressV4{d});
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(is_public(ep));
|
||||
CHECK(!is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(!is_loopback(ep));
|
||||
CHECK(to_string(ep) == "166.78.151.147");
|
||||
// same address as v4 mapped in ipv6
|
||||
ep = Endpoint(boost::asio::ip::make_address_v6(
|
||||
boost::asio::ip::v4_mapped, AddressV4{d}));
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(is_public(ep));
|
||||
CHECK(!is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(!is_loopback(ep));
|
||||
CHECK(to_string(ep) == "::ffff:166.78.151.147");
|
||||
|
||||
// a private IPv6
|
||||
AddressV6::bytes_type d2 = {
|
||||
{253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}};
|
||||
ep = Endpoint(AddressV6{d2});
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(!is_public(ep));
|
||||
CHECK(is_private(ep));
|
||||
CHECK(!is_multicast(ep));
|
||||
CHECK(!is_loopback(ep));
|
||||
CHECK(to_string(ep) == "fd00::1");
|
||||
|
||||
{
|
||||
ep = Endpoint::from_string("192.0.2.112");
|
||||
CHECK(!is_unspecified(ep));
|
||||
CHECK(ep == Endpoint::from_string("192.0.2.112"));
|
||||
|
||||
auto const ep1 = Endpoint::from_string("192.0.2.112:2016");
|
||||
CHECK(!is_unspecified(ep1));
|
||||
CHECK(ep.address() == ep1.address());
|
||||
CHECK(ep1.port() == 2016);
|
||||
|
||||
auto const ep2 = Endpoint::from_string("192.0.2.112:2016");
|
||||
CHECK(!is_unspecified(ep2));
|
||||
CHECK(ep.address() == ep2.address());
|
||||
CHECK(ep2.port() == 2016);
|
||||
CHECK(ep1 == ep2);
|
||||
|
||||
auto const ep3 = Endpoint::from_string("192.0.2.112 2016");
|
||||
CHECK(!is_unspecified(ep3));
|
||||
CHECK(ep.address() == ep3.address());
|
||||
CHECK(ep3.port() == 2016);
|
||||
CHECK(ep2 == ep3);
|
||||
|
||||
auto const ep4 = Endpoint::from_string("192.0.2.112 2016");
|
||||
CHECK(!is_unspecified(ep4));
|
||||
CHECK(ep.address() == ep4.address());
|
||||
CHECK(ep4.port() == 2016);
|
||||
CHECK(ep3 == ep4);
|
||||
|
||||
CHECK(to_string(ep1) == to_string(ep2));
|
||||
CHECK(to_string(ep1) == to_string(ep3));
|
||||
CHECK(to_string(ep1) == to_string(ep4));
|
||||
}
|
||||
|
||||
{
|
||||
ep = Endpoint::from_string("[::]:2017");
|
||||
CHECK(is_unspecified(ep));
|
||||
CHECK(ep.port() == 2017);
|
||||
CHECK(ep.address() == AddressV6{});
|
||||
}
|
||||
|
||||
// Failures:
|
||||
failParseEP("192.0.2.112:port");
|
||||
failParseEP("ip:port");
|
||||
failParseEP("");
|
||||
failParseEP("1.2.3.256");
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
// windows asio bugs...false positives
|
||||
shouldParseEPV4("255", {{0, 0, 0, 255}}, 0, "0.0.0.255");
|
||||
shouldParseEPV4("512", {{0, 0, 2, 0}}, 0, "0.0.2.0");
|
||||
shouldParseEPV4("1.2.3:80", {{1, 2, 0, 3}}, 80, "1.2.0.3:80");
|
||||
#else
|
||||
failParseEP("255");
|
||||
failParseEP("512");
|
||||
failParseEP("1.2.3:80");
|
||||
#endif
|
||||
|
||||
failParseEP("1.2.3.4:65536");
|
||||
failParseEP("1.2.3.4:89119");
|
||||
failParseEP("1.2.3:89119");
|
||||
failParseEP("[::1]:89119");
|
||||
failParseEP("[::az]:1");
|
||||
failParseEP("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1");
|
||||
failParseEP("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345");
|
||||
failParseEP("abcdef:12345");
|
||||
failParseEP("[abcdef]:12345");
|
||||
failParseEP("foo.org 12345");
|
||||
|
||||
// test with hashed container
|
||||
std::unordered_set<Endpoint> eps;
|
||||
constexpr auto items{100};
|
||||
float max_lf{0};
|
||||
for (auto i = 0; i < items; ++i)
|
||||
{
|
||||
eps.insert(randomEP(xrpl::rand_int(0, 1) == 1));
|
||||
max_lf = std::max(max_lf, eps.load_factor());
|
||||
}
|
||||
CHECK(eps.bucket_count() >= items);
|
||||
CHECK(max_lf > 0.90);
|
||||
}
|
||||
|
||||
TEST_CASE("Parse Endpoint")
|
||||
{
|
||||
shouldPass<Endpoint>("0.0.0.0");
|
||||
shouldPass<Endpoint>("192.168.0.1");
|
||||
shouldPass<Endpoint>("168.127.149.132");
|
||||
shouldPass<Endpoint>("168.127.149.132:80");
|
||||
shouldPass<Endpoint>("168.127.149.132:54321");
|
||||
shouldPass<Endpoint>("2001:db8:a0b:12f0::1");
|
||||
shouldPass<Endpoint>("[2001:db8:a0b:12f0::1]:8");
|
||||
shouldPass<Endpoint>("2001:db8:a0b:12f0::1 8", "[2001:db8:a0b:12f0::1]:8");
|
||||
shouldPass<Endpoint>("[::1]:8");
|
||||
shouldPass<Endpoint>("[2001:2002:2003:2004:2005:2006:2007:2008]:65535");
|
||||
|
||||
shouldFail<Endpoint>("1.2.3.256");
|
||||
shouldFail<Endpoint>("");
|
||||
#if BOOST_OS_WINDOWS
|
||||
// windows asio bugs...false positives
|
||||
shouldPass<Endpoint>("512", "0.0.2.0");
|
||||
shouldPass<Endpoint>("255", "0.0.0.255");
|
||||
shouldPass<Endpoint>("1.2.3:80", "1.2.0.3:80");
|
||||
#else
|
||||
shouldFail<Endpoint>("512");
|
||||
shouldFail<Endpoint>("255");
|
||||
shouldFail<Endpoint>("1.2.3:80");
|
||||
#endif
|
||||
shouldFail<Endpoint>("1.2.3:65536");
|
||||
shouldFail<Endpoint>("1.2.3:72131");
|
||||
shouldFail<Endpoint>("[::1]:89119");
|
||||
shouldFail<Endpoint>("[::az]:1");
|
||||
shouldFail<Endpoint>("[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:1");
|
||||
shouldFail<Endpoint>(
|
||||
"[1234:5678:90ab:cdef:1234:5678:90ab:cdef:1111]:12345");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
95
src/doctest/beast/Journal.cpp
Normal file
95
src/doctest/beast/Journal.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace beast;
|
||||
|
||||
TEST_SUITE_BEGIN("Journal");
|
||||
|
||||
namespace {
|
||||
|
||||
class TestSink : public Journal::Sink
|
||||
{
|
||||
private:
|
||||
int m_count;
|
||||
|
||||
public:
|
||||
TestSink() : Sink(severities::kWarning, false), m_count(0)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
count() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
void
|
||||
reset()
|
||||
{
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
void
|
||||
write(severities::Severity level, std::string const&) override
|
||||
{
|
||||
if (level >= threshold())
|
||||
++m_count;
|
||||
}
|
||||
|
||||
void
|
||||
writeAlways(severities::Severity level, std::string const&) override
|
||||
{
|
||||
++m_count;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Journal threshold kInfo")
|
||||
{
|
||||
TestSink sink;
|
||||
|
||||
using namespace beast::severities;
|
||||
sink.threshold(kInfo);
|
||||
|
||||
Journal j(sink);
|
||||
|
||||
j.trace() << " ";
|
||||
CHECK(sink.count() == 0);
|
||||
j.debug() << " ";
|
||||
CHECK(sink.count() == 0);
|
||||
j.info() << " ";
|
||||
CHECK(sink.count() == 1);
|
||||
j.warn() << " ";
|
||||
CHECK(sink.count() == 2);
|
||||
j.error() << " ";
|
||||
CHECK(sink.count() == 3);
|
||||
j.fatal() << " ";
|
||||
CHECK(sink.count() == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("Journal threshold kDebug")
|
||||
{
|
||||
TestSink sink;
|
||||
|
||||
using namespace beast::severities;
|
||||
sink.threshold(kDebug);
|
||||
|
||||
Journal j(sink);
|
||||
|
||||
j.trace() << " ";
|
||||
CHECK(sink.count() == 0);
|
||||
j.debug() << " ";
|
||||
CHECK(sink.count() == 1);
|
||||
j.info() << " ";
|
||||
CHECK(sink.count() == 2);
|
||||
j.warn() << " ";
|
||||
CHECK(sink.count() == 3);
|
||||
j.error() << " ";
|
||||
CHECK(sink.count() == 4);
|
||||
j.fatal() << " ";
|
||||
CHECK(sink.count() == 5);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
254
src/doctest/beast/LexicalCast.cpp
Normal file
254
src/doctest/beast/LexicalCast.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace beast;
|
||||
|
||||
TEST_SUITE_BEGIN("LexicalCast");
|
||||
|
||||
namespace {
|
||||
|
||||
template <class IntType>
|
||||
IntType
|
||||
nextRandomInt(xor_shift_engine& r)
|
||||
{
|
||||
return static_cast<IntType>(r());
|
||||
}
|
||||
|
||||
template <class IntType>
|
||||
void
|
||||
testInteger(IntType in)
|
||||
{
|
||||
std::string s;
|
||||
IntType out(in + 1);
|
||||
|
||||
CHECK(lexicalCastChecked(s, in));
|
||||
CHECK(lexicalCastChecked(out, s));
|
||||
CHECK(out == in);
|
||||
}
|
||||
|
||||
template <class IntType>
|
||||
void
|
||||
testIntegers(xor_shift_engine& r)
|
||||
{
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
IntType const value(nextRandomInt<IntType>(r));
|
||||
testInteger(value);
|
||||
}
|
||||
|
||||
testInteger(std::numeric_limits<IntType>::min());
|
||||
testInteger(std::numeric_limits<IntType>::max());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
tryBadConvert(std::string const& s)
|
||||
{
|
||||
T out;
|
||||
CHECK_FALSE(lexicalCastChecked(out, s));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
tryEdgeCase(std::string const& s)
|
||||
{
|
||||
T ret;
|
||||
|
||||
bool const result = lexicalCastChecked(ret, s);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
return s == std::to_string(ret);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
testThrowConvert(std::string const& s, bool success)
|
||||
{
|
||||
bool result = !success;
|
||||
T out;
|
||||
|
||||
try
|
||||
{
|
||||
out = lexicalCastThrow<T>(s);
|
||||
result = true;
|
||||
}
|
||||
catch (BadLexicalCast const&)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
CHECK(result == success);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("random integers")
|
||||
{
|
||||
std::int64_t const seedValue = 50;
|
||||
xor_shift_engine r(seedValue);
|
||||
|
||||
SUBCASE("int")
|
||||
{
|
||||
testIntegers<int>(r);
|
||||
}
|
||||
SUBCASE("unsigned int")
|
||||
{
|
||||
testIntegers<unsigned int>(r);
|
||||
}
|
||||
SUBCASE("short")
|
||||
{
|
||||
testIntegers<short>(r);
|
||||
}
|
||||
SUBCASE("unsigned short")
|
||||
{
|
||||
testIntegers<unsigned short>(r);
|
||||
}
|
||||
SUBCASE("int32_t")
|
||||
{
|
||||
testIntegers<std::int32_t>(r);
|
||||
}
|
||||
SUBCASE("uint32_t")
|
||||
{
|
||||
testIntegers<std::uint32_t>(r);
|
||||
}
|
||||
SUBCASE("int64_t")
|
||||
{
|
||||
testIntegers<std::int64_t>(r);
|
||||
}
|
||||
SUBCASE("uint64_t")
|
||||
{
|
||||
testIntegers<std::uint64_t>(r);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("pathologies")
|
||||
{
|
||||
CHECK_THROWS_AS(
|
||||
lexicalCastThrow<int>("\xef\xbc\x91\xef\xbc\x90"), BadLexicalCast);
|
||||
}
|
||||
|
||||
TEST_CASE("conversion overflows")
|
||||
{
|
||||
tryBadConvert<std::uint64_t>("99999999999999999999");
|
||||
tryBadConvert<std::uint32_t>("4294967300");
|
||||
tryBadConvert<std::uint16_t>("75821");
|
||||
}
|
||||
|
||||
TEST_CASE("conversion underflows")
|
||||
{
|
||||
tryBadConvert<std::uint32_t>("-1");
|
||||
|
||||
tryBadConvert<std::int64_t>("-99999999999999999999");
|
||||
tryBadConvert<std::int32_t>("-4294967300");
|
||||
tryBadConvert<std::int16_t>("-75821");
|
||||
}
|
||||
|
||||
TEST_CASE("conversion edge cases")
|
||||
{
|
||||
CHECK(tryEdgeCase<std::uint64_t>("18446744073709551614"));
|
||||
CHECK(tryEdgeCase<std::uint64_t>("18446744073709551615"));
|
||||
CHECK_FALSE(tryEdgeCase<std::uint64_t>("18446744073709551616"));
|
||||
|
||||
CHECK(tryEdgeCase<std::int64_t>("9223372036854775806"));
|
||||
CHECK(tryEdgeCase<std::int64_t>("9223372036854775807"));
|
||||
CHECK_FALSE(tryEdgeCase<std::int64_t>("9223372036854775808"));
|
||||
|
||||
CHECK(tryEdgeCase<std::int64_t>("-9223372036854775807"));
|
||||
CHECK(tryEdgeCase<std::int64_t>("-9223372036854775808"));
|
||||
CHECK_FALSE(tryEdgeCase<std::int64_t>("-9223372036854775809"));
|
||||
|
||||
CHECK(tryEdgeCase<std::uint32_t>("4294967294"));
|
||||
CHECK(tryEdgeCase<std::uint32_t>("4294967295"));
|
||||
CHECK_FALSE(tryEdgeCase<std::uint32_t>("4294967296"));
|
||||
|
||||
CHECK(tryEdgeCase<std::int32_t>("2147483646"));
|
||||
CHECK(tryEdgeCase<std::int32_t>("2147483647"));
|
||||
CHECK_FALSE(tryEdgeCase<std::int32_t>("2147483648"));
|
||||
|
||||
CHECK(tryEdgeCase<std::int32_t>("-2147483647"));
|
||||
CHECK(tryEdgeCase<std::int32_t>("-2147483648"));
|
||||
CHECK_FALSE(tryEdgeCase<std::int32_t>("-2147483649"));
|
||||
|
||||
CHECK(tryEdgeCase<std::uint16_t>("65534"));
|
||||
CHECK(tryEdgeCase<std::uint16_t>("65535"));
|
||||
CHECK_FALSE(tryEdgeCase<std::uint16_t>("65536"));
|
||||
|
||||
CHECK(tryEdgeCase<std::int16_t>("32766"));
|
||||
CHECK(tryEdgeCase<std::int16_t>("32767"));
|
||||
CHECK_FALSE(tryEdgeCase<std::int16_t>("32768"));
|
||||
|
||||
CHECK(tryEdgeCase<std::int16_t>("-32767"));
|
||||
CHECK(tryEdgeCase<std::int16_t>("-32768"));
|
||||
CHECK_FALSE(tryEdgeCase<std::int16_t>("-32769"));
|
||||
}
|
||||
|
||||
TEST_CASE("throwing conversion")
|
||||
{
|
||||
testThrowConvert<std::uint64_t>("99999999999999999999", false);
|
||||
testThrowConvert<std::uint64_t>("9223372036854775806", true);
|
||||
|
||||
testThrowConvert<std::uint32_t>("4294967290", true);
|
||||
testThrowConvert<std::uint32_t>("42949672900", false);
|
||||
testThrowConvert<std::uint32_t>("429496729000", false);
|
||||
testThrowConvert<std::uint32_t>("4294967290000", false);
|
||||
|
||||
testThrowConvert<std::int32_t>("5294967295", false);
|
||||
testThrowConvert<std::int32_t>("-2147483644", true);
|
||||
|
||||
testThrowConvert<std::int16_t>("66666", false);
|
||||
testThrowConvert<std::int16_t>("-5711", true);
|
||||
}
|
||||
|
||||
TEST_CASE("zero conversion")
|
||||
{
|
||||
SUBCASE("signed")
|
||||
{
|
||||
std::int32_t out;
|
||||
|
||||
CHECK(lexicalCastChecked(out, "-0"));
|
||||
CHECK(lexicalCastChecked(out, "0"));
|
||||
CHECK(lexicalCastChecked(out, "+0"));
|
||||
}
|
||||
|
||||
SUBCASE("unsigned")
|
||||
{
|
||||
std::uint32_t out;
|
||||
|
||||
CHECK_FALSE(lexicalCastChecked(out, "-0"));
|
||||
CHECK(lexicalCastChecked(out, "0"));
|
||||
CHECK(lexicalCastChecked(out, "+0"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("entire range")
|
||||
{
|
||||
std::int32_t i = std::numeric_limits<std::int16_t>::min();
|
||||
std::string const empty("");
|
||||
|
||||
while (i <= std::numeric_limits<std::int16_t>::max())
|
||||
{
|
||||
std::int16_t j = static_cast<std::int16_t>(i);
|
||||
|
||||
auto actual = std::to_string(j);
|
||||
|
||||
auto result = lexicalCast(j, empty);
|
||||
|
||||
CHECK(result == actual);
|
||||
|
||||
if (result == actual)
|
||||
{
|
||||
auto number = lexicalCast<std::int16_t>(result);
|
||||
CHECK(number == j);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
216
src/doctest/beast/PropertyStream.cpp
Normal file
216
src/doctest/beast/PropertyStream.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace beast;
|
||||
using Source = PropertyStream::Source;
|
||||
|
||||
TEST_SUITE_BEGIN("PropertyStream");
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
test_peel_name(
|
||||
std::string s,
|
||||
std::string const& expected,
|
||||
std::string const& expected_remainder)
|
||||
{
|
||||
std::string const peeled_name = Source::peel_name(&s);
|
||||
CHECK(peeled_name == expected);
|
||||
CHECK(s == expected_remainder);
|
||||
}
|
||||
|
||||
void
|
||||
test_peel_leading_slash(
|
||||
std::string s,
|
||||
std::string const& expected,
|
||||
bool should_be_found)
|
||||
{
|
||||
bool const found(Source::peel_leading_slash(&s));
|
||||
CHECK(found == should_be_found);
|
||||
CHECK(s == expected);
|
||||
}
|
||||
|
||||
void
|
||||
test_peel_trailing_slashstar(
|
||||
std::string s,
|
||||
std::string const& expected_remainder,
|
||||
bool should_be_found)
|
||||
{
|
||||
bool const found(Source::peel_trailing_slashstar(&s));
|
||||
CHECK(found == should_be_found);
|
||||
CHECK(s == expected_remainder);
|
||||
}
|
||||
|
||||
void
|
||||
test_find_one(Source& root, Source* expected, std::string const& name)
|
||||
{
|
||||
Source* source(root.find_one(name));
|
||||
CHECK(source == expected);
|
||||
}
|
||||
|
||||
void
|
||||
test_find_path(Source& root, std::string const& path, Source* expected)
|
||||
{
|
||||
Source* source(root.find_path(path));
|
||||
CHECK(source == expected);
|
||||
}
|
||||
|
||||
void
|
||||
test_find_one_deep(Source& root, std::string const& name, Source* expected)
|
||||
{
|
||||
Source* source(root.find_one_deep(name));
|
||||
CHECK(source == expected);
|
||||
}
|
||||
|
||||
void
|
||||
test_find(Source& root, std::string path, Source* expected, bool expected_star)
|
||||
{
|
||||
auto const result(root.find(path));
|
||||
CHECK(result.first == expected);
|
||||
CHECK(result.second == expected_star);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("peel_name")
|
||||
{
|
||||
test_peel_name("a", "a", "");
|
||||
test_peel_name("foo/bar", "foo", "bar");
|
||||
test_peel_name("foo/goo/bar", "foo", "goo/bar");
|
||||
test_peel_name("", "", "");
|
||||
}
|
||||
|
||||
TEST_CASE("peel_leading_slash")
|
||||
{
|
||||
test_peel_leading_slash("foo/", "foo/", false);
|
||||
test_peel_leading_slash("foo", "foo", false);
|
||||
test_peel_leading_slash("/foo/", "foo/", true);
|
||||
test_peel_leading_slash("/foo", "foo", true);
|
||||
}
|
||||
|
||||
TEST_CASE("peel_trailing_slashstar")
|
||||
{
|
||||
test_peel_trailing_slashstar("/foo/goo/*", "/foo/goo", true);
|
||||
test_peel_trailing_slashstar("foo/goo/*", "foo/goo", true);
|
||||
test_peel_trailing_slashstar("/foo/goo/", "/foo/goo", false);
|
||||
test_peel_trailing_slashstar("foo/goo", "foo/goo", false);
|
||||
test_peel_trailing_slashstar("", "", false);
|
||||
test_peel_trailing_slashstar("/", "", false);
|
||||
test_peel_trailing_slashstar("/*", "", true);
|
||||
test_peel_trailing_slashstar("//", "/", false);
|
||||
test_peel_trailing_slashstar("**", "*", true);
|
||||
test_peel_trailing_slashstar("*/", "*", false);
|
||||
}
|
||||
|
||||
TEST_CASE("find_one")
|
||||
{
|
||||
Source a("a");
|
||||
Source b("b");
|
||||
Source c("c");
|
||||
Source d("d");
|
||||
Source e("e");
|
||||
Source f("f");
|
||||
Source g("g");
|
||||
|
||||
// a { b { d { f }, e }, c { g } }
|
||||
a.add(b);
|
||||
a.add(c);
|
||||
c.add(g);
|
||||
b.add(d);
|
||||
b.add(e);
|
||||
d.add(f);
|
||||
|
||||
test_find_one(a, &b, "b");
|
||||
test_find_one(a, nullptr, "d");
|
||||
test_find_one(b, &e, "e");
|
||||
test_find_one(d, &f, "f");
|
||||
}
|
||||
|
||||
TEST_CASE("find_path")
|
||||
{
|
||||
Source a("a");
|
||||
Source b("b");
|
||||
Source c("c");
|
||||
Source d("d");
|
||||
Source e("e");
|
||||
Source f("f");
|
||||
Source g("g");
|
||||
|
||||
a.add(b);
|
||||
a.add(c);
|
||||
c.add(g);
|
||||
b.add(d);
|
||||
b.add(e);
|
||||
d.add(f);
|
||||
|
||||
test_find_path(a, "a", nullptr);
|
||||
test_find_path(a, "e", nullptr);
|
||||
test_find_path(a, "a/b", nullptr);
|
||||
test_find_path(a, "a/b/e", nullptr);
|
||||
test_find_path(a, "b/e/g", nullptr);
|
||||
test_find_path(a, "b/e/f", nullptr);
|
||||
test_find_path(a, "b", &b);
|
||||
test_find_path(a, "b/e", &e);
|
||||
test_find_path(a, "b/d/f", &f);
|
||||
}
|
||||
|
||||
TEST_CASE("find_one_deep")
|
||||
{
|
||||
Source a("a");
|
||||
Source b("b");
|
||||
Source c("c");
|
||||
Source d("d");
|
||||
Source e("e");
|
||||
Source f("f");
|
||||
Source g("g");
|
||||
|
||||
a.add(b);
|
||||
a.add(c);
|
||||
c.add(g);
|
||||
b.add(d);
|
||||
b.add(e);
|
||||
d.add(f);
|
||||
|
||||
test_find_one_deep(a, "z", nullptr);
|
||||
test_find_one_deep(a, "g", &g);
|
||||
test_find_one_deep(a, "b", &b);
|
||||
test_find_one_deep(a, "d", &d);
|
||||
test_find_one_deep(a, "f", &f);
|
||||
}
|
||||
|
||||
TEST_CASE("find")
|
||||
{
|
||||
Source a("a");
|
||||
Source b("b");
|
||||
Source c("c");
|
||||
Source d("d");
|
||||
Source e("e");
|
||||
Source f("f");
|
||||
Source g("g");
|
||||
|
||||
a.add(b);
|
||||
a.add(c);
|
||||
c.add(g);
|
||||
b.add(d);
|
||||
b.add(e);
|
||||
d.add(f);
|
||||
|
||||
test_find(a, "", &a, false);
|
||||
test_find(a, "*", &a, true);
|
||||
test_find(a, "/b", &b, false);
|
||||
test_find(a, "b", &b, false);
|
||||
test_find(a, "d", &d, false);
|
||||
test_find(a, "/b*", &b, true);
|
||||
test_find(a, "b*", &b, true);
|
||||
test_find(a, "d*", &d, true);
|
||||
test_find(a, "/b/*", &b, true);
|
||||
test_find(a, "b/*", &b, true);
|
||||
test_find(a, "d/*", &d, true);
|
||||
test_find(a, "a", nullptr, false);
|
||||
test_find(a, "/d", nullptr, false);
|
||||
test_find(a, "/d*", nullptr, true);
|
||||
test_find(a, "/d/*", nullptr, true);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
248
src/doctest/beast/SemanticVersion.cpp
Normal file
248
src/doctest/beast/SemanticVersion.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
#include <xrpl/beast/core/SemanticVersion.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace beast;
|
||||
|
||||
TEST_SUITE_BEGIN("SemanticVersion");
|
||||
|
||||
using identifier_list = SemanticVersion::identifier_list;
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
checkPass(std::string const& input, bool shouldPass = true)
|
||||
{
|
||||
SemanticVersion v;
|
||||
|
||||
if (shouldPass)
|
||||
{
|
||||
CHECK(v.parse(input));
|
||||
CHECK(v.print() == input);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_FALSE(v.parse(input));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
checkFail(std::string const& input)
|
||||
{
|
||||
checkPass(input, false);
|
||||
}
|
||||
|
||||
// check input and input with appended metadata
|
||||
void
|
||||
checkMeta(std::string const& input, bool shouldPass)
|
||||
{
|
||||
checkPass(input, shouldPass);
|
||||
|
||||
checkPass(input + "+a", shouldPass);
|
||||
checkPass(input + "+1", shouldPass);
|
||||
checkPass(input + "+a.b", shouldPass);
|
||||
checkPass(input + "+ab.cd", shouldPass);
|
||||
|
||||
checkFail(input + "!");
|
||||
checkFail(input + "+");
|
||||
checkFail(input + "++");
|
||||
checkFail(input + "+!");
|
||||
checkFail(input + "+.");
|
||||
checkFail(input + "+a.!");
|
||||
}
|
||||
|
||||
void
|
||||
checkMetaFail(std::string const& input)
|
||||
{
|
||||
checkMeta(input, false);
|
||||
}
|
||||
|
||||
// check input, input with appended release data,
|
||||
// input with appended metadata, and input with both
|
||||
// appended release data and appended metadata
|
||||
void
|
||||
checkRelease(std::string const& input, bool shouldPass = true)
|
||||
{
|
||||
checkMeta(input, shouldPass);
|
||||
|
||||
checkMeta(input + "-1", shouldPass);
|
||||
checkMeta(input + "-a", shouldPass);
|
||||
checkMeta(input + "-a1", shouldPass);
|
||||
checkMeta(input + "-a1.b1", shouldPass);
|
||||
checkMeta(input + "-ab.cd", shouldPass);
|
||||
checkMeta(input + "--", shouldPass);
|
||||
|
||||
checkMetaFail(input + "+");
|
||||
checkMetaFail(input + "!");
|
||||
checkMetaFail(input + "-");
|
||||
checkMetaFail(input + "-!");
|
||||
checkMetaFail(input + "-.");
|
||||
checkMetaFail(input + "-a.!");
|
||||
checkMetaFail(input + "-0.a");
|
||||
}
|
||||
|
||||
// Checks the major.minor.version string alone and with all
|
||||
// possible combinations of release identifiers and metadata.
|
||||
void
|
||||
check(std::string const& input, bool shouldPass = true)
|
||||
{
|
||||
checkRelease(input, shouldPass);
|
||||
}
|
||||
|
||||
void
|
||||
negcheck(std::string const& input)
|
||||
{
|
||||
check(input, false);
|
||||
}
|
||||
|
||||
identifier_list
|
||||
ids()
|
||||
{
|
||||
return identifier_list();
|
||||
}
|
||||
|
||||
identifier_list
|
||||
ids(std::string const& s1)
|
||||
{
|
||||
identifier_list v;
|
||||
v.push_back(s1);
|
||||
return v;
|
||||
}
|
||||
|
||||
identifier_list
|
||||
ids(std::string const& s1, std::string const& s2)
|
||||
{
|
||||
identifier_list v;
|
||||
v.push_back(s1);
|
||||
v.push_back(s2);
|
||||
return v;
|
||||
}
|
||||
|
||||
identifier_list
|
||||
ids(std::string const& s1, std::string const& s2, std::string const& s3)
|
||||
{
|
||||
identifier_list v;
|
||||
v.push_back(s1);
|
||||
v.push_back(s2);
|
||||
v.push_back(s3);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Checks the decomposition of the input into appropriate values
|
||||
void
|
||||
checkValues(
|
||||
std::string const& input,
|
||||
int majorVersion,
|
||||
int minorVersion,
|
||||
int patchVersion,
|
||||
identifier_list const& preReleaseIdentifiers = identifier_list(),
|
||||
identifier_list const& metaData = identifier_list())
|
||||
{
|
||||
SemanticVersion v;
|
||||
|
||||
CHECK(v.parse(input));
|
||||
|
||||
CHECK(v.majorVersion == majorVersion);
|
||||
CHECK(v.minorVersion == minorVersion);
|
||||
CHECK(v.patchVersion == patchVersion);
|
||||
|
||||
CHECK(v.preReleaseIdentifiers == preReleaseIdentifiers);
|
||||
CHECK(v.metaData == metaData);
|
||||
}
|
||||
|
||||
// makes sure the left version is less than the right
|
||||
void
|
||||
checkLessInternal(std::string const& lhs, std::string const& rhs)
|
||||
{
|
||||
SemanticVersion left;
|
||||
SemanticVersion right;
|
||||
|
||||
CHECK(left.parse(lhs));
|
||||
CHECK(right.parse(rhs));
|
||||
|
||||
CHECK(compare(left, left) == 0);
|
||||
CHECK(compare(right, right) == 0);
|
||||
CHECK(compare(left, right) < 0);
|
||||
CHECK(compare(right, left) > 0);
|
||||
|
||||
CHECK(left < right);
|
||||
CHECK(right > left);
|
||||
CHECK(left == left);
|
||||
CHECK(right == right);
|
||||
}
|
||||
|
||||
void
|
||||
checkLess(std::string const& lhs, std::string const& rhs)
|
||||
{
|
||||
checkLessInternal(lhs, rhs);
|
||||
checkLessInternal(lhs + "+meta", rhs);
|
||||
checkLessInternal(lhs, rhs + "+meta");
|
||||
checkLessInternal(lhs + "+meta", rhs + "+meta");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("parsing")
|
||||
{
|
||||
check("0.0.0");
|
||||
check("1.2.3");
|
||||
check("2147483647.2147483647.2147483647"); // max int
|
||||
|
||||
// negative values
|
||||
negcheck("-1.2.3");
|
||||
negcheck("1.-2.3");
|
||||
negcheck("1.2.-3");
|
||||
|
||||
// missing parts
|
||||
negcheck("");
|
||||
negcheck("1");
|
||||
negcheck("1.");
|
||||
negcheck("1.2");
|
||||
negcheck("1.2.");
|
||||
negcheck(".2.3");
|
||||
|
||||
// whitespace
|
||||
negcheck(" 1.2.3");
|
||||
negcheck("1 .2.3");
|
||||
negcheck("1.2 .3");
|
||||
negcheck("1.2.3 ");
|
||||
|
||||
// leading zeroes
|
||||
negcheck("01.2.3");
|
||||
negcheck("1.02.3");
|
||||
negcheck("1.2.03");
|
||||
}
|
||||
|
||||
TEST_CASE("values")
|
||||
{
|
||||
checkValues("0.1.2", 0, 1, 2);
|
||||
checkValues("1.2.3", 1, 2, 3);
|
||||
checkValues("1.2.3-rc1", 1, 2, 3, ids("rc1"));
|
||||
checkValues("1.2.3-rc1.debug", 1, 2, 3, ids("rc1", "debug"));
|
||||
checkValues("1.2.3-rc1.debug.asm", 1, 2, 3, ids("rc1", "debug", "asm"));
|
||||
checkValues("1.2.3+full", 1, 2, 3, ids(), ids("full"));
|
||||
checkValues("1.2.3+full.prod", 1, 2, 3, ids(), ids("full", "prod"));
|
||||
checkValues(
|
||||
"1.2.3+full.prod.x86", 1, 2, 3, ids(), ids("full", "prod", "x86"));
|
||||
checkValues(
|
||||
"1.2.3-rc1.debug.asm+full.prod.x86",
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
ids("rc1", "debug", "asm"),
|
||||
ids("full", "prod", "x86"));
|
||||
}
|
||||
|
||||
TEST_CASE("comparisons")
|
||||
{
|
||||
checkLess("1.0.0-alpha", "1.0.0-alpha.1");
|
||||
checkLess("1.0.0-alpha.1", "1.0.0-alpha.beta");
|
||||
checkLess("1.0.0-alpha.beta", "1.0.0-beta");
|
||||
checkLess("1.0.0-beta", "1.0.0-beta.2");
|
||||
checkLess("1.0.0-beta.2", "1.0.0-beta.11");
|
||||
checkLess("1.0.0-beta.11", "1.0.0-rc.1");
|
||||
checkLess("1.0.0-rc.1", "1.0.0");
|
||||
checkLess("0.9.9", "1.0.0");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
1168
src/doctest/beast/aged_associative_container.cpp
Normal file
1168
src/doctest/beast/aged_associative_container.cpp
Normal file
File diff suppressed because it is too large
Load Diff
17
src/doctest/beast/basic_seconds_clock.cpp
Normal file
17
src/doctest/beast/basic_seconds_clock.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <xrpl/beast/clock/basic_seconds_clock.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace beast;
|
||||
|
||||
TEST_SUITE_BEGIN("basic_seconds_clock");
|
||||
|
||||
TEST_CASE("basic_seconds_clock::now() works")
|
||||
{
|
||||
// Just verify that now() can be called without throwing
|
||||
auto t = basic_seconds_clock::now();
|
||||
// Verify that the time point is valid (not zero)
|
||||
CHECK(t.time_since_epoch().count() > 0);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
98
src/doctest/beast/beast_Zero.cpp
Normal file
98
src/doctest/beast/beast_Zero.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
namespace beast {
|
||||
|
||||
struct adl_tester
|
||||
{
|
||||
};
|
||||
|
||||
int
|
||||
signum(adl_tester)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace inner_adl_test {
|
||||
|
||||
struct adl_tester2
|
||||
{
|
||||
};
|
||||
|
||||
int
|
||||
signum(adl_tester2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace inner_adl_test
|
||||
|
||||
} // namespace beast
|
||||
|
||||
using namespace beast;
|
||||
|
||||
TEST_SUITE_BEGIN("Zero");
|
||||
|
||||
namespace {
|
||||
|
||||
struct IntegerWrapper
|
||||
{
|
||||
int value;
|
||||
|
||||
IntegerWrapper(int v) : value(v)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
signum() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
test_lhs_zero(IntegerWrapper x)
|
||||
{
|
||||
CHECK((x >= zero) == (x.signum() >= 0));
|
||||
CHECK((x > zero) == (x.signum() > 0));
|
||||
CHECK((x == zero) == (x.signum() == 0));
|
||||
CHECK((x != zero) == (x.signum() != 0));
|
||||
CHECK((x < zero) == (x.signum() < 0));
|
||||
CHECK((x <= zero) == (x.signum() <= 0));
|
||||
}
|
||||
|
||||
void
|
||||
test_rhs_zero(IntegerWrapper x)
|
||||
{
|
||||
CHECK((zero >= x) == (0 >= x.signum()));
|
||||
CHECK((zero > x) == (0 > x.signum()));
|
||||
CHECK((zero == x) == (0 == x.signum()));
|
||||
CHECK((zero != x) == (0 != x.signum()));
|
||||
CHECK((zero < x) == (0 < x.signum()));
|
||||
CHECK((zero <= x) == (0 <= x.signum()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("lhs zero")
|
||||
{
|
||||
test_lhs_zero(-7);
|
||||
test_lhs_zero(0);
|
||||
test_lhs_zero(32);
|
||||
}
|
||||
|
||||
TEST_CASE("rhs zero")
|
||||
{
|
||||
test_rhs_zero(-4);
|
||||
test_rhs_zero(0);
|
||||
test_rhs_zero(64);
|
||||
}
|
||||
|
||||
TEST_CASE("ADL")
|
||||
{
|
||||
CHECK(adl_tester{} == zero);
|
||||
CHECK(inner_adl_test::adl_tester2{} == zero);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
2
src/doctest/beast/main.cpp
Normal file
2
src/doctest/beast/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
168
src/doctest/beast/xxhasher.cpp
Normal file
168
src/doctest/beast/xxhasher.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include <xrpl/beast/hash/xxhasher.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace beast;
|
||||
|
||||
TEST_SUITE_BEGIN("XXHasher");
|
||||
|
||||
TEST_CASE("Without seed")
|
||||
{
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 16042857369214894119ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("With seed")
|
||||
{
|
||||
xxhasher hasher{static_cast<std::uint32_t>(102)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 14440132435660934800ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("With two seeds")
|
||||
{
|
||||
xxhasher hasher{
|
||||
static_cast<std::uint32_t>(102), static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 14440132435660934800ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Big object with multiple small updates without seed")
|
||||
{
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
}
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 15296278154063476002ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Big object with multiple small updates with seed")
|
||||
{
|
||||
xxhasher hasher{static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
}
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 17285302196561698791ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Big object with small and big updates without seed")
|
||||
{
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
std::string bigObject;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
bigObject += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
hasher(bigObject.data(), bigObject.size());
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(static_cast<xxhasher::result_type>(hasher) == 1865045178324729219ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Big object with small and big updates with seed")
|
||||
{
|
||||
xxhasher hasher{static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
std::string bigObject;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
bigObject += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
hasher(bigObject.data(), bigObject.size());
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 16189862915636005281ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Big object with one update without seed")
|
||||
{
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
objectToHash += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 15296278154063476002ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Big object with one update with seed")
|
||||
{
|
||||
xxhasher hasher{static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
objectToHash += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
CHECK(
|
||||
static_cast<xxhasher::result_type>(hasher) == 17285302196561698791ULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Operator result type doesn't change the internal state")
|
||||
{
|
||||
SUBCASE("small object")
|
||||
{
|
||||
xxhasher hasher;
|
||||
|
||||
std::string object{"Hello xxhash"};
|
||||
hasher(object.data(), object.size());
|
||||
auto xxhashResult1 = static_cast<xxhasher::result_type>(hasher);
|
||||
auto xxhashResult2 = static_cast<xxhasher::result_type>(hasher);
|
||||
|
||||
CHECK(xxhashResult1 == xxhashResult2);
|
||||
}
|
||||
SUBCASE("big object")
|
||||
{
|
||||
xxhasher hasher;
|
||||
|
||||
std::string object;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
object += "Hello, xxHash!";
|
||||
}
|
||||
hasher(object.data(), object.size());
|
||||
auto xxhashResult1 = hasher.operator xxhasher::result_type();
|
||||
auto xxhashResult2 = hasher.operator xxhasher::result_type();
|
||||
|
||||
CHECK(xxhashResult1 == xxhashResult2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
168
src/doctest/core/Workers.cpp
Normal file
168
src/doctest/core/Workers.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include <xrpl/core/PerfLog.h>
|
||||
#include <xrpl/core/detail/Workers.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
TEST_SUITE_BEGIN("Workers");
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Dummy class for unit tests.
|
||||
*/
|
||||
class PerfLogTest : public perf::PerfLog
|
||||
{
|
||||
void
|
||||
rpcStart(std::string const& method, std::uint64_t requestId) override
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
rpcFinish(std::string const& method, std::uint64_t requestId) override
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
rpcError(std::string const& method, std::uint64_t dur) override
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
jobQueue(JobType const type) override
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
jobStart(
|
||||
JobType const type,
|
||||
std::chrono::microseconds dur,
|
||||
std::chrono::time_point<std::chrono::steady_clock> startTime,
|
||||
int instance) override
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
jobFinish(JobType const type, std::chrono::microseconds dur, int instance)
|
||||
override
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value
|
||||
countersJson() const override
|
||||
{
|
||||
return Json::Value();
|
||||
}
|
||||
|
||||
Json::Value
|
||||
currentJson() const override
|
||||
{
|
||||
return Json::Value();
|
||||
}
|
||||
|
||||
void
|
||||
resizeJobs(int const resize) override
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
rotate() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct TestCallback : Workers::Callback
|
||||
{
|
||||
void
|
||||
processTask(int instance) override
|
||||
{
|
||||
std::lock_guard lk{mut};
|
||||
if (--count == 0)
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex mut;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
void
|
||||
testThreads(int const tc1, int const tc2, int const tc3)
|
||||
{
|
||||
TestCallback cb;
|
||||
std::unique_ptr<perf::PerfLog> perfLog = std::make_unique<PerfLogTest>();
|
||||
|
||||
Workers w(cb, perfLog.get(), "Test", tc1);
|
||||
CHECK(w.getNumberOfThreads() == tc1);
|
||||
|
||||
auto testForThreadCount = [&cb, &w](int const threadCount) {
|
||||
// Prepare the callback.
|
||||
cb.count = threadCount;
|
||||
|
||||
// Execute the test.
|
||||
w.setNumberOfThreads(threadCount);
|
||||
CHECK(w.getNumberOfThreads() == threadCount);
|
||||
|
||||
for (int i = 0; i < threadCount; ++i)
|
||||
w.addTask();
|
||||
|
||||
// 10 seconds should be enough to finish on any system
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock<std::mutex> lk{cb.mut};
|
||||
bool const signaled =
|
||||
cb.cv.wait_for(lk, 10s, [&cb] { return cb.count == 0; });
|
||||
CHECK(signaled);
|
||||
CHECK(cb.count == 0);
|
||||
};
|
||||
testForThreadCount(tc1);
|
||||
testForThreadCount(tc2);
|
||||
testForThreadCount(tc3);
|
||||
w.stop();
|
||||
|
||||
// We had better finished all our work!
|
||||
CHECK(cb.count == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("threadCounts: 0 -> 0 -> 0")
|
||||
{
|
||||
testThreads(0, 0, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("threadCounts: 1 -> 0 -> 1")
|
||||
{
|
||||
testThreads(1, 0, 1);
|
||||
}
|
||||
|
||||
TEST_CASE("threadCounts: 2 -> 1 -> 2")
|
||||
{
|
||||
testThreads(2, 1, 2);
|
||||
}
|
||||
|
||||
TEST_CASE("threadCounts: 4 -> 3 -> 5")
|
||||
{
|
||||
testThreads(4, 3, 5);
|
||||
}
|
||||
|
||||
TEST_CASE("threadCounts: 16 -> 4 -> 15")
|
||||
{
|
||||
testThreads(16, 4, 15);
|
||||
}
|
||||
|
||||
TEST_CASE("threadCounts: 64 -> 3 -> 65")
|
||||
{
|
||||
testThreads(64, 3, 65);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
} // namespace xrpl
|
||||
2
src/doctest/core/main.cpp
Normal file
2
src/doctest/core/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
122
src/doctest/csf/BasicNetwork.cpp
Normal file
122
src/doctest/csf/BasicNetwork.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <test/csf/Scheduler.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
using namespace xrpl::test::csf;
|
||||
|
||||
TEST_SUITE_BEGIN("BasicNetwork");
|
||||
|
||||
namespace {
|
||||
|
||||
struct Peer
|
||||
{
|
||||
int id;
|
||||
std::set<int> set;
|
||||
|
||||
Peer(Peer const&) = default;
|
||||
Peer(Peer&&) = default;
|
||||
|
||||
explicit Peer(int id_) : id(id_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
void
|
||||
start(Scheduler& scheduler, Net& net)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto t = scheduler.in(1s, [&] { set.insert(0); });
|
||||
if (id == 0)
|
||||
{
|
||||
for (auto const link : net.links(this))
|
||||
net.send(this, link.target, [&, to = link.target] {
|
||||
to->receive(net, this, 1);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
scheduler.cancel(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
void
|
||||
receive(Net& net, Peer* from, int m)
|
||||
{
|
||||
set.insert(m);
|
||||
++m;
|
||||
if (m < 5)
|
||||
{
|
||||
for (auto const link : net.links(this))
|
||||
net.send(this, link.target, [&, mm = m, to = link.target] {
|
||||
to->receive(net, this, mm);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("BasicNetwork operations")
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::vector<Peer> pv;
|
||||
pv.emplace_back(0);
|
||||
pv.emplace_back(1);
|
||||
pv.emplace_back(2);
|
||||
Scheduler scheduler;
|
||||
BasicNetwork<Peer*> net(scheduler);
|
||||
CHECK(!net.connect(&pv[0], &pv[0]));
|
||||
CHECK(net.connect(&pv[0], &pv[1], 1s));
|
||||
CHECK(net.connect(&pv[1], &pv[2], 1s));
|
||||
CHECK(!net.connect(&pv[0], &pv[1]));
|
||||
for (auto& peer : pv)
|
||||
peer.start(scheduler, net);
|
||||
CHECK(scheduler.step_for(0s));
|
||||
CHECK(scheduler.step_for(1s));
|
||||
CHECK(scheduler.step());
|
||||
CHECK(!scheduler.step());
|
||||
CHECK(!scheduler.step_for(1s));
|
||||
net.send(&pv[0], &pv[1], [] {});
|
||||
net.send(&pv[1], &pv[0], [] {});
|
||||
CHECK(net.disconnect(&pv[0], &pv[1]));
|
||||
CHECK(!net.disconnect(&pv[0], &pv[1]));
|
||||
for (;;)
|
||||
{
|
||||
auto const links = net.links(&pv[1]);
|
||||
if (links.empty())
|
||||
break;
|
||||
CHECK(net.disconnect(&pv[1], links[0].target));
|
||||
}
|
||||
CHECK(pv[0].set == std::set<int>({0, 2, 4}));
|
||||
CHECK(pv[1].set == std::set<int>({1, 3}));
|
||||
CHECK(pv[2].set == std::set<int>({2, 4}));
|
||||
}
|
||||
|
||||
TEST_CASE("BasicNetwork disconnect")
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
Scheduler scheduler;
|
||||
BasicNetwork<int> net(scheduler);
|
||||
CHECK(net.connect(0, 1, 1s));
|
||||
CHECK(net.connect(0, 2, 2s));
|
||||
|
||||
std::set<int> delivered;
|
||||
net.send(0, 1, [&]() { delivered.insert(1); });
|
||||
net.send(0, 2, [&]() { delivered.insert(2); });
|
||||
|
||||
scheduler.in(1000ms, [&]() { CHECK(net.disconnect(0, 2)); });
|
||||
scheduler.in(1100ms, [&]() { CHECK(net.connect(0, 2)); });
|
||||
|
||||
scheduler.step();
|
||||
|
||||
// only the first message is delivered because the disconnect at 1 s
|
||||
// purges all pending messages from 0 to 2
|
||||
CHECK(delivered == std::set<int>({1}));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
72
src/doctest/csf/Digraph.cpp
Normal file
72
src/doctest/csf/Digraph.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <test/csf/Digraph.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace xrpl::test::csf;
|
||||
|
||||
TEST_SUITE_BEGIN("Digraph");
|
||||
|
||||
TEST_CASE("Digraph basic operations")
|
||||
{
|
||||
using Graph = Digraph<char, std::string>;
|
||||
Graph graph;
|
||||
|
||||
CHECK(!graph.connected('a', 'b'));
|
||||
CHECK(!graph.edge('a', 'b'));
|
||||
CHECK(!graph.disconnect('a', 'b'));
|
||||
|
||||
CHECK(graph.connect('a', 'b', "foobar"));
|
||||
CHECK(graph.connected('a', 'b'));
|
||||
CHECK(*graph.edge('a', 'b') == "foobar");
|
||||
|
||||
CHECK(!graph.connect('a', 'b', "repeat"));
|
||||
CHECK(graph.disconnect('a', 'b'));
|
||||
CHECK(graph.connect('a', 'b', "repeat"));
|
||||
CHECK(graph.connected('a', 'b'));
|
||||
CHECK(*graph.edge('a', 'b') == "repeat");
|
||||
|
||||
CHECK(graph.connect('a', 'c', "tree"));
|
||||
|
||||
{
|
||||
std::vector<std::tuple<char, char, std::string>> edges;
|
||||
|
||||
for (auto const& edge : graph.outEdges('a'))
|
||||
{
|
||||
edges.emplace_back(edge.source, edge.target, edge.data);
|
||||
}
|
||||
|
||||
std::vector<std::tuple<char, char, std::string>> expected;
|
||||
expected.emplace_back('a', 'b', "repeat");
|
||||
expected.emplace_back('a', 'c', "tree");
|
||||
CHECK(edges == expected);
|
||||
CHECK(graph.outDegree('a') == expected.size());
|
||||
}
|
||||
|
||||
CHECK(graph.outEdges('r').size() == 0);
|
||||
CHECK(graph.outDegree('r') == 0);
|
||||
CHECK(graph.outDegree('c') == 0);
|
||||
|
||||
// only 'a' has out edges
|
||||
CHECK(graph.outVertices().size() == 1);
|
||||
std::vector<char> expected = {'b', 'c'};
|
||||
|
||||
CHECK((graph.outVertices('a') == expected));
|
||||
CHECK(graph.outVertices('b').size() == 0);
|
||||
CHECK(graph.outVertices('c').size() == 0);
|
||||
CHECK(graph.outVertices('r').size() == 0);
|
||||
|
||||
std::stringstream ss;
|
||||
graph.saveDot(ss, [](char v) { return v; });
|
||||
std::string expectedDot =
|
||||
"digraph {\n"
|
||||
"a -> b;\n"
|
||||
"a -> c;\n"
|
||||
"}\n";
|
||||
CHECK(ss.str() == expectedDot);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
75
src/doctest/csf/Histogram.cpp
Normal file
75
src/doctest/csf/Histogram.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <test/csf/Histogram.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl::test::csf;
|
||||
|
||||
TEST_SUITE_BEGIN("Histogram");
|
||||
|
||||
TEST_CASE("Histogram empty")
|
||||
{
|
||||
Histogram<int> hist;
|
||||
|
||||
CHECK(hist.size() == 0);
|
||||
CHECK(hist.numBins() == 0);
|
||||
CHECK(hist.minValue() == 0);
|
||||
CHECK(hist.maxValue() == 0);
|
||||
CHECK(hist.avg() == 0);
|
||||
CHECK(hist.percentile(0.0f) == hist.minValue());
|
||||
CHECK(hist.percentile(0.5f) == 0);
|
||||
CHECK(hist.percentile(0.9f) == 0);
|
||||
CHECK(hist.percentile(1.0f) == hist.maxValue());
|
||||
}
|
||||
|
||||
TEST_CASE("Histogram single element")
|
||||
{
|
||||
Histogram<int> hist;
|
||||
hist.insert(1);
|
||||
|
||||
CHECK(hist.size() == 1);
|
||||
CHECK(hist.numBins() == 1);
|
||||
CHECK(hist.minValue() == 1);
|
||||
CHECK(hist.maxValue() == 1);
|
||||
CHECK(hist.avg() == 1);
|
||||
CHECK(hist.percentile(0.0f) == hist.minValue());
|
||||
CHECK(hist.percentile(0.5f) == 1);
|
||||
CHECK(hist.percentile(0.9f) == 1);
|
||||
CHECK(hist.percentile(1.0f) == hist.maxValue());
|
||||
}
|
||||
|
||||
TEST_CASE("Histogram two elements")
|
||||
{
|
||||
Histogram<int> hist;
|
||||
hist.insert(1);
|
||||
hist.insert(9);
|
||||
|
||||
CHECK(hist.size() == 2);
|
||||
CHECK(hist.numBins() == 2);
|
||||
CHECK(hist.minValue() == 1);
|
||||
CHECK(hist.maxValue() == 9);
|
||||
CHECK(hist.avg() == 5);
|
||||
CHECK(hist.percentile(0.0f) == hist.minValue());
|
||||
CHECK(hist.percentile(0.5f) == 1);
|
||||
CHECK(hist.percentile(0.9f) == 9);
|
||||
CHECK(hist.percentile(1.0f) == hist.maxValue());
|
||||
}
|
||||
|
||||
TEST_CASE("Histogram duplicate elements")
|
||||
{
|
||||
Histogram<int> hist;
|
||||
hist.insert(1);
|
||||
hist.insert(9);
|
||||
hist.insert(1);
|
||||
|
||||
CHECK(hist.size() == 3);
|
||||
CHECK(hist.numBins() == 2);
|
||||
CHECK(hist.minValue() == 1);
|
||||
CHECK(hist.maxValue() == 9);
|
||||
CHECK(hist.avg() == 11 / 3);
|
||||
CHECK(hist.percentile(0.0f) == hist.minValue());
|
||||
CHECK(hist.percentile(0.5f) == 1);
|
||||
CHECK(hist.percentile(0.9f) == 9);
|
||||
CHECK(hist.percentile(1.0f) == hist.maxValue());
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user