Compare commits

..

114 Commits

Author SHA1 Message Date
dependabot[bot]
1599d55a3d Bump actions/checkout from 4 to 5 in the github-actions group
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 04:21:47 +00: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
Jesse Beder
f732014112 Bump version to 0.8.0. 2023-08-10 14:27:56 -05:00
Diogo Teles Sant'Anna
f6c8b17c3a Create Security-Policy (#1189) 2023-08-10 15:02:27 -04:00
Kefu Chai
35b4498026 node/convert: support conversion for std::string_view (#1148) 2023-07-24 14:51:05 -05:00
Ryan
b8882652fc Add namespace for the exported targets (#1196)
This matches the name for the ALIAS targets, and fixes #1025.
2023-06-28 11:37:09 -05:00
Jesse Beder
0e6e28d1a3 Revert "Move CTest to test entry (#1181)" (#1182)
This reverts commit 74f63c1181.
2023-03-22 19:14:49 -05:00
Jelin
74f63c1181 Move CTest to test entry (#1181) 2023-03-21 11:24:19 -05:00
Matthijs van der Burgh
987a604256 Delete .travis.yml (#1177) 2023-03-04 16:18:51 -06:00
Matthijs van der Burgh
55a8037daa (appveyor) remove, because testing windows in GH actions (#1176) 2023-03-04 14:07:09 -06:00
Matthijs van der Burgh
3ff7ab07ac Make sure tests are run in CI (#1170)
* Test(CMake) set NAME and COMMAND in add_test

* (CMake) add enable_testing()

* (CMake) move cmake_dependent_option up, before using them

* (CMake) use YAML_CPP_MAIN_PROJECT in cmake_dependent_option

* (CMake) log values regarding tests

* (CMake) always find CTest, but don't enable tests

* (CMAKE)(temp) fix logging

* (actions) set YAML_CPP_BUILD_TESTS for tests

* (actions) provide YAML_CPP_BUILD_TESTS to ctest

* (actions) set -DYAML_CPP_BUILD_TESTS at build

* (actions) don't fail false

* (actions) build tests in Test step

* (actions) run tests verbose

* (CMake) remove temp logging

* (actions) split building from running tests

* (actions) ctest Debug

* (actions) ctest Debug

* Remove enable_testing
2023-03-04 14:06:01 -06:00
Diogo Teles Sant'Anna
d7f672d141 CI: define read-only permission for GitHub Workflow (#1175)
It secures the repo against erroneous or malicious actions from external jobs you call from your workflow. It's specially important for the case they get compromised, for example.
2023-03-03 23:33:51 -06:00
Matthijs van der Burgh
bdc5582b35 Add tests for iterator and const_iterator on sequence (#1169) 2023-03-01 19:27:51 -06:00
James Brown
1b50109f7b Include property nodes in the long key check (#1140) 2022-10-04 18:51:53 -05:00
sfalmo
4ae4cb7309 Support conversion for std::valarray (#956) 2022-09-20 00:31:51 -05:00
Derzsi Dániel
97ebcf035a Export library directory and shared libs to CMake config (#1113)
This pull request adds two new exported variables to the CMake config:

YAML_CPP_LIBRARY_DIR - points to the directory containing the built library files
YAML_CPP_SHARED_LIBS_BUILT - boolean value, lets users know whether shared libraries were built or not
2022-09-20 00:26:59 -05:00
stephematician
85ad599d79 Basic update to CMakeLists.txt to address #1116 (#1117)
Add definitions to static yaml-cpp target (fixes #1116)

Also updates CONTRIBUTING instructions for tests
2022-09-20 00:24:35 -05:00
Baruch
c86a9e424c Minimize warnings when not the top-level project (#1124)
Minimize warnings when not the top-level project

Should fix #970 and #764 when trying to add yaml-cpp to other project
2022-09-20 00:22:11 -05:00
Timo Gurr
255bf1f9bc Install pkgconfig and cmake files into arch-dependent locations (#1055) 2022-09-20 00:17:13 -05:00
rdzehtsiar
763b7d6c70 Fix compilation errors in build for Oracle Solaris OS (#1133)
On Oracle Solaris the following statement is declared in file /usr/include/sys/regset.h:
#define SS 18 /* only stored on a privilege transition */

Because of this template type name SS is substituted by numeric literal, which results in a compilation error:
error: expected nested-name-specifier before numeric constant

Fixed by renaming template type names.

Co-authored-by: Roman Degtyar <Roman.Degtyar@veeam.com>
2022-09-20 00:04:18 -05:00
Skywol
e87ed7e5b1 Add the "make uninstall" target (#1130) 2022-09-20 00:03:40 -05:00
Igor [hyperxor]
c73ee34704 Small perfomance optimization in Directives and Tag methods (#1112) 2022-05-31 18:05:22 -05:00
Igor [hyperxor]
190ad502b5 Remove redundant include (#1110) 2022-05-21 07:20:18 -05:00
Dr. Andre Vehreschild
420c982310 Improve bazel build for Windows. (#1100)
Windows builds need some defines being set to use the static linking.

Also add bazel builds and test to Github-CI.
2022-04-27 09:10:13 -05:00
Dr. Andre Vehreschild
13626af92a Fix escaping anchors in keys (#1101) 2022-04-21 08:19:51 -05:00
Dr. Andre Vehreschild
4c982d59dc Improve build and fix single cr on windows (#1099) 2022-04-19 12:39:55 -05:00
SpaceIm
7d06655611 Allow yaml-cpp to be installed from another project (#1007)
Changes YAML_CPP_INSTALL from a cmake_dependent_option to an option.

Fixes #756, #847, and #1011.
2022-04-03 16:57:28 -05:00
xiaozhuai, Weihang Ding
0e6e98e8fb Extend options that disable testing and formatting to looking for dependencies also. (#1039)
The option `YAML_CPP_BUILD_TESTS` currently enables or disables building of tests; but unconditionally the CMake file includes CTest; this PR makes that conditional on the option.

Also, there is no option for enabling formatting, but it does check whether it can find the `clang-format` executable; this PR adds an option (default to true) that skips even looking for the executable if disabled.
2022-03-31 22:36:43 -05:00
Felix Schwitzer
4aad2b1666 Fix CMake export files (#1077)
After configuring the file `yaml-cpp-config.cmake.in`, the result ends up with
empty variables.  (see also the discussion in #774).

Rework this file and the call to `configure_package_config_file` according the
cmake documentation
(https://cmake.org/cmake/help/v3.22/module/CMakePackageConfigHelpers.html?highlight=configure_package_config#command:configure_package_config_file)
to overcome this issue and allow a simple `find_package` after install.

As there was some discussion about the place where to install the
`yaml-cpp-config.cmake` file, e.g. #1055, factor out the install location into
an extra variable to make it easier changing this location in the future.

Also untabify CMakeLists.txt in some places to align with the other code parts in this file.
2022-03-31 22:26:47 -05:00
Dr. Andre Vehreschild
a6db7e32ac Fix single cr not recognized (#1094)
Complies with YAML Standard [5.4](https://yaml.org/spec/1.2.2/#54-line-break-characters) [25] instead of matching `\r` only in combination with `\n`.
2022-03-31 22:16:35 -05:00
Michael Martz
669af4eb55 Make YAML_MSVC_SHARED_RT depend on "CMAKE_SYSTEM_NAME MATCHES Windows" instead of "MSVC".
Fixes issue #1053.
2022-03-31 22:13:19 -05:00
Akash Patel
edadfecdc6 Update gtest to 1.11.0 (#1086)
Properly resolves #1083, #996.
2022-02-17 12:08:56 -06:00
Sam4uk
26e3b70407 Update old style cast from (T) to static_cast<T>. 2022-02-08 16:06:36 -06:00
Christian Rauch
cdf89fa352 set POSITION_INDEPENDENT_CODE if BUILD_SHARED_LIBS=OFF (#1076) 2022-01-27 17:03:10 -06:00
TheVice
f8e1a7be3b Add MinGW to build pipeline
Also refactor the build action to use a matrix.
2022-01-27 17:00:08 -06:00
Chen
bce601f2bf Support the conversion for std::unordered_map (#932) 2022-01-05 18:12:40 -06:00
TheVice
d8d9419092 Fix shared library tests for Windows (#1074)
Add copying of shared library to the output directory with a test binary.

[binary] removed using of non unsigned char as argument at
'std::isspace' function that was provokes undefined behavior.

[.github/workflows/build.yml] enabled run of test at the
'windows-latest' environment.
2021-12-15 13:38:09 -06:00
Jesse Beder
974349d015 Fix typo in README about building shared libraries. 2021-12-10 08:54:35 -06:00
Jesse Beder
5057a53293 Fix github actions badge. 2021-11-22 22:44:31 -06:00
Jesse Beder
634578e477 Add github actions badge to README
This replaces the old Travis CI badge that didn't mean anything.
2021-11-22 22:42:51 -06:00
Pavel Karelin
2b65c65e1b Partially revert commit 0733aeb4 to make dll.h a non-generated file (#1064)
Partial revert of "Revert "Revert "Hide most of non-public symbols by default (#984)" (#1038)" (#1045)"

This reverts commit 0733aeb451.
2021-11-22 22:40:08 -06:00
385 changed files with 42634 additions and 45128 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

@@ -5,36 +5,137 @@ on:
pull_request: pull_request:
branches: [ master ] branches: [ master ]
workflow_dispatch: workflow_dispatch:
permissions: read-all
defaults:
run:
shell: bash
jobs: jobs:
build: cmake-build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
cxx_standard: [11, 17, 20]
build: [static, shared]
googletest: [build, system]
generator: ["Default Generator", "MinGW Makefiles"]
exclude:
- os: ubuntu-latest
cxx_standard: 11
googletest: system
- os: macos-latest
build: shared
- os: macos-latest
generator: "MinGW Makefiles"
- os: ubuntu-latest
generator: "MinGW Makefiles"
- os: macos-latest
googletest: system
- os: windows-latest
googletest: system
env:
YAML_BUILD_SHARED_LIBS: ${{ matrix.build == 'shared' && 'ON' || 'OFF' }}
YAML_USE_SYSTEM_GTEST: ${{ matrix.googletest == 'system' && 'ON' || 'OFF' }}
CMAKE_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 }}
steps:
- uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-latest'
with:
packages: googletest libgmock-dev libgtest-dev
version: 1.0
- uses: actions/checkout@v5
- 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
run: |
cmake \
--build build \
--config ${{ env.CMAKE_BUILD_TYPE }} \
--verbose \
--parallel
- name: Run Tests
shell: bash
run: |
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:
strategy: strategy:
matrix: matrix:
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@v5
- name: Build static - name: Build
run: |
cd "${{ github.workspace }}"
bazel build :all
- name: Test
run: |
cd "${{ github.workspace }}"
bazel test test
bzlmod-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Build
shell: bash shell: bash
run: | run: |
mkdir -p build && cd build cd "${{ github.workspace }}"
cmake .. bazel build --enable_bzlmod :all
cmake --build . --parallel 4
- name: Test static - name: Test
shell: bash
run: cd build && ctest --output-on-failure
- name: Build shared
shell: bash shell: bash
run: | run: |
rm -rf build && mkdir -p build && cd build cd "${{ github.workspace }}"
cmake .. -DYAML_BUILD_SHARED_LIBS=ON bazel test --enable_bzlmod test
cmake --build . --parallel 4
# tests are failing for unknown reasons
- if: matrix.os == 'ubuntu-latest'
name: Test shared
shell: bash
run: cd build && ctest --output-on-failure
# test all ASAP

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@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # 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,37 +0,0 @@
language: c++
matrix:
include:
- os: linux
compiler: gcc
- os: osx
compiler: clang
- os: osx
compiler: gcc
env:
- CTEST_OUTPUT_ON_FAILURE=1
before_script:
- mkdir build
- cd build
- cmake ..
- cd ..
script:
- cmake --build build
- cmake --build build --target test
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.9
packages:
- g++-4.9
- clang-3.9
update: true
homebrew:
packages:
- ccache
- gcc@4.9
- llvm@4
update: true

View File

@@ -1,3 +1,9 @@
yaml_cpp_defines = select({
# On Windows, ensure static linking is used.
"@platforms//os:windows": ["YAML_CPP_STATIC_DEFINE", "YAML_CPP_NO_CONTRIB"],
"//conditions:default": [],
})
cc_library( cc_library(
name = "yaml-cpp_internal", name = "yaml-cpp_internal",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
@@ -11,4 +17,5 @@ cc_library(
includes = ["include"], includes = ["include"],
hdrs = glob(["include/**/*.h"]), hdrs = glob(["include/**/*.h"]),
srcs = glob(["src/**/*.cpp", "src/**/*.h"]), srcs = glob(["src/**/*.cpp", "src/**/*.h"]),
defines = yaml_cpp_defines,
) )

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
@@ -7,7 +8,12 @@ if(POLICY CMP0091)
cmake_policy(SET CMP0091 NEW) cmake_policy(SET CMP0091 NEW)
endif() endif()
project(YAML_CPP VERSION 0.7.0 LANGUAGES CXX) project(YAML_CPP VERSION 0.8.0 LANGUAGES CXX)
set(YAML_CPP_MAIN_PROJECT OFF)
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(YAML_CPP_MAIN_PROJECT ON)
endif()
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
include(CMakeDependentOption) include(CMakeDependentOption)
@@ -15,27 +21,34 @@ include(CheckCXXCompilerFlag)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CTest) include(CTest)
find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format)
option(YAML_CPP_BUILD_CONTRIB "Enable yaml-cpp contrib in library" ON) 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_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" ON "Enable yaml-cpp tests" OFF
"BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) "BUILD_TESTING;YAML_CPP_MAIN_PROJECT" OFF)
cmake_dependent_option(YAML_CPP_INSTALL
"Enable generation of yaml-cpp install targets" ON
"CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" 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
"MSVC" 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)
find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format)
endif()
set(yaml-cpp-type STATIC)
set(yaml-cpp-label-postfix "static")
if (YAML_BUILD_SHARED_LIBS) if (YAML_BUILD_SHARED_LIBS)
set(yaml-cpp-type SHARED) set(yaml-cpp-type SHARED)
set(yaml-cpp-label-postfix "shared") set(yaml-cpp-label-postfix "shared")
else()
set(yaml-cpp-type STATIC)
set(yaml-cpp-label-postfix "static")
endif() endif()
set(build-shared $<BOOL:${YAML_BUILD_SHARED_LIBS}>) set(build-shared $<BOOL:${YAML_BUILD_SHARED_LIBS}>)
@@ -78,6 +91,10 @@ set_property(TARGET yaml-cpp
PROPERTY PROPERTY
CXX_STANDARD_REQUIRED ON) CXX_STANDARD_REQUIRED ON)
if (NOT YAML_BUILD_SHARED_LIBS)
set_property(TARGET yaml-cpp PROPERTY POSITION_INDEPENDENT_CODE ${YAML_ENABLE_PIC})
endif()
target_include_directories(yaml-cpp target_include_directories(yaml-cpp
PUBLIC PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@@ -91,11 +108,15 @@ if (NOT DEFINED CMAKE_CXX_STANDARD)
CXX_STANDARD 11) CXX_STANDARD 11)
endif() endif()
if(YAML_CPP_MAIN_PROJECT)
target_compile_options(yaml-cpp
PRIVATE
$<${not-msvc}:-Wall -Wextra -Wshadow -Weffc++ -Wno-long-long>
$<${not-msvc}:-pedantic -pedantic-errors>)
endif()
target_compile_options(yaml-cpp target_compile_options(yaml-cpp
PRIVATE PRIVATE
$<${not-msvc}:-Wall -Wextra -Wshadow -Weffc++ -Wno-long-long>
$<${not-msvc}:-pedantic -pedantic-errors>
$<$<AND:${backport-msvc-runtime},${msvc-rt-mtd-static}>:-MTd> $<$<AND:${backport-msvc-runtime},${msvc-rt-mtd-static}>:-MTd>
$<$<AND:${backport-msvc-runtime},${msvc-rt-mt-static}>:-MT> $<$<AND:${backport-msvc-runtime},${msvc-rt-mt-static}>:-MT>
$<$<AND:${backport-msvc-runtime},${msvc-rt-mtd-dll}>:-MDd> $<$<AND:${backport-msvc-runtime},${msvc-rt-mtd-dll}>:-MDd>
@@ -108,6 +129,8 @@ target_compile_options(yaml-cpp
$<$<CXX_COMPILER_ID:MSVC>:/W3 /wd4127 /wd4355>) $<$<CXX_COMPILER_ID:MSVC>:/W3 /wd4127 /wd4355>)
target_compile_definitions(yaml-cpp target_compile_definitions(yaml-cpp
PUBLIC
$<$<NOT:$<BOOL:${YAML_BUILD_SHARED_LIBS}>>:YAML_CPP_STATIC_DEFINE>
PRIVATE PRIVATE
$<${build-windows-dll}:${PROJECT_NAME}_DLL> $<${build-windows-dll}:${PROJECT_NAME}_DLL>
$<$<NOT:$<BOOL:${YAML_CPP_BUILD_CONTRIB}>>:YAML_CPP_NO_CONTRIB>) $<$<NOT:$<BOOL:${YAML_CPP_BUILD_CONTRIB}>>:YAML_CPP_NO_CONTRIB>)
@@ -127,10 +150,13 @@ 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(EXPORT_TARGETS yaml-cpp::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 "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") INSTALL_DESTINATION "${YAML_CPP_INSTALL_CMAKEDIR}"
PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR)
unset(EXPORT_TARGETS)
write_basic_package_version_file( write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake"
@@ -139,36 +165,49 @@ write_basic_package_version_file(
configure_file(yaml-cpp.pc.in yaml-cpp.pc @ONLY) configure_file(yaml-cpp.pc.in yaml-cpp.pc @ONLY)
if (YAML_CPP_INSTALL) if (YAML_CPP_INSTALL)
install(TARGETS yaml-cpp install(TARGETS yaml-cpp
EXPORT yaml-cpp-targets EXPORT yaml-cpp-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.h") FILES_MATCHING PATTERN "*.h")
install(EXPORT yaml-cpp-targets install(EXPORT yaml-cpp-targets
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") NAMESPACE yaml-cpp::
install(FILES DESTINATION "${YAML_CPP_INSTALL_CMAKEDIR}")
"${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" install(FILES
"${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake"
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake"
DESTINATION "${YAML_CPP_INSTALL_CMAKEDIR}")
install(FILES "${PROJECT_BINARY_DIR}/yaml-cpp.pc" install(FILES "${PROJECT_BINARY_DIR}/yaml-cpp.pc"
DESTINATION ${CMAKE_INSTALL_DATADIR}/pkgconfig) DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif() endif()
if(YAML_CPP_BUILD_TESTS) if(YAML_CPP_BUILD_TESTS)
add_subdirectory(test) add_subdirectory(test)
endif() endif()
if(YAML_CPP_BUILD_TOOLS) if(YAML_CPP_BUILD_TOOLS)
add_subdirectory(util) add_subdirectory(util)
endif() endif()
if (YAML_CPP_CLANG_FORMAT_EXE) if (YAML_CPP_FORMAT_SOURCE AND YAML_CPP_CLANG_FORMAT_EXE)
add_custom_target(format add_custom_target(format
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
if(YAML_CPP_INSTALL AND NOT YAML_CPP_DISABLE_UNINSTALL AND NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()

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 `tests/run_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

@@ -1,4 +1,4 @@
# yaml-cpp [![Build Status](https://travis-ci.org/jbeder/yaml-cpp.svg?branch=master)](https://travis-ci.org/jbeder/yaml-cpp) [![Documentation](https://codedocs.xyz/jbeder/yaml-cpp.svg)](https://codedocs.xyz/jbeder/yaml-cpp/) # yaml-cpp ![Build Status](https://github.com/jbeder/yaml-cpp/actions/workflows/build.yml/badge.svg) [![Documentation](https://codedocs.xyz/jbeder/yaml-cpp.svg)](https://codedocs.xyz/jbeder/yaml-cpp/)
`yaml-cpp` is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html). `yaml-cpp` is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html).
@@ -21,7 +21,7 @@ If you find a bug, post an [issue](https://github.com/jbeder/yaml-cpp/issues)! I
```sh ```sh
mkdir build mkdir build
cd build cd build
cmake [-G generator] [-YAML_BUILD_SHARED_LIBS=on|OFF] .. cmake [-G generator] [-DYAML_BUILD_SHARED_LIBS=on|OFF] ..
``` ```
* The `generator` option is the build system you'd like to use. Run `cmake` without arguments to see a full list of available generators. * The `generator` option is the build system you'd like to use. Run `cmake` without arguments to see a full list of available generators.
@@ -29,7 +29,18 @@ cmake [-G generator] [-YAML_BUILD_SHARED_LIBS=on|OFF] ..
* On OS X, you might use "Xcode". * On OS X, you might use "Xcode".
* On a UNIX-like system, omit the option (for a Makefile). * On a UNIX-like system, omit the option (for a Makefile).
* `yaml-cpp` builds a static library by default, you may want to build a shared library by specifying `-YAML_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.
@@ -38,13 +49,30 @@ cmake [-G generator] [-YAML_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

13
SECURITY.md Normal file
View File

@@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
Security updates are applied only to the latest release.
## Reporting a Vulnerability
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
Please disclose it at [security advisory](https://github.com/jbeder/yaml-cpp/security/advisories/new).
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base.

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

@@ -1,28 +0,0 @@
version: 1.0.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMAKE_GENERATOR: Visual Studio 14 2015
CMAKE_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMAKE_GENERATOR: Visual Studio 14 2015
CMAKE_PLATFORM: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMAKE_GENERATOR: Visual Studio 15 2017
CMAKE_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMAKE_GENERATOR: Visual Studio 15 2017
CMAKE_PLATFORM: x64
before_build:
- cmd: mkdir build
- cmd: cd build
- cmd: cmake .. -G "%CMAKE_GENERATOR%" -DCMAKE_GENERATOR_PLATFORM=%CMAKE_PLATFORM%
- cmd: cd ..
build_script:
- cmake --build build
test_script:
- cmd: cd build
- ctest

21
cmake_uninstall.cmake.in Normal file
View File

@@ -0,0 +1,21 @@
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif()
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif()
endforeach()

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
@@ -227,4 +223,4 @@ assert(out.good());
out << YAML::Key; out << YAML::Key;
assert(!out.good()); assert(!out.good());
std::cout << "Emitter error: " << out.GetLastError() << "\n"; std::cout << "Emitter error: " << out.GetLastError() << "\n";
``` ```

View File

@@ -1,33 +1,59 @@
#ifndef DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #ifndef DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #define DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#if defined(_MSC_VER) || \ // Definition YAML_CPP_STATIC_DEFINE using to building YAML-CPP as static
(defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ // library (definition created by CMake or defined manually)
(__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
#pragma once // Definition yaml_cpp_EXPORTS using to building YAML-CPP as dll/so library
// (definition created by CMake or defined manually)
#ifdef YAML_CPP_STATIC_DEFINE
# define YAML_CPP_API
# define YAML_CPP_NO_EXPORT
#else
# if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
# ifndef YAML_CPP_API
# ifdef yaml_cpp_EXPORTS
/* We are building this library */
# define YAML_CPP_API __declspec(dllexport)
# else
/* We are using this library */
# define YAML_CPP_API __declspec(dllimport)
# endif
# endif
# ifndef YAML_CPP_NO_EXPORT
# define YAML_CPP_NO_EXPORT
# endif
# else /* No _MSC_VER */
# ifndef YAML_CPP_API
# ifdef yaml_cpp_EXPORTS
/* We are building this library */
# define YAML_CPP_API __attribute__((visibility("default")))
# else
/* We are using this library */
# define YAML_CPP_API __attribute__((visibility("default")))
# endif
# endif
# ifndef YAML_CPP_NO_EXPORT
# define YAML_CPP_NO_EXPORT __attribute__((visibility("hidden")))
# endif
# endif /* _MSC_VER */
#endif /* YAML_CPP_STATIC_DEFINE */
#ifndef YAML_CPP_DEPRECATED
# ifdef _MSC_VER
# define YAML_CPP_DEPRECATED __declspec(deprecated)
# else
# define YAML_CPP_DEPRECATED __attribute__ ((__deprecated__))
# endif
#endif #endif
// The following ifdef block is the standard way of creating macros which make #ifndef YAML_CPP_DEPRECATED_EXPORT
// exporting from a DLL simpler. All files within this DLL are compiled with the # define YAML_CPP_DEPRECATED_EXPORT YAML_CPP_API YAML_CPP_DEPRECATED
// yaml_cpp_EXPORTS symbol defined on the command line. This symbol should not #endif
// be defined on any project that uses this DLL. This way any other project
// whose source files include this file see YAML_CPP_API functions as being
// imported from a DLL, whereas this DLL sees symbols defined with this macro as
// being exported.
#undef YAML_CPP_API
#ifdef YAML_CPP_DLL // Using or Building YAML-CPP DLL (definition defined #ifndef YAML_CPP_DEPRECATED_NO_EXPORT
// manually) # define YAML_CPP_DEPRECATED_NO_EXPORT YAML_CPP_NO_EXPORT YAML_CPP_DEPRECATED
#ifdef yaml_cpp_EXPORTS // Building YAML-CPP DLL (definition created by CMake #endif
// or defined manually)
// #pragma message( "Defining YAML_CPP_API for DLL export" )
#define YAML_CPP_API __declspec(dllexport)
#else // yaml_cpp_EXPORTS
// #pragma message( "Defining YAML_CPP_API for DLL import" )
#define YAML_CPP_API __declspec(dllimport)
#endif // yaml_cpp_EXPORTS
#else // YAML_CPP_DLL
#define YAML_CPP_API
#endif // YAML_CPP_DLL
#endif // DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #endif /* DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 */

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

@@ -12,16 +12,23 @@
#include <limits> #include <limits>
#include <list> #include <list>
#include <map> #include <map>
#include <unordered_map>
#include <sstream> #include <sstream>
#include <type_traits> #include <type_traits>
#include <valarray>
#include <vector> #include <vector>
#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/node/impl.h" #include "yaml-cpp/node/impl.h"
#include "yaml-cpp/node/iterator.h" #include "yaml-cpp/node/iterator.h"
#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 {
@@ -87,6 +94,20 @@ struct convert<char[N]> {
static Node encode(const char* rhs) { return Node(rhs); } static Node encode(const char* rhs) { return Node(rhs); }
}; };
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template <>
struct convert<std::string_view> {
static Node encode(std::string_view rhs) { return Node(std::string(rhs)); }
static bool decode(const Node& node, std::string_view& rhs) {
if (!node.IsScalar())
return false;
rhs = node.Scalar();
return true;
}
};
#endif
template <> template <>
struct convert<_Null> { struct convert<_Null> {
static Node encode(const _Null& /* rhs */) { return Node(); } static Node encode(const _Null& /* rhs */) { return Node(); }
@@ -109,7 +130,7 @@ inner_encode(const T& rhs, std::stringstream& stream){
stream << ".inf"; stream << ".inf";
} }
} else { } else {
stream << rhs; stream << FpToString(rhs, stream.precision());
} }
} }
@@ -127,7 +148,7 @@ ConvertStreamTo(std::stringstream& stream, T& rhs) {
if ((stream >> std::noskipws >> num) && (stream >> std::ws).eof()) { if ((stream >> std::noskipws >> num) && (stream >> std::ws).eof()) {
if (num >= (std::numeric_limits<T>::min)() && if (num >= (std::numeric_limits<T>::min)() &&
num <= (std::numeric_limits<T>::max)()) { num <= (std::numeric_limits<T>::max)()) {
rhs = (T)num; rhs = static_cast<T>(num);
return true; return true;
} }
} }
@@ -151,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()); \
@@ -162,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; \
@@ -251,6 +274,32 @@ struct convert<std::map<K, V, C, A>> {
} }
}; };
// std::unordered_map
template <typename K, typename V, typename H, typename P, typename A>
struct convert<std::unordered_map<K, V, H, P, A>> {
static Node encode(const std::unordered_map<K, V, H, P, A>& rhs) {
Node node(NodeType::Map);
for (const auto& element : rhs)
node.force_insert(element.first, element.second);
return node;
}
static bool decode(const Node& node, std::unordered_map<K, V, H, P, A>& rhs) {
if (!node.IsMap())
return false;
rhs.clear();
for (const auto& element : node)
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs[element.first.template as<K>()] = element.second.template as<V>();
#else
rhs[element.first.as<K>()] = element.second.as<V>();
#endif
return true;
}
};
// std::vector // std::vector
template <typename T, typename A> template <typename T, typename A>
struct convert<std::vector<T, A>> { struct convert<std::vector<T, A>> {
@@ -336,6 +385,37 @@ struct convert<std::array<T, N>> {
} }
}; };
// std::valarray
template <typename T>
struct convert<std::valarray<T>> {
static Node encode(const std::valarray<T>& rhs) {
Node node(NodeType::Sequence);
for (const auto& element : rhs) {
node.push_back(element);
}
return node;
}
static bool decode(const Node& node, std::valarray<T>& rhs) {
if (!node.IsSequence()) {
return false;
}
rhs.resize(node.size());
for (auto i = 0u; i < node.size(); ++i) {
#if defined(__GNUC__) && __GNUC__ < 4
// workaround for GCC 3:
rhs[i] = node[i].template as<T>();
#else
rhs[i] = node[i].as<T>();
#endif
}
return true;
}
};
// std::pair // std::pair
template <typename T, typename U> template <typename T, typename U>
struct convert<std::pair<T, U>> { struct convert<std::pair<T, U>> {

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

@@ -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

@@ -7,7 +7,6 @@
#pragma once #pragma once
#endif #endif
#include "yaml-cpp/dll.h"
#include <memory> #include <memory>
namespace YAML { namespace YAML {

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

@@ -107,9 +107,9 @@ struct disable_if : public disable_if_c<Cond::value, T> {};
template <typename S, typename T> template <typename S, typename T>
struct is_streamable { struct is_streamable {
template <typename SS, typename TT> template <typename StreamT, typename ValueT>
static auto test(int) static auto test(int)
-> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type()); -> decltype(std::declval<StreamT&>() << std::declval<ValueT>(), std::true_type());
template <typename, typename> template <typename, typename>
static auto test(...) -> std::false_type; static auto test(...) -> std::false_type;
@@ -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();
} }

View File

@@ -75,7 +75,7 @@ std::vector<unsigned char> DecodeBase64(const std::string &input) {
unsigned value = 0; unsigned value = 0;
for (std::size_t i = 0, cnt = 0; i < input.size(); i++) { for (std::size_t i = 0, cnt = 0; i < input.size(); i++) {
if (std::isspace(input[i])) { if (std::isspace(static_cast<unsigned char>(input[i]))) {
// skip newlines // skip newlines
continue; continue;
} }

4196
src/contrib/dragonbox.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,6 @@
#include "yaml-cpp/anchor.h" #include "yaml-cpp/anchor.h"
#include "yaml-cpp/contrib/anchordict.h" #include "yaml-cpp/contrib/anchordict.h"
#include "yaml-cpp/contrib/graphbuilder.h"
#include "yaml-cpp/emitterstyle.h" #include "yaml-cpp/emitterstyle.h"
#include "yaml-cpp/eventhandler.h" #include "yaml-cpp/eventhandler.h"

View File

@@ -3,7 +3,7 @@
namespace YAML { namespace YAML {
Directives::Directives() : version{true, 1, 2}, tags{} {} Directives::Directives() : version{true, 1, 2}, tags{} {}
const std::string Directives::TranslateTagHandle( std::string Directives::TranslateTagHandle(
const std::string& handle) const { const std::string& handle) const {
auto it = tags.find(handle); auto it = tags.find(handle);
if (it == tags.end()) { if (it == tags.end()) {

View File

@@ -19,7 +19,7 @@ struct Version {
struct Directives { struct Directives {
Directives(); Directives();
const std::string TranslateTagHandle(const std::string& handle) const; std::string TranslateTagHandle(const std::string& handle) const;
Version version; Version version;
std::map<std::string, std::string> tags; std::map<std::string, std::string> tags;

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 {
@@ -533,7 +534,8 @@ void Emitter::BlockMapPrepareNode(EmitterNodeType::value child) {
if (m_pState->GetMapKeyFormat() == LongKey) if (m_pState->GetMapKeyFormat() == LongKey)
m_pState->SetLongKey(); m_pState->SetLongKey();
if (child == EmitterNodeType::BlockSeq || if (child == EmitterNodeType::BlockSeq ||
child == EmitterNodeType::BlockMap) child == EmitterNodeType::BlockMap ||
child == EmitterNodeType::Property)
m_pState->SetLongKey(); m_pState->SetLongKey();
if (m_pState->CurGroupLongKey()) if (m_pState->CurGroupLongKey())
@@ -714,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;
} }
@@ -750,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();
} }
@@ -863,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;
} }
@@ -886,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;
} }
@@ -935,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::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::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);

View File

@@ -37,7 +37,7 @@ inline const RegEx& Blank() {
return e; return e;
} }
inline const RegEx& Break() { inline const RegEx& Break() {
static const RegEx e = RegEx('\n') | RegEx("\r\n"); static const RegEx e = RegEx('\n') | RegEx("\r\n") | RegEx('\r');
return e; return e;
} }
inline const RegEx& BlankOrBreak() { inline const RegEx& BlankOrBreak() {
@@ -117,6 +117,10 @@ inline const RegEx& ValueInJSONFlow() {
static const RegEx e = RegEx(':'); static const RegEx e = RegEx(':');
return e; return e;
} }
inline const RegEx& Ampersand() {
static const RegEx e = RegEx('&');
return e;
}
inline const RegEx Comment() { inline const RegEx Comment() {
static const RegEx e = RegEx('#'); static const RegEx e = RegEx('#');
return e; return e;

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

@@ -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

@@ -9,9 +9,7 @@
#include <cstddef> #include <cstddef>
#include <ios> #include <ios>
#include <map>
#include <queue> #include <queue>
#include <set>
#include <stack> #include <stack>
#include <string> #include <string>
@@ -179,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>
@@ -93,9 +92,9 @@ void SingleDocParser::HandleNode(EventHandler& eventHandler) {
// add non-specific tags // add non-specific tags
if (tag.empty()) if (tag.empty())
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

@@ -29,7 +29,7 @@ Tag::Tag(const Token& token)
} }
} }
const std::string Tag::Translate(const Directives& directives) { std::string Tag::Translate(const Directives& directives) {
switch (type) { switch (type) {
case VERBATIM: case VERBATIM:
return value; return value;

View File

@@ -23,7 +23,7 @@ struct Tag {
}; };
Tag(const Token& token); Tag(const Token& token);
const std::string Translate(const Directives& directives); std::string Translate(const Directives& directives);
TYPE type; TYPE type;
std::string handle, value; std::string handle, value;

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.10.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.10.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)
@@ -46,4 +53,11 @@ if (NOT DEFINED CMAKE_CXX_STANDARD)
endif() endif()
add_test(yaml-cpp::test yaml-cpp-tests) add_test(NAME yaml-cpp::test COMMAND yaml-cpp-tests)
if (build-windows-dll)
add_custom_command(
TARGET yaml-cpp-tests
POST_BUILD COMMAND ${CMAKE_COMMAND} -E
copy_if_different "$<TARGET_FILE:yaml-cpp>" "$<TARGET_FILE_DIR:yaml-cpp-tests>")
endif()

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,15 +30,38 @@
# #
# 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"])
config_setting(
name = "qnx",
constraint_values = ["@platforms//os:qnx"],
)
config_setting( config_setting(
name = "windows", name = "windows",
constraint_values = ["@bazel_tools//platforms: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(
name = "msvc_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
},
visibility = [":__subpackages__"],
) )
config_setting( config_setting(
@@ -76,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"],
}), }),
@@ -94,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({
@@ -102,9 +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: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,23 +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.9) 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.10.0) cmake_policy(SET CMP0069 NEW)
endif (POLICY CMP0069)
if (CMAKE_VERSION VERSION_LESS "3.1") if (POLICY CMP0077)
add_definitions(-std=c++11) cmake_policy(SET CMP0077 NEW)
else() endif (POLICY CMP0077)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) project(googletest-distribution)
if(NOT CYGWIN) set(GOOGLETEST_VERSION 1.13.0)
set(CMAKE_CXX_EXTENSIONS OFF)
endif() if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
set(CMAKE_CXX_EXTENSIONS OFF)
endif() endif()
enable_testing() enable_testing()
@@ -28,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,14 +21,14 @@ 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
1. Submit an issue describing your proposed change to the 1. Submit an issue describing your proposed change to the
[issue tracker](https://github.com/google/googletest). [issue tracker](https://github.com/google/googletest/issues).
2. Please don't mix more than one logical change per submittal, because it 2. Please don't mix more than one logical change per submittal, because it
makes the history hard to follow. If you want to make a change that doesn't makes the history hard to follow. If you want to make a change that doesn't
have a corresponding issue in the issue tracker, please create one. have a corresponding issue in the issue tracker, please create one.
@@ -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
@@ -89,7 +90,7 @@ and their own tests from a git checkout, which has further requirements:
* [Python](https://www.python.org/) v2.3 or newer (for running some of the * [Python](https://www.python.org/) v2.3 or newer (for running some of the
tests and re-generating certain source files from templates) tests and re-generating certain source files from templates)
* [CMake](https://cmake.org/) v2.6.4 or newer * [CMake](https://cmake.org/) v2.8.12 or newer
## Developing Google Test and Google Mock ## Developing Google Test and Google Mock
@@ -128,15 +129,3 @@ To run the tests, do
make test make test
All tests should pass. All tests should pass.
### Regenerating Source Files
Some of Google Test's source files are generated from templates (not in the C++
sense) using a script. For example, the file
include/gtest/internal/gtest-type-util.h.pump is used to generate
gtest-type-util.h in the same directory.
You don't need to worry about regenerating the source files unless you need to
modify them. You would then modify the corresponding `.pump` files and run the
'[pump.py](googletest/scripts/pump.py)' generator script. See the
[Pump Manual](googletest/docs/pump_manual.md).

View File

@@ -5,33 +5,61 @@
Ajay Joshi <jaj@google.com> Ajay Joshi <jaj@google.com>
Balázs Dán <balazs.dan@gmail.com> Balázs Dán <balazs.dan@gmail.com>
Benoit Sigoure <tsuna@google.com>
Bharat Mediratta <bharat@menalto.com> Bharat Mediratta <bharat@menalto.com>
Bogdan Piloca <boo@google.com>
Chandler Carruth <chandlerc@google.com> Chandler Carruth <chandlerc@google.com>
Chris Prince <cprince@google.com> Chris Prince <cprince@google.com>
Chris Taylor <taylorc@google.com> Chris Taylor <taylorc@google.com>
Dan Egnor <egnor@google.com> Dan Egnor <egnor@google.com>
Dave MacLachlan <dmaclach@gmail.com>
David Anderson <danderson@google.com>
Dean Sturtevant
Eric Roman <eroman@chromium.org> Eric Roman <eroman@chromium.org>
Gene Volovich <gv@cite.com>
Hady Zalek <hady.zalek@gmail.com> Hady Zalek <hady.zalek@gmail.com>
Hal Burch <gmock@hburch.com>
Jeffrey Yasskin <jyasskin@google.com> Jeffrey Yasskin <jyasskin@google.com>
Jim Keller <jimkeller@google.com>
Joe Walnes <joe@truemesh.com>
Jon Wray <jwray@google.com>
Jói Sigurðsson <joi@google.com> Jói Sigurðsson <joi@google.com>
Keir Mierle <mierle@gmail.com> Keir Mierle <mierle@gmail.com>
Keith Ray <keith.ray@gmail.com> Keith Ray <keith.ray@gmail.com>
Kenton Varda <kenton@google.com> Kenton Varda <kenton@google.com>
Kostya Serebryany <kcc@google.com>
Krystian Kuzniarek <krystian.kuzniarek@gmail.com>
Lev Makhlis
Manuel Klimek <klimek@google.com> Manuel Klimek <klimek@google.com>
Mario Tanev <radix@google.com>
Mark Paskin
Markus Heule <markus.heule@gmail.com> Markus Heule <markus.heule@gmail.com>
Martijn Vels <mvels@google.com>
Matthew Simmons <simmonmt@acm.org>
Mika Raento <mikie@iki.fi> Mika Raento <mikie@iki.fi>
Mike Bland <mbland@google.com>
Miklós Fazekas <mfazekas@szemafor.com> Miklós Fazekas <mfazekas@szemafor.com>
Neal Norwitz <nnorwitz@gmail.com>
Nermin Ozkiranartli <nermin@google.com>
Owen Carlsen <ocarlsen@google.com>
Paneendra Ba <paneendra@google.com>
Pasi Valminen <pasi.valminen@gmail.com> Pasi Valminen <pasi.valminen@gmail.com>
Patrick Hanna <phanna@google.com> Patrick Hanna <phanna@google.com>
Patrick Riley <pfr@google.com> Patrick Riley <pfr@google.com>
Paul Menage <menage@google.com>
Peter Kaminski <piotrk@google.com> Peter Kaminski <piotrk@google.com>
Piotr Kaminski <piotrk@google.com>
Preston Jackson <preston.a.jackson@gmail.com> Preston Jackson <preston.a.jackson@gmail.com>
Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com> Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
Russ Cox <rsc@google.com> Russ Cox <rsc@google.com>
Russ Rufer <russ@pentad.com> 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>
Szymon Sobik <sobik.szymon@gmail.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>
Vlad Losev <vladl@google.com> Vlad Losev <vladl@google.com>
Wolfgang Klier <wklier@google.com>
Zhanyong Wan <wan@google.com> Zhanyong Wan <wan@google.com>

View File

@@ -0,0 +1,117 @@
# GoogleTest
### Announcements
#### Live at Head
GoogleTest now follows the
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
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
Our documentation is now live on GitHub Pages at
https://google.github.io/googletest/. We recommend browsing the documentation on
GitHub Pages rather than directly in the repository.
#### Release 1.12.1
[Release 1.12.1](https://github.com/google/googletest/releases/tag/release-1.12.1)
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
* We are planning to take a dependency on
[Abseil](https://github.com/abseil/abseil-cpp).
* More documentation improvements are planned.
## Welcome to **GoogleTest**, Google's C++ test framework!
This repository is a merger of the formerly separate GoogleTest and GoogleMock
projects. These were so closely related that it makes sense to maintain and
release them together.
### Getting Started
See the [GoogleTest User's Guide](https://google.github.io/googletest/) for
documentation. We recommend starting with the
[GoogleTest Primer](https://google.github.io/googletest/primer.html).
More information about building GoogleTest can be found at
[googletest/README.md](googletest/README.md).
## Features
* An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
* Test discovery.
* A rich set of assertions.
* User-defined assertions.
* Death tests.
* Fatal and non-fatal failures.
* Value-parameterized tests.
* Type-parameterized tests.
* Various options for running the tests.
* XML test report generation.
## Supported Platforms
GoogleTest follows Google's
[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support).
See
[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md)
for a list of currently supported versions compilers, platforms, and build
tools.
## Who Is Using GoogleTest?
In addition to many internal projects at Google, GoogleTest is also used by the
following notable projects:
* The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser
and Chrome OS).
* The [LLVM](http://llvm.org/) compiler.
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
interchange format.
* The [OpenCV](http://opencv.org/) computer vision library.
## Related Open Source Projects
[GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based
automated test-runner and Graphical User Interface with powerful features for
Windows and Linux platforms.
[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
displays a list of test failures. Clicking on one shows failure text. GoogleTest
UI is written in C#.
[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
listener for GoogleTest that implements the
[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
result output. If your test runner understands TAP, you may find it useful.
[gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that
runs tests from your binary in parallel to provide significant speed-up.
[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
your tests.
[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
tests.
[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser
that generates stub code for GoogleTest.
## Contributing Changes
Please read
[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/main/CONTRIBUTING.md)
for details on how to contribute to this project.
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

@@ -0,0 +1,134 @@
#!/bin/bash
#
# Copyright 2020, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -euox pipefail
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:20220621"
if [[ -z ${GTEST_ROOT:-} ]]; then
GTEST_ROOT="$(realpath $(dirname ${0})/..)"
fi
if [[ -z ${STD:-} ]]; then
STD="c++14 c++17 c++20"
fi
# Test the CMake build
for cc in /usr/local/bin/gcc /opt/llvm/clang/bin/clang; do
for cmake_off_on in OFF ON; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--tmpfs="/build:exec" \
--workdir="/build" \
--rm \
--env="CC=${cc}" \
--env="CXX_FLAGS=\"-Werror -Wdeprecated\"" \
${LINUX_LATEST_CONTAINER} \
/bin/bash -c "
cmake /src \
-DCMAKE_CXX_STANDARD=14 \
-Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \
-Dcxx_no_exception=${cmake_off_on} \
-Dcxx_no_rtti=${cmake_off_on} && \
make -j$(nproc) && \
ctest -j$(nproc) --output-on-failure"
done
done
# Do one test with an older version of GCC
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--workdir="/src" \
--rm \
--env="CC=/usr/local/bin/gcc" \
--env="BAZEL_CXXOPTS=-std=c++14" \
${LINUX_GCC_FLOOR_CONTAINER} \
/usr/local/bin/bazel test ... \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wuninitialized" \
--copt="-Wno-error=pragmas" \
--distdir="/bazel-distdir" \
--features=external_include_paths \
--keep_going \
--show_timestamps \
--test_output=errors
# Test GCC
for std in ${STD}; do
for absl in 0 1; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--workdir="/src" \
--rm \
--env="CC=/usr/local/bin/gcc" \
--env="BAZEL_CXXOPTS=-std=${std}" \
${LINUX_LATEST_CONTAINER} \
/usr/local/bin/bazel test ... \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wuninitialized" \
--define="absl=${absl}" \
--distdir="/bazel-distdir" \
--features=external_include_paths \
--keep_going \
--show_timestamps \
--test_output=errors
done
done
# Test Clang
for std in ${STD}; do
for absl in 0 1; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--workdir="/src" \
--rm \
--env="CC=/opt/llvm/clang/bin/clang" \
--env="BAZEL_CXXOPTS=-std=${std}" \
${LINUX_LATEST_CONTAINER} \
/usr/local/bin/bazel test ... \
--copt="--gcc-toolchain=/usr/local" \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wuninitialized" \
--define="absl=${absl}" \
--distdir="/bazel-distdir" \
--features=external_include_paths \
--keep_going \
--linkopt="--gcc-toolchain=/usr/local" \
--show_timestamps \
--test_output=errors
done
done

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/bin/bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
# #
# Copyright 2020, Google Inc.
# All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are # modification, are permitted provided that the following conditions are
@@ -29,20 +29,47 @@
# (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.
# This file is typically sourced by another script. set -euox pipefail
# if possible, ask for the precise number of processors,
# otherwise take 2 processors as reasonable default; see if [[ -z ${GTEST_ROOT:-} ]]; then
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization GTEST_ROOT="$(realpath $(dirname ${0})/..)"
if [ -x /usr/bin/getconf ]; then
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
else
NPROCESSORS=2
fi fi
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build # Test the CMake build
# crashes if parallelized too much (maybe memory consumption problem), for cmake_off_on in OFF ON; do
# so limit to 4 processors for the time being. BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX)
if [ $NPROCESSORS -gt 4 ] ; then cd ${BUILD_DIR}
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4." time cmake ${GTEST_ROOT} \
NPROCESSORS=4 -DCMAKE_CXX_STANDARD=14 \
-Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \
-Dcxx_no_exception=${cmake_off_on} \
-Dcxx_no_rtti=${cmake_off_on}
time make
time ctest -j$(nproc) --output-on-failure
done
# Test the Bazel build
# If we are running on Kokoro, check for a versioned Bazel binary.
KOKORO_GFILE_BAZEL_BIN="bazel-5.1.1-darwin-x86_64"
if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
chmod +x ${BAZEL_BIN}
else
BAZEL_BIN="bazel"
fi fi
cd ${GTEST_ROOT}
for absl in 0 1; do
${BAZEL_BIN} test ... \
--copt="-Wall" \
--copt="-Werror" \
--cxxopt="-std=c++14" \
--define="absl=${absl}" \
--features=external_include_paths \
--keep_going \
--show_timestamps \
--test_output=errors
done

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

@@ -0,0 +1 @@
title: GoogleTest

View File

@@ -0,0 +1,43 @@
nav:
- section: "Get Started"
items:
- title: "Supported Platforms"
url: "/platforms.html"
- title: "Quickstart: Bazel"
url: "/quickstart-bazel.html"
- title: "Quickstart: CMake"
url: "/quickstart-cmake.html"
- section: "Guides"
items:
- title: "GoogleTest Primer"
url: "/primer.html"
- title: "Advanced Topics"
url: "/advanced.html"
- title: "Mocking for Dummies"
url: "/gmock_for_dummies.html"
- title: "Mocking Cookbook"
url: "/gmock_cook_book.html"
- title: "Mocking Cheat Sheet"
url: "/gmock_cheat_sheet.html"
- section: "References"
items:
- title: "Testing Reference"
url: "/reference/testing.html"
- title: "Mocking Reference"
url: "/reference/mocking.html"
- title: "Assertions"
url: "/reference/assertions.html"
- title: "Matchers"
url: "/reference/matchers.html"
- title: "Actions"
url: "/reference/actions.html"
- title: "Testing FAQ"
url: "/faq.html"
- title: "Mocking FAQ"
url: "/gmock_faq.html"
- title: "Code Samples"
url: "/samples.html"
- title: "Using pkg-config"
url: "/pkgconfig.html"
- title: "Community Documentation"
url: "/community_created_documentation.html"

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% seo %}
<link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-197576187-1', { 'storage': 'none' });
ga('set', 'referrer', document.referrer.split('?')[0]);
ga('set', 'location', window.location.href.split('?')[0]);
ga('set', 'anonymizeIp', true);
ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>
</head>
<body>
<div class="sidebar">
<div class="header">
<h1><a href="{{ "/" | relative_url }}">{{ site.title | default: "Documentation" }}</a></h1>
</div>
<input type="checkbox" id="nav-toggle" class="nav-toggle">
<label for="nav-toggle" class="expander">
<span class="arrow"></span>
</label>
<nav>
{% for item in site.data.navigation.nav %}
<h2>{{ item.section }}</h2>
<ul>
{% for subitem in item.items %}
<a href="{{subitem.url | relative_url }}">
<li class="{% if subitem.url == page.url %}active{% endif %}">
{{ subitem.title }}
</li>
</a>
{% endfor %}
</ul>
{% endfor %}
</nav>
</div>
<div class="main markdown-body">
<div class="main-inner">
{{ content }}
</div>
<div class="footer">
GoogleTest &middot;
<a href="https://github.com/google/googletest">GitHub Repository</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>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.0/anchor.min.js" integrity="sha256-lZaRhKri35AyJSypXXs4o6OPFTbTmUoltBbDCbdzegg=" crossorigin="anonymous"></script>
<script>anchors.add('.main h2, .main h3, .main h4, .main h5, .main h6');</script>
</body>
</html>

View File

@@ -0,0 +1,200 @@
// Styles for GoogleTest docs website on GitHub Pages.
// Color variables are defined in
// https://github.com/pages-themes/primer/tree/master/_sass/primer-support/lib/variables
$sidebar-width: 260px;
body {
display: flex;
margin: 0;
}
.sidebar {
background: $black;
color: $text-white;
flex-shrink: 0;
height: 100vh;
overflow: auto;
position: sticky;
top: 0;
width: $sidebar-width;
}
.sidebar h1 {
font-size: 1.5em;
}
.sidebar h2 {
color: $gray-light;
font-size: 0.8em;
font-weight: normal;
margin-bottom: 0.8em;
padding-left: 2.5em;
text-transform: uppercase;
}
.sidebar .header {
background: $black;
padding: 2em;
position: sticky;
top: 0;
width: 100%;
}
.sidebar .header a {
color: $text-white;
text-decoration: none;
}
.sidebar .nav-toggle {
display: none;
}
.sidebar .expander {
cursor: pointer;
display: none;
height: 3em;
position: absolute;
right: 1em;
top: 1.5em;
width: 3em;
}
.sidebar .expander .arrow {
border: solid $white;
border-width: 0 3px 3px 0;
display: block;
height: 0.7em;
margin: 1em auto;
transform: rotate(45deg);
transition: transform 0.5s;
width: 0.7em;
}
.sidebar nav {
width: 100%;
}
.sidebar nav ul {
list-style-type: none;
margin-bottom: 1em;
padding: 0;
&:last-child {
margin-bottom: 2em;
}
a {
text-decoration: none;
}
li {
color: $text-white;
padding-left: 2em;
text-decoration: none;
}
li.active {
background: $border-gray-darker;
font-weight: bold;
}
li:hover {
background: $border-gray-darker;
}
}
.main {
background-color: $bg-gray;
width: calc(100% - #{$sidebar-width});
}
.main .main-inner {
background-color: $white;
padding: 2em;
}
.main .footer {
margin: 0;
padding: 2em;
}
.main table th {
text-align: left;
}
.main .callout {
border-left: 0.25em solid $white;
padding: 1em;
a {
text-decoration: underline;
}
&.important {
background-color: $bg-yellow-light;
border-color: $bg-yellow;
color: $black;
}
&.note {
background-color: $bg-blue-light;
border-color: $text-blue;
color: $text-blue;
}
&.tip {
background-color: $green-000;
border-color: $green-700;
color: $green-700;
}
&.warning {
background-color: $red-000;
border-color: $text-red;
color: $text-red;
}
}
.main .good pre {
background-color: $bg-green-light;
}
.main .bad pre {
background-color: $red-000;
}
@media all and (max-width: 768px) {
body {
flex-direction: column;
}
.sidebar {
height: auto;
position: relative;
width: 100%;
}
.sidebar .expander {
display: block;
}
.sidebar nav {
height: 0;
overflow: hidden;
}
.sidebar .nav-toggle:checked {
& ~ nav {
height: auto;
}
& + .expander .arrow {
transform: rotate(-135deg);
}
}
.main {
width: 100%;
}
}

View File

@@ -0,0 +1,5 @@
---
---
@import "jekyll-theme-primer";
@import "main";

View File

@@ -0,0 +1,7 @@
# Community-Created Documentation
The following is a list, in no particular order, of links to documentation
created by the Googletest community.
* [Googlemock Insights](https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/googletest/insights.md),
by [ElectricRCAircraftGuy](https://github.com/ElectricRCAircraftGuy)

View File

@@ -1,9 +1,12 @@
# Googletest FAQ # GoogleTest FAQ
<!-- GOOGLETEST_CM0014 DO NOT DELETE -->
## Why should test suite names and test names not contain underscore? ## Why should test suite names and test names not contain underscore?
{: .callout .note}
Note: GoogleTest reserves underscore (`_`) for special purpose keywords, such as
[the `DISABLED_` prefix](advanced.md#temporarily-disabling-tests), in addition
to the following rationale.
Underscore (`_`) is special, as C++ reserves the following to be used by the Underscore (`_`) is special, as C++ reserves the following to be used by the
compiler and the standard library: compiler and the standard library:
@@ -47,42 +50,42 @@ 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 `EXPECT_NE(nullptr, ptr)` and `ASSERT_NE(nullptr, First of all, you can use `nullptr` with each of these macros, e.g.
ptr)`. This is the preferred syntax in the style guide because nullptr does not `EXPECT_EQ(ptr, nullptr)`, `EXPECT_NE(ptr, nullptr)`, `ASSERT_EQ(ptr, nullptr)`,
have the type problems that NULL does. Which is why NULL does not work. `ASSERT_NE(ptr, nullptr)`. This is the preferred syntax in the style guide
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).
The `EXPECT_EQ()` macro takes the *expected* value as its first argument and the Historically, the `EXPECT_EQ()` macro took the *expected* value as its first
*actual* value as the second. It's reasonable that someone wants to write argument and the *actual* value as the second, though this argument order is now
`EXPECT_EQ(NULL, some_expression)`, and this indeed was requested several times. discouraged. It was reasonable that someone wanted
Therefore we implemented it. to write `EXPECT_EQ(NULL, some_expression)`, and this indeed was requested
several times. Therefore we implemented it.
The need for `EXPECT_NE(NULL, ptr)` isn't nearly as strong. When the assertion The need for `EXPECT_NE(NULL, ptr)` wasn't nearly as strong. When the assertion
fails, you already know that `ptr` must be `NULL`, so it doesn't add any fails, you already know that `ptr` must be `NULL`, so it doesn't add any
information to print `ptr` in this case. That means `EXPECT_TRUE(ptr != NULL)` information to print `ptr` in this case. That means `EXPECT_TRUE(ptr != NULL)`
works just as well. works just as well.
If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'll have to If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'd have to
support `EXPECT_NE(ptr, NULL)` as well, as unlike `EXPECT_EQ`, we don't have a support `EXPECT_NE(ptr, NULL)` as well. This means using the template meta
convention on the order of the two arguments for `EXPECT_NE`. This means using programming tricks twice in the implementation, making it even harder to
the template meta programming tricks twice in the implementation, making it even understand and maintain. We believe the benefit doesn't justify the cost.
harder to understand and maintain. We believe the benefit doesn't justify the
cost.
Finally, with the growth of the gMock matcher library, we are encouraging people Finally, with the growth of the gMock matcher library, we are encouraging people
to use the unified `EXPECT_THAT(value, matcher)` syntax more often in tests. One to use the unified `EXPECT_THAT(value, matcher)` syntax more often in tests. One
@@ -127,6 +130,7 @@ can much more easily decide which one to use the next time.
## I got some run-time errors about invalid proto descriptors when using `ProtocolMessageEquals`. Help! ## I got some run-time errors about invalid proto descriptors when using `ProtocolMessageEquals`. Help!
{: .callout .note}
**Note:** `ProtocolMessageEquals` and `ProtocolMessageEquiv` are *deprecated* **Note:** `ProtocolMessageEquals` and `ProtocolMessageEquiv` are *deprecated*
now. Please use `EqualsProto`, etc instead. now. Please use `EqualsProto`, etc instead.
@@ -158,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()`.
@@ -176,18 +180,6 @@ template argument, and thus doesn't compile in opt mode when `a` contains a call
to `htonl()`. It is difficult to make `EXPECT_EQ` bypass the `htonl()` bug, as to `htonl()`. It is difficult to make `EXPECT_EQ` bypass the `htonl()` bug, as
the solution must work with different compilers on various platforms. the solution must work with different compilers on various platforms.
`htonl()` has some other problems as described in `//util/endian/endian.h`,
which defines `ghtonl()` to replace it. `ghtonl()` does the same thing `htonl()`
does, only without its problems. We suggest you to use `ghtonl()` instead of
`htonl()`, both in your tests and production code.
`//util/endian/endian.h` also defines `ghtons()`, which solves similar problems
in `htons()`.
Don't forget to add `//util/endian` to the list of dependencies in the `BUILD`
file wherever `ghtonl()` and `ghtons()` are used. The library consists of a
single header file and will not bloat your binary.
## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong? ## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong?
If your class has a static data member: If your class has a static data member:
@@ -207,10 +199,22 @@ 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. :-)
If the declaration of the static data member is `constexpr` then it is
implicitly an `inline` definition, and a separate definition in `foo.cc` is not
needed:
```c++
// foo.h
class Foo {
...
static constexpr int kBar = 100; // Defines kBar, no need to do it in foo.cc.
};
```
## Can I derive a test fixture from another? ## Can I derive a test fixture from another?
Yes. Yes.
@@ -221,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.
@@ -260,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](../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?
@@ -274,9 +278,10 @@ 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. delicate. To write death tests you really need to understand how they work—see
Please make sure you have read [this](advanced.md#how-it-works). the details at [Death Assertions](reference/assertions.md#death) in the
Assertions Reference.
In particular, death tests don't like having multiple threads in the parent In particular, death tests don't like having multiple threads in the parent
process. So the first thing you can try is to eliminate creating threads outside process. So the first thing you can try is to eliminate creating threads outside
@@ -295,13 +300,13 @@ program from the beginning in the child process. Therefore make sure your
program can run side-by-side with itself and is deterministic. program can run side-by-side with itself and is deterministic.
In the end, this boils down to good concurrent programming. You have to make In the end, this boils down to good concurrent programming. You have to make
sure that there is no race conditions or dead locks in your program. No silver sure that there are no race conditions or deadlocks in your program. No silver
bullet - sorry! 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.
@@ -323,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
@@ -332,89 +337,25 @@ You may still want to use `SetUp()/TearDown()` in the following cases:
* In the body of a constructor (or destructor), it's not possible to use the * In the body of a constructor (or destructor), it's not possible to use the
`ASSERT_xx` macros. Therefore, if the set-up operation could cause a fatal `ASSERT_xx` macros. Therefore, if the set-up operation could cause a fatal
test failure that should prevent the test from running, it's necessary to test failure that should prevent the test from running, it's necessary to
use `abort` <!-- GOOGLETEST_CM0015 DO NOT DELETE --> and abort the whole test executable, use `abort` and abort the whole test
or to use `SetUp()` instead of a constructor. executable, or to use `SetUp()` instead of a constructor.
* If the tear-down operation could throw an exception, you must use * If the tear-down operation could throw an exception, you must use
`TearDown()` as opposed to the destructor, as throwing in a destructor leads `TearDown()` as opposed to the destructor, as throwing in a destructor leads
to undefined behavior and usually will kill your program right away. Note to undefined behavior and usually will kill your program right away. Note
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?
If the predicate function you use in `ASSERT_PRED*` or `EXPECT_PRED*` is See details for [`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) in the
overloaded or a template, the compiler will have trouble figuring out which Assertions Reference.
overloaded version it should use. `ASSERT_PRED_FORMAT*` and
`EXPECT_PRED_FORMAT*` don't have this problem.
If you see this error, you might want to switch to
`(ASSERT|EXPECT)_PRED_FORMAT*`, which will also give you a better failure
message. If, however, that is not an option, you can resolve the problem by
explicitly telling the compiler which version to pick.
For example, suppose you have
```c++
bool IsPositive(int n) {
return n > 0;
}
bool IsPositive(double x) {
return x > 0;
}
```
you will get a compiler error if you write
```c++
EXPECT_PRED1(IsPositive, 5);
```
However, this will work:
```c++
EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
```
(The stuff inside the angled brackets for the `static_cast` operator is the type
of the function pointer for the `int`-version of `IsPositive()`.)
As another example, when you have a template function
```c++
template <typename T>
bool IsNegative(T x) {
return x < 0;
}
```
you can use it in a predicate assertion like this:
```c++
ASSERT_PRED1(IsNegative<int>, -5);
```
Things are more interesting if your template has more than one parameters. The
following won't compile:
```c++
ASSERT_PRED2(GreaterThan<int, int>, 5, 0);
```
as the C++ pre-processor thinks you are giving `ASSERT_PRED2` 4 arguments, which
is one more than expected. The workaround is to wrap the predicate function in
parentheses:
```c++
ASSERT_PRED2((GreaterThan<int, int>), 5, 0);
```
## My compiler complains about "ignoring return value" when I call RUN_ALL_TESTS(). Why? ## My compiler complains about "ignoring return value" when I call RUN_ALL_TESTS(). Why?
@@ -434,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
@@ -469,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
@@ -500,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:
@@ -531,8 +471,8 @@ There are several good reasons:
## What can the statement argument in ASSERT_DEATH() be? ## What can the statement argument in ASSERT_DEATH() be?
`ASSERT_DEATH(*statement*, *regex*)` (or any death assertion macro) can be used `ASSERT_DEATH(statement, matcher)` (or any death assertion macro) can be used
wherever `*statement*` is valid. So basically `*statement*` can be any C++ wherever *`statement`* is valid. So basically *`statement`* can be any C++
statement that makes sense in the current context. In particular, it can statement that makes sense in the current context. In particular, it can
reference global and/or local variables, and can be: reference global and/or local variables, and can be:
@@ -555,7 +495,7 @@ TEST(MyDeathTest, ComplexExpression) {
"(Func1|Method) failed"); "(Func1|Method) failed");
} }
// Death assertions can be used any where in a function. In // Death assertions can be used anywhere in a function. In
// particular, they can be inside a loop. // particular, they can be inside a loop.
TEST(MyDeathTest, InsideLoop) { TEST(MyDeathTest, InsideLoop) {
// Verifies that Foo(0), Foo(1), ..., and Foo(4) all die. // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.
@@ -578,11 +518,9 @@ TEST(MyDeathTest, CompoundStatement) {
} }
``` ```
gtest-death-test_test.cc contains more examples if you are interested.
## 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:
@@ -597,7 +535,7 @@ However, there are cases where you have to define your own:
## Why does ASSERT_DEATH complain about previous threads that were already joined? ## Why does ASSERT_DEATH complain about previous threads that were already joined?
With the Linux pthread library, there is no turning back once you cross the line With the Linux pthread library, there is no turning back once you cross the line
from single thread to multiple threads. The first time you create a thread, a from a single thread to multiple threads. The first time you create a thread, a
manager thread is created in addition, so you get 3, not 2, threads. Later when manager thread is created in addition, so you get 3, not 2, threads. Later when
the thread you create joins the main thread, the thread count decrements by 1, the thread you create joins the main thread, the thread count decrements by 1,
but the manager thread will never be killed, so you still have 2 threads, which but the manager thread will never be killed, so you still have 2 threads, which
@@ -607,12 +545,12 @@ 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 afterwords. 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.
@@ -650,29 +588,30 @@ 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
match). Admittedly, this is a hack. We'll consider a more permanent solution match). Admittedly, this is a hack. We'll consider a more permanent solution
after the fork-and-exec-style death tests are implemented. after the fork-and-exec-style death tests are implemented.
## The compiler complains about "no match for 'operator<<'" when I use an assertion. What gives? ## The compiler complains about `no match for 'operator<<'` when I use an assertion. What gives?
If you use a user-defined type `FooType` in an assertion, you must make sure If you use a user-defined type `FooType` in an assertion, you must make sure
there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function
defined such that we can print a value of `FooType`. defined such that we can print a value of `FooType`.
In addition, if `FooType` is declared in a name space, the `<<` operator also In addition, if `FooType` is declared in a name space, the `<<` operator also
needs to be defined in the *same* name space. See https://abseil.io/tips/49 for details. needs to be defined in the *same* name space. See
[Tip of the Week #49](http://abseil.io/tips/49) for details.
## 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
@@ -686,13 +625,13 @@ 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](https://en.wikipedia.org/wiki/Dependency_injection). You can inject test is [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection). You can inject
different functionality from the test and from the production code. Since your different functionality from the test and from the production code. Since your
production code doesn't link in the for-test logic at all (the production code doesn't link in the for-test logic at all (the
[`testonly`](https://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly) attribute for BUILD targets helps to ensure [`testonly`](http://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly) attribute for BUILD targets helps to ensure
that), there is no danger in accidentally running it. that), there is no danger in accidentally running it.
However, if you *really*, *really*, *really* have no choice, and if you follow However, if you *really*, *really*, *really* have no choice, and if you follow
@@ -703,12 +642,12 @@ whether the code is under test.
## How do I temporarily disable a test? ## How do I temporarily disable a test?
If you have a broken test that you cannot fix right away, you can add the If you have a broken test that you cannot fix right away, you can add the
DISABLED_ prefix to its name. This will exclude it from execution. This is `DISABLED_` prefix to its name. This will exclude it from execution. This is
better than commenting out the code or using #if 0, as disabled tests are still better than commenting out the code or using `#if 0`, as disabled tests are
compiled (and thus won't rot). still compiled (and thus won't rot).
To include disabled tests in test execution, just invoke the test program with To include disabled tests in test execution, just invoke the test program with
the --gtest_also_run_disabled_tests flag. the `--gtest_also_run_disabled_tests` flag.
## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces? ## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces?
@@ -733,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

@@ -0,0 +1,241 @@
# gMock Cheat Sheet
## Defining a Mock Class
### Mocking a Normal Class {#MockClass}
Given
```cpp
class Foo {
public:
virtual ~Foo();
virtual int GetSize() const = 0;
virtual string Describe(const char* name) = 0;
virtual string Describe(int type) = 0;
virtual bool Process(Bar elem, int count) = 0;
};
```
(note that `~Foo()` **must** be virtual) we can define its mock as
```cpp
#include "gmock/gmock.h"
class MockFoo : public Foo {
public:
MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(string, Describe, (const char* name), (override));
MOCK_METHOD(string, Describe, (int type), (override));
MOCK_METHOD(bool, Process, (Bar elem, int count), (override));
};
```
To create a "nice" mock, which ignores all uninteresting calls, a "naggy" mock,
which warns on all uninteresting calls, or a "strict" mock, which treats them as
failures:
```cpp
using ::testing::NiceMock;
using ::testing::NaggyMock;
using ::testing::StrictMock;
NiceMock<MockFoo> nice_foo; // The type is a subclass of MockFoo.
NaggyMock<MockFoo> naggy_foo; // The type is a subclass of MockFoo.
StrictMock<MockFoo> strict_foo; // The type is a subclass of MockFoo.
```
{: .callout .note}
**Note:** A mock object is currently naggy by default. We may make it nice by
default in the future.
### Mocking a Class Template {#MockTemplate}
Class templates can be mocked just like any class.
To mock
```cpp
template <typename Elem>
class StackInterface {
public:
virtual ~StackInterface();
virtual int GetSize() const = 0;
virtual void Push(const Elem& x) = 0;
};
```
(note that all member functions that are mocked, including `~StackInterface()`
**must** be virtual).
```cpp
template <typename Elem>
class MockStack : public StackInterface<Elem> {
public:
MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(void, Push, (const Elem& x), (override));
};
```
### Specifying Calling Conventions for Mock Functions
If your mock function doesn't use the default calling convention, you can
specify it by adding `Calltype(convention)` to `MOCK_METHOD`'s 4th parameter.
For example,
```cpp
MOCK_METHOD(bool, Foo, (int n), (Calltype(STDMETHODCALLTYPE)));
MOCK_METHOD(int, Bar, (double x, double y),
(const, Calltype(STDMETHODCALLTYPE)));
```
where `STDMETHODCALLTYPE` is defined by `<objbase.h>` on Windows.
## Using Mocks in Tests {#UsingMocks}
The typical work flow is:
1. Import the gMock names you need to use. All gMock symbols are in the
`testing` namespace unless they are macros or otherwise noted.
2. Create the mock objects.
3. Optionally, set the default actions of the mock objects.
4. Set your expectations on the mock objects (How will they be called? What
will they do?).
5. Exercise code that uses the mock objects; if necessary, check the result
using googletest assertions.
6. When a mock object is destructed, gMock automatically verifies that all
expectations on it have been satisfied.
Here's an example:
```cpp
using ::testing::Return; // #1
TEST(BarTest, DoesThis) {
MockFoo foo; // #2
ON_CALL(foo, GetSize()) // #3
.WillByDefault(Return(1));
// ... other default actions ...
EXPECT_CALL(foo, Describe(5)) // #4
.Times(3)
.WillRepeatedly(Return("Category 5"));
// ... other expectations ...
EXPECT_EQ(MyProductionFunction(&foo), "good"); // #5
} // #6
```
## Setting Default Actions {#OnCall}
gMock has a **built-in default action** for any function that returns `void`,
`bool`, a numeric value, or a pointer. In C++11, it will additionally returns
the default-constructed value, if one exists for the given type.
To customize the default action for functions with return type `T`, use
[`DefaultValue<T>`](reference/mocking.md#DefaultValue). For example:
```cpp
// Sets the default action for return type std::unique_ptr<Buzz> to
// creating a new Buzz every time.
DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
[] { return std::make_unique<Buzz>(AccessLevel::kInternal); });
// When this fires, the default action of MakeBuzz() will run, which
// will return a new Buzz object.
EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber());
auto buzz1 = mock_buzzer_.MakeBuzz("hello");
auto buzz2 = mock_buzzer_.MakeBuzz("hello");
EXPECT_NE(buzz1, nullptr);
EXPECT_NE(buzz2, nullptr);
EXPECT_NE(buzz1, buzz2);
// Resets the default action for return type std::unique_ptr<Buzz>,
// to avoid interfere with other tests.
DefaultValue<std::unique_ptr<Buzz>>::Clear();
```
To customize the default action for a particular method of a specific mock
object, use [`ON_CALL`](reference/mocking.md#ON_CALL). `ON_CALL` has a similar
syntax to `EXPECT_CALL`, but it is used for setting default behaviors when you
do not require that the mock method is called. See
[Knowing When to Expect](gmock_cook_book.md#UseOnCall) for a more detailed
discussion.
## Setting Expectations {#ExpectCall}
See [`EXPECT_CALL`](reference/mocking.md#EXPECT_CALL) in the Mocking Reference.
## Matchers {#MatcherList}
See the [Matchers Reference](reference/matchers.md).
## Actions {#ActionList}
See the [Actions Reference](reference/actions.md).
## Cardinalities {#CardinalityList}
See the [`Times` clause](reference/mocking.md#EXPECT_CALL.Times) of
`EXPECT_CALL` in the Mocking Reference.
## Expectation Order
By default, expectations can be matched in *any* order. If some or all
expectations must be matched in a given order, you can use the
[`After` clause](reference/mocking.md#EXPECT_CALL.After) or
[`InSequence` clause](reference/mocking.md#EXPECT_CALL.InSequence) of
`EXPECT_CALL`, or use an [`InSequence` object](reference/mocking.md#InSequence).
## Verifying and Resetting a Mock
gMock will verify the expectations on a mock object when it is destructed, or
you can do it earlier:
```cpp
using ::testing::Mock;
...
// Verifies and removes the expectations on mock_obj;
// returns true if and only if successful.
Mock::VerifyAndClearExpectations(&mock_obj);
...
// Verifies and removes the expectations on mock_obj;
// also removes the default actions set by ON_CALL();
// returns true if and only if successful.
Mock::VerifyAndClear(&mock_obj);
```
Do not set new expectations after verifying and clearing a mock after its use.
Setting expectations after code that exercises the mock has undefined behavior.
See [Using Mocks in Tests](gmock_for_dummies.md#using-mocks-in-tests) for more
information.
You can also tell gMock that a mock object can be leaked and doesn't need to be
verified:
```cpp
Mock::AllowLeak(&mock_obj);
```
## Mock Classes
gMock defines a convenient mock class template
```cpp
class MockFunction<R(A1, ..., An)> {
public:
MOCK_METHOD(R, Call, (A1, ..., An));
};
```
See this [recipe](gmock_cook_book.md#UsingCheckPoints) for one application of
it.
## Flags
| Flag | Description |
| :----------------------------- | :---------------------------------------- |
| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. |
| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. |

View File

@@ -1,11 +1,9 @@
## Legacy gMock FAQ {#GMockFaq} # Legacy gMock FAQ
<!-- GOOGLETEST_CM0021 DO NOT DELETE -->
### When I call a method on my mock object, the method for the real object is invoked instead. What's the problem? ### When I call a method on my mock object, the method for the real object is invoked instead. What's the problem?
In order for a method to be mocked, it must be *virtual*, unless you use the In order for a method to be mocked, it must be *virtual*, unless you use the
[high-perf dependency injection technique](#MockingNonVirtualMethods). [high-perf dependency injection technique](gmock_cook_book.md#MockingNonVirtualMethods).
### Can I mock a variadic function? ### Can I mock a variadic function?
@@ -81,8 +79,6 @@ void Bar(int* p); // Neither p nor *p is const.
void Bar(const int* p); // p is not const, but *p is. void Bar(const int* p); // p is not const, but *p is.
``` ```
<!-- GOOGLETEST_CM0030 DO NOT DELETE -->
### I can't figure out why gMock thinks my expectations are not satisfied. What should I do? ### I can't figure out why gMock thinks my expectations are not satisfied. What should I do?
You might want to run your test with `--gmock_verbose=info`. This flag lets You might want to run your test with `--gmock_verbose=info`. This flag lets
@@ -91,7 +87,7 @@ trace, you'll gain insights on why the expectations you set are not met.
If you see the message "The mock function has no default action set, and its If you see the message "The mock function has no default action set, and its
return type has no default value set.", then try return type has no default value set.", then try
[adding a default action](for_dummies.md#DefaultValue). Due to a known issue, [adding a default action](gmock_cheat_sheet.md#OnCall). Due to a known issue,
unexpected calls on mocks without default actions don't print out a detailed unexpected calls on mocks without default actions don't print out a detailed
comparison between the actual arguments and the expected arguments. comparison between the actual arguments and the expected arguments.
@@ -126,8 +122,6 @@ using ::testing::_;
.Times(0); .Times(0);
``` ```
<!-- GOOGLETEST_CM0031 DO NOT DELETE -->
### I have a failed test where gMock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? ### I have a failed test where gMock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant?
When gMock detects a failure, it prints relevant information (the mock function When gMock detects a failure, it prints relevant information (the mock function
@@ -375,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?
@@ -386,8 +380,8 @@ doesn't say what the return value should be. You need `DoAll()` to chain a
`SetArgPointee()` with a `Return()` that provides a value appropriate to the API `SetArgPointee()` with a `Return()` that provides a value appropriate to the API
being mocked. being mocked.
See this [recipe](cook_book.md#mocking-side-effects) for more details and an See this [recipe](gmock_cook_book.md#mocking-side-effects) for more details and
example. an example.
### I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? ### I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do?

View File

@@ -1,8 +1,6 @@
## gMock for Dummies {#GMockForDummies} # gMock for Dummies
<!-- GOOGLETEST_CM0013 DO NOT DELETE --> ## What Is gMock?
### What Is gMock?
When you write a prototype or test, often it's not feasible or wise to rely on When you write a prototype or test, often it's not feasible or wise to rely on
real objects entirely. A **mock object** implements the same interface as a real real objects entirely. A **mock object** implements the same interface as a real
@@ -10,9 +8,9 @@ object (so it can be used as one), but lets you specify at run time how it will
be used and what it should do (which methods will be called? in which order? how be used and what it should do (which methods will be called? in which order? how
many times? with what arguments? what will they return? etc). many times? with what arguments? what will they return? etc).
**Note:** It is easy to confuse the term *fake objects* with mock objects. Fakes It is easy to confuse the term *fake objects* with mock objects. Fakes and mocks
and mocks actually mean very different things in the Test-Driven Development actually mean very different things in the Test-Driven Development (TDD)
(TDD) community: community:
* **Fake** objects have working implementations, but usually take some * **Fake** objects have working implementations, but usually take some
shortcut (perhaps to make the operations less expensive), which makes them shortcut (perhaps to make the operations less expensive), which makes them
@@ -39,7 +37,7 @@ When using gMock,
3. then you exercise code that uses the mock objects. gMock will catch any 3. then you exercise code that uses the mock objects. gMock will catch any
violation to the expectations as soon as it arises. violation to the expectations as soon as it arises.
### Why gMock? ## Why gMock?
While mock objects help you remove unnecessary dependencies in tests and make While mock objects help you remove unnecessary dependencies in tests and make
them fast and reliable, using mocks manually in C++ is *hard*: them fast and reliable, using mocks manually in C++ is *hard*:
@@ -53,9 +51,9 @@ them fast and reliable, using mocks manually in C++ is *hard*:
one. one.
In contrast, Java and Python programmers have some fine mock frameworks (jMock, In contrast, Java and Python programmers have some fine mock frameworks (jMock,
EasyMock, [Mox](http://wtf/mox), etc), which automate the creation of mocks. As EasyMock, etc), which automate the creation of mocks. As a result, mocking is a
a result, mocking is a proven effective technique and widely adopted practice in proven effective technique and widely adopted practice in those communities.
those communities. Having the right tool absolutely makes the difference. Having the right tool absolutely makes the difference.
gMock was built to help C++ programmers. It was inspired by jMock and EasyMock, gMock was built to help C++ programmers. It was inspired by jMock and EasyMock,
but designed with C++'s specifics in mind. It is your friend if any of the but designed with C++'s specifics in mind. It is your friend if any of the
@@ -85,11 +83,11 @@ We encourage you to use gMock as
* a *testing* tool to cut your tests' outbound dependencies and probe the * a *testing* tool to cut your tests' outbound dependencies and probe the
interaction between your module and its collaborators. interaction between your module and its collaborators.
### Getting Started ## Getting Started
gMock is bundled with googletest. gMock is bundled with googletest.
### A Case for Mock Turtles ## A Case for Mock Turtles
Let's look at an example. Suppose you are developing a graphics program that Let's look at an example. Suppose you are developing a graphics program that
relies on a [LOGO](http://en.wikipedia.org/wiki/Logo_programming_language)-like relies on a [LOGO](http://en.wikipedia.org/wiki/Logo_programming_language)-like
@@ -106,7 +104,7 @@ the API in an interface (say, `Turtle`) and code to that interface:
```cpp ```cpp
class Turtle { class Turtle {
... ...
virtual ~Turtle() {}; virtual ~Turtle() {}
virtual void PenUp() = 0; virtual void PenUp() = 0;
virtual void PenDown() = 0; virtual void PenDown() = 0;
virtual void Forward(int distance) = 0; virtual void Forward(int distance) = 0;
@@ -135,20 +133,20 @@ because your new machine does anti-aliasing differently), easier to read and
maintain (the intent of a test is expressed in the code, not in some binary maintain (the intent of a test is expressed in the code, not in some binary
images), and run *much, much faster*. images), and run *much, much faster*.
### Writing the Mock Class ## Writing the Mock Class
If you are lucky, the mocks you need to use have already been implemented by If you are lucky, the mocks you need to use have already been implemented by
some nice people. If, however, you find yourself in the position to write a mock some nice people. If, however, you find yourself in the position to write a mock
class, relax - gMock turns this task into a fun game! (Well, almost.) class, relax - gMock turns this task into a fun game! (Well, almost.)
#### How to Define It ### How to Define It
Using the `Turtle` interface as example, here are the simple steps you need to Using the `Turtle` interface as example, here are the simple steps you need to
follow: follow:
* Derive a class `MockTurtle` from `Turtle`. * Derive a class `MockTurtle` from `Turtle`.
* Take a *virtual* function of `Turtle` (while it's possible to * Take a *virtual* function of `Turtle` (while it's possible to
[mock non-virtual methods using templates](cook_book.md#MockingNonVirtualMethods), [mock non-virtual methods using templates](gmock_cook_book.md#MockingNonVirtualMethods),
it's much more involved). it's much more involved).
* In the `public:` section of the child class, write `MOCK_METHOD();` * In the `public:` section of the child class, write `MOCK_METHOD();`
* Now comes the fun part: you take the function signature, cut-and-paste it * Now comes the fun part: you take the function signature, cut-and-paste it
@@ -184,7 +182,7 @@ class MockTurtle : public Turtle {
You don't need to define these mock methods somewhere else - the `MOCK_METHOD` You don't need to define these mock methods somewhere else - the `MOCK_METHOD`
macro will generate the definitions for you. It's that simple! macro will generate the definitions for you. It's that simple!
#### Where to Put It ### Where to Put It
When you define a mock class, you need to decide where to put its definition. When you define a mock class, you need to decide where to put its definition.
Some people put it in a `_test.cc`. This is fine when the interface being mocked Some people put it in a `_test.cc`. This is fine when the interface being mocked
@@ -192,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.
@@ -206,14 +204,12 @@ choosing the adaptor interface can make your code easier to write and more
readable (a net win in the long run), as you can choose `FooAdaptor` to fit your readable (a net win in the long run), as you can choose `FooAdaptor` to fit your
specific domain much better than `Foo` does. specific domain much better than `Foo` does.
<!-- GOOGLETEST_CM0029 DO NOT DELETE --> ## Using Mocks in Tests
### Using Mocks in Tests
Once you have a mock class, using it is easy. The typical work flow is: Once you have a mock class, using it is easy. The typical work flow is:
1. Import the gMock names from the `testing` namespace such that you can use 1. Import the gMock names from the `testing` namespace such that you can use
them unqualified (You only have to do it once per file. Remember that them unqualified (You only have to do it once per file). Remember that
namespaces are a good idea. namespaces are a good idea.
2. Create some mock objects. 2. Create some mock objects.
3. Specify your expectations on them (How many times will a method be called? 3. Specify your expectations on them (How many times will a method be called?
@@ -257,8 +253,8 @@ Stack trace:
... ...
``` ```
**Tip 1:** If you run the test from an Emacs buffer, you can hit <Enter> on the **Tip 1:** If you run the test from an Emacs buffer, you can hit `<Enter>` on
line number to jump right to the failed expectation. the line number to jump right to the failed expectation.
**Tip 2:** If your mock objects are never deleted, the final verification won't **Tip 2:** If your mock objects are never deleted, the final verification won't
happen. Therefore it's a good idea to turn on the heap checker in your tests happen. Therefore it's a good idea to turn on the heap checker in your tests
@@ -266,8 +262,9 @@ when you allocate mocks on the heap. You get that automatically if you use the
`gtest_main` library already. `gtest_main` library already.
**Important note:** gMock requires expectations to be set **before** the mock **Important note:** gMock requires expectations to be set **before** the mock
functions are called, otherwise the behavior is **undefined**. In particular, functions are called, otherwise the behavior is **undefined**. Do not alternate
you mustn't interleave `EXPECT_CALL()s` and calls to the mock functions. between calls to `EXPECT_CALL()` and calls to the mock functions, and do not set
any expectations on a mock after passing the mock to an API.
This means `EXPECT_CALL()` should be read as expecting that a call will occur This means `EXPECT_CALL()` should be read as expecting that a call will occur
*in the future*, not that a call has occurred. Why does gMock work like that? *in the future*, not that a call has occurred. Why does gMock work like that?
@@ -279,7 +276,7 @@ Admittedly, this test is contrived and doesn't do much. You can easily achieve
the same effect without using gMock. However, as we shall reveal soon, gMock the same effect without using gMock. However, as we shall reveal soon, gMock
allows you to do *so much more* with the mocks. allows you to do *so much more* with the mocks.
### Setting Expectations ## Setting Expectations
The key to using a mock object successfully is to set the *right expectations* The key to using a mock object successfully is to set the *right expectations*
on it. If you set the expectations too strict, your test will fail as the result on it. If you set the expectations too strict, your test will fail as the result
@@ -288,7 +285,7 @@ to do it just right such that your test can catch exactly the kind of bugs you
intend it to catch. gMock provides the necessary means for you to do it "just intend it to catch. gMock provides the necessary means for you to do it "just
right." right."
#### General Syntax ### General Syntax
In gMock we use the `EXPECT_CALL()` macro to set an expectation on a mock In gMock we use the `EXPECT_CALL()` macro to set an expectation on a mock
method. The general syntax is: method. The general syntax is:
@@ -314,8 +311,8 @@ EXPECT_CALL(mock_object, non-overloaded-method)
This syntax allows the test writer to specify "called with any arguments" This syntax allows the test writer to specify "called with any arguments"
without explicitly specifying the number or types of arguments. To avoid without explicitly specifying the number or types of arguments. To avoid
unintended ambiguity, this syntax may only be used for methods which are not unintended ambiguity, this syntax may only be used for methods that are not
overloaded overloaded.
Either form of the macro can be followed by some optional *clauses* that provide Either form of the macro can be followed by some optional *clauses* that provide
more information about the expectation. We'll discuss how each clause works in more information about the expectation. We'll discuss how each clause works in
@@ -338,12 +335,13 @@ says that the `turtle` object's `GetX()` method will be called five times, it
will return 100 the first time, 150 the second time, and then 200 every time. will return 100 the first time, 150 the second time, and then 200 every time.
Some people like to call this style of syntax a Domain-Specific Language (DSL). Some people like to call this style of syntax a Domain-Specific Language (DSL).
{: .callout .note}
**Note:** Why do we use a macro to do this? Well it serves two purposes: first **Note:** Why do we use a macro to do this? Well it serves two purposes: first
it makes expectations easily identifiable (either by `gsearch` or by a human it makes expectations easily identifiable (either by `grep` or by a human
reader), and second it allows gMock to include the source file location of a reader), and second it allows gMock to include the source file location of a
failed expectation in messages, making debugging easier. failed expectation in messages, making debugging easier.
#### Matchers: What Arguments Do We Expect? ### Matchers: What Arguments Do We Expect?
When a mock function takes arguments, we may specify what arguments we are When a mock function takes arguments, we may specify what arguments we are
expecting, for example: expecting, for example:
@@ -374,8 +372,8 @@ convenient way of saying "any value".
In the above examples, `100` and `50` are also matchers; implicitly, they are In the above examples, `100` and `50` are also matchers; implicitly, they are
the same as `Eq(100)` and `Eq(50)`, which specify that the argument must be the same as `Eq(100)` and `Eq(50)`, which specify that the argument must be
equal (using `operator==`) to the matcher argument. There are many equal (using `operator==`) to the matcher argument. There are many
[built-in matchers](#MatcherList) for common types (as well as [built-in matchers](reference/matchers.md) for common types (as well as
[custom matchers](cook_book.md#NewMatchers)); for example: [custom matchers](gmock_cook_book.md#NewMatchers)); for example:
```cpp ```cpp
using ::testing::Ge; using ::testing::Ge;
@@ -397,9 +395,9 @@ EXPECT_CALL(turtle, GoTo);
This works for all non-overloaded methods; if a method is overloaded, you need This works for all non-overloaded methods; if a method is overloaded, you need
to help gMock resolve which overload is expected by specifying the number of to help gMock resolve which overload is expected by specifying the number of
arguments and possibly also the arguments and possibly also the
[types of the arguments](cook_book.md#SelectOverload). [types of the arguments](gmock_cook_book.md#SelectOverload).
#### Cardinalities: How Many Times Will It Be Called? ### Cardinalities: How Many Times Will It Be Called?
The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We
call its argument a **cardinality** as it tells *how many times* the call should call its argument a **cardinality** as it tells *how many times* the call should
@@ -414,7 +412,7 @@ called.
We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the
list of built-in cardinalities you can use, see list of built-in cardinalities you can use, see
[here](cheat_sheet.md#CardinalityList). [here](gmock_cheat_sheet.md#CardinalityList).
The `Times()` clause can be omitted. **If you omit `Times()`, gMock will infer The `Times()` clause can be omitted. **If you omit `Times()`, gMock will infer
the cardinality for you.** The rules are easy to remember: the cardinality for you.** The rules are easy to remember:
@@ -429,7 +427,7 @@ the cardinality for you.** The rules are easy to remember:
**Quick quiz:** what do you think will happen if a function is expected to be **Quick quiz:** what do you think will happen if a function is expected to be
called twice but actually called four times? called twice but actually called four times?
#### Actions: What Should It Do? ### Actions: What Should It Do?
Remember that a mock object doesn't really have a working implementation? We as Remember that a mock object doesn't really have a working implementation? We as
users have to tell it what to do when a method is invoked. This is easy in users have to tell it what to do when a method is invoked. This is easy in
@@ -482,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](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
@@ -503,7 +501,7 @@ always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)`
will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will
return the same pointer every time. If you want the side effect to happen every return the same pointer every time. If you want the side effect to happen every
time, you need to define a custom action, which we'll teach in the time, you need to define a custom action, which we'll teach in the
[cook book](http://<!-- GOOGLETEST_CM0012 DO NOT DELETE -->). [cook book](gmock_cook_book.md).
Time for another quiz! What do you think the following means? Time for another quiz! What do you think the following means?
@@ -522,7 +520,7 @@ will be taken afterwards. So the right answer is that `turtle.GetY()` will
return 100 the first time, but **return 0 from the second time on**, as return 100 the first time, but **return 0 from the second time on**, as
returning 0 is the default action for `int` functions. returning 0 is the default action for `int` functions.
#### Using Multiple Expectations {#MultiExpectations} ### Using Multiple Expectations {#MultiExpectations}
So far we've only shown examples where you have a single expectation. More So far we've only shown examples where you have a single expectation. More
realistically, you'll specify expectations on multiple mock methods which may be realistically, you'll specify expectations on multiple mock methods which may be
@@ -547,6 +545,7 @@ error, as the last matching expectation (#2) has been saturated. If, however,
the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK,
as now #1 will be the matching expectation. as now #1 will be the matching expectation.
{: .callout .note}
**Note:** Why does gMock search for a match in the *reverse* order of the **Note:** Why does gMock search for a match in the *reverse* order of the
expectations? The reason is that this allows a user to set up the default expectations? The reason is that this allows a user to set up the default
expectations in a mock object's constructor or the test fixture's set-up phase expectations in a mock object's constructor or the test fixture's set-up phase
@@ -555,15 +554,16 @@ body. So, if you have two expectations on the same method, you want to put the
one with more specific matchers **after** the other, or the more specific rule one with more specific matchers **after** the other, or the more specific rule
would be shadowed by the more general one that comes after it. would be shadowed by the more general one that comes after it.
{: .callout .tip}
**Tip:** It is very common to start with a catch-all expectation for a method **Tip:** It is very common to start with a catch-all expectation for a method
and `Times(AnyNumber())` (omitting arguments, or with `_` for all arguments, if and `Times(AnyNumber())` (omitting arguments, or with `_` for all arguments, if
overloaded). This makes any calls to the method expected. This is not necessary overloaded). This makes any calls to the method expected. This is not necessary
for methods that are not mentioned at all (these are "uninteresting"), but is for methods that are not mentioned at all (these are "uninteresting"), but is
useful for methods that have some expectations, but for which other calls are useful for methods that have some expectations, but for which other calls are
ok. See ok. See
[Understanding Uninteresting vs Unexpected Calls](cook_book.md#uninteresting-vs-unexpected). [Understanding Uninteresting vs Unexpected Calls](gmock_cook_book.md#uninteresting-vs-unexpected).
#### Ordered vs Unordered Calls {#OrderedCalls} ### Ordered vs Unordered Calls {#OrderedCalls}
By default, an expectation can match a call even though an earlier expectation By default, an expectation can match a call even though an earlier expectation
hasn't been satisfied. In other words, the calls don't have to occur in the hasn't been satisfied. In other words, the calls don't have to occur in the
@@ -598,9 +598,9 @@ order as written. If a call is made out-of-order, it will be an error.
(What if you care about the relative order of some of the calls, but not all of (What if you care about the relative order of some of the calls, but not all of
them? Can you specify an arbitrary partial order? The answer is ... yes! The them? Can you specify an arbitrary partial order? The answer is ... yes! The
details can be found [here](cook_book.md#OrderedCalls).) details can be found [here](gmock_cook_book.md#OrderedCalls).)
#### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations} ### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations}
Now let's do a quick quiz to see how well you can use this mock stuff already. Now let's do a quick quiz to see how well you can use this mock stuff already.
How would you test that the turtle is asked to go to the origin *exactly twice* How would you test that the turtle is asked to go to the origin *exactly twice*
@@ -688,7 +688,7 @@ it's in a sequence - as soon as another expectation that comes after it in the
sequence has been used, it automatically retires (and will never be used to sequence has been used, it automatically retires (and will never be used to
match any call). match any call).
#### Uninteresting Calls ### Uninteresting Calls
A mock object may have many methods, and not all of them are that interesting. A mock object may have many methods, and not all of them are that interesting.
For example, in some tests we may not care about how many times `GetX()` and For example, in some tests we may not care about how many times `GetX()` and
@@ -697,4 +697,4 @@ For example, in some tests we may not care about how many times `GetX()` and
In gMock, if you are not interested in a method, just don't say anything about In gMock, if you are not interested in a method, just don't say anything about
it. If a call to this method occurs, you'll see a warning in the test output, it. If a call to this method occurs, you'll see a warning in the test output,
but it won't be a failure. This is called "naggy" behavior; to change, see but it won't be a failure. This is called "naggy" behavior; to change, see
[The Nice, the Strict, and the Naggy](cook_book.md#NiceStrictNaggy). [The Nice, the Strict, and the Naggy](gmock_cook_book.md#NiceStrictNaggy).

View File

@@ -0,0 +1,22 @@
# GoogleTest User's Guide
## Welcome to GoogleTest!
GoogleTest is Google's C++ testing and mocking framework. This user's guide has
the following contents:
* [GoogleTest Primer](primer.md) - Teaches you how to write simple tests using
GoogleTest. Read this first if you are new to GoogleTest.
* [GoogleTest Advanced](advanced.md) - Read this when you've finished the
Primer and want to utilize GoogleTest to its full potential.
* [GoogleTest Samples](samples.md) - Describes some GoogleTest samples.
* [GoogleTest FAQ](faq.md) - Have a question? Want some tips? Check here
first.
* [Mocking for Dummies](gmock_for_dummies.md) - Teaches you how to create mock
objects and use them in tests.
* [Mocking Cookbook](gmock_cook_book.md) - Includes tips and approaches to
common mocking use cases.
* [Mocking Cheat Sheet](gmock_cheat_sheet.md) - A handy reference for
matchers, actions, invariants, and more.
* [Mocking FAQ](gmock_faq.md) - Contains answers to some mocking-specific
questions.

View File

@@ -45,77 +45,6 @@ splitting the pkg-config `Cflags` variable into include dirs and macros for
goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which happens goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which happens
to discard `-L` flags and `-pthread`. to discard `-L` flags and `-pthread`.
### Autotools
Finding GoogleTest in Autoconf and using it from Automake is also fairly easy:
In your `configure.ac`:
```
AC_PREREQ([2.69])
AC_INIT([my_gtest_pkgconfig], [0.0.1])
AC_CONFIG_SRCDIR([samples/sample3_unittest.cc])
AC_PROG_CXX
PKG_CHECK_MODULES([GTEST], [gtest_main])
AM_INIT_AUTOMAKE([foreign subdir-objects])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
```
and in your `Makefile.am`:
```
check_PROGRAMS = testapp
TESTS = $(check_PROGRAMS)
testapp_SOURCES = samples/sample3_unittest.cc
testapp_CXXFLAGS = $(GTEST_CFLAGS)
testapp_LDADD = $(GTEST_LIBS)
```
### Meson
Meson natively uses pkgconfig to query dependencies:
```
project('my_gtest_pkgconfig', 'cpp', version : '0.0.1')
gtest_dep = dependency('gtest_main')
testapp = executable(
'testapp',
files(['samples/sample3_unittest.cc']),
dependencies : gtest_dep,
install : false)
test('first_and_only_test', testapp)
```
### Plain Makefiles
Since `pkg-config` is a small Unix command-line utility, it can be used in
handwritten `Makefile`s too:
```makefile
GTEST_CFLAGS = `pkg-config --cflags gtest_main`
GTEST_LIBS = `pkg-config --libs gtest_main`
.PHONY: tests all
tests: all
./testapp
all: testapp
testapp: testapp.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ $(GTEST_LIBS)
testapp.o: samples/sample3_unittest.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $< -c -o $@ $(GTEST_CFLAGS)
```
### Help! pkg-config can't find GoogleTest! ### Help! pkg-config can't find GoogleTest!
Let's say you have a `CMakeLists.txt` along the lines of the one in this Let's say you have a `CMakeLists.txt` along the lines of the one in this
@@ -139,3 +68,81 @@ export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
``` ```
pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`. pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`.
### Using pkg-config in a cross-compilation setting
Pkg-config can be used in a cross-compilation setting too. To do this, let's
assume the final prefix of the cross-compiled installation will be `/usr`, and
your sysroot is `/home/MYUSER/sysroot`. Configure and install GTest using
```
mkdir build && cmake -DCMAKE_INSTALL_PREFIX=/usr ..
```
Install into the sysroot using `DESTDIR`:
```
make -j install DESTDIR=/home/MYUSER/sysroot
```
Before we continue, it is recommended to **always** define the following two
variables for pkg-config in a cross-compilation setting:
```
export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=yes
export PKG_CONFIG_ALLOW_SYSTEM_LIBS=yes
```
otherwise `pkg-config` will filter `-I` and `-L` flags against standard prefixes
such as `/usr` (see https://bugs.freedesktop.org/show_bug.cgi?id=28264#c3 for
reasons why this stripping needs to occur usually).
If you look at the generated pkg-config file, it will look something like
```
libdir=/usr/lib64
includedir=/usr/include
Name: gtest
Description: GoogleTest (without main() function)
Version: 1.11.0
URL: https://github.com/google/googletest
Libs: -L${libdir} -lgtest -lpthread
Cflags: -I${includedir} -DGTEST_HAS_PTHREAD=1 -lpthread
```
Notice that the sysroot is not included in `libdir` and `includedir`! If you try
to run `pkg-config` with the correct
`PKG_CONFIG_LIBDIR=/home/MYUSER/sysroot/usr/lib64/pkgconfig` against this `.pc`
file, you will get
```
$ pkg-config --cflags gtest
-DGTEST_HAS_PTHREAD=1 -lpthread -I/usr/include
$ pkg-config --libs gtest
-L/usr/lib64 -lgtest -lpthread
```
which is obviously wrong and points to the `CBUILD` and not `CHOST` root. In
order to use this in a cross-compilation setting, we need to tell pkg-config to
inject the actual sysroot into `-I` and `-L` variables. Let us now tell
pkg-config about the actual sysroot
```
export PKG_CONFIG_DIR=
export PKG_CONFIG_SYSROOT_DIR=/home/MYUSER/sysroot
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_SYSROOT_DIR}/usr/lib64/pkgconfig
```
and running `pkg-config` again we get
```
$ pkg-config --cflags gtest
-DGTEST_HAS_PTHREAD=1 -lpthread -I/home/MYUSER/sysroot/usr/include
$ pkg-config --libs gtest
-L/home/MYUSER/sysroot/usr/lib64 -lgtest -lpthread
```
which contains the correct sysroot now. For a more comprehensive guide to also
including `${CHOST}` in build system calls, see the excellent tutorial by Diego
Elio Pettenò: <https://autotools.io/pkgconfig/cross-compiling.html>

View File

@@ -0,0 +1,35 @@
# Supported Platforms
GoogleTest requires a codebase and compiler compliant with the C++11 standard or
newer.
The GoogleTest code is officially supported on the following platforms.
Operating systems or tools not listed below are community-supported. For
community-supported platforms, patches that do not complicate the code may be
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/)
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.

View File

@@ -44,6 +44,7 @@ minutes to learn the basics and get started. So let's go!
## Beware of the nomenclature ## Beware of the nomenclature
{: .callout .note}
_Note:_ There might be some confusion arising from different definitions of the _Note:_ There might be some confusion arising from different definitions of the
terms _Test_, _Test Case_ and _Test Suite_, so beware of misunderstanding these. terms _Test_, _Test Case_ and _Test Suite_, so beware of misunderstanding these.
@@ -66,13 +67,11 @@ deprecated and refactored away.
So please be aware of the different definitions of the terms: So please be aware of the different definitions of the terms:
<!-- mdformat off(github rendering does not support multiline tables) -->
Meaning | googletest Term | [ISTQB](http://www.istqb.org/) Term Meaning | googletest Term | [ISTQB](http://www.istqb.org/) Term
:----------------------------------------------------------------------------------- | :---------------------- | :---------------------------------- :----------------------------------------------------------------------------------- | :---------------------- | :----------------------------------
Exercise a particular program path with specific input values and verify the results | [TEST()](#simple-tests) | [Test Case][istqb test case] Exercise a particular program path with specific input values and verify the results | [TEST()](#simple-tests) | [Test Case][istqb test case]
<!-- mdformat on -->
[istqb test case]: http://glossary.istqb.org/en/search/test%20case [istqb test case]: http://glossary.istqb.org/en/search/test%20case
[istqb test suite]: http://glossary.istqb.org/en/search/test%20suite [istqb test suite]: http://glossary.istqb.org/en/search/test%20suite
@@ -119,7 +118,9 @@ Depending on the nature of the leak, it may or may not be worth fixing - so keep
this in mind if you get a heap checker error in addition to assertion errors. this in mind if you get a heap checker error in addition to assertion errors.
To provide a custom failure message, simply stream it into the macro using the To provide a custom failure message, simply stream it into the macro using the
`<<` operator or a sequence of such operators. An example: `<<` operator or a sequence of such operators. See the following example, using
the [`ASSERT_EQ` and `EXPECT_EQ`](reference/assertions.md#EXPECT_EQ) macros to
verify value equality:
```c++ ```c++
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
@@ -134,112 +135,12 @@ macro--in particular, C strings and `string` objects. If a wide string
(`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is (`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is
streamed to an assertion, it will be translated to UTF-8 when printed. streamed to an assertion, it will be translated to UTF-8 when printed.
### Basic Assertions GoogleTest provides a collection of assertions for verifying the behavior of
your code in various ways. You can check Boolean conditions, compare values
These assertions do basic true/false condition testing. based on relational operators, verify string values, floating-point values, and
much more. There are even assertions that enable you to verify more complex
Fatal assertion | Nonfatal assertion | Verifies states by providing custom predicates. For the complete list of assertions
-------------------------- | -------------------------- | -------------------- provided by GoogleTest, see the [Assertions Reference](reference/assertions.md).
`ASSERT_TRUE(condition);` | `EXPECT_TRUE(condition);` | `condition` is true
`ASSERT_FALSE(condition);` | `EXPECT_FALSE(condition);` | `condition` is false
Remember, when they fail, `ASSERT_*` yields a fatal failure and returns from the
current function, while `EXPECT_*` yields a nonfatal failure, allowing the
function to continue running. In either case, an assertion failure means its
containing test fails.
**Availability**: Linux, Windows, Mac.
### Binary Comparison
This section describes assertions that compare two values.
Fatal assertion | Nonfatal assertion | Verifies
------------------------ | ------------------------ | --------------
`ASSERT_EQ(val1, val2);` | `EXPECT_EQ(val1, val2);` | `val1 == val2`
`ASSERT_NE(val1, val2);` | `EXPECT_NE(val1, val2);` | `val1 != val2`
`ASSERT_LT(val1, val2);` | `EXPECT_LT(val1, val2);` | `val1 < val2`
`ASSERT_LE(val1, val2);` | `EXPECT_LE(val1, val2);` | `val1 <= val2`
`ASSERT_GT(val1, val2);` | `EXPECT_GT(val1, val2);` | `val1 > val2`
`ASSERT_GE(val1, val2);` | `EXPECT_GE(val1, val2);` | `val1 >= val2`
Value arguments must be comparable by the assertion's comparison operator or
you'll get a compiler error. We used to require the arguments to support the
`<<` operator for streaming to an `ostream`, but this is no longer necessary. If
`<<` is supported, it will be called to print the arguments when the assertion
fails; otherwise googletest will attempt to print them in the best way it can.
For more details and how to customize the printing of the arguments, see the
[documentation](../../googlemock/docs/cook_book.md#teaching-gmock-how-to-print-your-values).
These assertions can work with a user-defined type, but only if you define the
corresponding comparison operator (e.g., `==` or `<`). Since this is discouraged
by the Google
[C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Operator_Overloading),
you may need to use `ASSERT_TRUE()` or `EXPECT_TRUE()` to assert the equality of
two objects of a user-defined type.
However, when possible, `ASSERT_EQ(actual, expected)` is preferred to
`ASSERT_TRUE(actual == expected)`, since it tells you `actual` and `expected`'s
values on failure.
Arguments are always evaluated exactly once. Therefore, it's OK for the
arguments to have side effects. However, as with any ordinary C/C++ function,
the arguments' evaluation order is undefined (i.e., the compiler is free to
choose any order), and your code should not depend on any particular argument
evaluation order.
`ASSERT_EQ()` does pointer equality on pointers. If used on two C strings, it
tests if they are in the same memory location, not if they have the same value.
Therefore, if you want to compare C strings (e.g. `const char*`) by value, use
`ASSERT_STREQ()`, which will be described later on. In particular, to assert
that a C string is `NULL`, use `ASSERT_STREQ(c_string, NULL)`. Consider using
`ASSERT_EQ(c_string, nullptr)` if c++11 is supported. To compare two `string`
objects, you should use `ASSERT_EQ`.
When doing pointer comparisons use `*_EQ(ptr, nullptr)` and `*_NE(ptr, nullptr)`
instead of `*_EQ(ptr, NULL)` and `*_NE(ptr, NULL)`. This is because `nullptr` is
typed, while `NULL` is not. See the [FAQ](faq.md) for more details.
If you're working with floating point numbers, you may want to use the floating
point variations of some of these macros in order to avoid problems caused by
rounding. See [Advanced googletest Topics](advanced.md) for details.
Macros in this section work with both narrow and wide string objects (`string`
and `wstring`).
**Availability**: Linux, Windows, Mac.
**Historical note**: Before February 2016 `*_EQ` had a convention of calling it
as `ASSERT_EQ(expected, actual)`, so lots of existing code uses this order. Now
`*_EQ` treats both parameters in the same way.
### String Comparison
The assertions in this group compare two **C strings**. If you want to compare
two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
<!-- mdformat off(github rendering does not support multiline tables) -->
| Fatal assertion | Nonfatal assertion | Verifies |
| -------------------------- | ------------------------------ | -------------------------------------------------------- |
| `ASSERT_STREQ(str1,str2);` | `EXPECT_STREQ(str1,str2);` | the two C strings have the same content |
| `ASSERT_STRNE(str1,str2);` | `EXPECT_STRNE(str1,str2);` | the two C strings have different contents |
| `ASSERT_STRCASEEQ(str1,str2);` | `EXPECT_STRCASEEQ(str1,str2);` | the two C strings have the same content, ignoring case |
| `ASSERT_STRCASENE(str1,str2);` | `EXPECT_STRCASENE(str1,str2);` | the two C strings have different contents, ignoring case |
<!-- mdformat on-->
Note that "CASE" in an assertion name means that case is ignored. A `NULL`
pointer and an empty string are considered *different*.
`*STREQ*` and `*STRNE*` also accept wide C strings (`wchar_t*`). If a comparison
of two wide strings fails, their values will be printed as UTF-8 narrow strings.
**Availability**: Linux, Windows, Mac.
**See also**: For more string comparison tricks (substring, prefix, suffix, and
regular expression matching, for example), see [this](advanced.md) in the
Advanced googletest Guide.
## Simple Tests ## Simple Tests
@@ -261,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
case. 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:
@@ -344,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
@@ -373,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);
@@ -418,7 +320,7 @@ The above uses both `ASSERT_*` and `EXPECT_*` assertions. The rule of thumb is
to use `EXPECT_*` when you want the test to continue to reveal more errors after to use `EXPECT_*` when you want the test to continue to reveal more errors after
the assertion failure, and use `ASSERT_*` when continuing after failure doesn't the assertion failure, and use `ASSERT_*` when continuing after failure doesn't
make sense. For example, the second assertion in the `Dequeue` test is make sense. For example, the second assertion in the `Dequeue` test is
`ASSERT_NE(nullptr, n)`, as we need to dereference the pointer `n` later, which `ASSERT_NE(n, nullptr)`, as we need to dereference the pointer `n` later, which
would lead to a segfault when `n` is `NULL`. would lead to a segfault when `n` is `NULL`.
When these tests run, the following happens: When these tests run, the following happens:
@@ -441,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:
@@ -464,6 +366,7 @@ When invoked, the `RUN_ALL_TESTS()` macro:
If a fatal failure happens the subsequent steps will be skipped. If a fatal failure happens the subsequent steps will be skipped.
{: .callout .important}
> IMPORTANT: You must **not** ignore the return value of `RUN_ALL_TESTS()`, or > IMPORTANT: You must **not** ignore the return value of `RUN_ALL_TESTS()`, or
> you will get a compiler error. The rationale for this design is that the > you will get a compiler error. The rationale for this design is that the
> automated testing service determines whether a test has passed based on its > automated testing service determines whether a test has passed based on its
@@ -478,22 +381,31 @@ If a fatal failure happens the subsequent steps will be skipped.
## Writing the main() Function ## Writing the main() Function
Write your own main() function, which should return the value of Most users should _not_ need to write their own `main` function and instead link
with `gtest_main` (as opposed to with `gtest`), which defines a suitable entry
point. See the end of this section for details. The remainder of this section
should only apply when you need to do something custom before the tests run that
cannot be expressed within the framework of fixtures and test suites.
If you write your own `main` function, it should return the value of
`RUN_ALL_TESTS()`. `RUN_ALL_TESTS()`.
You can start from this boilerplate: You can start from this boilerplate:
```c++ ```c++
#include "this/package/foo.h" #include "this/package/foo.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace my {
namespace project {
namespace { namespace {
// The fixture for testing class Foo. // The fixture for testing class Foo.
class FooTest : public ::testing::Test { class FooTest : public ::testing::Test {
protected: protected:
// You can remove any or all of the following functions if its body // You can remove any or all of the following functions if their bodies would
// is empty. // be empty.
FooTest() { FooTest() {
// You can do set-up work for each test here. // You can do set-up work for each test here.
@@ -516,7 +428,8 @@ class FooTest : public ::testing::Test {
// before the destructor). // before the destructor).
} }
// Objects declared here can be used by all tests in the test suite for Foo. // Class members declared here can be used by all tests in the test suite
// for Foo.
}; };
// Tests that the Foo::Bar() method does Abc. // Tests that the Foo::Bar() method does Abc.
@@ -533,6 +446,8 @@ TEST_F(FooTest, DoesXyz) {
} }
} // namespace } // namespace
} // namespace project
} // namespace my
int main(int argc, char **argv) { int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
@@ -542,18 +457,19 @@ 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
in programs compiled in `UNICODE` mode as well. in programs compiled in `UNICODE` mode as well.
But maybe you think that writing all those main() functions is too much work? We But maybe you think that writing all those `main` functions is too much work? We
agree with you completely, and that's why Google Test provides a basic agree with you completely, and that's why Google Test provides a basic
implementation of main(). If it fits your needs, then just link your test with implementation of main(). If it fits your needs, then just link your test with
gtest\_main library and you are good to go. the `gtest_main` library and you are good to go.
{: .callout .note}
NOTE: `ParseGUnitFlags()` is deprecated in favor of `InitGoogleTest()`. NOTE: `ParseGUnitFlags()` is deprecated in favor of `InitGoogleTest()`.
## Known Limitations ## Known Limitations

View File

@@ -0,0 +1,146 @@
# Quickstart: Building with Bazel
This tutorial aims to get you up and running with GoogleTest using the Bazel
build system. If you're using GoogleTest for the first time or need a refresher,
we recommend this tutorial as a starting point.
## Prerequisites
To complete this tutorial, you'll need:
* A compatible operating system (e.g. Linux, macOS, Windows).
* A compatible C++ compiler that supports at least C++14.
* [Bazel](https://bazel.build/), the preferred build system used by the
GoogleTest team.
See [Supported Platforms](platforms.md) for more information about platforms
compatible with GoogleTest.
If you don't already have Bazel installed, see the
[Bazel installation guide](https://bazel.build/install).
{: .callout .note} Note: The terminal commands in this tutorial show a Unix
shell prompt, but the commands work on the Windows command line as well.
## Set up a Bazel workspace
A
[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
software you want to build. Each workspace directory has a text file named
`WORKSPACE` which may be empty, or may contain references to external
dependencies required to build the outputs.
First, create a directory for your workspace:
```
$ mkdir my_workspace && cd my_workspace
```
Next, youll create the `WORKSPACE` file to specify dependencies. A common and
recommended way to depend on GoogleTest is to use a
[Bazel external dependency](https://docs.bazel.build/versions/main/external.html)
via the
[`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
file named `WORKSPACE` with the following contents:
```
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/5ab508a01f9eb089207ee87fd547d290da39d015.zip"],
strip_prefix = "googletest-5ab508a01f9eb089207ee87fd547d290da39d015",
)
```
The above configuration declares a dependency on GoogleTest which is downloaded
as a ZIP archive from GitHub. In the above example,
`5ab508a01f9eb089207ee87fd547d290da39d015` is the Git commit hash of the
GoogleTest version to use; we recommend updating the hash often to point to the
latest version. Use a recent hash on the `main` branch.
Now you're ready to build C++ code that uses GoogleTest.
## Create and run a binary
With your Bazel workspace set up, you can now use GoogleTest code within your
own project.
As an example, create a file named `hello_test.cc` in your `my_workspace`
directory with the following contents:
```cpp
#include <gtest/gtest.h>
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}
```
GoogleTest provides [assertions](primer.md#assertions) that you use to test the
behavior of your code. The above sample includes the main GoogleTest header file
and demonstrates some basic assertions.
To build the code, create a file named `BUILD` in the same directory with the
following contents:
```
cc_test(
name = "hello_test",
size = "small",
srcs = ["hello_test.cc"],
deps = ["@com_google_googletest//:gtest_main"],
)
```
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`
file (`@com_google_googletest`). For more information about Bazel `BUILD` files,
see the
[Bazel C++ Tutorial](https://docs.bazel.build/versions/main/tutorial/cpp.html).
Now you can build and run your test:
<pre>
<strong>my_workspace$ bazel test --test_output=all //:hello_test</strong>
INFO: Analyzed target //:hello_test (26 packages loaded, 362 targets configured).
INFO: Found 1 test target...
INFO: From Testing //:hello_test:
==================== Test output for //:hello_test:
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from HelloTest
[ RUN ] HelloTest.BasicAssertions
[ OK ] HelloTest.BasicAssertions (0 ms)
[----------] 1 test from HelloTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
================================================================================
Target //:hello_test up-to-date:
bazel-bin/hello_test
INFO: Elapsed time: 4.190s, Critical Path: 3.05s
INFO: 27 processes: 8 internal, 19 linux-sandbox.
INFO: Build completed successfully, 27 total actions
//:hello_test PASSED in 0.1s
INFO: Build completed successfully, 27 total actions
</pre>
Congratulations! You've successfully built and run a test binary using
GoogleTest.
## Next steps
* [Check out the Primer](primer.md) to start learning how to write simple
tests.
* [See the code samples](samples.md) for more examples showing how to use a
variety of GoogleTest features.

View File

@@ -0,0 +1,156 @@
# Quickstart: Building with CMake
This tutorial aims to get you up and running with GoogleTest using CMake. If
you're using GoogleTest for the first time or need a refresher, we recommend
this tutorial as a starting point. If your project uses Bazel, see the
[Quickstart for Bazel](quickstart-bazel.md) instead.
## Prerequisites
To complete this tutorial, you'll need:
* A compatible operating system (e.g. Linux, macOS, Windows).
* A compatible C++ compiler that supports at least C++14.
* [CMake](https://cmake.org/) and a compatible build tool for building the
project.
* Compatible build tools include
[Make](https://www.gnu.org/software/make/),
[Ninja](https://ninja-build.org/), and others - see
[CMake Generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
for more information.
See [Supported Platforms](platforms.md) for more information about platforms
compatible with GoogleTest.
If you don't already have CMake installed, see the
[CMake installation guide](https://cmake.org/install).
{: .callout .note}
Note: The terminal commands in this tutorial show a Unix shell prompt, but the
commands work on the Windows command line as well.
## Set up a project
CMake uses a file named `CMakeLists.txt` to configure the build system for a
project. You'll use this file to set up your project and declare a dependency on
GoogleTest.
First, create a directory for your project:
```
$ mkdir my_project && cd my_project
```
Next, you'll create the `CMakeLists.txt` file and declare a dependency on
GoogleTest. There are many ways to express dependencies in the CMake ecosystem;
in this quickstart, you'll use the
[`FetchContent` CMake module](https://cmake.org/cmake/help/latest/module/FetchContent.html).
To do this, in your project directory (`my_project`), create a file named
`CMakeLists.txt` with the following contents:
```cmake
cmake_minimum_required(VERSION 3.14)
project(my_project)
# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
```
The above configuration declares a dependency on GoogleTest which is downloaded
from GitHub. In the above example, `03597a01ee50ed33e9dfd640b249b4be3799d395` is
the Git commit hash of the GoogleTest version to use; we recommend updating the
hash often to point to the latest version.
For more information about how to create `CMakeLists.txt` files, see the
[CMake Tutorial](https://cmake.org/cmake/help/latest/guide/tutorial/index.html).
## Create and run a binary
With GoogleTest declared as a dependency, you can use GoogleTest code within
your own project.
As an example, create a file named `hello_test.cc` in your `my_project`
directory with the following contents:
```cpp
#include <gtest/gtest.h>
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}
```
GoogleTest provides [assertions](primer.md#assertions) that you use to test the
behavior of your code. The above sample includes the main GoogleTest header file
and demonstrates some basic assertions.
To build the code, add the following to the end of your `CMakeLists.txt` file:
```cmake
enable_testing()
add_executable(
hello_test
hello_test.cc
)
target_link_libraries(
hello_test
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(hello_test)
```
The above configuration enables testing in CMake, declares the C++ test binary
you want to build (`hello_test`), and links it to GoogleTest (`gtest_main`). The
last two lines enable CMake's test runner to discover the tests included in the
binary, using the
[`GoogleTest` CMake module](https://cmake.org/cmake/help/git-stage/module/GoogleTest.html).
Now you can build and run your test:
<pre>
<strong>my_project$ cmake -S . -B build</strong>
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
...
-- Build files have been written to: .../my_project/build
<strong>my_project$ cmake --build build</strong>
Scanning dependencies of target gtest
...
[100%] Built target gmock_main
<strong>my_project$ cd build && ctest</strong>
Test project .../my_project/build
Start 1: HelloTest.BasicAssertions
1/1 Test #1: HelloTest.BasicAssertions ........ Passed 0.00 sec
100% tests passed, 0 tests failed out of 1
Total Test time (real) = 0.01 sec
</pre>
Congratulations! You've successfully built and run a test binary using
GoogleTest.
## Next steps
* [Check out the Primer](primer.md) to start learning how to write simple
tests.
* [See the code samples](samples.md) for more examples showing how to use a
variety of GoogleTest features.

View File

@@ -0,0 +1,115 @@
# Actions Reference
[**Actions**](../gmock_for_dummies.md#actions-what-should-it-do) specify what a
mock function should do when invoked. This page lists the built-in actions
provided by GoogleTest. All actions are defined in the `::testing` namespace.
## Returning a Value
| Action | Description |
| :-------------------------------- | :-------------------------------------------- |
| `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. |
| `ReturnArg<N>()` | Return the `N`-th (0-based) argument. |
| `ReturnNew<T>(a1, ..., ak)` | Return `new T(a1, ..., ak)`; a different object is created each time. |
| `ReturnNull()` | Return a null pointer. |
| `ReturnPointee(ptr)` | Return the value pointed to by `ptr`. |
| `ReturnRef(variable)` | Return a reference to `variable`. |
| `ReturnRefOfCopy(value)` | Return a reference to a copy of `value`; the copy lives as long as the action. |
| `ReturnRoundRobin({a1, ..., ak})` | Each call will return the next `ai` in the list, starting at the beginning when the end of the list is reached. |
## Side Effects
| Action | Description |
| :--------------------------------- | :-------------------------------------- |
| `Assign(&variable, value)` | Assign `value` to variable. |
| `DeleteArg<N>()` | Delete the `N`-th (0-based) argument, which must be a pointer. |
| `SaveArg<N>(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. |
| `SaveArgPointee<N>(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. |
| `SetArgReferee<N>(value)` | Assign `value` to the variable referenced by the `N`-th (0-based) argument. |
| `SetArgPointee<N>(value)` | Assign `value` to the variable pointed by the `N`-th (0-based) argument. |
| `SetArgumentPointee<N>(value)` | Same as `SetArgPointee<N>(value)`. Deprecated. Will be removed in v1.7.0. |
| `SetArrayArgument<N>(first, last)` | Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range. |
| `SetErrnoAndReturn(error, value)` | Set `errno` to `error` and return `value`. |
| `Throw(exception)` | Throws the given exception, which can be any copyable value. Available since v1.1.0. |
## Using a Function, Functor, or Lambda as an Action
In the following, by "callable" we mean a free function, `std::function`,
functor, or lambda.
| Action | Description |
| :---------------------------------- | :------------------------------------- |
| `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(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(object_pointer, &class::method)` | Invoke the method on the object, which takes no arguments. |
| `InvokeArgument<N>(arg1, arg2, ..., argk)` | Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments. |
The return value of the invoked function is used as the return value of the
action.
When defining a callable to be used with `Invoke*()`, you can declare any unused
parameters as `Unused`:
```cpp
using ::testing::Invoke;
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
...
EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
```
`Invoke(callback)` and `InvokeWithoutArgs(callback)` take ownership of
`callback`, which must be permanent. The type of `callback` must be a base
callback type instead of a derived one, e.g.
```cpp
BlockingClosure* done = new BlockingClosure;
... Invoke(done) ...; // This won't compile!
Closure* done2 = new BlockingClosure;
... Invoke(done2) ...; // This works.
```
In `InvokeArgument<N>(...)`, if an argument needs to be passed by reference,
wrap it inside `std::ref()`. For example,
```cpp
using ::testing::InvokeArgument;
...
InvokeArgument<2>(5, string("Hi"), std::ref(foo))
```
calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by
value, and `foo` by reference.
## Default Action
| Action | Description |
| :------------ | :----------------------------------------------------- |
| `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). |
{: .callout .note}
**Note:** due to technical reasons, `DoDefault()` cannot be used inside a
composite action - trying to do so will result in a run-time error.
## 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. |
| `IgnoreResult(a)` | Perform action `a` and ignore its result. `a` must not return void. |
| `WithArg<N>(a)` | Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. |
| `WithArgs<N1, N2, ..., Nk>(a)` | Pass the selected (0-based) arguments of the mock function to action `a` and perform it. |
| `WithoutArgs(a)` | Perform action `a` without any arguments. |
## 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_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_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. |
The `ACTION*` macros cannot be used inside a function or class.

View File

@@ -0,0 +1,633 @@
# Assertions Reference
This page lists the assertion macros provided by GoogleTest for verifying code
behavior. To use them, include the header `gtest/gtest.h`.
The majority of the macros listed below come as a pair with an `EXPECT_` variant
and an `ASSERT_` variant. Upon failure, `EXPECT_` macros generate nonfatal
failures and allow the current function to continue running, while `ASSERT_`
macros generate fatal failures and abort the current function.
All assertion macros support streaming a custom failure message into them with
the `<<` operator, for example:
```cpp
EXPECT_TRUE(my_condition) << "My condition is not true";
```
Anything that can be streamed to an `ostream` can be streamed to an assertion
macro—in particular, C strings and string objects. If a wide string (`wchar_t*`,
`TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is streamed to an
assertion, it will be translated to UTF-8 when printed.
## Explicit Success and Failure {#success-failure}
The assertions in this section generate a success or failure directly instead of
testing a value or expression. These are useful when control flow, rather than a
Boolean expression, determines the test's success or failure, as shown by the
following example:
```c++
switch(expression) {
case 1:
... some checks ...
case 2:
... some other checks ...
default:
FAIL() << "We shouldn't get here.";
}
```
### SUCCEED {#SUCCEED}
`SUCCEED()`
Generates a success. This *does not* make the overall test succeed. A test is
considered successful only if none of its assertions fail during its execution.
The `SUCCEED` assertion is purely documentary and currently doesn't generate any
user-visible output. However, we may add `SUCCEED` messages to GoogleTest output
in the future.
### FAIL {#FAIL}
`FAIL()`
Generates a fatal failure, which returns from the current function.
Can only be used in functions that return `void`. See
[Assertion Placement](../advanced.md#assertion-placement) for more information.
### ADD_FAILURE {#ADD_FAILURE}
`ADD_FAILURE()`
Generates a nonfatal failure, which allows the current function to continue
running.
### ADD_FAILURE_AT {#ADD_FAILURE_AT}
`ADD_FAILURE_AT(`*`file_path`*`,`*`line_number`*`)`
Generates a nonfatal failure at the file and line number specified.
## Generalized Assertion {#generalized}
The following assertion allows [matchers](matchers.md) to be used to verify
values.
### EXPECT_THAT {#EXPECT_THAT}
`EXPECT_THAT(`*`value`*`,`*`matcher`*`)` \
`ASSERT_THAT(`*`value`*`,`*`matcher`*`)`
Verifies that *`value`* matches the [matcher](matchers.md) *`matcher`*.
For example, the following code verifies that the string `value1` starts with
`"Hello"`, `value2` matches a regular expression, and `value3` is between 5 and
10:
```cpp
#include "gmock/gmock.h"
using ::testing::AllOf;
using ::testing::Gt;
using ::testing::Lt;
using ::testing::MatchesRegex;
using ::testing::StartsWith;
...
EXPECT_THAT(value1, StartsWith("Hello"));
EXPECT_THAT(value2, MatchesRegex("Line \\d+"));
ASSERT_THAT(value3, AllOf(Gt(5), Lt(10)));
```
Matchers enable assertions of this form to read like English and generate
informative failure messages. For example, if the above assertion on `value1`
fails, the resulting message will be similar to the following:
```
Value of: value1
Actual: "Hi, world!"
Expected: starts with "Hello"
```
GoogleTest provides a built-in library of matchers—see the
[Matchers Reference](matchers.md). It is also possible to write your own
matchers—see [Writing New Matchers Quickly](../gmock_cook_book.md#NewMatchers).
The use of matchers makes `EXPECT_THAT` a powerful, extensible assertion.
*The idea for this assertion was borrowed from Joe Walnes' Hamcrest project,
which adds `assertThat()` to JUnit.*
## Boolean Conditions {#boolean}
The following assertions test Boolean conditions.
### EXPECT_TRUE {#EXPECT_TRUE}
`EXPECT_TRUE(`*`condition`*`)` \
`ASSERT_TRUE(`*`condition`*`)`
Verifies that *`condition`* is true.
### EXPECT_FALSE {#EXPECT_FALSE}
`EXPECT_FALSE(`*`condition`*`)` \
`ASSERT_FALSE(`*`condition`*`)`
Verifies that *`condition`* is false.
## Binary Comparison {#binary-comparison}
The following assertions compare two values. The value arguments must be
comparable by the assertion's comparison operator, otherwise a compiler error
will result.
If an argument supports the `<<` operator, it will be called to print the
argument when the assertion fails. Otherwise, GoogleTest will attempt to print
them in the best way it can—see
[Teaching GoogleTest How to Print Your Values](../advanced.md#teaching-googletest-how-to-print-your-values).
Arguments are always evaluated exactly once, so it's OK for the arguments to
have side effects. However, the argument evaluation order is undefined and
programs should not depend on any particular argument evaluation order.
These assertions work with both narrow and wide string objects (`string` and
`wstring`).
See also the [Floating-Point Comparison](#floating-point) assertions to compare
floating-point numbers and avoid problems caused by rounding.
### EXPECT_EQ {#EXPECT_EQ}
`EXPECT_EQ(`*`val1`*`,`*`val2`*`)` \
`ASSERT_EQ(`*`val1`*`,`*`val2`*`)`
Verifies that *`val1`*`==`*`val2`*.
Does pointer equality on pointers. If used on two C strings, it tests if they
are in the same memory location, not if they have the same value. Use
[`EXPECT_STREQ`](#EXPECT_STREQ) to compare C strings (e.g. `const char*`) by
value.
When comparing a pointer to `NULL`, use `EXPECT_EQ(`*`ptr`*`, nullptr)` instead
of `EXPECT_EQ(`*`ptr`*`, NULL)`.
### EXPECT_NE {#EXPECT_NE}
`EXPECT_NE(`*`val1`*`,`*`val2`*`)` \
`ASSERT_NE(`*`val1`*`,`*`val2`*`)`
Verifies that *`val1`*`!=`*`val2`*.
Does pointer equality on pointers. If used on two C strings, it tests if they
are in different memory locations, not if they have different values. Use
[`EXPECT_STRNE`](#EXPECT_STRNE) to compare C strings (e.g. `const char*`) by
value.
When comparing a pointer to `NULL`, use `EXPECT_NE(`*`ptr`*`, nullptr)` instead
of `EXPECT_NE(`*`ptr`*`, NULL)`.
### EXPECT_LT {#EXPECT_LT}
`EXPECT_LT(`*`val1`*`,`*`val2`*`)` \
`ASSERT_LT(`*`val1`*`,`*`val2`*`)`
Verifies that *`val1`*`<`*`val2`*.
### EXPECT_LE {#EXPECT_LE}
`EXPECT_LE(`*`val1`*`,`*`val2`*`)` \
`ASSERT_LE(`*`val1`*`,`*`val2`*`)`
Verifies that *`val1`*`<=`*`val2`*.
### EXPECT_GT {#EXPECT_GT}
`EXPECT_GT(`*`val1`*`,`*`val2`*`)` \
`ASSERT_GT(`*`val1`*`,`*`val2`*`)`
Verifies that *`val1`*`>`*`val2`*.
### EXPECT_GE {#EXPECT_GE}
`EXPECT_GE(`*`val1`*`,`*`val2`*`)` \
`ASSERT_GE(`*`val1`*`,`*`val2`*`)`
Verifies that *`val1`*`>=`*`val2`*.
## String Comparison {#c-strings}
The following assertions compare two **C strings**. To compare two `string`
objects, use [`EXPECT_EQ`](#EXPECT_EQ) or [`EXPECT_NE`](#EXPECT_NE) instead.
These assertions also accept wide C strings (`wchar_t*`). If a comparison of two
wide strings fails, their values will be printed as UTF-8 narrow strings.
To compare a C string with `NULL`, use `EXPECT_EQ(`*`c_string`*`, nullptr)` or
`EXPECT_NE(`*`c_string`*`, nullptr)`.
### EXPECT_STREQ {#EXPECT_STREQ}
`EXPECT_STREQ(`*`str1`*`,`*`str2`*`)` \
`ASSERT_STREQ(`*`str1`*`,`*`str2`*`)`
Verifies that the two C strings *`str1`* and *`str2`* have the same contents.
### EXPECT_STRNE {#EXPECT_STRNE}
`EXPECT_STRNE(`*`str1`*`,`*`str2`*`)` \
`ASSERT_STRNE(`*`str1`*`,`*`str2`*`)`
Verifies that the two C strings *`str1`* and *`str2`* have different contents.
### EXPECT_STRCASEEQ {#EXPECT_STRCASEEQ}
`EXPECT_STRCASEEQ(`*`str1`*`,`*`str2`*`)` \
`ASSERT_STRCASEEQ(`*`str1`*`,`*`str2`*`)`
Verifies that the two C strings *`str1`* and *`str2`* have the same contents,
ignoring case.
### EXPECT_STRCASENE {#EXPECT_STRCASENE}
`EXPECT_STRCASENE(`*`str1`*`,`*`str2`*`)` \
`ASSERT_STRCASENE(`*`str1`*`,`*`str2`*`)`
Verifies that the two C strings *`str1`* and *`str2`* have different contents,
ignoring case.
## Floating-Point Comparison {#floating-point}
The following assertions compare two floating-point values.
Due to rounding errors, it is very unlikely that two floating-point values will
match exactly, so `EXPECT_EQ` is not suitable. In general, for floating-point
comparison to make sense, the user needs to carefully choose the error bound.
GoogleTest also provides assertions that use a default error bound based on
Units in the Last Place (ULPs). To learn more about ULPs, see the article
[Comparing Floating Point Numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
### EXPECT_FLOAT_EQ {#EXPECT_FLOAT_EQ}
`EXPECT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)` \
`ASSERT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)`
Verifies that the two `float` values *`val1`* and *`val2`* are approximately
equal, to within 4 ULPs from each other.
### EXPECT_DOUBLE_EQ {#EXPECT_DOUBLE_EQ}
`EXPECT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)` \
`ASSERT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)`
Verifies that the two `double` values *`val1`* and *`val2`* are approximately
equal, to within 4 ULPs from each other.
### EXPECT_NEAR {#EXPECT_NEAR}
`EXPECT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)` \
`ASSERT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)`
Verifies that the difference between *`val1`* and *`val2`* does not exceed the
absolute error bound *`abs_error`*.
## Exception Assertions {#exceptions}
The following assertions verify that a piece of code throws, or does not throw,
an exception. Usage requires exceptions to be enabled in the build environment.
Note that the piece of code under test can be a compound statement, for example:
```cpp
EXPECT_NO_THROW({
int n = 5;
DoSomething(&n);
});
```
### EXPECT_THROW {#EXPECT_THROW}
`EXPECT_THROW(`*`statement`*`,`*`exception_type`*`)` \
`ASSERT_THROW(`*`statement`*`,`*`exception_type`*`)`
Verifies that *`statement`* throws an exception of type *`exception_type`*.
### EXPECT_ANY_THROW {#EXPECT_ANY_THROW}
`EXPECT_ANY_THROW(`*`statement`*`)` \
`ASSERT_ANY_THROW(`*`statement`*`)`
Verifies that *`statement`* throws an exception of any type.
### EXPECT_NO_THROW {#EXPECT_NO_THROW}
`EXPECT_NO_THROW(`*`statement`*`)` \
`ASSERT_NO_THROW(`*`statement`*`)`
Verifies that *`statement`* does not throw any exception.
## Predicate Assertions {#predicates}
The following assertions enable more complex predicates to be verified while
printing a more clear failure message than if `EXPECT_TRUE` were used alone.
### EXPECT_PRED* {#EXPECT_PRED}
`EXPECT_PRED1(`*`pred`*`,`*`val1`*`)` \
`EXPECT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \
`EXPECT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
`EXPECT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \
`EXPECT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
`ASSERT_PRED1(`*`pred`*`,`*`val1`*`)` \
`ASSERT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \
`ASSERT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
`ASSERT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \
`ASSERT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
Verifies that the predicate *`pred`* returns `true` when passed the given values
as arguments.
The parameter *`pred`* is a function or functor that accepts as many arguments
as the corresponding macro accepts values. If *`pred`* returns `true` for the
given arguments, the assertion succeeds, otherwise the assertion fails.
When the assertion fails, it prints the value of each argument. Arguments are
always evaluated exactly once.
As an example, see the following code:
```cpp
// Returns true if m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
...
const int a = 3;
const int b = 4;
const int c = 10;
...
EXPECT_PRED2(MutuallyPrime, a, b); // Succeeds
EXPECT_PRED2(MutuallyPrime, b, c); // Fails
```
In the above example, the first assertion succeeds, and the second fails with
the following message:
```
MutuallyPrime(b, c) is false, where
b is 4
c is 10
```
Note that if the given predicate is an overloaded function or a function
template, the assertion macro might not be able to determine which version to
use, and it might be necessary to explicitly specify the type of the function.
For example, for a Boolean function `IsPositive()` overloaded to take either a
single `int` or `double` argument, it would be necessary to write one of the
following:
```cpp
EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
EXPECT_PRED1(static_cast<bool (*)(double)>(IsPositive), 3.14);
```
Writing simply `EXPECT_PRED1(IsPositive, 5);` would result in a compiler error.
Similarly, to use a template function, specify the template arguments:
```cpp
template <typename T>
bool IsNegative(T x) {
return x < 0;
}
...
EXPECT_PRED1(IsNegative<int>, -5); // Must specify type for IsNegative
```
If a template has multiple parameters, wrap the predicate in parentheses so the
macro arguments are parsed correctly:
```cpp
ASSERT_PRED2((MyPredicate<int, int>), 5, 0);
```
### EXPECT_PRED_FORMAT* {#EXPECT_PRED_FORMAT}
`EXPECT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \
`EXPECT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \
`EXPECT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
`EXPECT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)`
\
`EXPECT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
`ASSERT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \
`ASSERT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \
`ASSERT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
`ASSERT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)`
\
`ASSERT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
Verifies that the predicate *`pred_formatter`* succeeds when passed the given
values as arguments.
The parameter *`pred_formatter`* is a *predicate-formatter*, which is a function
or functor with the signature:
```cpp
testing::AssertionResult PredicateFormatter(const char* expr1,
const char* expr2,
...
const char* exprn,
T1 val1,
T2 val2,
...
Tn valn);
```
where *`val1`*, *`val2`*, ..., *`valn`* are the values of the predicate
arguments, and *`expr1`*, *`expr2`*, ..., *`exprn`* are the corresponding
expressions as they appear in the source code. The types `T1`, `T2`, ..., `Tn`
can be either value types or reference types; if an argument has type `T`, it
can be declared as either `T` or `const T&`, whichever is appropriate. For more
about the return type `testing::AssertionResult`, see
[Using a Function That Returns an AssertionResult](../advanced.md#using-a-function-that-returns-an-assertionresult).
As an example, see the following code:
```cpp
// Returns the smallest prime common divisor of m and n,
// or 1 when m and n are mutually prime.
int SmallestPrimeCommonDivisor(int m, int n) { ... }
// Returns true if m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
// A predicate-formatter for asserting that two integers are mutually prime.
testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
const char* n_expr,
int m,
int n) {
if (MutuallyPrime(m, n)) return testing::AssertionSuccess();
return testing::AssertionFailure() << m_expr << " and " << n_expr
<< " (" << m << " and " << n << ") are not mutually prime, "
<< "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n);
}
...
const int a = 3;
const int b = 4;
const int c = 10;
...
EXPECT_PRED_FORMAT2(AssertMutuallyPrime, a, b); // Succeeds
EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c); // Fails
```
In the above example, the final assertion fails and the predicate-formatter
produces the following failure message:
```
b and c (4 and 10) are not mutually prime, as they have a common divisor 2
```
## Windows HRESULT Assertions {#HRESULT}
The following assertions test for `HRESULT` success or failure. For example:
```cpp
CComPtr<IShellDispatch2> shell;
ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
CComVariant empty;
ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
```
The generated output contains the human-readable error message associated with
the returned `HRESULT` code.
### EXPECT_HRESULT_SUCCEEDED {#EXPECT_HRESULT_SUCCEEDED}
`EXPECT_HRESULT_SUCCEEDED(`*`expression`*`)` \
`ASSERT_HRESULT_SUCCEEDED(`*`expression`*`)`
Verifies that *`expression`* is a success `HRESULT`.
### EXPECT_HRESULT_FAILED {#EXPECT_HRESULT_FAILED}
`EXPECT_HRESULT_FAILED(`*`expression`*`)` \
`EXPECT_HRESULT_FAILED(`*`expression`*`)`
Verifies that *`expression`* is a failure `HRESULT`.
## Death Assertions {#death}
The following assertions verify that a piece of code causes the process to
terminate. For context, see [Death Tests](../advanced.md#death-tests).
These assertions spawn a new process and execute the code under test in that
process. How that happens depends on the platform and the variable
`::testing::GTEST_FLAG(death_test_style)`, which is initialized from the
command-line flag `--gtest_death_test_style`.
* On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the
child, after which:
* If the variable's value is `"fast"`, the death test statement is
immediately executed.
* If the variable's value is `"threadsafe"`, the child process re-executes
the unit test binary just as it was originally invoked, but with some
extra flags to cause just the single death test under consideration to
be run.
* On Windows, the child is spawned using the `CreateProcess()` API, and
re-executes the binary to cause just the single death test under
consideration to be run - much like the `"threadsafe"` mode on POSIX.
Other values for the variable are illegal and will cause the death test to fail.
Currently, the flag's default value is
**`"fast"`**.
If the death test statement runs to completion without dying, the child process
will nonetheless terminate, and the assertion fails.
Note that the piece of code under test can be a compound statement, for example:
```cpp
EXPECT_DEATH({
int n = 5;
DoSomething(&n);
}, "Error on line .* of DoSomething()");
```
### EXPECT_DEATH {#EXPECT_DEATH}
`EXPECT_DEATH(`*`statement`*`,`*`matcher`*`)` \
`ASSERT_DEATH(`*`statement`*`,`*`matcher`*`)`
Verifies that *`statement`* causes the process to terminate with a nonzero exit
status and produces `stderr` output that matches *`matcher`*.
The parameter *`matcher`* is either a [matcher](matchers.md) for a `const
std::string&`, or a regular expression (see
[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare
string *`s`* (with no matcher) is treated as
[`ContainsRegex(s)`](matchers.md#string-matchers), **not**
[`Eq(s)`](matchers.md#generic-comparison).
For example, the following code verifies that calling `DoSomething(42)` causes
the process to die with an error message that contains the text `My error`:
```cpp
EXPECT_DEATH(DoSomething(42), "My error");
```
### EXPECT_DEATH_IF_SUPPORTED {#EXPECT_DEATH_IF_SUPPORTED}
`EXPECT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)` \
`ASSERT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)`
If death tests are supported, behaves the same as
[`EXPECT_DEATH`](#EXPECT_DEATH). Otherwise, verifies nothing.
### EXPECT_DEBUG_DEATH {#EXPECT_DEBUG_DEATH}
`EXPECT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)` \
`ASSERT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)`
In debug mode, behaves the same as [`EXPECT_DEATH`](#EXPECT_DEATH). When not in
debug mode (i.e. `NDEBUG` is defined), just executes *`statement`*.
### EXPECT_EXIT {#EXPECT_EXIT}
`EXPECT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)` \
`ASSERT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)`
Verifies that *`statement`* causes the process to terminate with an exit status
that satisfies *`predicate`*, and produces `stderr` output that matches
*`matcher`*.
The parameter *`predicate`* is a function or functor that accepts an `int` exit
status and returns a `bool`. GoogleTest provides two predicates to handle common
cases:
```cpp
// Returns true if the program exited normally with the given exit status code.
::testing::ExitedWithCode(exit_code);
// Returns true if the program was killed by the given signal.
// Not available on Windows.
::testing::KilledBySignal(signal_number);
```
The parameter *`matcher`* is either a [matcher](matchers.md) for a `const
std::string&`, or a regular expression (see
[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare
string *`s`* (with no matcher) is treated as
[`ContainsRegex(s)`](matchers.md#string-matchers), **not**
[`Eq(s)`](matchers.md#generic-comparison).
For example, the following code verifies that calling `NormalExit()` causes the
process to print a message containing the text `Success` to `stderr` and exit
with exit status code 0:
```cpp
EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
```

View File

@@ -0,0 +1,290 @@
# Matchers Reference
A **matcher** matches a *single* argument. You can use it inside `ON_CALL()` or
`EXPECT_CALL()`, or use it to validate a value directly using two macros:
| Macro | Description |
| :----------------------------------- | :------------------------------------ |
| `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. |
{: .callout .warning}
**WARNING:** Equality matching via `EXPECT_THAT(actual_value, expected_value)`
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,
expected_value)`.
Built-in matchers (where `argument` is the function argument, e.g.
`actual_value` in the example above, or when used in the context of
`EXPECT_CALL(mock_object, method(matchers))`, the arguments of `method`) are
divided into several categories. All matchers are defined in the `::testing`
namespace unless otherwise noted.
## Wildcard
Matcher | Description
:-------------------------- | :-----------------------------------------------
`_` | `argument` can be any value of the correct type.
`A<type>()` or `An<type>()` | `argument` can be any value of type `type`.
## Generic Comparison
| Matcher | Description |
| :--------------------- | :-------------------------------------------------- |
| `Eq(value)` or `value` | `argument == value` |
| `Ge(value)` | `argument >= value` |
| `Gt(value)` | `argument > value` |
| `Le(value)` | `argument <= value` |
| `Lt(value)` | `argument < value` |
| `Ne(value)` | `argument != value` |
| `IsFalse()` | `argument` evaluates to `false` in a Boolean context. |
| `IsTrue()` | `argument` evaluates to `true` in a Boolean context. |
| `IsNull()` | `argument` is a `NULL` pointer (raw or smart). |
| `NotNull()` | `argument` is a non-null pointer (raw or smart). |
| `Optional(m)` | `argument` is `optional<>` that contains a value matching `m`. (For testing whether an `optional<>` is set, check for equality with `nullopt`. You may need to use `Eq(nullopt)` if the inner type doesn't have `==`.)|
| `VariantWith<T>(m)` | `argument` is `variant<>` that holds the alternative of type T with a value matching `m`. |
| `Ref(variable)` | `argument` is a reference to `variable`. |
| `TypedEq<type>(value)` | `argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded. |
Except `Ref()`, these matchers make a *copy* of `value` in case it's modified or
destructed later. If the compiler complains that `value` doesn't have a public
copy constructor, try wrap it in `std::ref()`, e.g.
`Eq(std::ref(non_copyable_value))`. If you do that, make sure
`non_copyable_value` is not changed afterwards, or the meaning of your matcher
will be changed.
`IsTrue` and `IsFalse` are useful when you need to use a matcher, or for types
that can be explicitly converted to Boolean, but are not implicitly converted to
Boolean. In other cases, you can use the basic
[`EXPECT_TRUE` and `EXPECT_FALSE`](assertions.md#boolean) assertions.
## Floating-Point Matchers {#FpMatchers}
| Matcher | Description |
| :------------------------------- | :--------------------------------- |
| `DoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal. |
| `FloatEq(a_float)` | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. |
| `NanSensitiveDoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. |
| `NanSensitiveFloatEq(a_float)` | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. |
| `IsNan()` | `argument` is any floating-point type with a NaN value. |
The above matchers use ULP-based comparison (the same as used in googletest).
They automatically pick a reasonable error bound based on the absolute value of
the expected value. `DoubleEq()` and `FloatEq()` conform to the IEEE standard,
which requires comparing two NaNs for equality to return false. The
`NanSensitive*` version instead treats two NaNs as equal, which is often what a
user wants.
| Matcher | Description |
| :------------------------------------------------ | :----------------------- |
| `DoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
| `FloatNear(a_float, max_abs_error)` | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
| `NanSensitiveDoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
| `NanSensitiveFloatNear(a_float, max_abs_error)` | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
## String Matchers
The `argument` can be either a C string or a C++ string object:
| Matcher | Description |
| :---------------------- | :------------------------------------------------- |
| `ContainsRegex(string)` | `argument` matches the given regular expression. |
| `EndsWith(suffix)` | `argument` ends with string `suffix`. |
| `HasSubstr(string)` | `argument` contains `string` as a sub-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. |
| `StartsWith(prefix)` | `argument` starts with string `prefix`. |
| `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. |
| `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. |
| `StrEq(string)` | `argument` is 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
use the regular expression syntax defined
[here](../advanced.md#regular-expression-syntax). All of these matchers, except
`ContainsRegex()` and `MatchesRegex()` work for wide strings as well.
## Container Matchers
Most STL-style containers support `==`, so you can use `Eq(expected_container)`
or simply `expected_container` to match a container exactly. If you want to
write the elements in-line, match them more flexibly, or get more informative
messages, you can use:
| Matcher | Description |
| :---------------------------------------- | :------------------------------- |
| `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. |
| `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. |
| `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. |
| `IsEmpty()` | `argument` is an empty container (`container.empty()`). |
| `IsSubsetOf({e0, e1, ..., en})`, `IsSubsetOf(a_container)`, `IsSubsetOf(begin, end)`, `IsSubsetOf(array)`, or `IsSubsetOf(array, count)` | `argument` matches `UnorderedElementsAre(x0, x1, ..., xk)` for some subset `{x0, x1, ..., xk}` of the expected matchers. |
| `IsSupersetOf({e0, e1, ..., en})`, `IsSupersetOf(a_container)`, `IsSupersetOf(begin, end)`, `IsSupersetOf(array)`, or `IsSupersetOf(array, count)` | Some subset of `argument` matches `UnorderedElementsAre(`expected matchers`)`. |
| `Pointwise(m, container)`, `Pointwise(m, {e0, e1, ..., en})` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. |
| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. |
| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under *some* permutation of the elements, each element matches an `ei` (for a different `i`), which can be a value or a matcher. |
| `UnorderedElementsAreArray({e0, e1, ..., en})`, `UnorderedElementsAreArray(a_container)`, `UnorderedElementsAreArray(begin, end)`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
| `UnorderedPointwise(m, container)`, `UnorderedPointwise(m, {e0, e1, ..., en})` | Like `Pointwise(m, container)`, but ignores the order of elements. |
| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements 1, 2, and 3, ignoring order. |
| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. |
**Notes:**
* These matchers can also match:
1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`),
and
2. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer,
int len)` -- see [Multi-argument Matchers](#MultiArgMatchers)).
* The array being matched may be multi-dimensional (i.e. its elements can be
arrays).
* `m` in `Pointwise(m, ...)` and `UnorderedPointwise(m, ...)` should be a
matcher for `::std::tuple<T, U>` where `T` and `U` are the element type of
the actual container and the expected container, respectively. For example,
to compare two `Foo` containers where `Foo` doesn't support `operator==`,
one might write:
```cpp
MATCHER(FooEq, "") {
return std::get<0>(arg).Equals(std::get<1>(arg));
}
...
EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
```
## Member Matchers
| Matcher | Description |
| :------------------------------ | :----------------------------------------- |
| `Field(&class::field, m)` | `argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. |
| `Field(field_name, &class::field, m)` | The same as the two-parameter version, but provides a better error message. |
| `Key(e)` | `argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`. |
| `Pair(m1, m2)` | `argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. |
| `FieldsAre(m...)` | `argument` is a compatible object where each field matches piecewise with the matchers `m...`. A compatible object is any that supports the `std::tuple_size<Obj>`+`get<I>(obj)` protocol. In C++17 and up this also supports types compatible with structured bindings, like aggregates. |
| `Property(&class::property, m)` | `argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. The method `property()` must take no argument and be declared as `const`. |
| `Property(property_name, &class::property, m)` | The same as the two-parameter version, but provides a better error message.
**Notes:**
* You can use `FieldsAre()` to match any type that supports structured
bindings, such as `std::tuple`, `std::pair`, `std::array`, and aggregate
types. For example:
```cpp
std::tuple<int, std::string> my_tuple{7, "hello world"};
EXPECT_THAT(my_tuple, FieldsAre(Ge(0), HasSubstr("hello")));
struct MyStruct {
int value = 42;
std::string greeting = "aloha";
};
MyStruct s;
EXPECT_THAT(s, FieldsAre(42, "aloha"));
```
* Don't use `Property()` against member functions that you do not own, because
taking addresses of functions is fragile and generally not part of the
contract of the function.
## Matching the Result of a Function, Functor, or Callback
| Matcher | Description |
| :--------------- | :------------------------------------------------ |
| `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
| Matcher | Description |
| :------------------------ | :---------------------------------------------- |
| `Address(m)` | the result of `std::addressof(argument)` matches `m`. |
| `Pointee(m)` | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. |
| `Pointer(m)` | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. |
| `WhenDynamicCastTo<T>(m)` | when `argument` is passed through `dynamic_cast<T>()`, it matches matcher `m`. |
## Multi-argument Matchers {#MultiArgMatchers}
Technically, all matchers match a *single* value. A "multi-argument" matcher is
just one that matches a *tuple*. The following matchers can be used to match a
tuple `(x, y)`:
Matcher | Description
:------ | :----------
`Eq()` | `x == y`
`Ge()` | `x >= y`
`Gt()` | `x > y`
`Le()` | `x <= y`
`Lt()` | `x < y`
`Ne()` | `x != y`
You can use the following selectors to pick a subset of the arguments (or
reorder them) to participate in the matching:
| Matcher | Description |
| :------------------------- | :---------------------------------------------- |
| `AllArgs(m)` | Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`. |
| `Args<N1, N2, ..., Nk>(m)` | The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`. |
## Composite Matchers
You can make a matcher from one or more other matchers:
| Matcher | Description |
| :------------------------------- | :-------------------------------------- |
| `AllOf(m1, m2, ..., mn)` | `argument` matches all of the matchers `m1` to `mn`. |
| `AllOfArray({m0, m1, ..., mn})`, `AllOfArray(a_container)`, `AllOfArray(begin, end)`, `AllOfArray(array)`, or `AllOfArray(array, count)` | The same as `AllOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
| `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. |
| `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
| Matcher | Description |
| :---------------------- | :------------------------------------ |
| `MatcherCast<T>(m)` | casts matcher `m` to type `Matcher<T>`. |
| `SafeMatcherCast<T>(m)` | [safely casts](../gmock_cook_book.md#SafeMatcherCast) matcher `m` to type `Matcher<T>`. |
| `Truly(predicate)` | `predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor. |
`AddressSatisfies(callback)` and `Truly(callback)` take ownership of `callback`,
which must be a permanent callback.
## Using Matchers as Predicates {#MatchersAsPredicatesCheat}
| Matcher | Description |
| :---------------------------- | :------------------------------------------ |
| `Matches(m)(value)` | evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor. |
| `ExplainMatchResult(m, value, result_listener)` | evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. |
| `Value(value, m)` | evaluates to `true` if `value` matches `m`. |
## Defining Matchers
| Macro | Description |
| :----------------------------------- | :------------------------------------ |
| `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_P2(IsBetween, a, b, absl::StrCat(negation ? "isn't" : "is", " between ", PrintToString(a), " and ", PrintToString(b))) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. |
**Notes:**
1. The `MATCHER*` macros cannot be used inside a function or class.
2. The matcher body must be *purely functional* (i.e. it cannot have any side
effect, and the result must not depend on anything other than the value
being matched and the matcher parameters).
3. You can use `PrintToString(x)` to convert a value `x` of any type to a
string.
4. You can use `ExplainMatchResult()` in a custom matcher to wrap another
matcher, for example:
```cpp
MATCHER_P(NestedPropertyMatches, matcher, "") {
return ExplainMatchResult(matcher, arg.nested().property(), result_listener);
}
```

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