Compare commits

...

75 Commits

Author SHA1 Message Date
Onyad
aa8d4e4750 up 2025-09-04 14:42:16 -05:00
Christoph Grüninger
2f86d13775 Update googletest to 1.13
Fix issue with CMake 4.0
2025-04-29 16:53:58 -05:00
Christoph Grüninger
c9371de783 [cmake] Require minimum CMake version 3.5
Soon CMake 4.0 will be released. It requires that projects
set a minimum required CMake version of 3.5. There is a
workaround with an additional flag, but it would is better
to increase the minimum required version.
2025-04-29 16:53:58 -05:00
Salim B
28f93bdec6 docs: Fix link in README 2025-03-13 14:45:33 -05:00
Patrick Diehl
39f737443b Update README.md
Bump the release note to the latest version
2025-01-24 16:01:30 -06:00
Jeremy Nimmer
73ef0060aa Avoid including <iostream> in library code
Including iostream means introducing the static (global) constructors
and destructors for std::cin, std::cerr, and std::cout. That extra
init and fini code is undesirable when those streams are not actually
used.

Instead, we'll use the narrower includes for exactly what's needed,
i.e., <istream> or <ostream>.
2024-12-30 11:27:02 -06:00
Daniel Levin
7470c2d871 emitter: Support std::string_view
Accept Emitter::operator<<(std::string_view).

ABI remains C++11 compatible by exposing new method
Emitter::Write(const char*, size_t).

All affected calls optimized to pass std::string values as pointer + size
tuple into appropriate routines.
2024-12-22 16:22:38 -06:00
Jesse Beder
8a9a7b74ef Update CONTRIBUTING.md
Add some details about testing.
2024-12-22 16:16:07 -06:00
Jesse Beder
d45c4fba3e Update README.md 2024-12-18 11:43:01 -06:00
Jesse Beder
61db89741d Exclude ubuntu/C++ 11/googletest system from the test matrix since it doesn't compile. 2024-12-17 15:59:47 -06:00
Jesse Beder
7def2046ff Fix bazel build, and commit module lock file.
Including:
- Remove WORKSPACE and upgrade to MODULE.bazel.
- The integration tests were not included since the directory was misspelled.
- Their header files were not accessible.
2024-12-17 14:49:17 -06:00
IMaloney
1da813f56a ensuring local tags are retained after loading a node + test 2024-12-17 09:20:46 -06:00
Phil Miller
f878043f12 Don't install embedded copy of GTest
Goole Test itself documents the `INSTALL_GTEST` option as something that projects embedding it should set to `OFF`. Leaving it on means that projects downstream from libraries that embed it are likely to encounter conflicting copies. This is particularly annoying because GTest does not maintain anything like a stable API, and so causes builds to fail if include paths pick up an inappropriately installed copy ahead of the one that the code wanted.

Fixes #488
2024-12-17 09:12:49 -06:00
Simon Gene Gottlieb
9ce5a25188 fix: spell fix 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
ebfbf27115 patch: added more defensive programming techniques 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
06ffaf3104 remove reduntant license information 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
0b945415fd fix: add missing include 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
82f3c04081 fix: add YAML_CPP_API for windows-shared library builds 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
d8de96524c patch: move dragonbox.h from 'include' folder to 'src' folder 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
5d9e4b6251 patch: split fp_to_string.h into fptostring.h and fptostring.cpp 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
28c0a1bc25 doc: add a little more information to dragonbox file 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
72865f7d32 fix: remove trailing return types 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
f4bca79dc4 fix: fp_to_string uses now 2 spaces 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
d4e00bd473 fix: rename fp_to_string to FpToString to match coding style 2024-11-07 15:14:36 -06:00
Simon Gene Gottlieb
bd070a7b76 fix: prettier floating point numbers
Add dragonbox to compute the required precision to print floating point
numbers. This avoids uglification of floating point numbers that
happen by default via std::stringstream.

Numbers like 34.34 used to be converted to '34.340000000000003' as strings.
With this version they will be converted to the string '34.34'.

This fixes issue https://github.com/jbeder/yaml-cpp/issues/1289
2024-11-07 15:14:36 -06:00
Reini Urban
3d2888cc8a missing keys should throw InvalidNode, not BadConversion
Fixes GH #1274
2024-11-01 10:25:39 -05:00
Federico Di Pierro
c2bec4c755 chore(test): moved infiniteloop test to load_node_test suite.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2024-10-29 22:21:05 -05:00
Federico Di Pierro
29c59c01d4 new(test): added a test to avoid future issues with LoadAll().
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2024-10-29 22:21:05 -05:00
Federico Di Pierro
bc67157109 fix(src): avoid possible infinite loop in LoadAll().
Leave at first empty root.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2024-10-29 22:21:05 -05:00
Federico Di Pierro
da82fd982c chore(test/integration): refactor some test cases to their own test.
Plus, check that the content is what we actually expect.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2024-09-13 10:18:41 +02:00
Federico Di Pierro
1f2b841949 fix(src,include,test): fixed multiple cases where a bad yaml was accepted.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2024-09-13 10:18:41 +02:00
Orgad Shaneh
47cd2725d6 Remove build debug messages
They were commented out before 0733aeb45, but when that commit
was reverted in 2b65c65e1 they were recovered uncommented.
2024-08-27 12:23:42 -05:00
Orgad Shaneh
850ec4f39e Fix reference types in iterators
Amends 26faac387c.
2024-08-27 11:12:58 -05:00
Simon Gene Gottlieb
84459a7f98 fix: missing token enum name 2024-08-22 07:55:33 -05:00
Simon Gene Gottlieb
ee9c4d19be fix: parse files with '\r' symbols as line ending correctly 2024-08-22 07:53:52 -05:00
Cristian Le
b38ac5b55f Use FetchContent_MakeAvailable 2024-08-21 12:44:27 -05:00
Christopher Fore
7b469b4220 emitterutils: Explicitly include <cstdint>
GCC 15 will no longer include it by default, resulting in build
failures in projects that do not explicitly include it.

Error:
src/emitterutils.cpp:221:11: error: 'uint16_t' was not declared in this scope
  221 | std::pair<uint16_t, uint16_t> EncodeUTF16SurrogatePair(int codePoint) {
      |           ^~~~~~~~
src/emitterutils.cpp:13:1: note: 'uint16_t' is defined in header '<cstdint>';
this is probably fixable by adding '#include <cstdint>'
   12 | #include "yaml-cpp/null.h"
  +++ |+#include <cstdint>
   13 | #include "yaml-cpp/ostream_wrapper.h"

Tests pass.

Closes: #1307
See-also: https://gcc.gnu.org/pipermail/gcc-cvs/2024-August/407124.html
See-also: https://bugs.gentoo.org/937412
Signed-off-by: Christopher Fore <csfore@posteo.net>
2024-08-18 15:19:15 -05:00
Simon Gene Gottlieb
8fbf344aee doc, fix: invalid liquid '{{...}}' tags
jekyll/liquid got hung up on `{{"Daniel", 26}, {"Jesse", 24}}`.

The reason is that `{{...}}` are used as variables that are replaced by
there values. In this case we have a YAML object that looks the same.
This issue can be fixed by surrounding the block into `{% raw %}...{%
endraw %}` tags.
2024-08-06 10:40:52 -05:00
NameSirius
06c3d1db51 fix compile warning(Pull requests #1305)(Issues #1306) 2024-08-06 10:39:36 -05:00
Jesse Beder
04dddd6999 Revert "Fix compile warning with -Wshadow"
This reverts commit 1f5e971f77.

See #1306; the previous commit caused an error with -Wpedantic:

yaml-cpp/include/yaml-cpp/emitterstyle.h:13:2: error: extra ‘;’ [-Wpedantic]

Since the original commit was to resolve warnings, reverting and the OP can produce a new one that fixes this issue.
2024-08-05 15:50:36 -05:00
NameSirius
1f5e971f77 Fix compile warning with -Wshadow 2024-08-05 09:50:21 -05:00
Josiah VanderZee
b11eaf1631 Run format target from project root
The CMake format target does not use the correct .clang-format file in
out-of-source builds. This instructs CMake to use the project root as
the working directory for running the clang-format command so that it
finds the .clang-format file.
2024-08-02 07:20:10 -05:00
Josiah VanderZee
9f7babc3ff Use c-strings to constant initialize token array
Since `std::string` has to be dynamically constructed and destructed,
it could be accessed before initialization or after destruction in a
multithreaded context. By using constant c-strings instead, we guarantee
that the array will be valid for the whole lifetime of the program. The
use of `constexpr` also enforces this requirement.

I have run clang-format on the file to format my changes according to
CONTRIBUTING.md.
2024-08-01 22:54:57 -05:00
Simon Gene Gottlieb
b95aa146ec fix: use C locale by default 2024-07-17 13:39:30 -05:00
Frank Osterfeld
1d8ca1f35e Avoid static reference to temporary
These caused issues when used in a wasm project.
2024-05-02 09:29:05 -05:00
Humberto Dias
76dc671573 Add option YAML_ENABLE_PIC=ON 2024-02-22 12:12:48 -06:00
Humberto Dias
5c1bbbd276 Skip Position Independent Code for PSP/PS3 2024-02-22 12:12:48 -06:00
dependabot[bot]
4afd53b0d3 Bump the github-actions group with 1 update
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).

- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-28 15:49:18 -06:00
Jason Beach
96f5c887f3 assign fallback value 2024-01-26 13:28:55 -06:00
Alejandro-FA
c67d701ad8 Fix indentation of empty sequences and add test 2024-01-26 12:30:06 -06:00
Trompettesib
9eb1142900 Fix GIT_TAG field in cmake integration example 2024-01-04 09:40:42 -06:00
Alexandre TISSOT
c28295bda4 Add CMake integration example 2024-01-04 09:40:42 -06:00
Levi Armstrong
94710bb221 Add default destructor to EmitFromEvents 2023-12-28 10:44:52 -06:00
Diogo Teles Sant'Anna
c7639e81d5 ci: configure dependabot to montly update GHAs
Signed-off-by: Diogo Teles Sant'Anna <diogoteles@google.com>
2023-12-14 09:13:33 -06:00
Diogo Teles Sant'Anna
f30582fb09 ci: hash-pin sensitive workflow dependency
Signed-off-by: Diogo Teles Sant'Anna <diogoteles@google.com>
2023-12-14 09:13:33 -06:00
Alex Thiessen
eaf7205372 README.md: Describe building with debug libstdc++ mode 2023-11-27 14:29:13 -06:00
Alex Thiessen
1c5a38eef3 workflows/build.yml: Use debug std C++ library on Ubuntu
Protect from regressions due to use of undefined or
implementation-specific behavior when using `std::` containers and smart
pointers.

This only has effect on platforms with the GNU standard C++ library.

Refer to https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html.
2023-11-27 14:29:13 -06:00
Levi Armstrong
51adc5f739 Update yaml-cpp-config.cmake.in 2023-11-06 19:25:04 -06:00
Levi Armstrong
30e6933279 Fix yaml-cpp-config.cmake.in 2023-11-06 19:25:04 -06:00
Gianfranco Costamagna
db03655d58 Make sure CXX_STANDARD can be overridden from outside (#1239) 2023-10-23 07:20:22 -05:00
Gianfranco Costamagna
145eec5f3e Add CMake option YAML_USE_SYSTEM_GTEST to use system googletest if available. (#1035)
There is no need to use the embedded gtest code copy in Linux systems, if they already provide the googletest framework system-wide.
Search for it, and fallback to the embedded one if the system one is not detected.

This patch has been also contributed by Simon Quigley <tsimonq2@debian.org>
2023-10-22 00:06:39 -05:00
Ezekiel Warren
f791b955d8 remove unncessary continue-on-error (#1234) 2023-10-12 19:27:47 -04:00
Ezekiel Warren
4fc5ec36bc automatically create bzlmod archive for release (#1230) 2023-10-12 11:52:22 -04:00
Pete Lewis
8106fcf1e5 Resolve warnings about unreferenced parameter 'style' in NullEventHandlers (#1193) 2023-10-12 11:49:34 -04:00
Pierre Wendling
c26e047223 CMake: Fixes for generated config (#1212)
* CMake: Add option to set the package install dir.

* CMake: Fix generated config.

- `YAML_CPP_SHARED_LIBS_BUILT` should not be set with a `PATH_VAR` as it
would always evaluate to true.
- `YAML_CPP_LIBRARIES` should used the exported target name including
the namespace, but `check_required_components` shouldn't.
- Use `CMAKE_CURRENT_LIST_DIR` to find the target file, instead of a
`PATH_VAR`. Package managers such as vcpkg move CMake configs after
installing.

* CI: Test the generated CMake package.

* CMake: Create add a deprecated yaml-cpp target.

This target is meant to provide compatibility with versions prior to
0.8.0.

* CMake: mark the yaml-cpp target as IMPORTED.

---------

Co-authored-by: Jesse Beder <jbeder+github@gmail.com>
2023-10-12 11:48:16 -04:00
Megamouse
ac144ed46c Hide uninstall target unless YAML_CPP_INSTALL is set, and allow it to be disabled by YAML_CPP_DISABLE_UNINSTALL
add_custom_target uninstall causes a clash with the same target in rtmidi (https://github.com/thestk/rtmidi).
"add_custom_target cannot create target "uninstall" because another target with the same name already exists.  The existing target is a custom target created in source directory"

Adds new option YAML_CPP_DISABLE_UNINSTALL 

fixes #1151

---------

Co-authored-by: Megamouse <studienricky89@googlemail.de>
Co-authored-by: Jesse Beder <jbeder+github@gmail.com>
2023-10-11 00:02:33 -04:00
Alfi Maulana
4c061a5058 Disable format target when not building as the main project (#1231) 2023-10-10 23:40:09 -04:00
Paul Jurczak
37f1b8b8c9 Update How-To-Emit-YAML.md (#976) 2023-09-21 15:38:56 -05:00
FireWolf
d046eea331 node/convert: Enable the template specialization for std::string_view properly when the library is compiled by MSVC on Windows. (#1227) 2023-09-17 11:58:31 -04:00
Daniel Stonier
9f31491b0f bzlmodded (#1224) 2023-09-17 11:57:52 -04:00
Kefu Chai
016b2e7769 CI: test with supported C++ standards (#1226)
* CI: test with supported C++ standards

let's test with the supported standards for better coverage.
C++23 standard was just out, but the support on the toolchain is
not quite ready yet. so let's leave it for a future change.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>

* CI: drop redundant Build step

"Build Tests" is strictly a superset of "Build". in addition to
the library, the former builds the tests also. both these
steps share the same set of command line arguments. by removing
"Build" step, we don't lose anything regarding the test coverage
and information for further investigation if the build fails.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>

---------

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
2023-09-10 08:38:51 -04:00
Kefu Chai
2383e6d0f2 Revert "node/convert: relax the check for string_view (#1222)" (#1225)
This reverts commit 6262201182.

in 62622011, we wanted address the needs to use the `string_view`
converter in C++98, but that requirement was based on wrong
preconditions. `std::string_view` was introduced in C++17, and
popular standard libraries like libstdc++ and libc++ both provide
`std::string_view` when the source is built with C++17.

furthermore 62622011 is buggy. because it uses `<version>` to tell
the feature set provided by the standard library. but `<version>`
is a part of C++20. so this defeats the purpose of the change of
62622011.

Fixes #1223
2023-09-10 08:28:52 -04:00
Kefu Chai
6262201182 node/convert: relax the check for string_view (#1222) 2023-09-06 07:57:12 -05:00
Gianfranco Costamagna
fcbb8193b9 Fix testsuite with gcc-13 (#1216)
cd /<<PKGBUILDDIR>>/build-static/test && /usr/bin/c++ -DYAML_CPP_STATIC_DEFINE -I/<<PKGBUILDDIR>>/test/integration -I/<<PKGBUILDDIR>>/test -I/<<PKGBUILDDIR>>/src -I/<<PKGBUILDDIR>>/include -isystem /usr/src/googletest/googlemock/include -isystem /usr/src/googletest/googlemock -isystem /usr/src/googletest/googletest/include -isystem /usr/src/googletest/googletest -g -O2 -ffile-prefix-map=/<<PKGBUILDDIR>>=. -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fdebug-prefix-map=/<<PKGBUILDDIR>>=/usr/src/yaml-cpp-0.8.0+dfsg-1~build1 -Wdate-time -D_FORTIFY_SOURCE=2 -std=gnu++14 -Wno-variadic-macros -Wno-sign-compare -DGTEST_HAS_PTHREAD=1 -MD -MT test/CMakeFiles/yaml-cpp-tests.dir/binary_test.cpp.o -MF CMakeFiles/yaml-cpp-tests.dir/binary_test.cpp.o.d -o CMakeFiles/yaml-cpp-tests.dir/binary_test.cpp.o -c /<<PKGBUILDDIR>>/test/binary_test.cpp
/<<PKGBUILDDIR>>/test/binary_test.cpp: In member function ‘virtual void BinaryTest_DecodingNoCrashOnNegative_Test::TestBody()’:
/<<PKGBUILDDIR>>/test/binary_test.cpp:11:38: error: narrowing conversion of ‘-58’ from ‘int’ to ‘char’ [-Wnarrowing]
   11 |   std::string input{-58, -1, -99, 109};
      |                                      ^
2023-08-19 15:36:16 -04:00
Craig Scott
c268020048 Specify CMake policy range to avoid deprecation warning (#1211) 2023-08-16 17:54:05 -05:00
334 changed files with 26854 additions and 25648 deletions

1
.bazelignore Normal file
View File

@@ -0,0 +1 @@
test/googletest-1.13.0

View File

@@ -8,7 +8,7 @@ EXAMPLE_PATH =
# One or more directories and files to exclude from documentation generation. # One or more directories and files to exclude from documentation generation.
# Use relative paths with respect to the repository root directory. # Use relative paths with respect to the repository root directory.
EXCLUDE = test/gtest-1.8.0/ EXCLUDE = test/googletest-1.13.0/
# One or more wildcard patterns to exclude files and directories from document # One or more wildcard patterns to exclude files and directories from document
# generation. # generation.

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
github-actions:
patterns:
- "*"

View File

@@ -6,49 +6,101 @@ on:
branches: [ master ] branches: [ master ]
workflow_dispatch: workflow_dispatch:
permissions: read-all permissions: read-all
defaults:
run:
shell: bash
jobs: jobs:
cmake-build: cmake-build:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest] os: [ubuntu-latest, windows-latest, macos-latest]
cxx_standard: [11, 17, 20]
build: [static, shared] build: [static, shared]
googletest: [build, system]
generator: ["Default Generator", "MinGW Makefiles"] generator: ["Default Generator", "MinGW Makefiles"]
exclude: exclude:
- os: ubuntu-latest
cxx_standard: 11
googletest: system
- os: macos-latest - os: macos-latest
build: shared build: shared
- os: macos-latest - os: macos-latest
generator: "MinGW Makefiles" generator: "MinGW Makefiles"
- os: ubuntu-latest - os: ubuntu-latest
generator: "MinGW Makefiles" generator: "MinGW Makefiles"
- os: macos-latest
googletest: system
- os: windows-latest
googletest: system
env: env:
YAML_BUILD_SHARED_LIBS: ${{ matrix.build == 'shared' && 'ON' || 'OFF' }} YAML_BUILD_SHARED_LIBS: ${{ matrix.build == 'shared' && 'ON' || 'OFF' }}
YAML_CPP_BUILD_TESTS: 'ON' YAML_USE_SYSTEM_GTEST: ${{ matrix.googletest == 'system' && 'ON' || 'OFF' }}
CMAKE_GENERATOR: >- CMAKE_GENERATOR: >-
${{format(matrix.generator != 'Default Generator' && '-G "{0}"' || '', matrix.generator)}} ${{format(matrix.generator != 'Default Generator' && '-G "{0}"' || '', matrix.generator)}}
CMAKE_INSTALL_PREFIX: "${{ github.workspace }}/install-prefix"
CMAKE_BUILD_TYPE: Debug
CMAKE_CXX_FLAGS_DEBUG: ${{ matrix.googletest == 'build' && '-g -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC' || '-g' }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2
- name: Get number of CPU cores - uses: awalsh128/cache-apt-pkgs-action@latest
uses: SimenB/github-actions-cpu-cores@v1 if: matrix.os == 'ubuntu-latest'
with:
packages: googletest libgmock-dev libgtest-dev
version: 1.0
- uses: actions/checkout@v4
- name: Configure
run: |
cmake \
${{ env.CMAKE_GENERATOR }} \
-S "${{ github.workspace }}" \
-B build \
-D CMAKE_CXX_STANDARD=${{ matrix.cxx_standard }} \
-D CMAKE_INSTALL_PREFIX="${{ env.CMAKE_INSTALL_PREFIX }}" \
-D CMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \
-D CMAKE_CXX_FLAGS_DEBUG="${{ env.CMAKE_CXX_FLAGS_DEBUG }}" \
-D YAML_BUILD_SHARED_LIBS=${{ env.YAML_BUILD_SHARED_LIBS }} \
-D YAML_USE_SYSTEM_GTEST=${{ env.YAML_USE_SYSTEM_GTEST }} \
-D YAML_CPP_BUILD_TESTS=ON
- name: Build - name: Build
shell: bash
run: | run: |
cmake ${{ env.CMAKE_GENERATOR }} -S "${{ github.workspace }}" -B build -DYAML_BUILD_SHARED_LIBS=${{ env.YAML_BUILD_SHARED_LIBS }} cmake \
cd build && cmake --build . --parallel ${{ steps.cpu-cores.outputs.count }} --build build \
--config ${{ env.CMAKE_BUILD_TYPE }} \
- name: Build Tests --verbose \
shell: bash --parallel
run: |
cmake ${{ env.CMAKE_GENERATOR }} -S "${{ github.workspace }}" -B build -DYAML_BUILD_SHARED_LIBS=${{ env.YAML_BUILD_SHARED_LIBS }} -DYAML_CPP_BUILD_TESTS=${{ env.YAML_CPP_BUILD_TESTS }}
cd build && cmake --build . --parallel ${{ steps.cpu-cores.outputs.count }}
- name: Run Tests - name: Run Tests
shell: bash shell: bash
run: | run: |
cd build && ctest -C Debug --output-on-failure --verbose ctest \
--test-dir build \
--build-config ${{ env.CMAKE_BUILD_TYPE }} \
--output-on-failure \
--verbose
- name: Install
run: cmake --install build --config ${{ env.CMAKE_BUILD_TYPE }}
- name: Configure CMake package test
run: |
cmake \
${{ env.CMAKE_GENERATOR }} \
-S "${{ github.workspace }}/test/cmake" \
-B consumer-build \
-D CMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }} \
-D CMAKE_PREFIX_PATH="${{ env.CMAKE_INSTALL_PREFIX }}"
- name: Build CMake package test
run: |
cmake \
--build consumer-build \
--config ${{ env.CMAKE_BUILD_TYPE }} \
--verbose
bazel-build: bazel-build:
strategy: strategy:
@@ -56,17 +108,34 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest] os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Build - name: Build
shell: bash
run: | run: |
cd "${{ github.workspace }}" cd "${{ github.workspace }}"
bazel build :all bazel build :all
- name: Test - name: Test
shell: bash
run: | run: |
cd "${{ github.workspace }}" cd "${{ github.workspace }}"
bazel test test bazel test test
bzlmod-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Build
shell: bash
run: |
cd "${{ github.workspace }}"
bazel build --enable_bzlmod :all
- name: Test
shell: bash
run: |
cd "${{ github.workspace }}"
bazel test --enable_bzlmod test

19
.github/workflows/bzlmod-archive.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Bazel Release
on:
release:
types: [published]
jobs:
# A release archive is required for bzlmod
# See: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
bazel-release-archive:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- run: git archive $GITHUB_REF -o "yaml-cpp-${GITHUB_REF:10}.tar.gz"
- run: gh release upload ${GITHUB_REF:10} "yaml-cpp-${GITHUB_REF:10}.tar.gz"
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -1,5 +1,6 @@
# 3.5 is actually available almost everywhere, but this a good minimum # 3.5 is actually available almost everywhere.
cmake_minimum_required(VERSION 3.4) # 3.30 as the upper policy limit avoids CMake deprecation warnings.
cmake_minimum_required(VERSION 3.5...3.30)
# enable MSVC_RUNTIME_LIBRARY target property # enable MSVC_RUNTIME_LIBRARY target property
# see https://cmake.org/cmake/help/latest/policy/CMP0091.html # see https://cmake.org/cmake/help/latest/policy/CMP0091.html
@@ -24,13 +25,19 @@ option(YAML_CPP_BUILD_CONTRIB "Enable yaml-cpp contrib in library" ON)
option(YAML_CPP_BUILD_TOOLS "Enable parse tools" ON) option(YAML_CPP_BUILD_TOOLS "Enable parse tools" ON)
option(YAML_BUILD_SHARED_LIBS "Build yaml-cpp shared library" ${BUILD_SHARED_LIBS}) option(YAML_BUILD_SHARED_LIBS "Build yaml-cpp shared library" ${BUILD_SHARED_LIBS})
option(YAML_CPP_INSTALL "Enable generation of yaml-cpp install targets" ${YAML_CPP_MAIN_PROJECT}) option(YAML_CPP_INSTALL "Enable generation of yaml-cpp install targets" ${YAML_CPP_MAIN_PROJECT})
option(YAML_CPP_FORMAT_SOURCE "Format source" ON) option(YAML_CPP_FORMAT_SOURCE "Format source" ${YAML_CPP_MAIN_PROJECT})
option(YAML_CPP_DISABLE_UNINSTALL "Disable uninstallation of yaml-cpp" OFF)
option(YAML_USE_SYSTEM_GTEST "Use system googletest if found" OFF)
option(YAML_ENABLE_PIC "Use Position-Independent Code " ON)
cmake_dependent_option(YAML_CPP_BUILD_TESTS cmake_dependent_option(YAML_CPP_BUILD_TESTS
"Enable yaml-cpp tests" OFF "Enable yaml-cpp tests" OFF
"BUILD_TESTING;YAML_CPP_MAIN_PROJECT" OFF) "BUILD_TESTING;YAML_CPP_MAIN_PROJECT" OFF)
cmake_dependent_option(YAML_MSVC_SHARED_RT cmake_dependent_option(YAML_MSVC_SHARED_RT
"MSVC: Build yaml-cpp with shared runtime libs (/MD)" ON "MSVC: Build yaml-cpp with shared runtime libs (/MD)" ON
"CMAKE_SYSTEM_NAME MATCHES Windows" OFF) "CMAKE_SYSTEM_NAME MATCHES Windows" OFF)
set(YAML_CPP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/yaml-cpp"
CACHE STRING "Path to install the CMake package to")
if (YAML_CPP_FORMAT_SOURCE) if (YAML_CPP_FORMAT_SOURCE)
find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format) find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format)
@@ -85,7 +92,7 @@ set_property(TARGET yaml-cpp
CXX_STANDARD_REQUIRED ON) CXX_STANDARD_REQUIRED ON)
if (NOT YAML_BUILD_SHARED_LIBS) if (NOT YAML_BUILD_SHARED_LIBS)
set_property(TARGET yaml-cpp PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET yaml-cpp PROPERTY POSITION_INDEPENDENT_CODE ${YAML_ENABLE_PIC})
endif() endif()
target_include_directories(yaml-cpp target_include_directories(yaml-cpp
@@ -143,13 +150,12 @@ set_target_properties(yaml-cpp PROPERTIES
PROJECT_LABEL "yaml-cpp ${yaml-cpp-label-postfix}" PROJECT_LABEL "yaml-cpp ${yaml-cpp-label-postfix}"
DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}") DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}")
set(CONFIG_EXPORT_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/yaml-cpp") set(EXPORT_TARGETS yaml-cpp::yaml-cpp)
set(EXPORT_TARGETS yaml-cpp)
configure_package_config_file( configure_package_config_file(
"${PROJECT_SOURCE_DIR}/yaml-cpp-config.cmake.in" "${PROJECT_SOURCE_DIR}/yaml-cpp-config.cmake.in"
"${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake"
INSTALL_DESTINATION "${CONFIG_EXPORT_DIR}" INSTALL_DESTINATION "${YAML_CPP_INSTALL_CMAKEDIR}"
PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR CONFIG_EXPORT_DIR YAML_BUILD_SHARED_LIBS) PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR)
unset(EXPORT_TARGETS) unset(EXPORT_TARGETS)
write_basic_package_version_file( write_basic_package_version_file(
@@ -169,15 +175,14 @@ if (YAML_CPP_INSTALL)
FILES_MATCHING PATTERN "*.h") FILES_MATCHING PATTERN "*.h")
install(EXPORT yaml-cpp-targets install(EXPORT yaml-cpp-targets
NAMESPACE yaml-cpp:: NAMESPACE yaml-cpp::
DESTINATION "${CONFIG_EXPORT_DIR}") DESTINATION "${YAML_CPP_INSTALL_CMAKEDIR}")
install(FILES install(FILES
"${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake"
"${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake"
DESTINATION "${CONFIG_EXPORT_DIR}") DESTINATION "${YAML_CPP_INSTALL_CMAKEDIR}")
install(FILES "${PROJECT_BINARY_DIR}/yaml-cpp.pc" install(FILES "${PROJECT_BINARY_DIR}/yaml-cpp.pc"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif() endif()
unset(CONFIG_EXPORT_DIR)
if(YAML_CPP_BUILD_TESTS) if(YAML_CPP_BUILD_TESTS)
add_subdirectory(test) add_subdirectory(test)
@@ -192,11 +197,12 @@ if (YAML_CPP_FORMAT_SOURCE AND YAML_CPP_CLANG_FORMAT_EXE)
COMMAND clang-format --style=file -i $<TARGET_PROPERTY:yaml-cpp,SOURCES> COMMAND clang-format --style=file -i $<TARGET_PROPERTY:yaml-cpp,SOURCES>
COMMAND_EXPAND_LISTS COMMAND_EXPAND_LISTS
COMMENT "Running clang-format" COMMENT "Running clang-format"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
VERBATIM) VERBATIM)
endif() endif()
# uninstall target # uninstall target
if(NOT TARGET uninstall) if(YAML_CPP_INSTALL AND NOT YAML_CPP_DISABLE_UNINSTALL AND NOT TARGET uninstall)
configure_file( configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"

View File

@@ -17,9 +17,9 @@ Commit messages should be in the imperative mood, as described in the [Git contr
# Tests # Tests
Please verify the tests pass by running the target `test/yaml-cpp-tests`. Please verify the tests pass by configuring CMake with `-D YAML_CPP_BUILD_TESTS=ON` and running the target `test/yaml-cpp-tests`.
If you are adding functionality, add tests accordingly. If you are adding functionality, add tests accordingly. Note that the "spec tests" are reserved for examples directly from the YAML spec, so if you have new examples, put them in other test files.
# Pull request process # Pull request process

14
MODULE.bazel Normal file
View File

@@ -0,0 +1,14 @@
"""
yaml-cpp is a YAML parser and emitter in c++ matching the YAML specification.
"""
module(
name = "yaml-cpp",
compatibility_level = 1,
version = "0.8.0",
)
bazel_dep(name = "platforms", version = "0.0.7")
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "googletest", version = "1.14.0", dev_dependency = True)

114
MODULE.bazel.lock generated Normal file
View File

@@ -0,0 +1,114 @@
{
"lockFileVersion": 11,
"registryFileHashes": {
"https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
"https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
"https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
"https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
"https://bcr.bazel.build/modules/abseil-cpp/20230125.1/source.json": "06cc0842d241da0c5edc755edb3c7d0d008d304330e57ecf2d6449fb0b633a82",
"https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef",
"https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862",
"https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
"https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015",
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
"https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
"https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
"https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
"https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
"https://bcr.bazel.build/modules/bazel_skylib/1.6.1/source.json": "082ed5f9837901fada8c68c2f3ddc958bb22b6d654f71dd73f3df30d45d4b749",
"https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
"https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
"https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
"https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
"https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5",
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
"https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
"https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
"https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6",
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
"https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b",
"https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
"https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
"https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
"https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
"https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
"https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430",
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
"https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1",
"https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d",
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35",
"https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
"https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
"https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a",
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c",
"https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
"https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
"https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9",
"https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
"https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7",
"https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014",
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
"https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
"https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459",
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
"https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d"
},
"selectedYankedVersions": {},
"moduleExtensions": {
"@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": {
"general": {
"bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=",
"usagesDigest": "+hz7IHWN6A1oVJJWNDB6yZRG+RYhF76wAYItpAeIUIg=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"local_config_apple_cc_toolchains": {
"bzlFile": "@@apple_support~//crosstool:setup.bzl",
"ruleClassName": "_apple_cc_autoconf_toolchains",
"attributes": {}
},
"local_config_apple_cc": {
"bzlFile": "@@apple_support~//crosstool:setup.bzl",
"ruleClassName": "_apple_cc_autoconf",
"attributes": {}
}
},
"recordedRepoMappingEntries": [
[
"apple_support~",
"bazel_tools",
"bazel_tools"
]
]
}
},
"@@platforms//host:extension.bzl%host_platform": {
"general": {
"bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=",
"usagesDigest": "pCYpDQmqMbmiiPI1p2Kd3VLm5T48rRAht5WdW0X2GlA=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"host_platform": {
"bzlFile": "@@platforms//host:extension.bzl",
"ruleClassName": "host_platform_repo",
"attributes": {}
}
},
"recordedRepoMappingEntries": []
}
}
}
}

View File

@@ -31,6 +31,17 @@ cmake [-G generator] [-DYAML_BUILD_SHARED_LIBS=on|OFF] ..
* `yaml-cpp` builds a static library by default, you may want to build a shared library by specifying `-DYAML_BUILD_SHARED_LIBS=ON`. * `yaml-cpp` builds a static library by default, you may want to build a shared library by specifying `-DYAML_BUILD_SHARED_LIBS=ON`.
* [Debug mode of the GNU standard C++
library](https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html)
can be used when both `yaml-cpp` and client code is compiled with the
`_GLIBCXX_DEBUG` flag (e.g. by calling CMake with `-D
CMAKE_CXX_FLAGS_DEBUG='-g -D_GLIBCXX_DEBUG'` option).
Note that for `yaml-cpp` unit tests to run successfully, the _GoogleTest_
library also must be built with this flag, i.e. the system one cannot be
used (the _YAML_USE_SYSTEM_GTEST_ CMake option must be _OFF_, which is the
default).
* For more options on customizing the build, see the [CMakeLists.txt](https://github.com/jbeder/yaml-cpp/blob/master/CMakeLists.txt) file. * For more options on customizing the build, see the [CMakeLists.txt](https://github.com/jbeder/yaml-cpp/blob/master/CMakeLists.txt) file.
#### 2. Build it! #### 2. Build it!
@@ -38,13 +49,30 @@ cmake [-G generator] [-DYAML_BUILD_SHARED_LIBS=on|OFF] ..
**Note:** To clean up, just remove the `build` directory. **Note:** To clean up, just remove the `build` directory.
## How to Integrate it within your project using CMake
You can use for example FetchContent :
```cmake
include(FetchContent)
FetchContent_Declare(
yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG <tag_name> # Can be a tag (yaml-cpp-x.x.x), a commit hash, or a branch name (master)
)
FetchContent_MakeAvailable(yaml-cpp)
target_link_libraries(YOUR_LIBRARY PUBLIC yaml-cpp::yaml-cpp) # The library or executable that require yaml-cpp library
```
## Recent Releases ## Recent Releases
[yaml-cpp 0.6.0](https://github.com/jbeder/yaml-cpp/releases/tag/yaml-cpp-0.6.0) released! This release requires C++11, and no longer depends on Boost. [yaml-cpp 0.8.0](https://github.com/jbeder/yaml-cpp/releases/tag/0.8.0) released!
[yaml-cpp 0.3.0](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.3.0) is still available if you want the old API. [yaml-cpp 0.3.0](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.3.0) is still available if you want the old API.
**The old API will continue to be supported, and will still receive bugfixes!** The 0.3.x and 0.4.x versions will be old API releases, and 0.5.x and above will all be new API releases. **The old API will stop receiving bugfixes in 2026.** The 0.3.x versions provide the old API, and 0.5.x and above all provide the new API.
# API Documentation # API Documentation

View File

@@ -1,10 +0,0 @@
workspace(name = "com_github_jbeder_yaml_cpp")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_google_googletest",
strip_prefix = "googletest-release-1.8.1",
url = "https://github.com/google/googletest/archive/release-1.8.1.tar.gz",
sha256 = "9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c",
)

View File

@@ -154,16 +154,11 @@ produces
# STL Containers, and Other Overloads # # STL Containers, and Other Overloads #
We overload `operator <<` for `std::vector`, `std::list`, and `std::map`, so you can write stuff like: We overload `operator <<` for `std::vector`, `std::list`, and `std::map`, so you can write stuff like:
{% raw %}
```cpp ```cpp
std::vector <int> squares; std::vector <int> squares = {1, 4, 9, 16};
squares.push_back(1);
squares.push_back(4);
squares.push_back(9);
squares.push_back(16);
std::map <std::string, int> ages; std::map <std::string, int> ages = {{"Daniel", 26}, {"Jesse", 24}};
ages["Daniel"] = 26;
ages["Jesse"] = 24;
YAML::Emitter out; YAML::Emitter out;
out << YAML::BeginSeq; out << YAML::BeginSeq;
@@ -171,6 +166,7 @@ out << YAML::Flow << squares;
out << ages; out << ages;
out << YAML::EndSeq; out << YAML::EndSeq;
``` ```
{% endraw %}
produces produces

View File

@@ -15,11 +15,9 @@
# ifndef YAML_CPP_API # ifndef YAML_CPP_API
# ifdef yaml_cpp_EXPORTS # ifdef yaml_cpp_EXPORTS
/* We are building this library */ /* We are building this library */
# pragma message( "Defining YAML_CPP_API for DLL export" )
# define YAML_CPP_API __declspec(dllexport) # define YAML_CPP_API __declspec(dllexport)
# else # else
/* We are using this library */ /* We are using this library */
# pragma message( "Defining YAML_CPP_API for DLL import" )
# define YAML_CPP_API __declspec(dllimport) # define YAML_CPP_API __declspec(dllimport)
# endif # endif
# endif # endif

View File

@@ -23,6 +23,7 @@ class Emitter;
class EmitFromEvents : public EventHandler { class EmitFromEvents : public EventHandler {
public: public:
EmitFromEvents(Emitter& emitter); EmitFromEvents(Emitter& emitter);
~EmitFromEvents() override = default;
void OnDocumentStart(const Mark& mark) override; void OnDocumentStart(const Mark& mark) override;
void OnDocumentEnd() override; void OnDocumentEnd() override;

View File

@@ -9,18 +9,24 @@
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <cstring>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
#include <string_view>
#endif
#include "yaml-cpp/binary.h" #include "yaml-cpp/binary.h"
#include "yaml-cpp/dll.h" #include "yaml-cpp/dll.h"
#include "yaml-cpp/emitterdef.h" #include "yaml-cpp/emitterdef.h"
#include "yaml-cpp/emittermanip.h" #include "yaml-cpp/emittermanip.h"
#include "yaml-cpp/null.h" #include "yaml-cpp/null.h"
#include "yaml-cpp/ostream_wrapper.h" #include "yaml-cpp/ostream_wrapper.h"
#include "yaml-cpp/fptostring.h"
namespace YAML { namespace YAML {
class Binary; class Binary;
@@ -67,6 +73,7 @@ class YAML_CPP_API Emitter {
Emitter& SetLocalPrecision(const _Precision& precision); Emitter& SetLocalPrecision(const _Precision& precision);
// overloads of write // overloads of write
Emitter& Write(const char* str, std::size_t size);
Emitter& Write(const std::string& str); Emitter& Write(const std::string& str);
Emitter& Write(bool b); Emitter& Write(bool b);
Emitter& Write(char ch); Emitter& Write(char ch);
@@ -141,6 +148,7 @@ inline Emitter& Emitter::WriteIntegralType(T value) {
PrepareNode(EmitterNodeType::Scalar); PrepareNode(EmitterNodeType::Scalar);
std::stringstream stream; std::stringstream stream;
stream.imbue(std::locale("C"));
PrepareIntegralStream(stream); PrepareIntegralStream(stream);
stream << value; stream << value;
m_stream << stream.str(); m_stream << stream.str();
@@ -158,6 +166,7 @@ inline Emitter& Emitter::WriteStreamable(T value) {
PrepareNode(EmitterNodeType::Scalar); PrepareNode(EmitterNodeType::Scalar);
std::stringstream stream; std::stringstream stream;
stream.imbue(std::locale("C"));
SetStreamablePrecision<T>(stream); SetStreamablePrecision<T>(stream);
bool special = false; bool special = false;
@@ -178,7 +187,7 @@ inline Emitter& Emitter::WriteStreamable(T value) {
} }
if (!special) { if (!special) {
stream << value; stream << FpToString(value, stream.precision());
} }
m_stream << stream.str(); m_stream << stream.str();
@@ -198,8 +207,13 @@ inline void Emitter::SetStreamablePrecision<double>(std::stringstream& stream) {
} }
// overloads of insertion // overloads of insertion
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
inline Emitter& operator<<(Emitter& emitter, const std::string_view& v) {
return emitter.Write(v.data(), v.size());
}
#endif
inline Emitter& operator<<(Emitter& emitter, const std::string& v) { inline Emitter& operator<<(Emitter& emitter, const std::string& v) {
return emitter.Write(v); return emitter.Write(v.data(), v.size());
} }
inline Emitter& operator<<(Emitter& emitter, bool v) { inline Emitter& operator<<(Emitter& emitter, bool v) {
return emitter.Write(v); return emitter.Write(v);
@@ -230,7 +244,7 @@ inline Emitter& operator<<(Emitter& emitter, const Binary& b) {
} }
inline Emitter& operator<<(Emitter& emitter, const char* v) { inline Emitter& operator<<(Emitter& emitter, const char* v) {
return emitter.Write(std::string(v)); return emitter.Write(v, std::strlen(v));
} }
inline Emitter& operator<<(Emitter& emitter, int v) { inline Emitter& operator<<(Emitter& emitter, int v) {

View File

@@ -8,9 +8,10 @@
#endif #endif
namespace YAML { namespace YAML {
struct EmitterStyle { namespace EmitterStyle {
enum value { Default, Block, Flow }; enum value { Default, Block, Flow };
}; }
} }
#endif // EMITTERSTYLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #endif // EMITTERSTYLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -48,6 +48,8 @@ const char* const UNKNOWN_TOKEN = "unknown token";
const char* const DOC_IN_SCALAR = "illegal document indicator in scalar"; const char* const DOC_IN_SCALAR = "illegal document indicator in scalar";
const char* const EOF_IN_SCALAR = "illegal EOF in scalar"; const char* const EOF_IN_SCALAR = "illegal EOF in scalar";
const char* const CHAR_IN_SCALAR = "illegal character in scalar"; const char* const CHAR_IN_SCALAR = "illegal character in scalar";
const char* const UNEXPECTED_SCALAR = "unexpected scalar";
const char* const UNEXPECTED_FLOW = "plain value cannot start with flow indicator character";
const char* const TAB_IN_INDENTATION = const char* const TAB_IN_INDENTATION =
"illegal tab when looking for indentation"; "illegal tab when looking for indentation";
const char* const FLOW_END = "illegal flow end"; const char* const FLOW_END = "illegal flow end";

View File

@@ -0,0 +1,15 @@
#ifndef YAML_H_FPTOSTRING
#define YAML_H_FPTOSTRING
#include "yaml-cpp/dll.h"
#include <string>
namespace YAML {
// "precision = 0" refers to shortest known unique representation of the value
YAML_CPP_API std::string FpToString(float v, size_t precision = 0);
YAML_CPP_API std::string FpToString(double v, size_t precision = 0);
YAML_CPP_API std::string FpToString(long double v, size_t precision = 0);
}
#endif

View File

@@ -18,7 +18,7 @@
#include <valarray> #include <valarray>
#include <vector> #include <vector>
#if __cplusplus >= 201703L #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
#include <string_view> #include <string_view>
#endif #endif
@@ -28,6 +28,7 @@
#include "yaml-cpp/node/node.h" #include "yaml-cpp/node/node.h"
#include "yaml-cpp/node/type.h" #include "yaml-cpp/node/type.h"
#include "yaml-cpp/null.h" #include "yaml-cpp/null.h"
#include "yaml-cpp/fptostring.h"
namespace YAML { namespace YAML {
@@ -93,7 +94,7 @@ struct convert<char[N]> {
static Node encode(const char* rhs) { return Node(rhs); } static Node encode(const char* rhs) { return Node(rhs); }
}; };
#if __cplusplus >= 201703L #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template <> template <>
struct convert<std::string_view> { struct convert<std::string_view> {
static Node encode(std::string_view rhs) { return Node(std::string(rhs)); } static Node encode(std::string_view rhs) { return Node(std::string(rhs)); }
@@ -129,7 +130,7 @@ inner_encode(const T& rhs, std::stringstream& stream){
stream << ".inf"; stream << ".inf";
} }
} else { } else {
stream << rhs; stream << FpToString(rhs, stream.precision());
} }
} }
@@ -171,6 +172,7 @@ ConvertStreamTo(std::stringstream& stream, T& rhs) {
\ \
static Node encode(const type& rhs) { \ static Node encode(const type& rhs) { \
std::stringstream stream; \ std::stringstream stream; \
stream.imbue(std::locale("C")); \
stream.precision(std::numeric_limits<type>::max_digits10); \ stream.precision(std::numeric_limits<type>::max_digits10); \
conversion::inner_encode(rhs, stream); \ conversion::inner_encode(rhs, stream); \
return Node(stream.str()); \ return Node(stream.str()); \
@@ -182,6 +184,7 @@ ConvertStreamTo(std::stringstream& stream, T& rhs) {
} \ } \
const std::string& input = node.Scalar(); \ const std::string& input = node.Scalar(); \
std::stringstream stream(input); \ std::stringstream stream(input); \
stream.imbue(std::locale("C")); \
stream.unsetf(std::ios::dec); \ stream.unsetf(std::ios::dec); \
if ((stream.peek() == '-') && std::is_unsigned<type>::value) { \ if ((stream.peek() == '-') && std::is_unsigned<type>::value) { \
return false; \ return false; \

View File

@@ -41,7 +41,7 @@ class iterator_base {
using value_type = V; using value_type = V;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = V*; using pointer = V*;
using reference = V; using reference = V&;
public: public:
iterator_base() : m_iterator(), m_pMemory() {} iterator_base() : m_iterator(), m_pMemory() {}

View File

@@ -25,6 +25,7 @@ class YAML_CPP_API memory {
memory() : m_nodes{} {} memory() : m_nodes{} {}
node& create_node(); node& create_node();
void merge(const memory& rhs); void merge(const memory& rhs);
size_t size() const;
private: private:
using Nodes = std::set<shared_node>; using Nodes = std::set<shared_node>;

View File

@@ -69,7 +69,7 @@ class node_iterator_base {
using value_type = node_iterator_value<V>; using value_type = node_iterator_value<V>;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = node_iterator_value<V>*; using pointer = node_iterator_value<V>*;
using reference = node_iterator_value<V>; using reference = node_iterator_value<V>&;
using SeqIter = typename node_iterator_type<V>::seq; using SeqIter = typename node_iterator_type<V>::seq;
using MapIter = typename node_iterator_type<V>::map; using MapIter = typename node_iterator_type<V>::map;

View File

@@ -97,7 +97,7 @@ struct as_if {
if (!node.m_pNode) if (!node.m_pNode)
return fallback; return fallback;
T t; T t = fallback;
if (convert<T>::decode(node, t)) if (convert<T>::decode(node, t))
return t; return t;
return fallback; return fallback;
@@ -124,8 +124,8 @@ struct as_if<T, void> {
const Node& node; const Node& node;
T operator()() const { T operator()() const {
if (!node.m_pNode) if (!node.m_pNode) // no fallback
throw TypedBadConversion<T>(node.Mark()); throw InvalidNode(node.m_invalidKey);
T t; T t;
if (convert<T>::decode(node, t)) if (convert<T>::decode(node, t))
@@ -140,6 +140,8 @@ struct as_if<std::string, void> {
const Node& node; const Node& node;
std::string operator()() const { std::string operator()() const {
if (node.Type() == NodeType::Undefined) // no fallback
throw InvalidNode(node.m_invalidKey);
if (node.Type() == NodeType::Null) if (node.Type() == NodeType::Null)
return "null"; return "null";
if (node.Type() != NodeType::Scalar) if (node.Type() != NodeType::Scalar)

View File

@@ -8,9 +8,10 @@
#endif #endif
namespace YAML { namespace YAML {
struct NodeType { namespace NodeType {
enum value { Undefined, Null, Scalar, Sequence, Map }; enum value { Undefined, Null, Scalar, Sequence, Map };
}; }
} }
#endif // VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #endif // VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -8,7 +8,7 @@
#endif #endif
#include "yaml-cpp/dll.h" #include "yaml-cpp/dll.h"
#include <string> #include <cstddef>
namespace YAML { namespace YAML {
class Node; class Node;
@@ -18,7 +18,7 @@ inline bool operator==(const _Null&, const _Null&) { return true; }
inline bool operator!=(const _Null&, const _Null&) { return false; } inline bool operator!=(const _Null&, const _Null&) { return false; }
YAML_CPP_API bool IsNull(const Node& node); // old API only YAML_CPP_API bool IsNull(const Node& node); // old API only
YAML_CPP_API bool IsNullString(const std::string& str); YAML_CPP_API bool IsNullString(const char* str, std::size_t size);
extern YAML_CPP_API _Null Null; extern YAML_CPP_API _Null Null;
} }

View File

@@ -121,6 +121,7 @@ template<typename Key, bool Streamable>
struct streamable_to_string { struct streamable_to_string {
static std::string impl(const Key& key) { static std::string impl(const Key& key) {
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale("C"));
ss << key; ss << key;
return ss.str(); return ss.str();
} }

4196
src/contrib/dragonbox.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -116,8 +116,13 @@ void EmitFromEvents::BeginNode() {
} }
void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) { void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) {
if (!tag.empty() && tag != "?" && tag != "!") if (!tag.empty() && tag != "?" && tag != "!"){
m_emitter << VerbatimTag(tag); if (tag[0] == '!') {
m_emitter << LocalTag(std::string(tag.begin()+1, tag.end()));
} else {
m_emitter << VerbatimTag(tag);
}
}
if (anchor) if (anchor)
m_emitter << Anchor(ToString(anchor)); m_emitter << Anchor(ToString(anchor));
} }

View File

@@ -213,7 +213,8 @@ void Emitter::EmitEndSeq() {
if (m_pState->CurGroupFlowType() == FlowType::Flow) { if (m_pState->CurGroupFlowType() == FlowType::Flow) {
if (m_stream.comment()) if (m_stream.comment())
m_stream << "\n"; m_stream << "\n";
m_stream << IndentTo(m_pState->CurIndent()); if (originalType == FlowType::Block || m_pState->HasBegunNode())
m_stream << IndentTo(m_pState->CurIndent());
if (originalType == FlowType::Block) { if (originalType == FlowType::Block) {
m_stream << "["; m_stream << "[";
} else { } else {
@@ -715,33 +716,33 @@ StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) {
} }
} }
Emitter& Emitter::Write(const std::string& str) { Emitter& Emitter::Write(const char* str, std::size_t size) {
if (!good()) if (!good())
return *this; return *this;
StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset()); StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset());
const StringFormat::value strFormat = const StringFormat::value strFormat =
Utils::ComputeStringFormat(str, m_pState->GetStringFormat(), Utils::ComputeStringFormat(str, size, m_pState->GetStringFormat(),
m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii); m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii);
if (strFormat == StringFormat::Literal || str.size() > 1024) if (strFormat == StringFormat::Literal || size > 1024)
m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local); m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local);
PrepareNode(EmitterNodeType::Scalar); PrepareNode(EmitterNodeType::Scalar);
switch (strFormat) { switch (strFormat) {
case StringFormat::Plain: case StringFormat::Plain:
m_stream << str; m_stream.write(str, size);
break; break;
case StringFormat::SingleQuoted: case StringFormat::SingleQuoted:
Utils::WriteSingleQuotedString(m_stream, str); Utils::WriteSingleQuotedString(m_stream, str, size);
break; break;
case StringFormat::DoubleQuoted: case StringFormat::DoubleQuoted:
Utils::WriteDoubleQuotedString(m_stream, str, stringEscaping); Utils::WriteDoubleQuotedString(m_stream, str, size, stringEscaping);
break; break;
case StringFormat::Literal: case StringFormat::Literal:
Utils::WriteLiteralString(m_stream, str, Utils::WriteLiteralString(m_stream, str, size,
m_pState->CurIndent() + m_pState->GetIndent()); m_pState->CurIndent() + m_pState->GetIndent());
break; break;
} }
@@ -751,6 +752,10 @@ Emitter& Emitter::Write(const std::string& str) {
return *this; return *this;
} }
Emitter& Emitter::Write(const std::string& str) {
return Write(str.data(), str.size());
}
std::size_t Emitter::GetFloatPrecision() const { std::size_t Emitter::GetFloatPrecision() const {
return m_pState->GetFloatPrecision(); return m_pState->GetFloatPrecision();
} }
@@ -864,7 +869,7 @@ Emitter& Emitter::Write(const _Alias& alias) {
PrepareNode(EmitterNodeType::Scalar); PrepareNode(EmitterNodeType::Scalar);
if (!Utils::WriteAlias(m_stream, alias.content)) { if (!Utils::WriteAlias(m_stream, alias.content.data(), alias.content.size())) {
m_pState->SetError(ErrorMsg::INVALID_ALIAS); m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this; return *this;
} }
@@ -887,7 +892,7 @@ Emitter& Emitter::Write(const _Anchor& anchor) {
PrepareNode(EmitterNodeType::Property); PrepareNode(EmitterNodeType::Property);
if (!Utils::WriteAnchor(m_stream, anchor.content)) { if (!Utils::WriteAnchor(m_stream, anchor.content.data(), anchor.content.size())) {
m_pState->SetError(ErrorMsg::INVALID_ANCHOR); m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this; return *this;
} }
@@ -936,7 +941,7 @@ Emitter& Emitter::Write(const _Comment& comment) {
if (m_stream.col() > 0) if (m_stream.col() > 0)
m_stream << Indentation(m_pState->GetPreCommentIndent()); m_stream << Indentation(m_pState->GetPreCommentIndent());
Utils::WriteComment(m_stream, comment.content, Utils::WriteComment(m_stream, comment.content.data(), comment.content.size(),
m_pState->GetPostCommentIndent()); m_pState->GetPostCommentIndent());
m_pState->SetNonContent(); m_pState->SetNonContent();

View File

@@ -1,4 +1,5 @@
#include <algorithm> #include <algorithm>
#include <cstdint>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
@@ -88,8 +89,8 @@ int Utf8BytesIndicated(char ch) {
bool IsTrailingByte(char ch) { return (ch & 0xC0) == 0x80; } bool IsTrailingByte(char ch) { return (ch & 0xC0) == 0x80; }
bool GetNextCodePointAndAdvance(int& codePoint, bool GetNextCodePointAndAdvance(int& codePoint,
std::string::const_iterator& first, const char*& first,
std::string::const_iterator last) { const char* last) {
if (first == last) if (first == last)
return false; return false;
@@ -152,39 +153,39 @@ void WriteCodePoint(ostream_wrapper& out, int codePoint) {
} }
} }
bool IsValidPlainScalar(const std::string& str, FlowType::value flowType, bool IsValidPlainScalar(const char* str, std::size_t size, FlowType::value flowType,
bool allowOnlyAscii) { bool allowOnlyAscii) {
// check against null // check against null
if (IsNullString(str)) { if (IsNullString(str, size)) {
return false; return false;
} }
// check the start // check the start
const RegEx& start = (flowType == FlowType::Flow ? Exp::PlainScalarInFlow() const RegEx& start = (flowType == FlowType::Flow ? Exp::PlainScalarInFlow()
: Exp::PlainScalar()); : Exp::PlainScalar());
if (!start.Matches(str)) { if (!start.Matches(StringCharSource(str, size))) {
return false; return false;
} }
// and check the end for plain whitespace (which can't be faithfully kept in a // and check the end for plain whitespace (which can't be faithfully kept in a
// plain scalar) // plain scalar)
if (!str.empty() && *str.rbegin() == ' ') { if (size != 0 && str[size - 1] == ' ') {
return false; return false;
} }
// then check until something is disallowed // then check until something is disallowed
static const RegEx& disallowed_flow = static const RegEx disallowed_flow =
Exp::EndScalarInFlow() | (Exp::BlankOrBreak() + Exp::Comment()) | Exp::EndScalarInFlow() | (Exp::BlankOrBreak() + Exp::Comment()) |
Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() | Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() |
Exp::Tab() | Exp::Ampersand(); Exp::Tab() | Exp::Ampersand();
static const RegEx& disallowed_block = static const RegEx disallowed_block =
Exp::EndScalar() | (Exp::BlankOrBreak() + Exp::Comment()) | Exp::EndScalar() | (Exp::BlankOrBreak() + Exp::Comment()) |
Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() | Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() |
Exp::Tab() | Exp::Ampersand(); Exp::Tab() | Exp::Ampersand();
const RegEx& disallowed = const RegEx& disallowed =
flowType == FlowType::Flow ? disallowed_flow : disallowed_block; flowType == FlowType::Flow ? disallowed_flow : disallowed_block;
StringCharSource buffer(str.c_str(), str.size()); StringCharSource buffer(str, size);
while (buffer) { while (buffer) {
if (disallowed.Matches(buffer)) { if (disallowed.Matches(buffer)) {
return false; return false;
@@ -198,22 +199,22 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType,
return true; return true;
} }
bool IsValidSingleQuotedScalar(const std::string& str, bool escapeNonAscii) { bool IsValidSingleQuotedScalar(const char* str, std::size_t size, bool escapeNonAscii) {
// TODO: check for non-printable characters? // TODO: check for non-printable characters?
return std::none_of(str.begin(), str.end(), [=](char ch) { return std::none_of(str, str + size, [=](char ch) {
return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) || return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) ||
(ch == '\n'); (ch == '\n');
}); });
} }
bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType, bool IsValidLiteralScalar(const char* str, std::size_t size, FlowType::value flowType,
bool escapeNonAscii) { bool escapeNonAscii) {
if (flowType == FlowType::Flow) { if (flowType == FlowType::Flow) {
return false; return false;
} }
// TODO: check for non-printable characters? // TODO: check for non-printable characters?
return std::none_of(str.begin(), str.end(), [=](char ch) { return std::none_of(str, str + size, [=](char ch) {
return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))); return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch)));
}); });
} }
@@ -253,10 +254,10 @@ void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint, StringE
out << hexDigits[(codePoint >> (4 * (digits - 1))) & 0xF]; out << hexDigits[(codePoint >> (4 * (digits - 1))) & 0xF];
} }
bool WriteAliasName(ostream_wrapper& out, const std::string& str) { bool WriteAliasName(ostream_wrapper& out, const char* str, std::size_t size) {
int codePoint; int codePoint;
for (std::string::const_iterator i = str.begin(); for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str.end());) { GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (!IsAnchorChar(codePoint)) { if (!IsAnchorChar(codePoint)) {
return false; return false;
} }
@@ -267,25 +268,25 @@ bool WriteAliasName(ostream_wrapper& out, const std::string& str) {
} }
} // namespace } // namespace
StringFormat::value ComputeStringFormat(const std::string& str, StringFormat::value ComputeStringFormat(const char* str, std::size_t size,
EMITTER_MANIP strFormat, EMITTER_MANIP strFormat,
FlowType::value flowType, FlowType::value flowType,
bool escapeNonAscii) { bool escapeNonAscii) {
switch (strFormat) { switch (strFormat) {
case Auto: case Auto:
if (IsValidPlainScalar(str, flowType, escapeNonAscii)) { if (IsValidPlainScalar(str, size, flowType, escapeNonAscii)) {
return StringFormat::Plain; return StringFormat::Plain;
} }
return StringFormat::DoubleQuoted; return StringFormat::DoubleQuoted;
case SingleQuoted: case SingleQuoted:
if (IsValidSingleQuotedScalar(str, escapeNonAscii)) { if (IsValidSingleQuotedScalar(str, size, escapeNonAscii)) {
return StringFormat::SingleQuoted; return StringFormat::SingleQuoted;
} }
return StringFormat::DoubleQuoted; return StringFormat::DoubleQuoted;
case DoubleQuoted: case DoubleQuoted:
return StringFormat::DoubleQuoted; return StringFormat::DoubleQuoted;
case Literal: case Literal:
if (IsValidLiteralScalar(str, flowType, escapeNonAscii)) { if (IsValidLiteralScalar(str, size, flowType, escapeNonAscii)) {
return StringFormat::Literal; return StringFormat::Literal;
} }
return StringFormat::DoubleQuoted; return StringFormat::DoubleQuoted;
@@ -296,11 +297,11 @@ StringFormat::value ComputeStringFormat(const std::string& str,
return StringFormat::DoubleQuoted; return StringFormat::DoubleQuoted;
} }
bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) { bool WriteSingleQuotedString(ostream_wrapper& out, const char* str, std::size_t size) {
out << "'"; out << "'";
int codePoint; int codePoint;
for (std::string::const_iterator i = str.begin(); for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str.end());) { GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (codePoint == '\n') { if (codePoint == '\n') {
return false; // We can't handle a new line and the attendant indentation return false; // We can't handle a new line and the attendant indentation
// yet // yet
@@ -316,12 +317,12 @@ bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) {
return true; return true;
} }
bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str, bool WriteDoubleQuotedString(ostream_wrapper& out, const char* str, std::size_t size,
StringEscaping::value stringEscaping) { StringEscaping::value stringEscaping) {
out << "\""; out << "\"";
int codePoint; int codePoint;
for (std::string::const_iterator i = str.begin(); for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str.end());) { GetNextCodePointAndAdvance(codePoint, i, str + size);) {
switch (codePoint) { switch (codePoint) {
case '\"': case '\"':
out << "\\\""; out << "\\\"";
@@ -363,12 +364,12 @@ bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
return true; return true;
} }
bool WriteLiteralString(ostream_wrapper& out, const std::string& str, bool WriteLiteralString(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t indent) { std::size_t indent) {
out << "|\n"; out << "|\n";
int codePoint; int codePoint;
for (std::string::const_iterator i = str.begin(); for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str.end());) { GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (codePoint == '\n') { if (codePoint == '\n') {
out << "\n"; out << "\n";
} else { } else {
@@ -406,14 +407,14 @@ bool WriteChar(ostream_wrapper& out, char ch, StringEscaping::value stringEscapi
return true; return true;
} }
bool WriteComment(ostream_wrapper& out, const std::string& str, bool WriteComment(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t postCommentIndent) { std::size_t postCommentIndent) {
const std::size_t curIndent = out.col(); const std::size_t curIndent = out.col();
out << "#" << Indentation(postCommentIndent); out << "#" << Indentation(postCommentIndent);
out.set_comment(); out.set_comment();
int codePoint; int codePoint;
for (std::string::const_iterator i = str.begin(); for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str.end());) { GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (codePoint == '\n') { if (codePoint == '\n') {
out << "\n" out << "\n"
<< IndentTo(curIndent) << "#" << Indentation(postCommentIndent); << IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
@@ -425,14 +426,14 @@ bool WriteComment(ostream_wrapper& out, const std::string& str,
return true; return true;
} }
bool WriteAlias(ostream_wrapper& out, const std::string& str) { bool WriteAlias(ostream_wrapper& out, const char* str, std::size_t size) {
out << "*"; out << "*";
return WriteAliasName(out, str); return WriteAliasName(out, str, size);
} }
bool WriteAnchor(ostream_wrapper& out, const std::string& str) { bool WriteAnchor(ostream_wrapper& out, const char* str, std::size_t size) {
out << "&"; out << "&";
return WriteAliasName(out, str); return WriteAliasName(out, str, size);
} }
bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim) { bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim) {
@@ -489,7 +490,8 @@ bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
} }
bool WriteBinary(ostream_wrapper& out, const Binary& binary) { bool WriteBinary(ostream_wrapper& out, const Binary& binary) {
WriteDoubleQuotedString(out, EncodeBase64(binary.data(), binary.size()), std::string encoded = EncodeBase64(binary.data(), binary.size());
WriteDoubleQuotedString(out, encoded.data(), encoded.size(),
StringEscaping::None); StringEscaping::None);
return true; return true;
} }

View File

@@ -29,22 +29,22 @@ struct StringEscaping {
}; };
namespace Utils { namespace Utils {
StringFormat::value ComputeStringFormat(const std::string& str, StringFormat::value ComputeStringFormat(const char* str, std::size_t size,
EMITTER_MANIP strFormat, EMITTER_MANIP strFormat,
FlowType::value flowType, FlowType::value flowType,
bool escapeNonAscii); bool escapeNonAscii);
bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str); bool WriteSingleQuotedString(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str, bool WriteDoubleQuotedString(ostream_wrapper& out, const char* str, std::size_t size,
StringEscaping::value stringEscaping); StringEscaping::value stringEscaping);
bool WriteLiteralString(ostream_wrapper& out, const std::string& str, bool WriteLiteralString(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t indent); std::size_t indent);
bool WriteChar(ostream_wrapper& out, char ch, bool WriteChar(ostream_wrapper& out, char ch,
StringEscaping::value stringEscapingStyle); StringEscaping::value stringEscapingStyle);
bool WriteComment(ostream_wrapper& out, const std::string& str, bool WriteComment(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t postCommentIndent); std::size_t postCommentIndent);
bool WriteAlias(ostream_wrapper& out, const std::string& str); bool WriteAlias(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteAnchor(ostream_wrapper& out, const std::string& str); bool WriteAnchor(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim); bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim);
bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix, bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
const std::string& tag); const std::string& tag);

238
src/fptostring.cpp Normal file
View File

@@ -0,0 +1,238 @@
#include "yaml-cpp/fptostring.h"
#include "contrib/dragonbox.h"
#include <array>
#include <cassert>
#include <cmath>
#include <limits>
#include <sstream>
#include <tuple>
namespace YAML {
namespace detail {
namespace fp_formatting {
/**
* Converts a integer into its ASCII digits.
*
* @param begin/end - a buffer, must be at least 20bytes long.
* @param value - input value.
* @param width - minimum number of digits, fill with '0' to the left. Must be equal or smaller than the buffer size.
* @return - number of digits filled into the buffer (or -1 if preconditions are not meet)
*
* Example:
* std::array<char, 20> buffer;
* auto ct = ConvertToChars(buffer.begin(), buffer.end(), 23, 3);
* assert(ct = 3);
* assert(buffer[0] == '0');
* assert(buffer[1] == '2');
* assert(buffer[2] == '3');
*/
int ConvertToChars(char* begin, char* end, size_t value, int width=1) {
// precondition of this function (will trigger in debug build)
assert(width >= 1);
assert(end >= begin); // end must be after begin
assert(end-begin >= width); // Buffer must be large enough
assert(end-begin >= 20); // 2^64 has 20digits, so at least 20 digits must be available
// defensive programming, abort if precondition are not met (will trigger in release build)
if (width < 1) {
return -1;
}
if (end < begin) {
return -1;
}
if (end-begin < width) {
return -1;
}
if (end-begin < 20) {
return -1;
}
// count number of digits, and fill digits array accordingly
int digits_ct{};
while (value > 0) {
char c = value % 10 + '0';
value = value / 10;
digits_ct += 1;
*(end-digits_ct) = c;
}
while(digits_ct < width) {
assert(digits_ct < 64);
digits_ct += 1;
*(end-digits_ct) = '0';
}
// move data to the front of the array
std::memmove(begin, end-digits_ct, digits_ct);
return digits_ct;
}
/**
* Converts a float or double to a string.
*
* converts a value 'v' to a string. Uses dragonbox for formatting.
*/
template <typename T>
std::string FpToString(T v, int precision = 0) {
// hard coded constant, at which exponent should switch to a scientific notation
int const lowerExponentThreshold = -5;
int const upperExponentThreshold = (precision==0)?6:precision;
if (precision == 0) {
precision = 6;
}
// dragonbox/to_decimal does not handle value 0, inf, NaN
if (v == 0 || std::isinf(v) || std::isnan(v)) {
std::stringstream ss;
ss.imbue(std::locale("C"));
ss << v;
return ss.str();
}
auto r = jkj::dragonbox::to_decimal(v);
auto digits = std::array<char, 20>{}; // max digits of size_t is 20.
auto digits_ct = ConvertToChars(digits.data(), digits.data() + digits.size(), r.significand);
// defensive programming, ConvertToChars arguments are invalid
if (digits_ct == -1) {
std::stringstream ss;
ss.imbue(std::locale("C"));
ss << v;
return ss.str();
}
// check if requested precision is lower than
// required digits for exact representation
if (digits_ct > precision) {
auto diff = digits_ct - precision;
r.exponent += diff;
digits_ct = precision;
// round numbers if required
if (digits[digits_ct] >= '5') {
int i{digits_ct-1};
digits[i] += 1;
while (digits[i] == '9'+1) {
digits_ct -= 1;
r.exponent += 1;
if (i > 0) {
digits[i-1] += 1;
i -= 1;
} else {
digits_ct = 1;
digits[0] = '1';
break;
}
}
}
}
std::array<char, 28> output_buffer; // max digits of size_t plus sign, a dot and 2 letters for 'e+' or 'e-' and 4 letters for the exponent
auto output_ptr = &output_buffer[0];
// print '-' symbol for negative numbers
if (r.is_negative) {
*(output_ptr++) = '-';
}
// exponent if only a single non-zero digit is before the decimal point
int const exponent = r.exponent + digits_ct - 1;
// case 1: scientific notation
if (exponent >= upperExponentThreshold || exponent <= lowerExponentThreshold) {
// print first digit
*(output_ptr++) = digits[0];
// print digits after decimal point
if (digits_ct > 1) {
*(output_ptr++) = '.';
// print significant numbers after decimal point
for (int i{1}; i < digits_ct; ++i) {
*(output_ptr++) = digits[i];
}
}
*(output_ptr++) = 'e';
*(output_ptr++) = (exponent>=0)?'+':'-';
auto exp_digits = std::array<char, 20>{};
auto exp_digits_ct = ConvertToChars(exp_digits.data(), exp_digits.data() + exp_digits.size(), std::abs(exponent), /*.precision=*/ 2);
// defensive programming, ConvertToChars arguments are invalid
if (exp_digits_ct == -1) {
std::stringstream ss;
ss.imbue(std::locale("C"));
ss << v;
return ss.str();
}
for (int i{0}; i < exp_digits_ct; ++i) {
*(output_ptr++) = exp_digits[i];
}
// case 2: default notation
} else {
auto const digits_end = digits.begin() + digits_ct;
auto digits_iter = digits.begin();
// print digits before point
int const before_decimal_digits = digits_ct + r.exponent;
if (before_decimal_digits > 0) {
// print digits before point
for (int i{0}; i < std::min(before_decimal_digits, digits_ct); ++i) {
*(output_ptr++) = *(digits_iter++);
}
// print trailing zeros before point
for (int i{0}; i < before_decimal_digits - digits_ct; ++i) {
*(output_ptr++) = '0';
}
// print 0 before point if none where printed before
} else {
*(output_ptr++) = '0';
}
if (digits_iter != digits_end) {
*(output_ptr++) = '.';
// print 0 after decimal point, to fill until first digits
int const after_decimal_zeros = -digits_ct - r.exponent;
for (int i{0}; i < after_decimal_zeros; ++i) {
*(output_ptr++) = '0';
}
// print significant numbers after decimal point
for (;digits_iter < digits_end; ++digits_iter) {
*(output_ptr++) = *digits_iter;
}
}
}
*output_ptr = '\0';
return std::string{&output_buffer[0], output_ptr};
}
}
}
std::string FpToString(float v, size_t precision) {
return detail::fp_formatting::FpToString(v, precision);
}
std::string FpToString(double v, size_t precision) {
return detail::fp_formatting::FpToString(v, precision);
}
/**
* dragonbox only works for floats/doubles not long double
*/
std::string FpToString(long double v, size_t precision) {
std::stringstream ss;
ss.imbue(std::locale("C"));
if (precision == 0) {
precision = std::numeric_limits<long double>::max_digits10;
}
ss.precision(precision);
ss << v;
return ss.str();
}
}

View File

@@ -7,7 +7,6 @@
#pragma once #pragma once
#endif #endif
#include <iostream>
#include <cstddef> #include <cstddef>
#include "yaml-cpp/ostream_wrapper.h" #include "yaml-cpp/ostream_wrapper.h"

View File

@@ -9,6 +9,10 @@ void memory_holder::merge(memory_holder& rhs) {
if (m_pMemory == rhs.m_pMemory) if (m_pMemory == rhs.m_pMemory)
return; return;
if (m_pMemory->size() < rhs.m_pMemory->size()) {
std::swap(m_pMemory, rhs.m_pMemory);
}
m_pMemory->merge(*rhs.m_pMemory); m_pMemory->merge(*rhs.m_pMemory);
rhs.m_pMemory = m_pMemory; rhs.m_pMemory = m_pMemory;
} }
@@ -22,5 +26,9 @@ node& memory::create_node() {
void memory::merge(const memory& rhs) { void memory::merge(const memory& rhs) {
m_nodes.insert(rhs.m_nodes.begin(), rhs.m_nodes.end()); m_nodes.insert(rhs.m_nodes.begin(), rhs.m_nodes.end());
} }
size_t memory::size() const {
return m_nodes.size();
}
} // namespace detail } // namespace detail
} // namespace YAML } // namespace YAML

View File

@@ -310,6 +310,7 @@ void node_data::convert_sequence_to_map(const shared_memory_holder& pMemory) {
reset_map(); reset_map();
for (std::size_t i = 0; i < m_sequence.size(); i++) { for (std::size_t i = 0; i < m_sequence.size(); i++) {
std::stringstream stream; std::stringstream stream;
stream.imbue(std::locale("C"));
stream << i; stream << i;
node& key = pMemory->create_node(); node& key = pMemory->create_node();

View File

@@ -1,10 +1,17 @@
#include "yaml-cpp/null.h" #include "yaml-cpp/null.h"
#include <cstring>
namespace YAML { namespace YAML {
_Null Null; _Null Null;
bool IsNullString(const std::string& str) { template <std::size_t N>
return str.empty() || str == "~" || str == "null" || str == "Null" || static bool same(const char* str, std::size_t size, const char (&literal)[N]) {
str == "NULL"; constexpr int literalSize = N - 1; // minus null terminator
return size == literalSize && std::strncmp(str, literal, literalSize) == 0;
}
bool IsNullString(const char* str, std::size_t size) {
return size == 0 || same(str, size, "~") || same(str, size, "null") ||
same(str, size, "Null") || same(str, size, "NULL");
} }
} // namespace YAML } // namespace YAML

View File

@@ -2,7 +2,7 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <iostream> #include <ostream>
namespace YAML { namespace YAML {
ostream_wrapper::ostream_wrapper() ostream_wrapper::ostream_wrapper()

View File

@@ -53,7 +53,7 @@ std::vector<Node> LoadAll(std::istream& input) {
Parser parser(input); Parser parser(input);
while (true) { while (true) {
NodeBuilder builder; NodeBuilder builder;
if (!parser.HandleNextDocument(builder)) { if (!parser.HandleNextDocument(builder) || builder.Root().IsNull()) {
break; break;
} }
docs.push_back(builder.Root()); docs.push_back(builder.Root());

View File

@@ -77,6 +77,7 @@ void Parser::HandleYamlDirective(const Token& token) {
} }
std::stringstream str(token.params[0]); std::stringstream str(token.params[0]);
str.imbue(std::locale("C"));
str >> m_pDirectives->version.major; str >> m_pDirectives->version.major;
str.get(); str.get();
str >> m_pDirectives->version.minor; str >> m_pDirectives->version.minor;

View File

@@ -27,6 +27,10 @@ inline bool RegEx::Matches(const Stream& in) const { return Match(in) >= 0; }
template <typename Source> template <typename Source>
inline bool RegEx::Matches(const Source& source) const { inline bool RegEx::Matches(const Source& source) const {
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) || __cplusplus >= 201103L)
static_assert(!std::is_same<Source, const char*>::value,
#endif
"Must use StringCharSource instead of plain C-string");
return Match(source) >= 0; return Match(source) >= 0;
} }

View File

@@ -13,6 +13,7 @@ Scanner::Scanner(std::istream& in)
m_startedStream(false), m_startedStream(false),
m_endedStream(false), m_endedStream(false),
m_simpleKeyAllowed(false), m_simpleKeyAllowed(false),
m_scalarValueAllowed(false),
m_canBeJSONFlow(false), m_canBeJSONFlow(false),
m_simpleKeys{}, m_simpleKeys{},
m_indents{}, m_indents{},
@@ -127,6 +128,17 @@ void Scanner::ScanNextToken() {
} }
if (INPUT.peek() == Keys::FlowEntry) { if (INPUT.peek() == Keys::FlowEntry) {
// values starting with `,` are not allowed.
// eg: reject `,foo`
if (INPUT.column() == 0) {
throw ParserException(INPUT.mark(), ErrorMsg::UNEXPECTED_FLOW);
}
// if we already parsed a quoted scalar value and we are not in a flow,
// then `,` is not a valid character.
// eg: reject `"foo",`
if (!m_scalarValueAllowed) {
throw ParserException(INPUT.mark(), ErrorMsg::UNEXPECTED_SCALAR);
}
return ScanFlowEntry(); return ScanFlowEntry();
} }
@@ -159,6 +171,13 @@ void Scanner::ScanNextToken() {
return ScanBlockScalar(); return ScanBlockScalar();
} }
// if we already parsed a quoted scalar value in this line,
// another scalar value is an error.
// eg: reject `"foo" "bar"`
if (!m_scalarValueAllowed) {
throw ParserException(INPUT.mark(), ErrorMsg::UNEXPECTED_SCALAR);
}
if (INPUT.peek() == '\'' || INPUT.peek() == '\"') { if (INPUT.peek() == '\'' || INPUT.peek() == '\"') {
return ScanQuotedScalar(); return ScanQuotedScalar();
} }
@@ -203,6 +222,9 @@ void Scanner::ScanToNextToken() {
// oh yeah, and let's get rid of that simple key // oh yeah, and let's get rid of that simple key
InvalidateSimpleKey(); InvalidateSimpleKey();
// new line - we accept a scalar value now
m_scalarValueAllowed = true;
// new line - we may be able to accept a simple key now // new line - we may be able to accept a simple key now
if (InBlockContext()) { if (InBlockContext()) {
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
@@ -245,6 +267,7 @@ const RegEx& Scanner::GetValueRegex() const {
void Scanner::StartStream() { void Scanner::StartStream() {
m_startedStream = true; m_startedStream = true;
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_scalarValueAllowed = true;
std::unique_ptr<IndentMarker> pIndent( std::unique_ptr<IndentMarker> pIndent(
new IndentMarker(-1, IndentMarker::NONE)); new IndentMarker(-1, IndentMarker::NONE));
m_indentRefs.push_back(std::move(pIndent)); m_indentRefs.push_back(std::move(pIndent));
@@ -261,6 +284,7 @@ void Scanner::EndStream() {
PopAllSimpleKeys(); PopAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_scalarValueAllowed = false;
m_endedStream = true; m_endedStream = true;
} }

View File

@@ -177,6 +177,7 @@ class Scanner {
// state info // state info
bool m_startedStream, m_endedStream; bool m_startedStream, m_endedStream;
bool m_simpleKeyAllowed; bool m_simpleKeyAllowed;
bool m_scalarValueAllowed;
bool m_canBeJSONFlow; bool m_canBeJSONFlow;
std::stack<SimpleKey> m_simpleKeys; std::stack<SimpleKey> m_simpleKeys;
std::stack<IndentMarker *> m_indents; std::stack<IndentMarker *> m_indents;

View File

@@ -211,6 +211,9 @@ void Scanner::ScanValue() {
m_simpleKeyAllowed = InBlockContext(); m_simpleKeyAllowed = InBlockContext();
} }
// we are parsing a `key: value` pair; scalars are always allowed
m_scalarValueAllowed = true;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
INPUT.eat(1); INPUT.eat(1);
@@ -360,6 +363,10 @@ void Scanner::ScanQuotedScalar() {
// and scan // and scan
scalar = ScanScalar(INPUT, params); scalar = ScanScalar(INPUT, params);
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// we just scanned a quoted scalar;
// we can only have another scalar in this line
// if we are in a flow, eg: `[ "foo", "bar" ]` is ok, but `"foo", "bar"` isn't.
m_scalarValueAllowed = InFlowContext();
m_canBeJSONFlow = true; m_canBeJSONFlow = true;
Token token(Token::NON_PLAIN_SCALAR, mark); Token token(Token::NON_PLAIN_SCALAR, mark);

View File

@@ -1,4 +1,3 @@
#include <algorithm>
#include <cstdio> #include <cstdio>
#include <sstream> #include <sstream>
@@ -95,7 +94,7 @@ void SingleDocParser::HandleNode(EventHandler& eventHandler) {
tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?"); tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?");
if (token.type == Token::PLAIN_SCALAR if (token.type == Token::PLAIN_SCALAR
&& tag.compare("?") == 0 && IsNullString(token.value)) { && tag.compare("?") == 0 && IsNullString(token.value.data(), token.value.size())) {
eventHandler.OnNull(mark, anchor); eventHandler.OnNull(mark, anchor);
m_scanner.pop(); m_scanner.pop();
return; return;

View File

@@ -1,4 +1,4 @@
#include <iostream> #include <istream>
#include "stream.h" #include "stream.h"
@@ -262,7 +262,24 @@ char Stream::get() {
AdvanceCurrent(); AdvanceCurrent();
m_mark.column++; m_mark.column++;
if (ch == '\n') { // if line ending symbol is unknown, set it to the first
// encountered line ending.
// if line ending '\r' set ending symbol to '\r'
// other wise set it to '\n'
if (!m_lineEndingSymbol) {
if (ch == '\n') { // line ending is '\n'
m_lineEndingSymbol = '\n';
} else if (ch == '\r') {
auto ch2 = peek();
if (ch2 == '\n') { // line ending is '\r\n'
m_lineEndingSymbol = '\n';
} else { // line ending is '\r'
m_lineEndingSymbol = '\r';
}
}
}
if (ch == m_lineEndingSymbol) {
m_mark.column = 0; m_mark.column = 0;
m_mark.line++; m_mark.line++;
} }

View File

@@ -11,7 +11,7 @@
#include <cstddef> #include <cstddef>
#include <deque> #include <deque>
#include <ios> #include <ios>
#include <iostream> #include <istream>
#include <set> #include <set>
#include <string> #include <string>
@@ -53,6 +53,7 @@ class Stream {
Mark m_mark; Mark m_mark;
CharacterSet m_charSet; CharacterSet m_charSet;
char m_lineEndingSymbol{}; // 0 means it is not determined yet, must be '\n' or '\r'
mutable std::deque<char> m_readahead; mutable std::deque<char> m_readahead;
unsigned char* const m_pPrefetched; unsigned char* const m_pPrefetched;
mutable size_t m_nPrefetchedAvailable; mutable size_t m_nPrefetchedAvailable;

View File

@@ -8,17 +8,18 @@
#endif #endif
#include "yaml-cpp/mark.h" #include "yaml-cpp/mark.h"
#include <iostream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
namespace YAML { namespace YAML {
const std::string TokenNames[] = { constexpr const char* TokenNames[] = {
"DIRECTIVE", "DOC_START", "DOC_END", "BLOCK_SEQ_START", "DIRECTIVE", "DOC_START", "DOC_END", "BLOCK_SEQ_START",
"BLOCK_MAP_START", "BLOCK_SEQ_END", "BLOCK_MAP_END", "BLOCK_ENTRY", "BLOCK_MAP_START", "BLOCK_SEQ_END", "BLOCK_MAP_END", "BLOCK_ENTRY",
"FLOW_SEQ_START", "FLOW_MAP_START", "FLOW_SEQ_END", "FLOW_MAP_END", "FLOW_SEQ_START", "FLOW_MAP_START", "FLOW_SEQ_END", "FLOW_MAP_END",
"FLOW_MAP_COMPACT", "FLOW_ENTRY", "KEY", "VALUE", "FLOW_MAP_COMPACT", "FLOW_ENTRY", "KEY", "VALUE",
"ANCHOR", "ALIAS", "TAG", "SCALAR"}; "ANCHOR", "ALIAS", "TAG", "SCALAR",
"NON_PLAIN_SCALAR"};
struct Token { struct Token {
// enums // enums

View File

@@ -1,14 +1,28 @@
package(default_visibility = ["//test:__subpackages__"])
cc_library(
name = "mock_event_handler",
hdrs = ["mock_event_handler.h"],
strip_include_prefix = "//test",
)
cc_library(
name = "specexamples",
hdrs = ["specexamples.h"],
strip_include_prefix = "//test",
)
cc_test( cc_test(
name = "test", name = "test",
srcs = glob([ srcs = glob([
"*.cpp", "*.cpp",
"*.h",
"integrations/*.cpp",
"node/*.cpp", "node/*.cpp",
]), ]),
deps = [ deps = [
":mock_event_handler",
":specexamples",
"//:yaml-cpp", "//:yaml-cpp",
"//:yaml-cpp_internal", "//:yaml-cpp_internal",
"@com_google_googletest//:gtest_main", "@googletest//:gtest_main",
], ],
) )

View File

@@ -2,13 +2,19 @@ find_package(Threads REQUIRED)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(BUILD_MOCK ON CACHE BOOL "" FORCE) set(BUILD_MOCK ON CACHE BOOL "" FORCE)
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW) set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
add_subdirectory( if(YAML_USE_SYSTEM_GTEST)
"${CMAKE_CURRENT_SOURCE_DIR}/gtest-1.11.0" find_package(GTest)
"${CMAKE_CURRENT_BINARY_DIR}/prefix") if (NOT GTEST_FOUND)
message(FATAL_ERROR "system googletest was requested but not found")
include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/gtest-1.11.0/googletest/include") endif()
else()
add_subdirectory(
"${CMAKE_CURRENT_SOURCE_DIR}/googletest-1.13.0"
"${CMAKE_CURRENT_BINARY_DIR}/prefix")
include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/googletest-1.13.0/googletest/include")
endif()
set(test-new-api-pattern "new-api/*.cpp") set(test-new-api-pattern "new-api/*.cpp")
set(test-source-pattern "*.cpp" "integration/*.cpp" "node/*.cpp") set(test-source-pattern "*.cpp" "integration/*.cpp" "node/*.cpp")
@@ -33,11 +39,12 @@ target_include_directories(yaml-cpp-tests
target_compile_options(yaml-cpp-tests target_compile_options(yaml-cpp-tests
PRIVATE PRIVATE
$<$<CXX_COMPILER_ID:Clang>:-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare> $<$<CXX_COMPILER_ID:Clang>:-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare>
$<$<CXX_COMPILER_ID:GNU>:-Wno-variadic-macros -Wno-sign-compare>) $<$<CXX_COMPILER_ID:GNU>:-Wno-variadic-macros -Wno-sign-compare -Wno-narrowing>)
target_link_libraries(yaml-cpp-tests target_link_libraries(yaml-cpp-tests
PRIVATE PRIVATE
Threads::Threads Threads::Threads
yaml-cpp yaml-cpp
gtest
gmock) gmock)
set_property(TARGET yaml-cpp-tests PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET yaml-cpp-tests PROPERTY CXX_STANDARD_REQUIRED ON)

21
test/cmake/CMakeLists.txt Normal file
View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.5)
project(yaml-cpp-consumer LANGUAGES CXX)
find_package(yaml-cpp CONFIG REQUIRED)
get_target_property(LIBRARY_TYPE yaml-cpp::yaml-cpp TYPE)
if(LIBRARY_TYPE STREQUAL "SHARED_LIBRARY")
if(NOT YAML_CPP_SHARED_LIBS_BUILT)
message(FATAL_ERROR "Library type (${LIBRARY_TYPE}) contradicts config: ${YAML_CPP_SHARED_LIBS_BUILT}")
endif()
else()
if(YAML_CPP_SHARED_LIBS_BUILT)
message(FATAL_ERROR "Library type (${LIBRARY_TYPE}) contradicts config: ${YAML_CPP_SHARED_LIBS_BUILT}")
endif()
endif()
add_executable(main main.cpp)
if (NOT DEFINED CMAKE_CXX_STANDARD)
set_target_properties(main PROPERTIES CXX_STANDARD 11)
endif()
target_link_libraries(main PRIVATE ${YAML_CPP_LIBRARIES})

3
test/cmake/main.cpp Normal file
View File

@@ -0,0 +1,3 @@
#include "yaml-cpp/yaml.h"
int main(int, char**) { YAML::Parser foo{}; }

242
test/fptostring_test.cpp Normal file
View File

@@ -0,0 +1,242 @@
#include "yaml-cpp/fptostring.h"
#include "gtest/gtest.h"
namespace YAML {
namespace {
/**
* Helper function, that converts double to string as std::stringstream would do
*/
template <typename T>
static std::string convert_with_stringstream(T v, size_t precision = 0) {
std::stringstream ss;
if (precision > 0) {
ss << std::setprecision(precision);
}
ss << v;
return ss.str();
}
// Caution: Test involving 'convert_with_stringstream' are based on std::stringstream
// having certain printing behavior, if these changes, the unit test might fail.
// This is not a fault of FpToString just a weakness of the way these
// tests are constructed
TEST(FpToStringTest, conversion_double) {
// Issue motivating FpToString function,
// https://github.com/jbeder/yaml-cpp/issues/1289
// Original problem at hand:
EXPECT_EQ("34.34", FpToString(34.34));
EXPECT_EQ("56.56", FpToString(56.56));
EXPECT_EQ("12.12", FpToString(12.12));
EXPECT_EQ("78.78", FpToString(78.78));
// Special challenge with rounding
// https://github.com/jbeder/yaml-cpp/issues/1289#issuecomment-2211705536
EXPECT_EQ("1.54743e+26", FpToString(1.5474250491e+26f));
EXPECT_EQ(convert_with_stringstream(1.5474250491e+26f), FpToString(1.5474250491e+26f));
EXPECT_EQ("1.5474251e+26", FpToString(1.5474250491e+26f, 8));
// prints the same way as std::stringstream
EXPECT_EQ(convert_with_stringstream(1.), FpToString(1.));
EXPECT_EQ(convert_with_stringstream(1e0), FpToString(1e0));
EXPECT_EQ(convert_with_stringstream(1e1), FpToString(1e1));
EXPECT_EQ(convert_with_stringstream(1e2), FpToString(1e2));
EXPECT_EQ(convert_with_stringstream(1e3), FpToString(1e3));
EXPECT_EQ(convert_with_stringstream(1e4), FpToString(1e4));
EXPECT_EQ(convert_with_stringstream(1e5), FpToString(1e5));
EXPECT_EQ(convert_with_stringstream(1e6), FpToString(1e6));
EXPECT_EQ(convert_with_stringstream(1e7), FpToString(1e7));
EXPECT_EQ(convert_with_stringstream(1e8), FpToString(1e8));
EXPECT_EQ(convert_with_stringstream(1e9), FpToString(1e9));
// Print by default values below 1e6 without scientific notation
EXPECT_EQ("1", FpToString(1.));
EXPECT_EQ("1", FpToString(1e0));
EXPECT_EQ("10", FpToString(1e1));
EXPECT_EQ("100", FpToString(1e2));
EXPECT_EQ("1000", FpToString(1e3));
EXPECT_EQ("10000", FpToString(1e4));
EXPECT_EQ("100000", FpToString(1e5));
EXPECT_EQ("1e+06", FpToString(1e6));
EXPECT_EQ("1e+07", FpToString(1e7));
EXPECT_EQ("1e+08", FpToString(1e8));
EXPECT_EQ("1e+09", FpToString(1e9));
// prints the same way as std::stringstream
EXPECT_EQ(convert_with_stringstream(1.), FpToString(1.));
EXPECT_EQ(convert_with_stringstream(1e-0), FpToString(1e-0));
EXPECT_EQ(convert_with_stringstream(1e-1), FpToString(1e-1));
EXPECT_EQ(convert_with_stringstream(1e-2), FpToString(1e-2));
EXPECT_EQ(convert_with_stringstream(1e-3), FpToString(1e-3));
EXPECT_EQ(convert_with_stringstream(1e-4), FpToString(1e-4));
EXPECT_EQ(convert_with_stringstream(1e-5), FpToString(1e-5));
EXPECT_EQ(convert_with_stringstream(1e-6), FpToString(1e-6));
EXPECT_EQ(convert_with_stringstream(1e-7), FpToString(1e-7));
EXPECT_EQ(convert_with_stringstream(1e-8), FpToString(1e-8));
EXPECT_EQ(convert_with_stringstream(1e-9), FpToString(1e-9));
// Print by default values above 1e-5 without scientific notation
EXPECT_EQ("1", FpToString(1.));
EXPECT_EQ("1", FpToString(1e-0));
EXPECT_EQ("0.1", FpToString(1e-1));
EXPECT_EQ("0.01", FpToString(1e-2));
EXPECT_EQ("0.001", FpToString(1e-3));
EXPECT_EQ("0.0001", FpToString(1e-4));
EXPECT_EQ("1e-05", FpToString(1e-5));
EXPECT_EQ("1e-06", FpToString(1e-6));
EXPECT_EQ("1e-07", FpToString(1e-7));
EXPECT_EQ("1e-08", FpToString(1e-8));
EXPECT_EQ("1e-09", FpToString(1e-9));
// changing precision has the same effect as std::stringstream
EXPECT_EQ(convert_with_stringstream(123., 1), FpToString(123., 1));
EXPECT_EQ(convert_with_stringstream(1234567., 7), FpToString(1234567., 7));
EXPECT_EQ(convert_with_stringstream(12345.67, 7), FpToString(12345.67, 7));
EXPECT_EQ(convert_with_stringstream(1234567e-9, 7), FpToString(1234567e-9, 7));
EXPECT_EQ(convert_with_stringstream(1234567e-9, 1), FpToString(1234567e-9, 1));
// known example that is difficult to round
EXPECT_EQ("1", FpToString(0.9999, 2));
EXPECT_EQ("-1", FpToString(-0.9999, 2));
// some more random tests
EXPECT_EQ("1.25", FpToString(1.25));
EXPECT_EQ("34.34", FpToString(34.34));
EXPECT_EQ("1e+20", FpToString(1e+20));
EXPECT_EQ("1.1e+20", FpToString(1.1e+20));
EXPECT_EQ("1e-20", FpToString(1e-20));
EXPECT_EQ("1.1e-20", FpToString(1.1e-20));
EXPECT_EQ("1e-20", FpToString(0.1e-19));
EXPECT_EQ("1.1e-20", FpToString(0.11e-19));
EXPECT_EQ("19", FpToString(18.9, 2));
EXPECT_EQ("20", FpToString(19.9, 2));
EXPECT_EQ("2e+01", FpToString(19.9, 1));
EXPECT_EQ("1.2e-05", FpToString(1.234e-5, 2));
EXPECT_EQ("1.3e-05", FpToString(1.299e-5, 2));
EXPECT_EQ("-1", FpToString(-1.));
EXPECT_EQ("-1.25", FpToString(-1.25));
EXPECT_EQ("-34.34", FpToString(-34.34));
EXPECT_EQ("-1e+20", FpToString(-1e+20));
EXPECT_EQ("-1.1e+20", FpToString(-1.1e+20));
EXPECT_EQ("-1e-20", FpToString(-1e-20));
EXPECT_EQ("-1.1e-20", FpToString(-1.1e-20));
EXPECT_EQ("-1e-20", FpToString(-0.1e-19));
EXPECT_EQ("-1.1e-20", FpToString(-0.11e-19));
EXPECT_EQ("-19", FpToString(-18.9, 2));
EXPECT_EQ("-20", FpToString(-19.9, 2));
EXPECT_EQ("-2e+01", FpToString(-19.9, 1));
EXPECT_EQ("-1.2e-05", FpToString(-1.234e-5, 2));
EXPECT_EQ("-1.3e-05", FpToString(-1.299e-5, 2));
}
TEST(FpToStringTest, conversion_float) {
// Issue motivating FpToString function,
// https://github.com/jbeder/yaml-cpp/issues/1289
// Original problem at hand:
EXPECT_EQ("34.34", FpToString(34.34f));
EXPECT_EQ("56.56", FpToString(56.56f));
EXPECT_EQ("12.12", FpToString(12.12f));
EXPECT_EQ("78.78", FpToString(78.78f));
// prints the same way as std::stringstream
EXPECT_EQ(convert_with_stringstream(1.f), FpToString(1.f));
EXPECT_EQ(convert_with_stringstream(1e0f), FpToString(1e0f));
EXPECT_EQ(convert_with_stringstream(1e1f), FpToString(1e1f));
EXPECT_EQ(convert_with_stringstream(1e2f), FpToString(1e2f));
EXPECT_EQ(convert_with_stringstream(1e3f), FpToString(1e3f));
EXPECT_EQ(convert_with_stringstream(1e4f), FpToString(1e4f));
EXPECT_EQ(convert_with_stringstream(1e5f), FpToString(1e5f));
EXPECT_EQ(convert_with_stringstream(1e6f), FpToString(1e6f));
EXPECT_EQ(convert_with_stringstream(1e7f), FpToString(1e7f));
EXPECT_EQ(convert_with_stringstream(1e8f), FpToString(1e8f));
EXPECT_EQ(convert_with_stringstream(1e9f), FpToString(1e9f));
// Print by default values below 1e6 without scientific notation
EXPECT_EQ("1", FpToString(1.f));
EXPECT_EQ("1", FpToString(1e0f));
EXPECT_EQ("10", FpToString(1e1f));
EXPECT_EQ("100", FpToString(1e2f));
EXPECT_EQ("1000", FpToString(1e3f));
EXPECT_EQ("10000", FpToString(1e4f));
EXPECT_EQ("100000", FpToString(1e5f));
EXPECT_EQ("1e+06", FpToString(1e6f));
EXPECT_EQ("1e+07", FpToString(1e7f));
EXPECT_EQ("1e+08", FpToString(1e8f));
EXPECT_EQ("1e+09", FpToString(1e9f));
// prints the same way as std::stringstream
EXPECT_EQ(convert_with_stringstream(1.f), FpToString(1.f));
EXPECT_EQ(convert_with_stringstream(1e-0f), FpToString(1e-0f));
EXPECT_EQ(convert_with_stringstream(1e-1f), FpToString(1e-1f));
EXPECT_EQ(convert_with_stringstream(1e-2f), FpToString(1e-2f));
EXPECT_EQ(convert_with_stringstream(1e-3f), FpToString(1e-3f));
EXPECT_EQ(convert_with_stringstream(1e-4f), FpToString(1e-4f));
EXPECT_EQ(convert_with_stringstream(1e-5f), FpToString(1e-5f));
EXPECT_EQ(convert_with_stringstream(1e-6f), FpToString(1e-6f));
EXPECT_EQ(convert_with_stringstream(1e-7f), FpToString(1e-7f));
EXPECT_EQ(convert_with_stringstream(1e-8f), FpToString(1e-8f));
EXPECT_EQ(convert_with_stringstream(1e-9f), FpToString(1e-9f));
// Print by default values above 1e-5 without scientific notation
EXPECT_EQ("1", FpToString(1.f));
EXPECT_EQ("1", FpToString(1e-0f));
EXPECT_EQ("0.1", FpToString(1e-1f));
EXPECT_EQ("0.01", FpToString(1e-2f));
EXPECT_EQ("0.001", FpToString(1e-3f));
EXPECT_EQ("0.0001", FpToString(1e-4f));
EXPECT_EQ("1e-05", FpToString(1e-5f));
EXPECT_EQ("1e-06", FpToString(1e-6f));
EXPECT_EQ("1e-07", FpToString(1e-7f));
EXPECT_EQ("1e-08", FpToString(1e-8f));
EXPECT_EQ("1e-09", FpToString(1e-9f));
// changing precision has the same effect as std::stringstream
EXPECT_EQ(convert_with_stringstream(123.f, 1), FpToString(123.f, 1));
EXPECT_EQ(convert_with_stringstream(1234567.f, 7), FpToString(1234567.f, 7));
EXPECT_EQ(convert_with_stringstream(12345.67f, 7), FpToString(12345.67f, 7));
EXPECT_EQ(convert_with_stringstream(1234567e-9f, 7), FpToString(1234567e-9f, 7));
EXPECT_EQ(convert_with_stringstream(1234567e-9f, 1), FpToString(1234567e-9f, 1));
// known example that is difficult to round
EXPECT_EQ("1", FpToString(0.9999f, 2));
EXPECT_EQ("-1", FpToString(-0.9999f, 2));
// some more random tests
EXPECT_EQ("1.25", FpToString(1.25f));
EXPECT_EQ("34.34", FpToString(34.34f));
EXPECT_EQ("1e+20", FpToString(1e+20f));
EXPECT_EQ("1.1e+20", FpToString(1.1e+20f));
EXPECT_EQ("1e-20", FpToString(1e-20f));
EXPECT_EQ("1.1e-20", FpToString(1.1e-20f));
EXPECT_EQ("1e-20", FpToString(0.1e-19f));
EXPECT_EQ("1.1e-20", FpToString(0.11e-19f));
EXPECT_EQ("19", FpToString(18.9f, 2));
EXPECT_EQ("20", FpToString(19.9f, 2));
EXPECT_EQ("2e+01", FpToString(19.9f, 1));
EXPECT_EQ("1.2e-05", FpToString(1.234e-5f, 2));
EXPECT_EQ("1.3e-05", FpToString(1.299e-5f, 2));
EXPECT_EQ("-1", FpToString(-1.f));
EXPECT_EQ("-1.25", FpToString(-1.25f));
EXPECT_EQ("-34.34", FpToString(-34.34f));
EXPECT_EQ("-1e+20", FpToString(-1e+20f));
EXPECT_EQ("-1.1e+20", FpToString(-1.1e+20f));
EXPECT_EQ("-1e-20", FpToString(-1e-20f));
EXPECT_EQ("-1.1e-20", FpToString(-1.1e-20f));
EXPECT_EQ("-1e-20", FpToString(-0.1e-19f));
EXPECT_EQ("-1.1e-20", FpToString(-0.11e-19f));
EXPECT_EQ("-19", FpToString(-18.9f, 2));
EXPECT_EQ("-20", FpToString(-19.9f, 2));
EXPECT_EQ("-2e+01", FpToString(-19.9f, 1));
EXPECT_EQ("-1.2e-05", FpToString(-1.234e-5f, 2));
EXPECT_EQ("-1.3e-05", FpToString(-1.299e-5f, 2));
}
} // namespace
} // namespace YAML

View File

@@ -0,0 +1,53 @@
name: Bug Report
description: Let us know that something does not work as expected.
title: "[Bug]: Please title this bug report"
body:
- type: textarea
id: what-happened
attributes:
label: Describe the issue
description: What happened, and what did you expect to happen?
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce the problem
description: It is important that we are able to reproduce the problem that you are experiencing. Please provide all code and relevant steps to reproduce the problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the problem are also helpful.
validations:
required: true
- type: textarea
id: version
attributes:
label: What version of GoogleTest are you using?
description: Please include the output of `git rev-parse HEAD` or the GoogleTest release version number that you are using.
validations:
required: true
- type: textarea
id: os
attributes:
label: What operating system and version are you using?
description: If you are using a Linux distribution please include the name and version of the distribution as well.
validations:
required: true
- type: textarea
id: compiler
attributes:
label: What compiler and version are you using?
description: Please include the output of `gcc -v` or `clang -v`, or the equivalent for your compiler.
validations:
required: true
- type: textarea
id: buildsystem
attributes:
label: What build system are you using?
description: Please include the output of `bazel --version` or `cmake --version`, or the equivalent for your build system.
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any other context about the problem here.
validations:
required: false

View File

@@ -0,0 +1,33 @@
name: Feature request
description: Propose a new feature.
title: "[FR]: Please title this feature request"
labels: "enhancement"
body:
- type: textarea
id: version
attributes:
label: Does the feature exist in the most recent commit?
description: We recommend using the latest commit from GitHub in your projects.
validations:
required: true
- type: textarea
id: why
attributes:
label: Why do we need this feature?
description: Ideally, explain why a combination of existing features cannot be used instead.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Describe the proposal.
description: Include a detailed description of the feature, with usage examples.
validations:
required: true
- type: textarea
id: platform
attributes:
label: Is the feature specific to an operating system, compiler, or build system version?
description: If it is, please specify which versions.
validations:
required: true

View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Get Help
url: https://github.com/google/googletest/discussions
about: Please ask and answer questions here.

View File

@@ -0,0 +1,43 @@
name: ci
on:
push:
pull_request:
env:
BAZEL_CXXOPTS: -std=c++14
jobs:
Linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Tests
run: bazel test --cxxopt=-std=c++14 --features=external_include_paths --test_output=errors ...
macOS:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Tests
run: bazel test --cxxopt=-std=c++14 --features=external_include_paths --test_output=errors ...
Windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Tests
run: bazel test --cxxopt=/std:c++14 --features=external_include_paths --test_output=errors ...

View File

@@ -30,19 +30,32 @@
# #
# Bazel Build for Google C++ Testing Framework(Google Test) # Bazel Build for Google C++ Testing Framework(Google Test)
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
licenses(["notice"]) licenses(["notice"])
exports_files(["LICENSE"]) exports_files(["LICENSE"])
config_setting(
name = "qnx",
constraint_values = ["@platforms//os:qnx"],
)
config_setting( config_setting(
name = "windows", name = "windows",
constraint_values = ["@platforms//os:windows"], constraint_values = ["@platforms//os:windows"],
) )
config_setting(
name = "freebsd",
constraint_values = ["@platforms//os:freebsd"],
)
config_setting(
name = "openbsd",
constraint_values = ["@platforms//os:openbsd"],
)
config_setting( config_setting(
name = "msvc_compiler", name = "msvc_compiler",
flag_values = { flag_values = {
@@ -86,6 +99,7 @@ cc_library(
"googlemock/include/gmock/*.h", "googlemock/include/gmock/*.h",
]), ]),
copts = select({ copts = select({
":qnx": [],
":windows": [], ":windows": [],
"//conditions:default": ["-pthread"], "//conditions:default": ["-pthread"],
}), }),
@@ -104,7 +118,16 @@ cc_library(
"googletest/include", "googletest/include",
], ],
linkopts = select({ linkopts = select({
":qnx": ["-lregex"],
":windows": [], ":windows": [],
":freebsd": [
"-lm",
"-pthread",
],
":openbsd": [
"-lm",
"-pthread",
],
"//conditions:default": ["-pthread"], "//conditions:default": ["-pthread"],
}), }),
deps = select({ deps = select({
@@ -112,10 +135,15 @@ cc_library(
"@com_google_absl//absl/debugging:failure_signal_handler", "@com_google_absl//absl/debugging:failure_signal_handler",
"@com_google_absl//absl/debugging:stacktrace", "@com_google_absl//absl/debugging:stacktrace",
"@com_google_absl//absl/debugging:symbolize", "@com_google_absl//absl/debugging:symbolize",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_absl//absl/flags:reflection",
"@com_google_absl//absl/flags:usage",
"@com_google_absl//absl/strings", "@com_google_absl//absl/strings",
"@com_google_absl//absl/types:any", "@com_google_absl//absl/types:any",
"@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:variant", "@com_google_absl//absl/types:variant",
"@com_googlesource_code_re2//:re2",
], ],
"//conditions:default": [], "//conditions:default": [],
}), }),

View File

@@ -1,19 +1,25 @@
# Note: CMake support is community-based. The maintainers do not use CMake # Note: CMake support is community-based. The maintainers do not use CMake
# internally. # internally.
cmake_minimum_required(VERSION 2.8.12) cmake_minimum_required(VERSION 3.5)
if (POLICY CMP0048) if (POLICY CMP0048)
cmake_policy(SET CMP0048 NEW) cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048) endif (POLICY CMP0048)
project(googletest-distribution) if (POLICY CMP0069)
set(GOOGLETEST_VERSION 1.11.0) cmake_policy(SET CMP0069 NEW)
endif (POLICY CMP0069)
if (CMAKE_VERSION VERSION_GREATER "3.0.2") if (POLICY CMP0077)
if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX) cmake_policy(SET CMP0077 NEW)
set(CMAKE_CXX_EXTENSIONS OFF) endif (POLICY CMP0077)
endif()
project(googletest-distribution)
set(GOOGLETEST_VERSION 1.13.0)
if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
set(CMAKE_CXX_EXTENSIONS OFF)
endif() endif()
enable_testing() enable_testing()
@@ -24,6 +30,7 @@ include(GNUInstallDirs)
#Note that googlemock target already builds googletest #Note that googlemock target already builds googletest
option(BUILD_GMOCK "Builds the googlemock subproject" ON) option(BUILD_GMOCK "Builds the googlemock subproject" ON)
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON) option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON)
option(GTEST_HAS_ABSL "Use Abseil and RE2. Requires Abseil and RE2 to be separately added to the build." OFF)
if(BUILD_GMOCK) if(BUILD_GMOCK)
add_subdirectory( googlemock ) add_subdirectory( googlemock )

View File

@@ -21,8 +21,8 @@ accept your pull requests.
## Are you a Googler? ## Are you a Googler?
If you are a Googler, please make an attempt to submit an internal change rather If you are a Googler, please make an attempt to submit an internal contribution
than a GitHub Pull Request. If you are not able to submit an internal change a rather than a GitHub Pull Request. If you are not able to submit internally, a
PR is acceptable as an alternative. PR is acceptable as an alternative.
## Contributing A Patch ## Contributing A Patch
@@ -36,7 +36,8 @@ PR is acceptable as an alternative.
This ensures that work isn't being duplicated and communicating your plan This ensures that work isn't being duplicated and communicating your plan
early also generally leads to better patches. early also generally leads to better patches.
4. If your proposed change is accepted, and you haven't already done so, sign a 4. If your proposed change is accepted, and you haven't already done so, sign a
Contributor License Agreement (see details above). Contributor License Agreement
([see details above](#contributor-license-agreements)).
5. Fork the desired repo, develop and test your code changes. 5. Fork the desired repo, develop and test your code changes.
6. Ensure that your code adheres to the existing style in the sample to which 6. Ensure that your code adheres to the existing style in the sample to which
you are contributing. you are contributing.
@@ -79,8 +80,8 @@ fairly rigid coding style, as defined by the
[google-styleguide](https://github.com/google/styleguide) project. All patches [google-styleguide](https://github.com/google/styleguide) project. All patches
will be expected to conform to the style outlined will be expected to conform to the style outlined
[here](https://google.github.io/styleguide/cppguide.html). Use [here](https://google.github.io/styleguide/cppguide.html). Use
[.clang-format](https://github.com/google/googletest/blob/master/.clang-format) [.clang-format](https://github.com/google/googletest/blob/main/.clang-format) to
to check your formatting. check your formatting.
## Requirements for Contributors ## Requirements for Contributors

View File

@@ -34,6 +34,7 @@ Manuel Klimek <klimek@google.com>
Mario Tanev <radix@google.com> Mario Tanev <radix@google.com>
Mark Paskin Mark Paskin
Markus Heule <markus.heule@gmail.com> Markus Heule <markus.heule@gmail.com>
Martijn Vels <mvels@google.com>
Matthew Simmons <simmonmt@acm.org> Matthew Simmons <simmonmt@acm.org>
Mika Raento <mikie@iki.fi> Mika Raento <mikie@iki.fi>
Mike Bland <mbland@google.com> Mike Bland <mbland@google.com>
@@ -55,6 +56,7 @@ Russ Rufer <russ@pentad.com>
Sean Mcafee <eefacm@gmail.com> Sean Mcafee <eefacm@gmail.com>
Sigurður Ásgeirsson <siggi@google.com> Sigurður Ásgeirsson <siggi@google.com>
Sverre Sundsdal <sundsdal@gmail.com> Sverre Sundsdal <sundsdal@gmail.com>
Szymon Sobik <sobik.szymon@gmail.com>
Takeshi Yoshino <tyoshino@google.com> Takeshi Yoshino <tyoshino@google.com>
Tracy Bialik <tracy@pentad.com> Tracy Bialik <tracy@pentad.com>
Vadim Berman <vadimb@google.com> Vadim Berman <vadimb@google.com>

View File

@@ -6,7 +6,8 @@
GoogleTest now follows the GoogleTest now follows the
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support). [Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
We recommend using the latest commit in the `master` branch in your projects. We recommend
[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it).
#### Documentation Updates #### Documentation Updates
@@ -14,11 +15,14 @@ Our documentation is now live on GitHub Pages at
https://google.github.io/googletest/. We recommend browsing the documentation on https://google.github.io/googletest/. We recommend browsing the documentation on
GitHub Pages rather than directly in the repository. GitHub Pages rather than directly in the repository.
#### Release 1.10.x #### Release 1.12.1
[Release 1.10.x](https://github.com/google/googletest/releases/tag/release-1.10.0) [Release 1.12.1](https://github.com/google/googletest/releases/tag/release-1.12.1)
is now available. is now available.
The 1.12.x branch will be the last to support C++11. Future releases will
require at least C++14.
#### Coming Soon #### Coming Soon
* We are planning to take a dependency on * We are planning to take a dependency on
@@ -55,39 +59,12 @@ More information about building GoogleTest can be found at
## Supported Platforms ## Supported Platforms
GoogleTest requires a codebase and compiler compliant with the C++11 standard or GoogleTest follows Google's
newer. [Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support).
See
The GoogleTest code is officially supported on the following platforms. [this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md)
Operating systems or tools not listed below are community-supported. For for a list of currently supported versions compilers, platforms, and build
community-supported platforms, patches that do not complicate the code may be tools.
considered.
If you notice any problems on your platform, please file an issue on the
[GoogleTest GitHub Issue Tracker](https://github.com/google/googletest/issues).
Pull requests containing fixes are welcome!
### Operating Systems
* Linux
* macOS
* Windows
### Compilers
* gcc 5.0+
* clang 5.0+
* MSVC 2015+
**macOS users:** Xcode 9.3+ provides clang 5.0+.
### Build Systems
* [Bazel](https://bazel.build/)
* [CMake](https://cmake.org/)
**Note:** Bazel is the build system used by the team internally and in tests.
CMake is supported on a best-effort basis and by the community.
## Who Is Using GoogleTest? ## Who Is Using GoogleTest?
@@ -109,8 +86,8 @@ Windows and Linux platforms.
[GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that [GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that
runs your test binary, allows you to track its progress via a progress bar, and runs your test binary, allows you to track its progress via a progress bar, and
displays a list of test failures. Clicking on one shows failure text. Google displays a list of test failures. Clicking on one shows failure text. GoogleTest
Test UI is written in C#. UI is written in C#.
[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event [GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
listener for GoogleTest that implements the listener for GoogleTest that implements the
@@ -121,11 +98,11 @@ result output. If your test runner understands TAP, you may find it useful.
runs tests from your binary in parallel to provide significant speed-up. runs tests from your binary in parallel to provide significant speed-up.
[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter) [GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter)
is a VS Code extension allowing to view GoogleTest in a tree view, and run/debug is a VS Code extension allowing to view GoogleTest in a tree view and run/debug
your tests. your tests.
[C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS [C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS
Code extension allowing to view GoogleTest in a tree view, and run/debug your Code extension allowing to view GoogleTest in a tree view and run/debug your
tests. tests.
[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser [Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser
@@ -134,7 +111,7 @@ that generates stub code for GoogleTest.
## Contributing Changes ## Contributing Changes
Please read Please read
[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/master/CONTRIBUTING.md) [`CONTRIBUTING.md`](https://github.com/google/googletest/blob/main/CONTRIBUTING.md)
for details on how to contribute to this project. for details on how to contribute to this project.
Happy testing! Happy testing!

View File

@@ -0,0 +1,40 @@
workspace(name = "com_google_googletest")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_google_absl", # 2023-01-10T21:08:25Z
sha256 = "f9a4e749f42c386a32a90fddf0e2913ed408d10c42f7f33ccf4c59ac4f0d1d05",
strip_prefix = "abseil-cpp-52835439ca90d86b27bf8cd1708296e95604d724",
urls = ["https://github.com/abseil/abseil-cpp/archive/52835439ca90d86b27bf8cd1708296e95604d724.zip"],
)
# Note this must use a commit from the `abseil` branch of the RE2 project.
# https://github.com/google/re2/tree/abseil
http_archive(
name = "com_googlesource_code_re2", # 2022-12-21T14:29:10Z
sha256 = "b9ce3a51beebb38534d11d40f8928d40509b9e18a735f6a4a97ad3d014c87cb5",
strip_prefix = "re2-d0b1f8f2ecc2ea74956c7608b6f915175314ff0e",
urls = ["https://github.com/google/re2/archive/d0b1f8f2ecc2ea74956c7608b6f915175314ff0e.zip"],
)
http_archive(
name = "rules_python", # 2023-01-10T22:00:51Z
sha256 = "5de54486a60ad8948dabe49605bb1c08053e04001a431ab3e96745b4d97a4419",
strip_prefix = "rules_python-70cce26432187a60b4e950118791385e6fb3c26f",
urls = ["https://github.com/bazelbuild/rules_python/archive/70cce26432187a60b4e950118791385e6fb3c26f.zip"],
)
http_archive(
name = "bazel_skylib", # 2022-11-16T18:29:32Z
sha256 = "a22290c26d29d3ecca286466f7f295ac6cbe32c0a9da3a91176a90e0725e3649",
strip_prefix = "bazel-skylib-5bfcb1a684550626ce138fe0fe8f5f702b3764c3",
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/5bfcb1a684550626ce138fe0fe8f5f702b3764c3.zip"],
)
http_archive(
name = "platforms", # 2022-11-09T19:18:22Z
sha256 = "b4a3b45dc4202e2b3e34e3bc49d2b5b37295fc23ea58d88fb9e01f3642ad9b55",
strip_prefix = "platforms-3fbc687756043fb58a407c2ea8c944bc2fe1d922",
urls = ["https://github.com/bazelbuild/platforms/archive/3fbc687756043fb58a407c2ea8c944bc2fe1d922.zip"],
)

View File

@@ -31,15 +31,15 @@
set -euox pipefail set -euox pipefail
readonly LINUX_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210525" readonly LINUX_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220217"
readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20201015" readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20220621"
if [[ -z ${GTEST_ROOT:-} ]]; then if [[ -z ${GTEST_ROOT:-} ]]; then
GTEST_ROOT="$(realpath $(dirname ${0})/..)" GTEST_ROOT="$(realpath $(dirname ${0})/..)"
fi fi
if [[ -z ${STD:-} ]]; then if [[ -z ${STD:-} ]]; then
STD="c++11 c++14 c++17 c++20" STD="c++14 c++17 c++20"
fi fi
# Test the CMake build # Test the CMake build
@@ -55,7 +55,7 @@ for cc in /usr/local/bin/gcc /opt/llvm/clang/bin/clang; do
${LINUX_LATEST_CONTAINER} \ ${LINUX_LATEST_CONTAINER} \
/bin/bash -c " /bin/bash -c "
cmake /src \ cmake /src \
-DCMAKE_CXX_STANDARD=11 \ -DCMAKE_CXX_STANDARD=14 \
-Dgtest_build_samples=ON \ -Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \ -Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \ -Dgmock_build_tests=ON \
@@ -72,11 +72,15 @@ time docker run \
--workdir="/src" \ --workdir="/src" \
--rm \ --rm \
--env="CC=/usr/local/bin/gcc" \ --env="CC=/usr/local/bin/gcc" \
--env="BAZEL_CXXOPTS=-std=c++14" \
${LINUX_GCC_FLOOR_CONTAINER} \ ${LINUX_GCC_FLOOR_CONTAINER} \
/usr/local/bin/bazel test ... \ /usr/local/bin/bazel test ... \
--copt="-Wall" \ --copt="-Wall" \
--copt="-Werror" \ --copt="-Werror" \
--copt="-Wuninitialized" \
--copt="-Wno-error=pragmas" \ --copt="-Wno-error=pragmas" \
--distdir="/bazel-distdir" \
--features=external_include_paths \
--keep_going \ --keep_going \
--show_timestamps \ --show_timestamps \
--test_output=errors --test_output=errors
@@ -94,8 +98,10 @@ for std in ${STD}; do
/usr/local/bin/bazel test ... \ /usr/local/bin/bazel test ... \
--copt="-Wall" \ --copt="-Wall" \
--copt="-Werror" \ --copt="-Werror" \
--copt="-Wuninitialized" \
--define="absl=${absl}" \ --define="absl=${absl}" \
--distdir="/bazel-distdir" \ --distdir="/bazel-distdir" \
--features=external_include_paths \
--keep_going \ --keep_going \
--show_timestamps \ --show_timestamps \
--test_output=errors --test_output=errors
@@ -116,8 +122,10 @@ for std in ${STD}; do
--copt="--gcc-toolchain=/usr/local" \ --copt="--gcc-toolchain=/usr/local" \
--copt="-Wall" \ --copt="-Wall" \
--copt="-Werror" \ --copt="-Werror" \
--copt="-Wuninitialized" \
--define="absl=${absl}" \ --define="absl=${absl}" \
--distdir="/bazel-distdir" \ --distdir="/bazel-distdir" \
--features=external_include_paths \
--keep_going \ --keep_going \
--linkopt="--gcc-toolchain=/usr/local" \ --linkopt="--gcc-toolchain=/usr/local" \
--show_timestamps \ --show_timestamps \

View File

@@ -40,7 +40,7 @@ for cmake_off_on in OFF ON; do
BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX) BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX)
cd ${BUILD_DIR} cd ${BUILD_DIR}
time cmake ${GTEST_ROOT} \ time cmake ${GTEST_ROOT} \
-DCMAKE_CXX_STANDARD=11 \ -DCMAKE_CXX_STANDARD=14 \
-Dgtest_build_samples=ON \ -Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \ -Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \ -Dgmock_build_tests=ON \
@@ -53,7 +53,7 @@ done
# Test the Bazel build # Test the Bazel build
# If we are running on Kokoro, check for a versioned Bazel binary. # If we are running on Kokoro, check for a versioned Bazel binary.
KOKORO_GFILE_BAZEL_BIN="bazel-3.7.0-darwin-x86_64" KOKORO_GFILE_BAZEL_BIN="bazel-5.1.1-darwin-x86_64"
if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}" BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
chmod +x ${BAZEL_BIN} chmod +x ${BAZEL_BIN}
@@ -66,7 +66,9 @@ for absl in 0 1; do
${BAZEL_BIN} test ... \ ${BAZEL_BIN} test ... \
--copt="-Wall" \ --copt="-Wall" \
--copt="-Werror" \ --copt="-Werror" \
--cxxopt="-std=c++14" \
--define="absl=${absl}" \ --define="absl=${absl}" \
--features=external_include_paths \
--keep_going \ --keep_going \
--show_timestamps \ --show_timestamps \
--test_output=errors --test_output=errors

View File

@@ -0,0 +1,56 @@
SETLOCAL ENABLEDELAYEDEXPANSION
SET BAZEL_EXE=%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe
SET PATH=C:\Python37;%PATH%
SET BAZEL_PYTHON=C:\python37\python.exe
SET BAZEL_SH=C:\tools\msys64\usr\bin\bash.exe
SET CMAKE_BIN="C:\Program Files\CMake\bin\cmake.exe"
SET CTEST_BIN="C:\Program Files\CMake\bin\ctest.exe"
SET CTEST_OUTPUT_ON_FAILURE=1
IF EXIST git\googletest (
CD git\googletest
) ELSE IF EXIST github\googletest (
CD github\googletest
)
IF %errorlevel% neq 0 EXIT /B 1
:: ----------------------------------------------------------------------------
:: CMake Visual Studio 15 2017 Win64
MKDIR cmake_msvc2017
CD cmake_msvc2017
%CMAKE_BIN% .. ^
-G "Visual Studio 15 2017 Win64" ^
-DPYTHON_EXECUTABLE:FILEPATH=c:\python37\python.exe ^
-DPYTHON_INCLUDE_DIR:PATH=c:\python37\include ^
-DPYTHON_LIBRARY:FILEPATH=c:\python37\lib\site-packages\pip ^
-Dgtest_build_samples=ON ^
-Dgtest_build_tests=ON ^
-Dgmock_build_tests=ON
IF %errorlevel% neq 0 EXIT /B 1
%CMAKE_BIN% --build . --target ALL_BUILD --config Debug -- -maxcpucount
IF %errorlevel% neq 0 EXIT /B 1
%CTEST_BIN% -C Debug --timeout 600
IF %errorlevel% neq 0 EXIT /B 1
CD ..
RMDIR /S /Q cmake_msvc2017
:: ----------------------------------------------------------------------------
:: Bazel Visual Studio 15 2017 Win64
SET BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC
%BAZEL_EXE% test ... ^
--compilation_mode=dbg ^
--copt=/std:c++14 ^
--copt=/WX ^
--features=external_include_paths ^
--keep_going ^
--test_output=errors ^
--test_tag_filters=-no_test_msvc2017
IF %errorlevel% neq 0 EXIT /B 1

View File

@@ -48,7 +48,7 @@
<div class="footer"> <div class="footer">
GoogleTest &middot; GoogleTest &middot;
<a href="https://github.com/google/googletest">GitHub Repository</a> &middot; <a href="https://github.com/google/googletest">GitHub Repository</a> &middot;
<a href="https://github.com/google/googletest/blob/master/LICENSE">License</a> &middot; <a href="https://github.com/google/googletest/blob/main/LICENSE">License</a> &middot;
<a href="https://policies.google.com/privacy">Privacy Policy</a> <a href="https://policies.google.com/privacy">Privacy Policy</a>
</div> </div>
</div> </div>

View File

@@ -157,8 +157,11 @@ that can be used in the predicate assertion macro
example: example:
```c++ ```c++
EXPECT_PRED_FORMAT2(testing::FloatLE, val1, val2); using ::testing::FloatLE;
EXPECT_PRED_FORMAT2(testing::DoubleLE, val1, val2); using ::testing::DoubleLE;
...
EXPECT_PRED_FORMAT2(FloatLE, val1, val2);
EXPECT_PRED_FORMAT2(DoubleLE, val1, val2);
``` ```
The above code verifies that `val1` is less than, or approximately equal to, The above code verifies that `val1` is less than, or approximately equal to,
@@ -202,10 +205,9 @@ You can call the function
to assert that types `T1` and `T2` are the same. The function does nothing if to assert that types `T1` and `T2` are the same. The function does nothing if
the assertion is satisfied. If the types are different, the function call will the assertion is satisfied. If the types are different, the function call will
fail to compile, the compiler error message will say that fail to compile, the compiler error message will say that `T1 and T2 are not the
`T1 and T2 are not the same type` and most likely (depending on the compiler) same type` and most likely (depending on the compiler) show you the actual
show you the actual values of `T1` and `T2`. This is mainly useful inside values of `T1` and `T2`. This is mainly useful inside template code.
template code.
**Caveat**: When used inside a member function of a class template or a function **Caveat**: When used inside a member function of a class template or a function
template, `StaticAssertTypeEq<T1, T2>()` is effective only if the function is template, `StaticAssertTypeEq<T1, T2>()` is effective only if the function is
@@ -383,10 +385,10 @@ EXPECT_TRUE(IsCorrectBarIntVector(bar_ints))
## Death Tests ## Death Tests
In many applications, there are assertions that can cause application failure if In many applications, there are assertions that can cause application failure if
a condition is not met. These sanity checks, which ensure that the program is in a condition is not met. These consistency checks, which ensure that the program
a known good state, are there to fail at the earliest possible time after some is in a known good state, are there to fail at the earliest possible time after
program state is corrupted. If the assertion checks the wrong condition, then some program state is corrupted. If the assertion checks the wrong condition,
the program may proceed in an erroneous state, which could lead to memory then the program may proceed in an erroneous state, which could lead to memory
corruption, security holes, or worse. Hence it is vitally important to test that corruption, security holes, or worse. Hence it is vitally important to test that
such assertion statements work as expected. such assertion statements work as expected.
@@ -480,10 +482,12 @@ TEST_F(FooDeathTest, DoesThat) {
### Regular Expression Syntax ### Regular Expression Syntax
On POSIX systems (e.g. Linux, Cygwin, and Mac), googletest uses the When built with Bazel and using Abseil, googletest uses the
[RE2](https://github.com/google/re2/wiki/Syntax) syntax. Otherwise, for POSIX
systems (Linux, Cygwin, Mac), googletest uses the
[POSIX extended regular expression](http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04) [POSIX extended regular expression](http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04)
syntax. To learn about this syntax, you may want to read this syntax. To learn about POSIX syntax, you may want to read this
[Wikipedia entry](http://en.wikipedia.org/wiki/Regular_expression#POSIX_Extended_Regular_Expressions). [Wikipedia entry](http://en.wikipedia.org/wiki/Regular_expression#POSIX_extended).
On Windows, googletest uses its own simple regular expression implementation. It On Windows, googletest uses its own simple regular expression implementation. It
lacks many features. For example, we don't support union (`"x|y"`), grouping lacks many features. For example, we don't support union (`"x|y"`), grouping
@@ -558,7 +562,7 @@ The automated testing framework does not set the style flag. You can choose a
particular style of death tests by setting the flag programmatically: particular style of death tests by setting the flag programmatically:
```c++ ```c++
testing::FLAGS_gtest_death_test_style="threadsafe" GTEST_FLAG_SET(death_test_style, "threadsafe")
``` ```
You can do this in `main()` to set the style for all death tests in the binary, You can do this in `main()` to set the style for all death tests in the binary,
@@ -568,12 +572,12 @@ restored afterwards, so you need not do that yourself. For example:
```c++ ```c++
int main(int argc, char** argv) { int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
testing::FLAGS_gtest_death_test_style = "fast"; GTEST_FLAG_SET(death_test_style, "fast");
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
TEST(MyDeathTest, TestOne) { TEST(MyDeathTest, TestOne) {
testing::FLAGS_gtest_death_test_style = "threadsafe"; GTEST_FLAG_SET(death_test_style, "threadsafe");
// This test is run in the "threadsafe" style: // This test is run in the "threadsafe" style:
ASSERT_DEATH(ThisShouldDie(), ""); ASSERT_DEATH(ThisShouldDie(), "");
} }
@@ -610,15 +614,14 @@ Despite the improved thread safety afforded by the "threadsafe" style of death
test, thread problems such as deadlock are still possible in the presence of test, thread problems such as deadlock are still possible in the presence of
handlers registered with `pthread_atfork(3)`. handlers registered with `pthread_atfork(3)`.
## Using Assertions in Sub-routines ## Using Assertions in Sub-routines
{: .callout .note} {: .callout .note}
Note: If you want to put a series of test assertions in a subroutine to check Note: If you want to put a series of test assertions in a subroutine to check
for a complex condition, consider using for a complex condition, consider using
[a custom GMock matcher](gmock_cook_book.md#NewMatchers) [a custom GMock matcher](gmock_cook_book.md#NewMatchers) instead. This lets you
instead. This lets you provide a more readable error message in case of failure provide a more readable error message in case of failure and avoid all of the
and avoid all of the issues described below. issues described below.
### Adding Traces to Assertions ### Adding Traces to Assertions
@@ -631,6 +634,7 @@ the `SCOPED_TRACE` macro or the `ScopedTrace` utility:
```c++ ```c++
SCOPED_TRACE(message); SCOPED_TRACE(message);
``` ```
```c++ ```c++
ScopedTrace trace("file_path", line_number, message); ScopedTrace trace("file_path", line_number, message);
``` ```
@@ -837,7 +841,7 @@ will output XML like this:
```xml ```xml
... ...
<testcase name="MinAndMaxWidgets" status="run" time="0.006" classname="WidgetUsageTest" MaximumWidgets="12" MinimumWidgets="9" /> <testcase name="MinAndMaxWidgets" file="test.cpp" line="1" status="run" time="0.006" classname="WidgetUsageTest" MaximumWidgets="12" MinimumWidgets="9" />
... ...
``` ```
@@ -888,6 +892,12 @@ preceding or following another. Also, the tests must either not modify the state
of any shared resource, or, if they do modify the state, they must restore the of any shared resource, or, if they do modify the state, they must restore the
state to its original value before passing control to the next test. state to its original value before passing control to the next test.
Note that `SetUpTestSuite()` may be called multiple times for a test fixture
class that has derived classes, so you should not expect code in the function
body to be run only once. Also, derived classes still have access to shared
resources defined as static members, so careful consideration is needed when
managing shared resources to avoid memory leaks.
Here's an example of per-test-suite set-up and tear-down: Here's an example of per-test-suite set-up and tear-down:
```c++ ```c++
@@ -897,7 +907,10 @@ class FooTest : public testing::Test {
// Called before the first test in this test suite. // Called before the first test in this test suite.
// Can be omitted if not needed. // Can be omitted if not needed.
static void SetUpTestSuite() { static void SetUpTestSuite() {
shared_resource_ = new ...; // Avoid reallocating static objects if called in subclasses of FooTest.
if (shared_resource_ == nullptr) {
shared_resource_ = new ...;
}
} }
// Per-test-suite tear-down. // Per-test-suite tear-down.
@@ -1082,6 +1095,11 @@ instantiation of the test suite. The next argument is the name of the test
pattern, and the last is the pattern, and the last is the
[parameter generator](reference/testing.md#param-generators). [parameter generator](reference/testing.md#param-generators).
The parameter generator expression is not evaluated until GoogleTest is
initialized (via `InitGoogleTest()`). Any prior initialization done in the
`main` function will be accessible from the parameter generator, for example,
the results of flag parsing.
You can instantiate a test pattern more than once, so to distinguish different You can instantiate a test pattern more than once, so to distinguish different
instances of the pattern, the instantiation name is added as a prefix to the instances of the pattern, the instantiation name is added as a prefix to the
actual test suite name. Remember to pick unique prefixes for different actual test suite name. Remember to pick unique prefixes for different
@@ -1129,8 +1147,8 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FooTest);
You can see [sample7_unittest.cc] and [sample8_unittest.cc] for more examples. You can see [sample7_unittest.cc] and [sample8_unittest.cc] for more examples.
[sample7_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample7_unittest.cc "Parameterized Test example" [sample7_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample7_unittest.cc "Parameterized Test example"
[sample8_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample8_unittest.cc "Parameterized Test example with multiple parameters" [sample8_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample8_unittest.cc "Parameterized Test example with multiple parameters"
### Creating Value-Parameterized Abstract Tests ### Creating Value-Parameterized Abstract Tests
@@ -1281,7 +1299,7 @@ TYPED_TEST(FooTest, HasPropertyA) { ... }
You can see [sample6_unittest.cc] for a complete example. You can see [sample6_unittest.cc] for a complete example.
[sample6_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample6_unittest.cc "Typed Test example" [sample6_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample6_unittest.cc "Typed Test example"
## Type-Parameterized Tests ## Type-Parameterized Tests
@@ -1302,6 +1320,7 @@ First, define a fixture class template, as we did with typed tests:
```c++ ```c++
template <typename T> template <typename T>
class FooTest : public testing::Test { class FooTest : public testing::Test {
void DoSomethingInteresting();
... ...
}; };
``` ```
@@ -1319,6 +1338,9 @@ this as many times as you want:
TYPED_TEST_P(FooTest, DoesBlah) { TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter. // Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0; TypeParam n = 0;
// You will need to use `this` explicitly to refer to fixture members.
this->DoSomethingInteresting()
... ...
} }
@@ -1481,8 +1503,8 @@ In frameworks that report a failure by throwing an exception, you could catch
the exception and assert on it. But googletest doesn't use exceptions, so how do the exception and assert on it. But googletest doesn't use exceptions, so how do
we test that a piece of code generates an expected failure? we test that a piece of code generates an expected failure?
`"gtest/gtest-spi.h"` contains some constructs to do this. After #including this header, `"gtest/gtest-spi.h"` contains some constructs to do this.
you can use After #including this header, you can use
```c++ ```c++
EXPECT_FATAL_FAILURE(statement, substring); EXPECT_FATAL_FAILURE(statement, substring);
@@ -1586,12 +1608,14 @@ void RegisterMyTests(const std::vector<int>& values) {
} }
... ...
int main(int argc, char** argv) { int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
std::vector<int> values_to_test = LoadValuesFromConfig(); std::vector<int> values_to_test = LoadValuesFromConfig();
RegisterMyTests(values_to_test); RegisterMyTests(values_to_test);
... ...
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
``` ```
## Getting the Current Test's Name ## Getting the Current Test's Name
Sometimes a function may need to know the name of the currently running test. Sometimes a function may need to know the name of the currently running test.
@@ -1714,7 +1738,7 @@ You can do so by adding one line:
Now, sit back and enjoy a completely different output from your tests. For more Now, sit back and enjoy a completely different output from your tests. For more
details, see [sample9_unittest.cc]. details, see [sample9_unittest.cc].
[sample9_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample9_unittest.cc "Event listener example" [sample9_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample9_unittest.cc "Event listener example"
You may append more than one listener to the list. When an `On*Start()` or You may append more than one listener to the list. When an `On*Start()` or
`OnTestPartResult()` event is fired, the listeners will receive it in the order `OnTestPartResult()` event is fired, the listeners will receive it in the order
@@ -1741,7 +1765,7 @@ by the former.
See [sample10_unittest.cc] for an example of a failure-raising listener. See [sample10_unittest.cc] for an example of a failure-raising listener.
[sample10_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample10_unittest.cc "Failure-raising listener example" [sample10_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample10_unittest.cc "Failure-raising listener example"
## Running Test Programs: Advanced Options ## Running Test Programs: Advanced Options
@@ -1816,8 +1840,7 @@ By default, a googletest program runs all tests the user has defined. In some
cases (e.g. iterative test development & execution) it may be desirable stop cases (e.g. iterative test development & execution) it may be desirable stop
test execution upon first failure (trading improved latency for completeness). test execution upon first failure (trading improved latency for completeness).
If `GTEST_FAIL_FAST` environment variable or `--gtest_fail_fast` flag is set, If `GTEST_FAIL_FAST` environment variable or `--gtest_fail_fast` flag is set,
the test runner will stop execution as soon as the first test failure is the test runner will stop execution as soon as the first test failure is found.
found.
#### Temporarily Disabling Tests #### Temporarily Disabling Tests
@@ -1890,8 +1913,12 @@ Repeat the tests whose name matches the filter 1000 times.
If your test program contains If your test program contains
[global set-up/tear-down](#global-set-up-and-tear-down) code, it will be [global set-up/tear-down](#global-set-up-and-tear-down) code, it will be
repeated in each iteration as well, as the flakiness may be in it. You can also repeated in each iteration as well, as the flakiness may be in it. To avoid
specify the repeat count by setting the `GTEST_REPEAT` environment variable. repeating global set-up/tear-down, specify
`--gtest_recreate_environments_when_repeating=false`{.nowrap}.
You can also specify the repeat count by setting the `GTEST_REPEAT` environment
variable.
### Shuffling the Tests ### Shuffling the Tests
@@ -1911,6 +1938,58 @@ time.
If you combine this with `--gtest_repeat=N`, googletest will pick a different If you combine this with `--gtest_repeat=N`, googletest will pick a different
random seed and re-shuffle the tests in each iteration. random seed and re-shuffle the tests in each iteration.
### Distributing Test Functions to Multiple Machines
If you have more than one machine you can use to run a test program, you might
want to run the test functions in parallel and get the result faster. We call
this technique *sharding*, where each machine is called a *shard*.
GoogleTest is compatible with test sharding. To take advantage of this feature,
your test runner (not part of GoogleTest) needs to do the following:
1. Allocate a number of machines (shards) to run the tests.
1. On each shard, set the `GTEST_TOTAL_SHARDS` environment variable to the total
number of shards. It must be the same for all shards.
1. On each shard, set the `GTEST_SHARD_INDEX` environment variable to the index
of the shard. Different shards must be assigned different indices, which
must be in the range `[0, GTEST_TOTAL_SHARDS - 1]`.
1. Run the same test program on all shards. When GoogleTest sees the above two
environment variables, it will select a subset of the test functions to run.
Across all shards, each test function in the program will be run exactly
once.
1. Wait for all shards to finish, then collect and report the results.
Your project may have tests that were written without GoogleTest and thus don't
understand this protocol. In order for your test runner to figure out which test
supports sharding, it can set the environment variable `GTEST_SHARD_STATUS_FILE`
to a non-existent file path. If a test program supports sharding, it will create
this file to acknowledge that fact; otherwise it will not create it. The actual
contents of the file are not important at this time, although we may put some
useful information in it in the future.
Here's an example to make it clear. Suppose you have a test program `foo_test`
that contains the following 5 test functions:
```
TEST(A, V)
TEST(A, W)
TEST(B, X)
TEST(B, Y)
TEST(B, Z)
```
Suppose you have 3 machines at your disposal. To run the test functions in
parallel, you would set `GTEST_TOTAL_SHARDS` to 3 on all machines, and set
`GTEST_SHARD_INDEX` to 0, 1, and 2 on the machines respectively. Then you would
run the same `foo_test` on each machine.
GoogleTest reserves the right to change how the work is distributed across the
shards, but here's one possible scenario:
* Machine #0 runs `A.V` and `B.X`.
* Machine #1 runs `A.W` and `B.Y`.
* Machine #2 runs `B.Z`.
### Controlling Test Output ### Controlling Test Output
#### Colored Terminal Output #### Colored Terminal Output
@@ -1965,8 +2044,6 @@ text because, for example, you don't have an UTF-8 compatible output medium, run
the test program with `--gtest_print_utf8=0` or set the `GTEST_PRINT_UTF8` the test program with `--gtest_print_utf8=0` or set the `GTEST_PRINT_UTF8`
environment variable to `0`. environment variable to `0`.
#### Generating an XML Report #### Generating an XML Report
googletest can emit a detailed XML report to a file in addition to its normal googletest can emit a detailed XML report to a file in addition to its normal
@@ -2020,15 +2097,15 @@ could generate this report:
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="3" failures="1" errors="0" time="0.035" timestamp="2011-10-31T18:52:42" name="AllTests"> <testsuites tests="3" failures="1" errors="0" time="0.035" timestamp="2011-10-31T18:52:42" name="AllTests">
<testsuite name="MathTest" tests="2" failures="1" errors="0" time="0.015"> <testsuite name="MathTest" tests="2" failures="1" errors="0" time="0.015">
<testcase name="Addition" status="run" time="0.007" classname=""> <testcase name="Addition" file="test.cpp" line="1" status="run" time="0.007" classname="">
<failure message="Value of: add(1, 1)&#x0A; Actual: 3&#x0A;Expected: 2" type="">...</failure> <failure message="Value of: add(1, 1)&#x0A; Actual: 3&#x0A;Expected: 2" type="">...</failure>
<failure message="Value of: add(1, -1)&#x0A; Actual: 1&#x0A;Expected: 0" type="">...</failure> <failure message="Value of: add(1, -1)&#x0A; Actual: 1&#x0A;Expected: 0" type="">...</failure>
</testcase> </testcase>
<testcase name="Subtraction" status="run" time="0.005" classname=""> <testcase name="Subtraction" file="test.cpp" line="2" status="run" time="0.005" classname="">
</testcase> </testcase>
</testsuite> </testsuite>
<testsuite name="LogicTest" tests="1" failures="0" errors="0" time="0.005"> <testsuite name="LogicTest" tests="1" failures="0" errors="0" time="0.005">
<testcase name="NonContradiction" status="run" time="0.005" classname=""> <testcase name="NonContradiction" file="test.cpp" line="3" status="run" time="0.005" classname="">
</testcase> </testcase>
</testsuite> </testsuite>
</testsuites> </testsuites>
@@ -2046,6 +2123,9 @@ Things to note:
* The `timestamp` attribute records the local date and time of the test * The `timestamp` attribute records the local date and time of the test
execution. execution.
* The `file` and `line` attributes record the source file location, where the
test was defined.
* Each `<failure>` element corresponds to a single failed googletest * Each `<failure>` element corresponds to a single failed googletest
assertion. assertion.
@@ -2085,6 +2165,8 @@ The report format conforms to the following JSON Schema:
"type": "object", "type": "object",
"properties": { "properties": {
"name": { "type": "string" }, "name": { "type": "string" },
"file": { "type": "string" },
"line": { "type": "integer" },
"status": { "status": {
"type": "string", "type": "string",
"enum": ["RUN", "NOTRUN"] "enum": ["RUN", "NOTRUN"]
@@ -2162,6 +2244,8 @@ message TestCase {
message TestInfo { message TestInfo {
string name = 1; string name = 1;
string file = 6;
int32 line = 7;
enum Status { enum Status {
RUN = 0; RUN = 0;
NOTRUN = 1; NOTRUN = 1;
@@ -2205,6 +2289,8 @@ could generate this report:
"testsuite": [ "testsuite": [
{ {
"name": "Addition", "name": "Addition",
"file": "test.cpp",
"line": 1,
"status": "RUN", "status": "RUN",
"time": "0.007s", "time": "0.007s",
"classname": "", "classname": "",
@@ -2221,6 +2307,8 @@ could generate this report:
}, },
{ {
"name": "Subtraction", "name": "Subtraction",
"file": "test.cpp",
"line": 2,
"status": "RUN", "status": "RUN",
"time": "0.005s", "time": "0.005s",
"classname": "" "classname": ""
@@ -2236,6 +2324,8 @@ could generate this report:
"testsuite": [ "testsuite": [
{ {
"name": "NonContradiction", "name": "NonContradiction",
"file": "test.cpp",
"line": 3,
"status": "RUN", "status": "RUN",
"time": "0.005s", "time": "0.005s",
"classname": "" "classname": ""
@@ -2253,12 +2343,11 @@ IMPORTANT: The exact format of the JSON document is subject to change.
#### Detecting Test Premature Exit #### Detecting Test Premature Exit
Google Test implements the _premature-exit-file_ protocol for test runners Google Test implements the _premature-exit-file_ protocol for test runners to
to catch any kind of unexpected exits of test programs. Upon start, catch any kind of unexpected exits of test programs. Upon start, Google Test
Google Test creates the file which will be automatically deleted after creates the file which will be automatically deleted after all work has been
all work has been finished. Then, the test runner can check if this file finished. Then, the test runner can check if this file exists. In case the file
exists. In case the file remains undeleted, the inspected test has exited remains undeleted, the inspected test has exited prematurely.
prematurely.
This feature is enabled only if the `TEST_PREMATURE_EXIT_FILE` environment This feature is enabled only if the `TEST_PREMATURE_EXIT_FILE` environment
variable has been set. variable has been set.

View File

@@ -1,9 +1,9 @@
# Googletest FAQ # GoogleTest FAQ
## Why should test suite names and test names not contain underscore? ## Why should test suite names and test names not contain underscore?
{: .callout .note} {: .callout .note}
Note: Googletest reserves underscore (`_`) for special purpose keywords, such as Note: GoogleTest reserves underscore (`_`) for special purpose keywords, such as
[the `DISABLED_` prefix](advanced.md#temporarily-disabling-tests), in addition [the `DISABLED_` prefix](advanced.md#temporarily-disabling-tests), in addition
to the following rationale. to the following rationale.
@@ -50,15 +50,15 @@ Now, the two `TEST`s will both generate the same class
So for simplicity, we just ask the users to avoid `_` in `TestSuiteName` and So for simplicity, we just ask the users to avoid `_` in `TestSuiteName` and
`TestName`. The rule is more constraining than necessary, but it's simple and `TestName`. The rule is more constraining than necessary, but it's simple and
easy to remember. It also gives googletest some wiggle room in case its easy to remember. It also gives GoogleTest some wiggle room in case its
implementation needs to change in the future. implementation needs to change in the future.
If you violate the rule, there may not be immediate consequences, but your test If you violate the rule, there may not be immediate consequences, but your test
may (just may) break with a new compiler (or a new version of the compiler you may (just may) break with a new compiler (or a new version of the compiler you
are using) or with a new version of googletest. Therefore it's best to follow are using) or with a new version of GoogleTest. Therefore it's best to follow
the rule. the rule.
## Why does googletest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`? ## Why does GoogleTest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`?
First of all, you can use `nullptr` with each of these macros, e.g. First of all, you can use `nullptr` with each of these macros, e.g.
`EXPECT_EQ(ptr, nullptr)`, `EXPECT_NE(ptr, nullptr)`, `ASSERT_EQ(ptr, nullptr)`, `EXPECT_EQ(ptr, nullptr)`, `EXPECT_NE(ptr, nullptr)`, `ASSERT_EQ(ptr, nullptr)`,
@@ -68,7 +68,7 @@ because `nullptr` does not have the type problems that `NULL` does.
Due to some peculiarity of C++, it requires some non-trivial template meta Due to some peculiarity of C++, it requires some non-trivial template meta
programming tricks to support using `NULL` as an argument of the `EXPECT_XX()` programming tricks to support using `NULL` as an argument of the `EXPECT_XX()`
and `ASSERT_XX()` macros. Therefore we only do it where it's most needed and `ASSERT_XX()` macros. Therefore we only do it where it's most needed
(otherwise we make the implementation of googletest harder to maintain and more (otherwise we make the implementation of GoogleTest harder to maintain and more
error-prone than necessary). error-prone than necessary).
Historically, the `EXPECT_EQ()` macro took the *expected* value as its first Historically, the `EXPECT_EQ()` macro took the *expected* value as its first
@@ -162,7 +162,7 @@ methods, the parent process will think the calls have never occurred. Therefore,
you may want to move your `EXPECT_CALL` statements inside the `EXPECT_DEATH` you may want to move your `EXPECT_CALL` statements inside the `EXPECT_DEATH`
macro. macro.
## EXPECT_EQ(htonl(blah), blah_blah) generates weird compiler errors in opt mode. Is this a googletest bug? ## EXPECT_EQ(htonl(blah), blah_blah) generates weird compiler errors in opt mode. Is this a GoogleTest bug?
Actually, the bug is in `htonl()`. Actually, the bug is in `htonl()`.
@@ -199,7 +199,7 @@ const int Foo::kBar; // No initializer here.
``` ```
Otherwise your code is **invalid C++**, and may break in unexpected ways. In Otherwise your code is **invalid C++**, and may break in unexpected ways. In
particular, using it in googletest comparison assertions (`EXPECT_EQ`, etc) will particular, using it in GoogleTest comparison assertions (`EXPECT_EQ`, etc) will
generate an "undefined reference" linker error. The fact that "it used to work" generate an "undefined reference" linker error. The fact that "it used to work"
doesn't mean it's valid. It just means that you were lucky. :-) doesn't mean it's valid. It just means that you were lucky. :-)
@@ -225,7 +225,7 @@ cases may want to use the same or slightly different fixtures. For example, you
may want to make sure that all of a GUI library's test suites don't leak may want to make sure that all of a GUI library's test suites don't leak
important system resources like fonts and brushes. important system resources like fonts and brushes.
In googletest, you share a fixture among test suites by putting the shared logic In GoogleTest, you share a fixture among test suites by putting the shared logic
in a base test fixture, then deriving from that base a separate fixture for each in a base test fixture, then deriving from that base a separate fixture for each
test suite that wants to use this common logic. You then use `TEST_F()` to write test suite that wants to use this common logic. You then use `TEST_F()` to write
tests using each derived fixture. tests using each derived fixture.
@@ -264,10 +264,10 @@ TEST_F(FooTest, Baz) { ... }
``` ```
If necessary, you can continue to derive test fixtures from a derived fixture. If necessary, you can continue to derive test fixtures from a derived fixture.
googletest has no limit on how deep the hierarchy can be. GoogleTest has no limit on how deep the hierarchy can be.
For a complete example using derived test fixtures, see For a complete example using derived test fixtures, see
[sample5_unittest.cc](https://github.com/google/googletest/blob/master/googletest/samples/sample5_unittest.cc). [sample5_unittest.cc](https://github.com/google/googletest/blob/main/googletest/samples/sample5_unittest.cc).
## My compiler complains "void value not ignored as it ought to be." What does this mean? ## My compiler complains "void value not ignored as it ought to be." What does this mean?
@@ -278,7 +278,7 @@ disabled by our build system. Please see more details
## My death test hangs (or seg-faults). How do I fix it? ## My death test hangs (or seg-faults). How do I fix it?
In googletest, death tests are run in a child process and the way they work is In GoogleTest, death tests are run in a child process and the way they work is
delicate. To write death tests you really need to understand how they work—see delicate. To write death tests you really need to understand how they work—see
the details at [Death Assertions](reference/assertions.md#death) in the the details at [Death Assertions](reference/assertions.md#death) in the
Assertions Reference. Assertions Reference.
@@ -305,8 +305,8 @@ bullet - sorry!
## Should I use the constructor/destructor of the test fixture or SetUp()/TearDown()? {#CtorVsSetUp} ## Should I use the constructor/destructor of the test fixture or SetUp()/TearDown()? {#CtorVsSetUp}
The first thing to remember is that googletest does **not** reuse the same test The first thing to remember is that GoogleTest does **not** reuse the same test
fixture object across multiple tests. For each `TEST_F`, googletest will create fixture object across multiple tests. For each `TEST_F`, GoogleTest will create
a **fresh** test fixture object, immediately call `SetUp()`, run the test body, a **fresh** test fixture object, immediately call `SetUp()`, run the test body,
call `TearDown()`, and then delete the test fixture object. call `TearDown()`, and then delete the test fixture object.
@@ -328,7 +328,7 @@ You may still want to use `SetUp()/TearDown()` in the following cases:
* C++ does not allow virtual function calls in constructors and destructors. * C++ does not allow virtual function calls in constructors and destructors.
You can call a method declared as virtual, but it will not use dynamic You can call a method declared as virtual, but it will not use dynamic
dispatch, it will use the definition from the class the constructor of which dispatch. It will use the definition from the class the constructor of which
is currently executing. This is because calling a virtual method before the is currently executing. This is because calling a virtual method before the
derived class constructor has a chance to run is very dangerous - the derived class constructor has a chance to run is very dangerous - the
virtual method might operate on uninitialized data. Therefore, if you need virtual method might operate on uninitialized data. Therefore, if you need
@@ -345,11 +345,11 @@ You may still want to use `SetUp()/TearDown()` in the following cases:
that many standard libraries (like STL) may throw when exceptions are that many standard libraries (like STL) may throw when exceptions are
enabled in the compiler. Therefore you should prefer `TearDown()` if you enabled in the compiler. Therefore you should prefer `TearDown()` if you
want to write portable tests that work with or without exceptions. want to write portable tests that work with or without exceptions.
* The googletest team is considering making the assertion macros throw on * The GoogleTest team is considering making the assertion macros throw on
platforms where exceptions are enabled (e.g. Windows, Mac OS, and Linux platforms where exceptions are enabled (e.g. Windows, Mac OS, and Linux
client-side), which will eliminate the need for the user to propagate client-side), which will eliminate the need for the user to propagate
failures from a subroutine to its caller. Therefore, you shouldn't use failures from a subroutine to its caller. Therefore, you shouldn't use
googletest assertions in a destructor if your code could run on such a GoogleTest assertions in a destructor if your code could run on such a
platform. platform.
## The compiler complains "no matching function to call" when I use ASSERT_PRED*. How do I fix it? ## The compiler complains "no matching function to call" when I use ASSERT_PRED*. How do I fix it?
@@ -375,7 +375,7 @@ they write
This is **wrong and dangerous**. The testing services needs to see the return This is **wrong and dangerous**. The testing services needs to see the return
value of `RUN_ALL_TESTS()` in order to determine if a test has passed. If your value of `RUN_ALL_TESTS()` in order to determine if a test has passed. If your
`main()` function ignores it, your test will be considered successful even if it `main()` function ignores it, your test will be considered successful even if it
has a googletest assertion failure. Very bad. has a GoogleTest assertion failure. Very bad.
We have decided to fix this (thanks to Michael Chastain for the idea). Now, your We have decided to fix this (thanks to Michael Chastain for the idea). Now, your
code will no longer be able to ignore `RUN_ALL_TESTS()` when compiled with code will no longer be able to ignore `RUN_ALL_TESTS()` when compiled with
@@ -410,7 +410,6 @@ C++ is case-sensitive. Did you spell it as `Setup()`?
Similarly, sometimes people spell `SetUpTestSuite()` as `SetupTestSuite()` and Similarly, sometimes people spell `SetUpTestSuite()` as `SetupTestSuite()` and
wonder why it's never called. wonder why it's never called.
## I have several test suites which share the same test fixture logic, do I have to define a new test fixture class for each of them? This seems pretty tedious. ## I have several test suites which share the same test fixture logic, do I have to define a new test fixture class for each of them? This seems pretty tedious.
You don't have to. Instead of You don't have to. Instead of
@@ -441,14 +440,14 @@ TEST_F(BarTest, Abc) { ... }
TEST_F(BarTest, Def) { ... } TEST_F(BarTest, Def) { ... }
``` ```
## googletest output is buried in a whole bunch of LOG messages. What do I do? ## GoogleTest output is buried in a whole bunch of LOG messages. What do I do?
The googletest output is meant to be a concise and human-friendly report. If The GoogleTest output is meant to be a concise and human-friendly report. If
your test generates textual output itself, it will mix with the googletest your test generates textual output itself, it will mix with the GoogleTest
output, making it hard to read. However, there is an easy solution to this output, making it hard to read. However, there is an easy solution to this
problem. problem.
Since `LOG` messages go to stderr, we decided to let googletest output go to Since `LOG` messages go to stderr, we decided to let GoogleTest output go to
stdout. This way, you can easily separate the two using redirection. For stdout. This way, you can easily separate the two using redirection. For
example: example:
@@ -521,7 +520,7 @@ TEST(MyDeathTest, CompoundStatement) {
## I have a fixture class `FooTest`, but `TEST_F(FooTest, Bar)` gives me error ``"no matching function for call to `FooTest::FooTest()'"``. Why? ## I have a fixture class `FooTest`, but `TEST_F(FooTest, Bar)` gives me error ``"no matching function for call to `FooTest::FooTest()'"``. Why?
Googletest needs to be able to create objects of your test fixture class, so it GoogleTest needs to be able to create objects of your test fixture class, so it
must have a default constructor. Normally the compiler will define one for you. must have a default constructor. Normally the compiler will define one for you.
However, there are cases where you have to define your own: However, there are cases where you have to define your own:
@@ -546,11 +545,11 @@ The new NPTL thread library doesn't suffer from this problem, as it doesn't
create a manager thread. However, if you don't control which machine your test create a manager thread. However, if you don't control which machine your test
runs on, you shouldn't depend on this. runs on, you shouldn't depend on this.
## Why does googletest require the entire test suite, instead of individual tests, to be named *DeathTest when it uses ASSERT_DEATH? ## Why does GoogleTest require the entire test suite, instead of individual tests, to be named *DeathTest when it uses ASSERT_DEATH?
googletest does not interleave tests from different test suites. That is, it GoogleTest does not interleave tests from different test suites. That is, it
runs all tests in one test suite first, and then runs all tests in the next test runs all tests in one test suite first, and then runs all tests in the next test
suite, and so on. googletest does this because it needs to set up a test suite suite, and so on. GoogleTest does this because it needs to set up a test suite
before the first test in it is run, and tear it down afterwards. Splitting up before the first test in it is run, and tear it down afterwards. Splitting up
the test case would require multiple set-up and tear-down processes, which is the test case would require multiple set-up and tear-down processes, which is
inefficient and makes the semantics unclean. inefficient and makes the semantics unclean.
@@ -589,11 +588,11 @@ TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... }
TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... } TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... }
``` ```
## googletest prints the LOG messages in a death test's child process only when the test fails. How can I see the LOG messages when the death test succeeds? ## GoogleTest prints the LOG messages in a death test's child process only when the test fails. How can I see the LOG messages when the death test succeeds?
Printing the LOG messages generated by the statement inside `EXPECT_DEATH()` Printing the LOG messages generated by the statement inside `EXPECT_DEATH()`
makes it harder to search for real problems in the parent's log. Therefore, makes it harder to search for real problems in the parent's log. Therefore,
googletest only prints them when the death test has failed. GoogleTest only prints them when the death test has failed.
If you really need to see such LOG messages, a workaround is to temporarily If you really need to see such LOG messages, a workaround is to temporarily
break the death test (e.g. by changing the regex pattern it is expected to break the death test (e.g. by changing the regex pattern it is expected to
@@ -612,7 +611,7 @@ needs to be defined in the *same* name space. See
## How do I suppress the memory leak messages on Windows? ## How do I suppress the memory leak messages on Windows?
Since the statically initialized googletest singleton requires allocations on Since the statically initialized GoogleTest singleton requires allocations on
the heap, the Visual C++ memory leak detector will report memory leaks at the the heap, the Visual C++ memory leak detector will report memory leaks at the
end of the program run. The easiest way to avoid this is to use the end of the program run. The easiest way to avoid this is to use the
`_CrtMemCheckpoint` and `_CrtMemDumpAllObjectsSince` calls to not report any `_CrtMemCheckpoint` and `_CrtMemDumpAllObjectsSince` calls to not report any
@@ -626,7 +625,7 @@ things accordingly, you are leaking test-only logic into production code and
there is no easy way to ensure that the test-only code paths aren't run by there is no easy way to ensure that the test-only code paths aren't run by
mistake in production. Such cleverness also leads to mistake in production. Such cleverness also leads to
[Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug). Therefore we strongly [Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug). Therefore we strongly
advise against the practice, and googletest doesn't provide a way to do it. advise against the practice, and GoogleTest doesn't provide a way to do it.
In general, the recommended way to cause the code to behave differently under In general, the recommended way to cause the code to behave differently under
test is [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection). You can inject test is [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection). You can inject
@@ -673,7 +672,7 @@ TEST(CoolTest, DoSomething) {
``` ```
However, the following code is **not allowed** and will produce a runtime error However, the following code is **not allowed** and will produce a runtime error
from googletest because the test methods are using different test fixture from GoogleTest because the test methods are using different test fixture
classes with the same test suite name. classes with the same test suite name.
```c++ ```c++

View File

@@ -8,7 +8,7 @@ Given
```cpp ```cpp
class Foo { class Foo {
... public:
virtual ~Foo(); virtual ~Foo();
virtual int GetSize() const = 0; virtual int GetSize() const = 0;
virtual string Describe(const char* name) = 0; virtual string Describe(const char* name) = 0;
@@ -23,7 +23,7 @@ class Foo {
#include "gmock/gmock.h" #include "gmock/gmock.h"
class MockFoo : public Foo { class MockFoo : public Foo {
... public:
MOCK_METHOD(int, GetSize, (), (const, override)); MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(string, Describe, (const char* name), (override)); MOCK_METHOD(string, Describe, (const char* name), (override));
MOCK_METHOD(string, Describe, (int type), (override)); MOCK_METHOD(string, Describe, (int type), (override));
@@ -58,7 +58,7 @@ To mock
```cpp ```cpp
template <typename Elem> template <typename Elem>
class StackInterface { class StackInterface {
... public:
virtual ~StackInterface(); virtual ~StackInterface();
virtual int GetSize() const = 0; virtual int GetSize() const = 0;
virtual void Push(const Elem& x) = 0; virtual void Push(const Elem& x) = 0;
@@ -71,7 +71,7 @@ class StackInterface {
```cpp ```cpp
template <typename Elem> template <typename Elem>
class MockStack : public StackInterface<Elem> { class MockStack : public StackInterface<Elem> {
... public:
MOCK_METHOD(int, GetSize, (), (const, override)); MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(void, Push, (const Elem& x), (override)); MOCK_METHOD(void, Push, (const Elem& x), (override));
}; };
@@ -140,7 +140,7 @@ To customize the default action for functions with return type `T`, use
// Sets the default action for return type std::unique_ptr<Buzz> to // Sets the default action for return type std::unique_ptr<Buzz> to
// creating a new Buzz every time. // creating a new Buzz every time.
DefaultValue<std::unique_ptr<Buzz>>::SetFactory( DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
[] { return MakeUnique<Buzz>(AccessLevel::kInternal); }); [] { return std::make_unique<Buzz>(AccessLevel::kInternal); });
// When this fires, the default action of MakeBuzz() will run, which // When this fires, the default action of MakeBuzz() will run, which
// will return a new Buzz object. // will return a new Buzz object.
@@ -230,7 +230,7 @@ class MockFunction<R(A1, ..., An)> {
}; };
``` ```
See this [recipe](gmock_cook_book.md#using-check-points) for one application of See this [recipe](gmock_cook_book.md#UsingCheckPoints) for one application of
it. it.
## Flags ## Flags

View File

@@ -392,8 +392,7 @@ Old macros and their new equivalents:
If a mock method has no `EXPECT_CALL` spec but is called, we say that it's an If a mock method has no `EXPECT_CALL` spec but is called, we say that it's an
"uninteresting call", and the default action (which can be specified using "uninteresting call", and the default action (which can be specified using
`ON_CALL()`) of the method will be taken. Currently, an uninteresting call will `ON_CALL()`) of the method will be taken. Currently, an uninteresting call will
also by default cause gMock to print a warning. (In the future, we might remove also by default cause gMock to print a warning.
this warning by default.)
However, sometimes you may want to ignore these uninteresting calls, and However, sometimes you may want to ignore these uninteresting calls, and
sometimes you may want to treat them as errors. gMock lets you make the decision sometimes you may want to treat them as errors. gMock lets you make the decision
@@ -905,7 +904,7 @@ using ::testing::Contains;
using ::testing::Property; using ::testing::Property;
inline constexpr auto HasFoo = [](const auto& f) { inline constexpr auto HasFoo = [](const auto& f) {
return Property(&MyClass::foo, Contains(f)); return Property("foo", &MyClass::foo, Contains(f));
}; };
... ...
EXPECT_THAT(x, HasFoo("blah")); EXPECT_THAT(x, HasFoo("blah"));
@@ -1084,7 +1083,7 @@ using ::testing::Lt;
``` ```
says that `Blah` will be called with arguments `x`, `y`, and `z` where `x < y < says that `Blah` will be called with arguments `x`, `y`, and `z` where `x < y <
z`. Note that in this example, it wasn't necessary specify the positional z`. Note that in this example, it wasn't necessary to specify the positional
matchers. matchers.
As a convenience and example, gMock provides some matchers for 2-tuples, As a convenience and example, gMock provides some matchers for 2-tuples,
@@ -1159,7 +1158,7 @@ int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; }
``` ```
Note that the predicate function / functor doesn't have to return `bool`. It Note that the predicate function / functor doesn't have to return `bool`. It
works as long as the return value can be used as the condition in in statement works as long as the return value can be used as the condition in the statement
`if (condition) ...`. `if (condition) ...`.
### Matching Arguments that Are Not Copyable ### Matching Arguments that Are Not Copyable
@@ -1300,23 +1299,27 @@ What if you have a pointer to pointer? You guessed it - you can use nested
`Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer that points `Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer that points
to a number less than 3 (what a mouthful...). to a number less than 3 (what a mouthful...).
### Testing a Certain Property of an Object ### Defining a Custom Matcher Class {#CustomMatcherClass}
Sometimes you want to specify that an object argument has a certain property, Most matchers can be simply defined using [the MATCHER* macros](#NewMatchers),
but there is no existing matcher that does this. If you want good error which are terse and flexible, and produce good error messages. However, these
messages, you should [define a matcher](#NewMatchers). If you want to do it macros are not very explicit about the interfaces they create and are not always
quick and dirty, you could get away with writing an ordinary function. suitable, especially for matchers that will be widely reused.
Let's say you have a mock function that takes an object of type `Foo`, which has For more advanced cases, you may need to define your own matcher class. A custom
an `int bar()` method and an `int baz()` method, and you want to constrain that matcher allows you to test a specific invariant property of that object. Let's
the argument's `bar()` value plus its `baz()` value is a given number. Here's take a look at how to do so.
how you can define a matcher to do it:
Imagine you have a mock function that takes an object of type `Foo`, which has
an `int bar()` method and an `int baz()` method. You want to constrain that the
argument's `bar()` value plus its `baz()` value is a given number. (This is an
invariant.) Here's how we can write and use a matcher class to do so:
```cpp ```cpp
using ::testing::Matcher;
class BarPlusBazEqMatcher { class BarPlusBazEqMatcher {
public: public:
using is_gtest_matcher = void;
explicit BarPlusBazEqMatcher(int expected_sum) explicit BarPlusBazEqMatcher(int expected_sum)
: expected_sum_(expected_sum) {} : expected_sum_(expected_sum) {}
@@ -1325,23 +1328,24 @@ class BarPlusBazEqMatcher {
return (foo.bar() + foo.baz()) == expected_sum_; return (foo.bar() + foo.baz()) == expected_sum_;
} }
void DescribeTo(std::ostream& os) const { void DescribeTo(std::ostream* os) const {
os << "bar() + baz() equals " << expected_sum_; *os << "bar() + baz() equals " << expected_sum_;
} }
void DescribeNegationTo(std::ostream& os) const { void DescribeNegationTo(std::ostream* os) const {
os << "bar() + baz() does not equal " << expected_sum_; *os << "bar() + baz() does not equal " << expected_sum_;
} }
private: private:
const int expected_sum_; const int expected_sum_;
}; };
Matcher<const Foo&> BarPlusBazEq(int expected_sum) { ::testing::Matcher<const Foo&> BarPlusBazEq(int expected_sum) {
return BarPlusBazEqMatcher(expected_sum); return BarPlusBazEqMatcher(expected_sum);
} }
... ...
EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; Foo foo;
EXPECT_THAT(foo, BarPlusBazEq(5))...;
``` ```
### Matching Containers ### Matching Containers
@@ -1420,11 +1424,12 @@ Use `Pair` when comparing maps or other associative containers.
{% raw %} {% raw %}
```cpp ```cpp
using testing::ElementsAre; using ::testing::UnorderedElementsAre;
using testing::Pair; using ::testing::Pair;
... ...
std::map<string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}}; absl::flat_hash_map<string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};
EXPECT_THAT(m, ElementsAre(Pair("a", 1), Pair("b", 2), Pair("c", 3))); EXPECT_THAT(m, UnorderedElementsAre(
Pair("a", 1), Pair("b", 2), Pair("c", 3)));
``` ```
{% endraw %} {% endraw %}
@@ -1441,8 +1446,8 @@ using testing::Pair;
* If the container is passed by pointer instead of by reference, just write * If the container is passed by pointer instead of by reference, just write
`Pointee(ElementsAre*(...))`. `Pointee(ElementsAre*(...))`.
* The order of elements *matters* for `ElementsAre*()`. If you are using it * The order of elements *matters* for `ElementsAre*()`. If you are using it
with containers whose element order are undefined (e.g. `hash_map`) you with containers whose element order are undefined (such as a
should use `WhenSorted` around `ElementsAre`. `std::unordered_map`) you should use `UnorderedElementsAre`.
### Sharing Matchers ### Sharing Matchers
@@ -1452,7 +1457,7 @@ the pointer is copied. When the last matcher that references the implementation
object dies, the implementation object will be deleted. object dies, the implementation object will be deleted.
Therefore, if you have some complex matcher that you want to use again and Therefore, if you have some complex matcher that you want to use again and
again, there is no need to build it everytime. Just assign it to a matcher again, there is no need to build it every time. Just assign it to a matcher
variable and use that variable repeatedly! For example, variable and use that variable repeatedly! For example,
```cpp ```cpp
@@ -1754,7 +1759,7 @@ specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> C -> D`):
| |
A ---| A ---|
| |
+---> C ---> D +---> C ---> D
``` ```
This means that A must occur before B and C, and C must occur before D. There's This means that A must occur before B and C, and C must occur before D. There's
@@ -1899,7 +1904,7 @@ using testing::ReturnPointee;
### Combining Actions ### Combining Actions
Want to do more than one thing when a function is called? That's fine. `DoAll()` Want to do more than one thing when a function is called? That's fine. `DoAll()`
allow you to do sequence of actions every time. Only the return value of the allows you to do a sequence of actions every time. Only the return value of the
last action in the sequence will be used. last action in the sequence will be used.
```cpp ```cpp
@@ -1980,6 +1985,7 @@ If the mock method also needs to return a value as well, you can chain
```cpp ```cpp
using ::testing::_; using ::testing::_;
using ::testing::DoAll;
using ::testing::Return; using ::testing::Return;
using ::testing::SetArgPointee; using ::testing::SetArgPointee;
@@ -2033,10 +2039,7 @@ class MockRolodex : public Rolodex {
} }
... ...
MockRolodex rolodex; MockRolodex rolodex;
vector<string> names; vector<string> names = {"George", "John", "Thomas"};
names.push_back("George");
names.push_back("John");
names.push_back("Thomas");
EXPECT_CALL(rolodex, GetNames(_)) EXPECT_CALL(rolodex, GetNames(_))
.WillOnce(SetArrayArgument<0>(names.begin(), names.end())); .WillOnce(SetArrayArgument<0>(names.begin(), names.end()));
``` ```
@@ -2604,7 +2607,7 @@ efficient. When the last action that references the implementation object dies,
the implementation object will be deleted. the implementation object will be deleted.
If you have some complex action that you want to use again and again, you may If you have some complex action that you want to use again and again, you may
not have to build it from scratch everytime. If the action doesn't have an not have to build it from scratch every time. If the action doesn't have an
internal state (i.e. if it always does the same thing no matter how many times internal state (i.e. if it always does the same thing no matter how many times
it has been called), you can assign it to an action variable and use that it has been called), you can assign it to an action variable and use that
variable repeatedly. For example: variable repeatedly. For example:
@@ -2781,7 +2784,7 @@ If you just need to return a pre-defined move-only value, you can use the
// When this fires, the unique_ptr<> specified by ByMove(...) will // When this fires, the unique_ptr<> specified by ByMove(...) will
// be returned. // be returned.
EXPECT_CALL(mock_buzzer_, MakeBuzz("world")) EXPECT_CALL(mock_buzzer_, MakeBuzz("world"))
.WillOnce(Return(ByMove(MakeUnique<Buzz>(AccessLevel::kInternal)))); .WillOnce(Return(ByMove(std::make_unique<Buzz>(AccessLevel::kInternal))));
EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("world")); EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("world"));
``` ```
@@ -2802,7 +2805,7 @@ pretty much anything you want:
```cpp ```cpp
EXPECT_CALL(mock_buzzer_, MakeBuzz("x")) EXPECT_CALL(mock_buzzer_, MakeBuzz("x"))
.WillRepeatedly([](StringPiece text) { .WillRepeatedly([](StringPiece text) {
return MakeUnique<Buzz>(AccessLevel::kInternal); return std::make_unique<Buzz>(AccessLevel::kInternal);
}); });
EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x"));
@@ -2821,7 +2824,7 @@ can always use `Return`, or a [lambda or functor](#FunctionsAsActions):
using ::testing::Unused; using ::testing::Unused;
EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)).WillOnce(Return(true)); EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)).WillOnce(Return(true));
EXPECT_TRUE(mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal)), EXPECT_TRUE(mock_buzzer_.ShareBuzz(std::make_unique<Buzz>(AccessLevel::kInternal)),
0); 0);
EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)).WillOnce( EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)).WillOnce(
@@ -2865,7 +2868,7 @@ method:
// When one calls ShareBuzz() on the MockBuzzer like this, the call is // When one calls ShareBuzz() on the MockBuzzer like this, the call is
// forwarded to DoShareBuzz(), which is mocked. Therefore this statement // forwarded to DoShareBuzz(), which is mocked. Therefore this statement
// will trigger the above EXPECT_CALL. // will trigger the above EXPECT_CALL.
mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal), 0); mock_buzzer_.ShareBuzz(std::make_unique<Buzz>(AccessLevel::kInternal), 0);
``` ```
### Making the Compilation Faster ### Making the Compilation Faster
@@ -3809,22 +3812,19 @@ Cardinality EvenNumber() {
.Times(EvenNumber()); .Times(EvenNumber());
``` ```
### Writing New Actions Quickly {#QuickNewActions} ### Writing New Actions {#QuickNewActions}
If the built-in actions don't work for you, you can easily define your own one. If the built-in actions don't work for you, you can easily define your own one.
Just define a functor class with a (possibly templated) call operator, matching All you need is a call operator with a signature compatible with the mocked
the signature of your action. function. So you can use a lambda:
```cpp ```
struct Increment { MockFunction<int(int)> mock;
template <typename T> EXPECT_CALL(mock, Call).WillOnce([](const int input) { return input * 7; });
T operator()(T* arg) { EXPECT_EQ(14, mock.AsStdFunction()(2));
return ++(*arg);
}
}
``` ```
The same approach works with stateful functors (or any callable, really): Or a struct with a call operator (even a templated one):
``` ```
struct MultiplyBy { struct MultiplyBy {
@@ -3832,12 +3832,54 @@ struct MultiplyBy {
T operator()(T arg) { return arg * multiplier; } T operator()(T arg) { return arg * multiplier; }
int multiplier; int multiplier;
} };
// Then use: // Then use:
// EXPECT_CALL(...).WillOnce(MultiplyBy{7}); // EXPECT_CALL(...).WillOnce(MultiplyBy{7});
``` ```
It's also fine for the callable to take no arguments, ignoring the arguments
supplied to the mock function:
```
MockFunction<int(int)> mock;
EXPECT_CALL(mock, Call).WillOnce([] { return 17; });
EXPECT_EQ(17, mock.AsStdFunction()(0));
```
When used with `WillOnce`, the callable can assume it will be called at most
once and is allowed to be a move-only type:
```
// An action that contains move-only types and has an &&-qualified operator,
// demanding in the type system that it be called at most once. This can be
// used with WillOnce, but the compiler will reject it if handed to
// WillRepeatedly.
struct MoveOnlyAction {
std::unique_ptr<int> move_only_state;
std::unique_ptr<int> operator()() && { return std::move(move_only_state); }
};
MockFunction<std::unique_ptr<int>()> mock;
EXPECT_CALL(mock, Call).WillOnce(MoveOnlyAction{std::make_unique<int>(17)});
EXPECT_THAT(mock.AsStdFunction()(), Pointee(Eq(17)));
```
More generally, to use with a mock function whose signature is `R(Args...)` the
object can be anything convertible to `OnceAction<R(Args...)>` or
`Action<R(Args...)`>. The difference between the two is that `OnceAction` has
weaker requirements (`Action` requires a copy-constructible input that can be
called repeatedly whereas `OnceAction` requires only move-constructible and
supports `&&`-qualified call operators), but can be used only with `WillOnce`.
`OnceAction` is typically relevant only when supporting move-only types or
actions that want a type-system guarantee that they will be called at most once.
Typically the `OnceAction` and `Action` templates need not be referenced
directly in your actions: a struct or class with a call operator is sufficient,
as in the examples above. But fancier polymorphic actions that need to know the
specific return type of the mock function can define templated conversion
operators to make that possible. See `gmock-actions.h` for examples.
#### Legacy macro-based Actions #### Legacy macro-based Actions
Before C++11, the functor-based actions were not supported; the old way of Before C++11, the functor-based actions were not supported; the old way of
@@ -4191,7 +4233,7 @@ This implementation class does *not* need to inherit from any particular class.
What matters is that it must have a `Perform()` method template. This method What matters is that it must have a `Perform()` method template. This method
template takes the mock function's arguments as a tuple in a **single** template takes the mock function's arguments as a tuple in a **single**
argument, and returns the result of the action. It can be either `const` or not, argument, and returns the result of the action. It can be either `const` or not,
but must be invokable with exactly one template argument, which is the result but must be invocable with exactly one template argument, which is the result
type. In other words, you must be able to call `Perform<R>(args)` where `R` is type. In other words, you must be able to call `Perform<R>(args)` where `R` is
the mock function's return type and `args` is its arguments in a tuple. the mock function's return type and `args` is its arguments in a tuple.

View File

@@ -369,8 +369,8 @@ Usually, if your action is for a particular function type, defining it using
different types (e.g. if you are defining `Return(*value*)`), different types (e.g. if you are defining `Return(*value*)`),
`MakePolymorphicAction()` is easiest. Sometimes you want precise control on what `MakePolymorphicAction()` is easiest. Sometimes you want precise control on what
types of functions the action can be used in, and implementing `ActionInterface` types of functions the action can be used in, and implementing `ActionInterface`
is the way to go here. See the implementation of `Return()` in is the way to go here. See the implementation of `Return()` in `gmock-actions.h`
`testing/base/public/gmock-actions.h` for an example. for an example.
### I use SetArgPointee() in WillOnce(), but gcc complains about "conflicting return type specified". What does it mean? ### I use SetArgPointee() in WillOnce(), but gcc complains about "conflicting return type specified". What does it mean?

View File

@@ -190,10 +190,10 @@ Some people put it in a `_test.cc`. This is fine when the interface being mocked
`Foo` changes it, your test could break. (You can't really expect `Foo`'s `Foo` changes it, your test could break. (You can't really expect `Foo`'s
maintainer to fix every test that uses `Foo`, can you?) maintainer to fix every test that uses `Foo`, can you?)
So, the rule of thumb is: if you need to mock `Foo` and it's owned by others, Generally, you should not mock classes you don't own. If you must mock such a
define the mock class in `Foo`'s package (better, in a `testing` sub-package class owned by others, define the mock class in `Foo`'s Bazel package (usually
such that you can clearly separate production code and testing utilities), put the same directory or a `testing` sub-directory), and put it in a `.h` and a
it in a `.h` and a `cc_library`. Then everyone can reference them from their `cc_library` with `testonly=True`. Then everyone can reference them from their
tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and
only tests that depend on the changed methods need to be fixed. only tests that depend on the changed methods need to be fixed.
@@ -480,8 +480,8 @@ the *default* action for the function every time (unless, of course, you have a
`WillRepeatedly()`.). `WillRepeatedly()`.).
What can we do inside `WillOnce()` besides `Return()`? You can return a What can we do inside `WillOnce()` besides `Return()`? You can return a
reference using `ReturnRef(*variable*)`, or invoke a pre-defined function, among reference using `ReturnRef(`*`variable`*`)`, or invoke a pre-defined function,
[others](gmock_cook_book.md#using-actions). among [others](gmock_cook_book.md#using-actions).
**Important note:** The `EXPECT_CALL()` statement evaluates the action clause **Important note:** The `EXPECT_CALL()` statement evaluates the action clause
only once, even though the action may be performed many times. Therefore you only once, even though the action may be performed many times. Therefore you

View File

@@ -105,7 +105,7 @@ includedir=/usr/include
Name: gtest Name: gtest
Description: GoogleTest (without main() function) Description: GoogleTest (without main() function)
Version: 1.10.0 Version: 1.11.0
URL: https://github.com/google/googletest URL: https://github.com/google/googletest
Libs: -L${libdir} -lgtest -lpthread Libs: -L${libdir} -lgtest -lpthread
Cflags: -I${includedir} -DGTEST_HAS_PTHREAD=1 -lpthread Cflags: -I${includedir} -DGTEST_HAS_PTHREAD=1 -lpthread

View File

@@ -162,9 +162,9 @@ TEST(TestSuiteName, TestName) {
`TEST()` arguments go from general to specific. The *first* argument is the name `TEST()` arguments go from general to specific. The *first* argument is the name
of the test suite, and the *second* argument is the test's name within the test of the test suite, and the *second* argument is the test's name within the test
suite. Both names must be valid C++ identifiers, and they should not contain suite. Both names must be valid C++ identifiers, and they should not contain any
any underscores (`_`). A test's *full name* consists of its containing test suite and underscores (`_`). A test's *full name* consists of its containing test suite
its individual name. Tests from different test suites can have the same and its individual name. Tests from different test suites can have the same
individual name. individual name.
For example, let's take a simple integer function: For example, let's take a simple integer function:
@@ -245,8 +245,8 @@ Also, you must first define a test fixture class before using it in a
declaration`". declaration`".
For each test defined with `TEST_F()`, googletest will create a *fresh* test For each test defined with `TEST_F()`, googletest will create a *fresh* test
fixture at runtime, immediately initialize it via `SetUp()`, run the test, fixture at runtime, immediately initialize it via `SetUp()`, run the test, clean
clean up by calling `TearDown()`, and then delete the test fixture. Note that up by calling `TearDown()`, and then delete the test fixture. Note that
different tests in the same test suite have different test fixture objects, and different tests in the same test suite have different test fixture objects, and
googletest always deletes a test fixture before it creates the next one. googletest always deletes a test fixture before it creates the next one.
googletest does **not** reuse the same test fixture for multiple tests. Any googletest does **not** reuse the same test fixture for multiple tests. Any
@@ -274,6 +274,7 @@ First, define a fixture class. By convention, you should give it the name
class QueueTest : public ::testing::Test { class QueueTest : public ::testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
// q0_ remains empty
q1_.Enqueue(1); q1_.Enqueue(1);
q2_.Enqueue(2); q2_.Enqueue(2);
q2_.Enqueue(3); q2_.Enqueue(3);
@@ -342,8 +343,8 @@ your defined tests in order to run them.
After defining your tests, you can run them with `RUN_ALL_TESTS()`, which After defining your tests, you can run them with `RUN_ALL_TESTS()`, which
returns `0` if all the tests are successful, or `1` otherwise. Note that returns `0` if all the tests are successful, or `1` otherwise. Note that
`RUN_ALL_TESTS()` runs *all tests* in your link unit--they can be from `RUN_ALL_TESTS()` runs *all tests* in your link unit--they can be from different
different test suites, or even different source files. test suites, or even different source files.
When invoked, the `RUN_ALL_TESTS()` macro: When invoked, the `RUN_ALL_TESTS()` macro:
@@ -456,8 +457,8 @@ int main(int argc, char **argv) {
The `::testing::InitGoogleTest()` function parses the command line for The `::testing::InitGoogleTest()` function parses the command line for
googletest flags, and removes all recognized flags. This allows the user to googletest flags, and removes all recognized flags. This allows the user to
control a test program's behavior via various flags, which we'll cover in control a test program's behavior via various flags, which we'll cover in the
the [AdvancedGuide](advanced.md). You **must** call this function before calling [AdvancedGuide](advanced.md). You **must** call this function before calling
`RUN_ALL_TESTS()`, or the flags won't be properly initialized. `RUN_ALL_TESTS()`, or the flags won't be properly initialized.
On Windows, `InitGoogleTest()` also works with wide strings, so it can be used On Windows, `InitGoogleTest()` also works with wide strings, so it can be used

View File

@@ -9,7 +9,7 @@ we recommend this tutorial as a starting point.
To complete this tutorial, you'll need: To complete this tutorial, you'll need:
* A compatible operating system (e.g. Linux, macOS, Windows). * A compatible operating system (e.g. Linux, macOS, Windows).
* A compatible C++ compiler that supports at least C++11. * A compatible C++ compiler that supports at least C++14.
* [Bazel](https://bazel.build/), the preferred build system used by the * [Bazel](https://bazel.build/), the preferred build system used by the
GoogleTest team. GoogleTest team.
@@ -17,16 +17,15 @@ See [Supported Platforms](platforms.md) for more information about platforms
compatible with GoogleTest. compatible with GoogleTest.
If you don't already have Bazel installed, see the If you don't already have Bazel installed, see the
[Bazel installation guide](https://docs.bazel.build/versions/master/install.html). [Bazel installation guide](https://bazel.build/install).
{: .callout .note} {: .callout .note} Note: The terminal commands in this tutorial show a Unix
Note: The terminal commands in this tutorial show a Unix shell prompt, but the shell prompt, but the commands work on the Windows command line as well.
commands work on the Windows command line as well.
## Set up a Bazel workspace ## Set up a Bazel workspace
A A
[Bazel workspace](https://docs.bazel.build/versions/master/build-ref.html#workspace) [Bazel workspace](https://docs.bazel.build/versions/main/build-ref.html#workspace)
is a directory on your filesystem that you use to manage source files for the is a directory on your filesystem that you use to manage source files for the
software you want to build. Each workspace directory has a text file named software you want to build. Each workspace directory has a text file named
`WORKSPACE` which may be empty, or may contain references to external `WORKSPACE` which may be empty, or may contain references to external
@@ -40,9 +39,9 @@ $ mkdir my_workspace && cd my_workspace
Next, youll create the `WORKSPACE` file to specify dependencies. A common and Next, youll create the `WORKSPACE` file to specify dependencies. A common and
recommended way to depend on GoogleTest is to use a recommended way to depend on GoogleTest is to use a
[Bazel external dependency](https://docs.bazel.build/versions/master/external.html) [Bazel external dependency](https://docs.bazel.build/versions/main/external.html)
via the via the
[`http_archive` rule](https://docs.bazel.build/versions/master/repo/http.html#http_archive). [`http_archive` rule](https://docs.bazel.build/versions/main/repo/http.html#http_archive).
To do this, in the root directory of your workspace (`my_workspace/`), create a To do this, in the root directory of your workspace (`my_workspace/`), create a
file named `WORKSPACE` with the following contents: file named `WORKSPACE` with the following contents:
@@ -51,28 +50,16 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive( http_archive(
name = "com_google_googletest", name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip"], urls = ["https://github.com/google/googletest/archive/5ab508a01f9eb089207ee87fd547d290da39d015.zip"],
strip_prefix = "googletest-609281088cfefc76f9d0ce82e1ff6c30cc3591e5", strip_prefix = "googletest-5ab508a01f9eb089207ee87fd547d290da39d015",
) )
``` ```
The above configuration declares a dependency on GoogleTest which is downloaded The above configuration declares a dependency on GoogleTest which is downloaded
as a ZIP archive from GitHub. In the above example, as a ZIP archive from GitHub. In the above example,
`609281088cfefc76f9d0ce82e1ff6c30cc3591e5` is the Git commit hash of the `5ab508a01f9eb089207ee87fd547d290da39d015` is the Git commit hash of the
GoogleTest version to use; we recommend updating the hash often to point to the GoogleTest version to use; we recommend updating the hash often to point to the
latest version. latest version. Use a recent hash on the `main` branch.
Bazel also needs a dependency on the
[`rules_cc` repository](https://github.com/bazelbuild/rules_cc) to build C++
code, so add the following to the `WORKSPACE` file:
```
http_archive(
name = "rules_cc",
urls = ["https://github.com/bazelbuild/rules_cc/archive/40548a2974f1aea06215272d9c2b47a14a24e556.zip"],
strip_prefix = "rules_cc-40548a2974f1aea06215272d9c2b47a14a24e556",
)
```
Now you're ready to build C++ code that uses GoogleTest. Now you're ready to build C++ code that uses GoogleTest.
@@ -104,8 +91,6 @@ To build the code, create a file named `BUILD` in the same directory with the
following contents: following contents:
``` ```
load("@rules_cc//cc:defs.bzl", "cc_test")
cc_test( cc_test(
name = "hello_test", name = "hello_test",
size = "small", size = "small",
@@ -118,7 +103,7 @@ This `cc_test` rule declares the C++ test binary you want to build, and links to
GoogleTest (`//:gtest_main`) using the prefix you specified in the `WORKSPACE` GoogleTest (`//:gtest_main`) using the prefix you specified in the `WORKSPACE`
file (`@com_google_googletest`). For more information about Bazel `BUILD` files, file (`@com_google_googletest`). For more information about Bazel `BUILD` files,
see the see the
[Bazel C++ Tutorial](https://docs.bazel.build/versions/master/tutorial/cpp.html). [Bazel C++ Tutorial](https://docs.bazel.build/versions/main/tutorial/cpp.html).
Now you can build and run your test: Now you can build and run your test:

View File

@@ -10,7 +10,7 @@ this tutorial as a starting point. If your project uses Bazel, see the
To complete this tutorial, you'll need: To complete this tutorial, you'll need:
* A compatible operating system (e.g. Linux, macOS, Windows). * A compatible operating system (e.g. Linux, macOS, Windows).
* A compatible C++ compiler that supports at least C++11. * A compatible C++ compiler that supports at least C++14.
* [CMake](https://cmake.org/) and a compatible build tool for building the * [CMake](https://cmake.org/) and a compatible build tool for building the
project. project.
* Compatible build tools include * Compatible build tools include
@@ -52,13 +52,13 @@ To do this, in your project directory (`my_project`), create a file named
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(my_project) project(my_project)
# GoogleTest requires at least C++11 # GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 14)
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
googletest googletest
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
) )
# For Windows: Prevent overriding the parent project's compiler/linker settings # For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
@@ -66,7 +66,7 @@ FetchContent_MakeAvailable(googletest)
``` ```
The above configuration declares a dependency on GoogleTest which is downloaded The above configuration declares a dependency on GoogleTest which is downloaded
from GitHub. In the above example, `609281088cfefc76f9d0ce82e1ff6c30cc3591e5` is from GitHub. In the above example, `03597a01ee50ed33e9dfd640b249b4be3799d395` is
the Git commit hash of the GoogleTest version to use; we recommend updating the the Git commit hash of the GoogleTest version to use; we recommend updating the
hash often to point to the latest version. hash often to point to the latest version.
@@ -108,7 +108,7 @@ add_executable(
) )
target_link_libraries( target_link_libraries(
hello_test hello_test
gtest_main GTest::gtest_main
) )
include(GoogleTest) include(GoogleTest)

View File

@@ -6,7 +6,7 @@ provided by GoogleTest. All actions are defined in the `::testing` namespace.
## Returning a Value ## Returning a Value
| | | | Action | Description |
| :-------------------------------- | :-------------------------------------------- | | :-------------------------------- | :-------------------------------------------- |
| `Return()` | Return from a `void` mock function. | | `Return()` | Return from a `void` mock function. |
| `Return(value)` | Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type <i>at the time the expectation is set</i>, not when the action is executed. | | `Return(value)` | Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type <i>at the time the expectation is set</i>, not when the action is executed. |
@@ -20,7 +20,7 @@ provided by GoogleTest. All actions are defined in the `::testing` namespace.
## Side Effects ## Side Effects
| | | | Action | Description |
| :--------------------------------- | :-------------------------------------- | | :--------------------------------- | :-------------------------------------- |
| `Assign(&variable, value)` | Assign `value` to variable. | | `Assign(&variable, value)` | Assign `value` to variable. |
| `DeleteArg<N>()` | Delete the `N`-th (0-based) argument, which must be a pointer. | | `DeleteArg<N>()` | Delete the `N`-th (0-based) argument, which must be a pointer. |
@@ -38,9 +38,9 @@ provided by GoogleTest. All actions are defined in the `::testing` namespace.
In the following, by "callable" we mean a free function, `std::function`, In the following, by "callable" we mean a free function, `std::function`,
functor, or lambda. functor, or lambda.
| | | | Action | Description |
| :---------------------------------- | :------------------------------------- | | :---------------------------------- | :------------------------------------- |
| `f` | Invoke f with the arguments passed to the mock function, where f is a callable. | | `f` | Invoke `f` with the arguments passed to the mock function, where `f` is a callable. |
| `Invoke(f)` | Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor. | | `Invoke(f)` | Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor. |
| `Invoke(object_pointer, &class::method)` | Invoke the method on the object with the arguments passed to the mock function. | | `Invoke(object_pointer, &class::method)` | Invoke the method on the object with the arguments passed to the mock function. |
| `InvokeWithoutArgs(f)` | Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | | `InvokeWithoutArgs(f)` | Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. |
@@ -86,7 +86,7 @@ value, and `foo` by reference.
## Default Action ## Default Action
| Matcher | Description | | Action | Description |
| :------------ | :----------------------------------------------------- | | :------------ | :----------------------------------------------------- |
| `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). | | `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). |
@@ -96,7 +96,7 @@ composite action - trying to do so will result in a run-time error.
## Composite Actions ## Composite Actions
| | | | Action | Description |
| :----------------------------- | :------------------------------------------ | | :----------------------------- | :------------------------------------------ |
| `DoAll(a1, a2, ..., an)` | Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void and will receive a readonly view of the arguments. | | `DoAll(a1, a2, ..., an)` | Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void and will receive a readonly view of the arguments. |
| `IgnoreResult(a)` | Perform action `a` and ignore its result. `a` must not return void. | | `IgnoreResult(a)` | Perform action `a` and ignore its result. `a` must not return void. |
@@ -106,7 +106,7 @@ composite action - trying to do so will result in a run-time error.
## Defining Actions ## Defining Actions
| | | | Macro | Description |
| :--------------------------------- | :-------------------------------------- | | :--------------------------------- | :-------------------------------------- |
| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | | `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. |
| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | | `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. |

View File

@@ -8,9 +8,13 @@ A **matcher** matches a *single* argument. You can use it inside `ON_CALL()` or
| `EXPECT_THAT(actual_value, matcher)` | Asserts that `actual_value` matches `matcher`. | | `EXPECT_THAT(actual_value, matcher)` | Asserts that `actual_value` matches `matcher`. |
| `ASSERT_THAT(actual_value, matcher)` | The same as `EXPECT_THAT(actual_value, matcher)`, except that it generates a **fatal** failure. | | `ASSERT_THAT(actual_value, matcher)` | The same as `EXPECT_THAT(actual_value, matcher)`, except that it generates a **fatal** failure. |
{: .callout .note} {: .callout .warning}
**Note:** Although equality matching via `EXPECT_THAT(actual_value, **WARNING:** Equality matching via `EXPECT_THAT(actual_value, expected_value)`
expected_value)` is supported, prefer to make the comparison explicit via is supported, however note that implicit conversions can cause surprising
results. For example, `EXPECT_THAT(some_bool, "some string")` will compile and
may pass unintentionally.
**BEST PRACTICE:** Prefer to make the comparison explicit via
`EXPECT_THAT(actual_value, Eq(expected_value))` or `EXPECT_EQ(actual_value, `EXPECT_THAT(actual_value, Eq(expected_value))` or `EXPECT_EQ(actual_value,
expected_value)`. expected_value)`.
@@ -88,16 +92,17 @@ The `argument` can be either a C string or a C++ string object:
| Matcher | Description | | Matcher | Description |
| :---------------------- | :------------------------------------------------- | | :---------------------- | :------------------------------------------------- |
| `ContainsRegex(string)` | `argument` matches the given regular expression. | | `ContainsRegex(string)` | `argument` matches the given regular expression. |
| `EndsWith(suffix)` | `argument` ends with string `suffix`. | | `EndsWith(suffix)` | `argument` ends with string `suffix`. |
| `HasSubstr(string)` | `argument` contains `string` as a sub-string. | | `HasSubstr(string)` | `argument` contains `string` as a sub-string. |
| `IsEmpty()` | `argument` is an empty string. | | `IsEmpty()` | `argument` is an empty string. |
| `MatchesRegex(string)` | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. | | `MatchesRegex(string)` | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. |
| `StartsWith(prefix)` | `argument` starts with string `prefix`. | | `StartsWith(prefix)` | `argument` starts with string `prefix`. |
| `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. | | `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. |
| `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. | | `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. |
| `StrEq(string)` | `argument` is equal to `string`. | | `StrEq(string)` | `argument` is equal to `string`. |
| `StrNe(string)` | `argument` is not equal to `string`. | | `StrNe(string)` | `argument` is not equal to `string`. |
| `WhenBase64Unescaped(m)` | `argument` is a base-64 escaped string whose unescaped string matches `m`. |
`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They `ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They
use the regular expression syntax defined use the regular expression syntax defined
@@ -116,6 +121,7 @@ messages, you can use:
| `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. | | `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. |
| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | | `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. |
| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | | `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. |
| `Contains(e).Times(n)` | `argument` contains elements that match `e`, which can be either a value or a matcher, and the number of matches is `n`, which can be either a value or a matcher. Unlike the plain `Contains` and `Each` this allows to check for arbitrary occurrences including testing for absence with `Contains(e).Times(0)`. |
| `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. | | `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. |
| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. | | `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. |
| `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. | | `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
@@ -146,7 +152,6 @@ messages, you can use:
one might write: one might write:
```cpp ```cpp
using ::std::get;
MATCHER(FooEq, "") { MATCHER(FooEq, "") {
return std::get<0>(arg).Equals(std::get<1>(arg)); return std::get<0>(arg).Equals(std::get<1>(arg));
} }
@@ -193,6 +198,7 @@ messages, you can use:
| Matcher | Description | | Matcher | Description |
| :--------------- | :------------------------------------------------ | | :--------------- | :------------------------------------------------ |
| `ResultOf(f, m)` | `f(argument)` matches matcher `m`, where `f` is a function or functor. | | `ResultOf(f, m)` | `f(argument)` matches matcher `m`, where `f` is a function or functor. |
| `ResultOf(result_description, f, m)` | The same as the two-parameter version, but provides a better error message.
## Pointer Matchers ## Pointer Matchers
@@ -237,6 +243,7 @@ You can make a matcher from one or more other matchers:
| `AnyOf(m1, m2, ..., mn)` | `argument` matches at least one of the matchers `m1` to `mn`. | | `AnyOf(m1, m2, ..., mn)` | `argument` matches at least one of the matchers `m1` to `mn`. |
| `AnyOfArray({m0, m1, ..., mn})`, `AnyOfArray(a_container)`, `AnyOfArray(begin, end)`, `AnyOfArray(array)`, or `AnyOfArray(array, count)` | The same as `AnyOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. | | `AnyOfArray({m0, m1, ..., mn})`, `AnyOfArray(a_container)`, `AnyOfArray(begin, end)`, `AnyOfArray(array)`, or `AnyOfArray(array, count)` | The same as `AnyOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
| `Not(m)` | `argument` doesn't match matcher `m`. | | `Not(m)` | `argument` doesn't match matcher `m`. |
| `Conditional(cond, m1, m2)` | Matches matcher `m1` if `cond` evaluates to true, else matches `m2`.|
## Adapters for Matchers ## Adapters for Matchers
@@ -259,7 +266,7 @@ which must be a permanent callback.
## Defining Matchers ## Defining Matchers
| Matcher | Description | | Macro | Description |
| :----------------------------------- | :------------------------------------ | | :----------------------------------- | :------------------------------------ |
| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | | `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. |
| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a matcher `IsDivisibleBy(n)` to match a number divisible by `n`. | | `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a matcher `IsDivisibleBy(n)` to match a number divisible by `n`. |

View File

@@ -248,7 +248,9 @@ EXPECT_CALL(my_mock, GetNumber())
.WillOnce(Return(3)); .WillOnce(Return(3));
``` ```
The `WillOnce` clause can be used any number of times on an expectation. The `WillOnce` clause can be used any number of times on an expectation. Unlike
`WillRepeatedly`, the action fed to each `WillOnce` call will be called at most
once, so may be a move-only type and/or have an `&&`-qualified call operator.
#### WillRepeatedly {#EXPECT_CALL.WillRepeatedly} #### WillRepeatedly {#EXPECT_CALL.WillRepeatedly}

View File

@@ -109,7 +109,7 @@ namespace:
| `ValuesIn(container)` or `ValuesIn(begin,end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. | | `ValuesIn(container)` or `ValuesIn(begin,end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. |
| `Bool()` | Yields sequence `{false, true}`. | | `Bool()` | Yields sequence `{false, true}`. |
| `Combine(g1, g2, ..., gN)` | Yields as `std::tuple` *n*-tuples all combinations (Cartesian product) of the values generated by the given *n* generators `g1`, `g2`, ..., `gN`. | | `Combine(g1, g2, ..., gN)` | Yields as `std::tuple` *n*-tuples all combinations (Cartesian product) of the values generated by the given *n* generators `g1`, `g2`, ..., `gN`. |
| `ConvertGenerator<T>(g)` | Yields values generated by generator `g`, `static_cast` to `T`. |
The optional last argument *`name_generator`* is a function or functor that The optional last argument *`name_generator`* is a function or functor that
generates custom test name suffixes based on the test parameters. The function generates custom test name suffixes based on the test parameters. The function
must accept an argument of type must accept an argument of type
@@ -518,8 +518,8 @@ Logs a property for the current test, test suite, or entire invocation of the
test program. Only the last value for a given key is logged. test program. Only the last value for a given key is logged.
The key must be a valid XML attribute name, and cannot conflict with the ones The key must be a valid XML attribute name, and cannot conflict with the ones
already used by GoogleTest (`name`, `status`, `time`, `classname`, `type_param`, already used by GoogleTest (`name`, `file`, `line`, `status`, `time`,
and `value_param`). `classname`, `type_param`, and `value_param`).
`RecordProperty` is `public static` so it can be called from utility functions `RecordProperty` is `public static` so it can be called from utility functions
that are not members of the test fixture. that are not members of the test fixture.

View File

@@ -1,7 +1,7 @@
# Googletest Samples # Googletest Samples
If you're like us, you'd like to look at If you're like us, you'd like to look at
[googletest samples.](https://github.com/google/googletest/tree/master/googletest/samples) [googletest samples.](https://github.com/google/googletest/blob/main/googletest/samples)
The sample directory has a number of well-commented samples showing how to use a The sample directory has a number of well-commented samples showing how to use a
variety of googletest features. variety of googletest features.

View File

@@ -36,13 +36,9 @@ endif()
# as ${gmock_SOURCE_DIR} and to the root binary directory as # as ${gmock_SOURCE_DIR} and to the root binary directory as
# ${gmock_BINARY_DIR}. # ${gmock_BINARY_DIR}.
# Language "C" is required for find_package(Threads). # Language "C" is required for find_package(Threads).
if (CMAKE_VERSION VERSION_LESS 3.0) cmake_minimum_required(VERSION 3.5)
project(gmock CXX C) cmake_policy(SET CMP0048 NEW)
else() project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
cmake_policy(SET CMP0048 NEW)
project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
endif()
cmake_minimum_required(VERSION 2.8.12)
if (COMMAND set_up_hermetic_build) if (COMMAND set_up_hermetic_build)
set_up_hermetic_build() set_up_hermetic_build()
@@ -109,11 +105,12 @@ endif()
# to the targets for when we are part of a parent build (ie being pulled # to the targets for when we are part of a parent build (ie being pulled
# in via add_subdirectory() rather than being a standalone build). # in via add_subdirectory() rather than being a standalone build).
if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
string(REPLACE ";" "$<SEMICOLON>" dirs "${gmock_build_include_dirs}")
target_include_directories(gmock SYSTEM INTERFACE target_include_directories(gmock SYSTEM INTERFACE
"$<BUILD_INTERFACE:${gmock_build_include_dirs}>" "$<BUILD_INTERFACE:${dirs}>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>") "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
target_include_directories(gmock_main SYSTEM INTERFACE target_include_directories(gmock_main SYSTEM INTERFACE
"$<BUILD_INTERFACE:${gmock_build_include_dirs}>" "$<BUILD_INTERFACE:${dirs}>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>") "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
endif() endif()
@@ -154,7 +151,10 @@ if (gmock_build_tests)
cxx_test(gmock_ex_test gmock_main) cxx_test(gmock_ex_test gmock_main)
cxx_test(gmock-function-mocker_test gmock_main) cxx_test(gmock-function-mocker_test gmock_main)
cxx_test(gmock-internal-utils_test gmock_main) cxx_test(gmock-internal-utils_test gmock_main)
cxx_test(gmock-matchers_test gmock_main) cxx_test(gmock-matchers-arithmetic_test gmock_main)
cxx_test(gmock-matchers-comparisons_test gmock_main)
cxx_test(gmock-matchers-containers_test gmock_main)
cxx_test(gmock-matchers-misc_test gmock_main)
cxx_test(gmock-more-actions_test gmock_main) cxx_test(gmock-more-actions_test gmock_main)
cxx_test(gmock-nice-strict_test gmock_main) cxx_test(gmock-nice-strict_test gmock_main)
cxx_test(gmock-port_test gmock_main) cxx_test(gmock-port_test gmock_main)

View File

@@ -35,10 +35,6 @@ Details and examples can be found here:
* [gMock Cookbook](https://google.github.io/googletest/gmock_cook_book.html) * [gMock Cookbook](https://google.github.io/googletest/gmock_cook_book.html)
* [gMock Cheat Sheet](https://google.github.io/googletest/gmock_cheat_sheet.html) * [gMock Cheat Sheet](https://google.github.io/googletest/gmock_cheat_sheet.html)
Please note that code under scripts/generator/ is from the
[cppclean project](http://code.google.com/p/cppclean/) and under the Apache
License, which is different from GoogleMock's license.
GoogleMock is a part of GoogleMock is a part of
[GoogleTest C++ testing framework](http://github.com/google/googletest/) and a [GoogleTest C++ testing framework](http://github.com/google/googletest/) and a
subject to the same requirements. subject to the same requirements.

View File

@@ -27,21 +27,23 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Google Mock - a framework for writing C++ mock classes. // Google Mock - a framework for writing C++ mock classes.
// //
// This file implements some commonly used cardinalities. More // This file implements some commonly used cardinalities. More
// cardinalities can be defined by the user implementing the // cardinalities can be defined by the user implementing the
// CardinalityInterface interface if necessary. // CardinalityInterface interface if necessary.
// GOOGLETEST_CM0002 DO NOT DELETE // IWYU pragma: private, include "gmock/gmock.h"
// IWYU pragma: friend gmock/.*
#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ #ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ #define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
#include <limits.h> #include <limits.h>
#include <memory> #include <memory>
#include <ostream> // NOLINT #include <ostream> // NOLINT
#include "gmock/internal/gmock-port.h" #include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@@ -116,7 +118,7 @@ class GTEST_API_ Cardinality {
// cardinality, i.e. exceed the maximum number of allowed calls. // cardinality, i.e. exceed the maximum number of allowed calls.
bool IsOverSaturatedByCallCount(int call_count) const { bool IsOverSaturatedByCallCount(int call_count) const {
return impl_->IsSaturatedByCallCount(call_count) && return impl_->IsSaturatedByCallCount(call_count) &&
!impl_->IsSatisfiedByCallCount(call_count); !impl_->IsSatisfiedByCallCount(call_count);
} }
// Describes self to an ostream // Describes self to an ostream

View File

@@ -31,10 +31,11 @@
// //
// This file implements MOCK_METHOD. // This file implements MOCK_METHOD.
// GOOGLETEST_CM0002 DO NOT DELETE // IWYU pragma: private, include "gmock/gmock.h"
// IWYU pragma: friend gmock/.*
#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT #ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_
#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT #define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_
#include <type_traits> // IWYU pragma: keep #include <type_traits> // IWYU pragma: keep
#include <utility> // IWYU pragma: keep #include <utility> // IWYU pragma: keep
@@ -64,6 +65,39 @@ struct ThisRefAdjuster {
} }
}; };
constexpr bool PrefixOf(const char* a, const char* b) {
return *a == 0 || (*a == *b && internal::PrefixOf(a + 1, b + 1));
}
template <int N, int M>
constexpr bool StartsWith(const char (&prefix)[N], const char (&str)[M]) {
return N <= M && internal::PrefixOf(prefix, str);
}
template <int N, int M>
constexpr bool EndsWith(const char (&suffix)[N], const char (&str)[M]) {
return N <= M && internal::PrefixOf(suffix, str + M - N);
}
template <int N, int M>
constexpr bool Equals(const char (&a)[N], const char (&b)[M]) {
return N == M && internal::PrefixOf(a, b);
}
template <int N>
constexpr bool ValidateSpec(const char (&spec)[N]) {
return internal::Equals("const", spec) ||
internal::Equals("override", spec) ||
internal::Equals("final", spec) ||
internal::Equals("noexcept", spec) ||
(internal::StartsWith("noexcept(", spec) &&
internal::EndsWith(")", spec)) ||
internal::Equals("ref(&)", spec) ||
internal::Equals("ref(&&)", spec) ||
(internal::StartsWith("Calltype(", spec) &&
internal::EndsWith(")", spec));
}
} // namespace internal } // namespace internal
// The style guide prohibits "using" statements in a namespace scope // The style guide prohibits "using" statements in a namespace scope
@@ -74,8 +108,11 @@ struct ThisRefAdjuster {
using internal::FunctionMocker; using internal::FunctionMocker;
} // namespace testing } // namespace testing
#define MOCK_METHOD(...) \ #define MOCK_METHOD(...) \
GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__) GMOCK_INTERNAL_WARNING_PUSH() \
GMOCK_INTERNAL_WARNING_CLANG(ignored, "-Wunused-member-function") \
GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__) \
GMOCK_INTERNAL_WARNING_POP()
#define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \ #define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \
GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
@@ -86,17 +123,18 @@ using internal::FunctionMocker;
#define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \ #define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \
GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ()) GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ())
#define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec) \ #define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec) \
GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args); \ GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args); \
GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec); \ GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec); \
GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \
GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)); \ GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)); \
GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \
GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ GMOCK_INTERNAL_MOCK_METHOD_IMPL( \
GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec), \ GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec), \
GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec), \ GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec), \
GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec), \ GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec), \
GMOCK_INTERNAL_GET_CALLTYPE(_Spec), GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \ GMOCK_INTERNAL_GET_CALLTYPE_SPEC(_Spec), \
GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \
(GMOCK_INTERNAL_SIGNATURE(_Ret, _Args))) (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)))
#define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \ #define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \
@@ -166,11 +204,11 @@ using internal::FunctionMocker;
GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N)); \ GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N)); \
} \ } \
mutable ::testing::FunctionMocker<GMOCK_PP_REMOVE_PARENS(_Signature)> \ mutable ::testing::FunctionMocker<GMOCK_PP_REMOVE_PARENS(_Signature)> \
GMOCK_MOCKER_(_N, _Constness, _MethodName) GMOCK_MOCKER_(_N, _Constness, _MethodName)
#define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__ #define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__
// Five Valid modifiers. // Valid modifiers.
#define GMOCK_INTERNAL_HAS_CONST(_Tuple) \ #define GMOCK_INTERNAL_HAS_CONST(_Tuple) \
GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple)) GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple))
@@ -189,6 +227,14 @@ using internal::FunctionMocker;
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \ GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \
_elem, ) _elem, )
#define GMOCK_INTERNAL_GET_CALLTYPE_SPEC(_Tuple) \
GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_CALLTYPE_SPEC_IF_CALLTYPE, ~, _Tuple)
#define GMOCK_INTERNAL_CALLTYPE_SPEC_IF_CALLTYPE(_i, _, _elem) \
GMOCK_PP_IF( \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CALLTYPE(_i, _, _elem)), \
GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), )
#define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \ #define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \
GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple) GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple)
@@ -196,19 +242,25 @@ using internal::FunctionMocker;
GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \ GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \
GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), ) GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), )
#define GMOCK_INTERNAL_GET_CALLTYPE(_Tuple) \ #ifdef GMOCK_INTERNAL_STRICT_SPEC_ASSERT
GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_CALLTYPE_IMPL, ~, _Tuple) #define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \
static_assert( \
#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \ ::testing::internal::ValidateSpec(GMOCK_PP_STRINGIZE(_elem)), \
static_assert( \ "Token \'" GMOCK_PP_STRINGIZE( \
(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) + \ _elem) "\' cannot be recognized as a valid specification " \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \ "modifier. Is a ',' missing?");
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) + \ #else
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \ #define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) + \ static_assert( \
GMOCK_INTERNAL_IS_CALLTYPE(_elem)) == 1, \ (GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) + \
GMOCK_PP_STRINGIZE( \ GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) + \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) + \
GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CALLTYPE(_i, _, _elem))) == 1, \
GMOCK_PP_STRINGIZE( \
_elem) " cannot be recognized as a valid specification modifier."); _elem) " cannot be recognized as a valid specification modifier.");
#endif // GMOCK_INTERNAL_STRICT_SPEC_ASSERT
// Modifiers implementation. // Modifiers implementation.
#define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \ #define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \
@@ -238,26 +290,12 @@ using internal::FunctionMocker;
#define GMOCK_INTERNAL_UNPACK_ref(x) x #define GMOCK_INTERNAL_UNPACK_ref(x) x
#define GMOCK_INTERNAL_GET_CALLTYPE_IMPL(_i, _, _elem) \ #define GMOCK_INTERNAL_DETECT_CALLTYPE(_i, _, _elem) \
GMOCK_PP_IF(GMOCK_INTERNAL_IS_CALLTYPE(_elem), \ GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CALLTYPE_I_, _elem)
GMOCK_INTERNAL_GET_VALUE_CALLTYPE, GMOCK_PP_EMPTY) \
(_elem)
// TODO(iserna): GMOCK_INTERNAL_IS_CALLTYPE and #define GMOCK_INTERNAL_DETECT_CALLTYPE_I_Calltype ,
// GMOCK_INTERNAL_GET_VALUE_CALLTYPE needed more expansions to work on windows
// maybe they can be simplified somehow.
#define GMOCK_INTERNAL_IS_CALLTYPE(_arg) \
GMOCK_INTERNAL_IS_CALLTYPE_I( \
GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
#define GMOCK_INTERNAL_IS_CALLTYPE_I(_arg) GMOCK_PP_IS_ENCLOSED_PARENS(_arg)
#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE(_arg) \ #define GMOCK_INTERNAL_UNPACK_Calltype(...) __VA_ARGS__
GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I( \
GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(_arg) \
GMOCK_PP_IDENTITY _arg
#define GMOCK_INTERNAL_IS_CALLTYPE_HELPER_Calltype
// Note: The use of `identity_t` here allows _Ret to represent return types that // Note: The use of `identity_t` here allows _Ret to represent return types that
// would normally need to be specified in a different way. For example, a method // would normally need to be specified in a different way. For example, a method
@@ -476,4 +514,4 @@ using internal::FunctionMocker;
#define GMOCK_MOCKER_(arity, constness, Method) \ #define GMOCK_MOCKER_(arity, constness, Method) \
GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ #endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_

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