From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/c-ares/test/.gitignore | 19 + src/c-ares/test/Makefile.am | 92 + src/c-ares/test/Makefile.inc | 32 + src/c-ares/test/Makefile.m32 | 48 + src/c-ares/test/Makefile.msvc | 304 + src/c-ares/test/README.md | 161 + src/c-ares/test/ares-fuzz.c | 58 + src/c-ares/test/ares-test-fuzz-name.c | 23 + src/c-ares/test/ares-test-fuzz.c | 46 + src/c-ares/test/ares-test-init.cc | 595 + src/c-ares/test/ares-test-internal.cc | 458 + src/c-ares/test/ares-test-live.cc | 633 + src/c-ares/test/ares-test-main.cc | 37 + src/c-ares/test/ares-test-misc.cc | 485 + src/c-ares/test/ares-test-mock.cc | 1161 ++ src/c-ares/test/ares-test-ns.cc | 199 + src/c-ares/test/ares-test-parse-a.cc | 362 + src/c-ares/test/ares-test-parse-aaaa.cc | 191 + src/c-ares/test/ares-test-parse-mx.cc | 141 + src/c-ares/test/ares-test-parse-naptr.cc | 119 + src/c-ares/test/ares-test-parse-ns.cc | 119 + src/c-ares/test/ares-test-parse-ptr.cc | 249 + src/c-ares/test/ares-test-parse-soa.cc | 108 + src/c-ares/test/ares-test-parse-srv.cc | 288 + src/c-ares/test/ares-test-parse-txt.cc | 266 + src/c-ares/test/ares-test-parse.cc | 219 + src/c-ares/test/ares-test.cc | 677 + src/c-ares/test/ares-test.h | 424 + src/c-ares/test/buildconf | 2 + src/c-ares/test/configure.ac | 22 + src/c-ares/test/dns-dump.cc | 35 + src/c-ares/test/dns-proto-test.cc | 131 + src/c-ares/test/dns-proto.cc | 637 + src/c-ares/test/dns-proto.h | 242 + src/c-ares/test/fuzzcheck.sh | 3 + .../fuzzinput/004a216d3cff18b0c5c6b68b807f1529 | Bin 0 -> 32 bytes .../fuzzinput/00539467ca159b36aea95e61f9729115 | Bin 0 -> 82 bytes .../fuzzinput/00e846db8f43f2f507cd1666ed5a753e | Bin 0 -> 62 bytes .../fuzzinput/0177b7566f08c013699eaea9a77abeb3 | Bin 0 -> 64 bytes .../fuzzinput/020a4fa317715bfdb236ed13751e6b65 | Bin 0 -> 31 bytes .../fuzzinput/0310f2e81bea31f4fe3f330872a877dd | Bin 0 -> 68 bytes .../fuzzinput/0449be67df1730b2d0887d412a9b7cc4 | Bin 0 -> 33 bytes .../fuzzinput/0449dd14f7aa94bf0d716bfe09b287a8 | Bin 0 -> 31 bytes .../fuzzinput/04c93cdf7208979aa4df80a3a0d5a2d8 | Bin 0 -> 31 bytes .../fuzzinput/0567e7171e08e75f3f91c4ca74c17adc | Bin 0 -> 32 bytes .../fuzzinput/05ba948578a397e9cbc6a7b3e78622fa | Bin 0 -> 64 bytes .../fuzzinput/060afe5ed25f3e2e86167e545f27edca | Bin 0 -> 136 bytes .../fuzzinput/06d47d3681493f1b1d41236f460d896f | Bin 0 -> 60 bytes .../fuzzinput/0724a810b0e131c2fddb6de9003b9064 | Bin 0 -> 33 bytes .../fuzzinput/0b5279148826f5b962bcf1896bdb4ede | Bin 0 -> 102 bytes .../fuzzinput/114048c0f6b10bdc67ce9166405d195e | Bin 0 -> 62 bytes .../fuzzinput/11b8464a0ef8735d202955c34c36b0c7 | Bin 0 -> 76 bytes .../fuzzinput/11cb626f1668c7b41954ce7d768fe528 | Bin 0 -> 60 bytes .../fuzzinput/14b133bf18125b75a1976fa63a1df6b7 | Bin 0 -> 62 bytes .../fuzzinput/153c6b3afa8faa03c8bc28f936a6d4cf | Bin 0 -> 67 bytes .../fuzzinput/182cad2a342ed7317b7c21a5d17020d1 | Bin 0 -> 60 bytes .../fuzzinput/1c61a61bb7057b52c5b15188345a5238 | Bin 0 -> 21 bytes .../fuzzinput/1dbe2cf62ed2e4fa1c3cb473f08710b5 | Bin 0 -> 29 bytes .../fuzzinput/21199be504fcfece5c7096ee0dbba507 | Bin 0 -> 107 bytes .../fuzzinput/21891480074b5635dbbe7137bdcabccd | Bin 0 -> 32 bytes .../fuzzinput/233aea42e15aa73e131eefabf16088c9 | Bin 0 -> 32 bytes .../fuzzinput/24660d4e7ac7aa21d600ea7a3d198bbb | Bin 0 -> 108 bytes .../fuzzinput/25589deb55c08429345f289d1c9b0254 | Bin 0 -> 57 bytes .../fuzzinput/2573bd823e4da11f727a17f8e1f35c26 | Bin 0 -> 29 bytes .../fuzzinput/276f12da56866273e76059ad0e7be97e | Bin 0 -> 32 bytes .../fuzzinput/29198a2e380cb19babec9e02116d213e | Bin 0 -> 29 bytes .../fuzzinput/2c94ba9434b1a1b9396fc5364f101363 | Bin 0 -> 40 bytes .../fuzzinput/2d578c357dc2f5e02dc55cddb30641d1 | Bin 0 -> 102 bytes .../fuzzinput/2dff6cc5a223e67fde9e5e79af456992 | Bin 0 -> 134 bytes .../fuzzinput/2f103b1f9477f2d8934bd84328d51c75 | Bin 0 -> 110 bytes .../fuzzinput/31cd3a8413de13d9624adbb1613784bf | Bin 0 -> 32 bytes .../fuzzinput/36415bdf1d180098fe6234b4186e69f3 | Bin 0 -> 31 bytes .../fuzzinput/3a04a80f0242e8dff0cd732e7c4767da | Bin 0 -> 377 bytes .../fuzzinput/44d0f973b7b0fb3e4a07770c943dcd5a | Bin 0 -> 32 bytes .../fuzzinput/50bc00daa0ddcd6cfb2b5d9f62c81f47 | Bin 0 -> 76 bytes .../fuzzinput/51ed2d1fb77b3078b54e94e85606b7df | Bin 0 -> 160 bytes .../fuzzinput/5c5e0e899cf2e7d053a9e45fb76f6e5a | Bin 0 -> 56 bytes .../fuzzinput/70152ed033f139443fbfb1b858bb3b1b | Bin 0 -> 32 bytes .../fuzzinput/7030ca2b24e5a7f9dd8f62096a48eb33 | Bin 0 -> 48 bytes .../fuzzinput/71eec1a0ef2d25bb9e2ef17f23be7e9e | Bin 0 -> 92 bytes .../fuzzinput/7a6b0177210ea4ef40b254daf99393c5 | Bin 0 -> 94 bytes .../fuzzinput/7f1567733711ffb61839621af0cbfa33 | Bin 0 -> 21 bytes .../fuzzinput/850c6d57c5bb7be8205fc2438d14d7e5 | Bin 0 -> 179 bytes .../fuzzinput/a5c8cd2784a5792b9e91c2d7895b3b34 | Bin 0 -> 102 bytes .../fuzzinput/a9135cdc7151d023300ff194bad90af9 | Bin 0 -> 102 bytes .../fuzzinput/af2597e8ac7dec1e8b4a47518312912a | Bin 0 -> 100 bytes src/c-ares/test/fuzzinput/answer_a | Bin 0 -> 62 bytes src/c-ares/test/fuzzinput/answer_aaaa | Bin 0 -> 62 bytes .../fuzzinput/b3f53ef826b831bb09dd25c7f5960249 | Bin 0 -> 56 bytes .../fuzzinput/cda0f8751f5c4993974c2b549d29bcc8 | Bin 0 -> 102 bytes .../fuzzinput/ce6c26c0e469339873d0e7f616ab0945 | Bin 0 -> 48 bytes .../fuzzinput/e4dd7e7c2dd4ed7c2e17a6af5d04f9c9 | Bin 0 -> 83 bytes .../fuzzinput/ed50ed8ee36230a5a69746ad830437e5 | Bin 0 -> 48 bytes .../fuzzinput/f1b900d50806021953321c3b604ee497 | Bin 0 -> 108 bytes .../fuzzinput/f89f6c8176b564a7dd646f14305573ce | Bin 0 -> 48 bytes .../fuzzinput/f9ad508d2dbd08d3aaaabc7d1174677d | Bin 0 -> 78 bytes src/c-ares/test/fuzznames/name01 | 1 + src/c-ares/test/fuzznames/name02 | 1 + src/c-ares/test/fuzznames/name03 | 1 + src/c-ares/test/fuzznames/name04 | 1 + src/c-ares/test/fuzznames/name05 | 1 + src/c-ares/test/fuzznames/name06 | 1 + src/c-ares/test/fuzznames/name07 | 1 + src/c-ares/test/fuzznames/name08 | 1 + src/c-ares/test/fuzznames/name09 | 1 + src/c-ares/test/gmock-1.7.0/CHANGES | 126 + src/c-ares/test/gmock-1.7.0/CMakeLists.txt | 171 + src/c-ares/test/gmock-1.7.0/CONTRIBUTORS | 40 + src/c-ares/test/gmock-1.7.0/LICENSE | 28 + src/c-ares/test/gmock-1.7.0/Makefile.am | 216 + src/c-ares/test/gmock-1.7.0/README | 369 + src/c-ares/test/gmock-1.7.0/build-aux/config.h.in | 69 + src/c-ares/test/gmock-1.7.0/configure.ac | 146 + .../test/gmock-1.7.0/fused-src/gmock-gtest-all.cc | 11443 +++++++++++ .../test/gmock-1.7.0/fused-src/gmock/gmock.h | 14198 +++++++++++++ .../test/gmock-1.7.0/fused-src/gmock_main.cc | 54 + .../test/gmock-1.7.0/fused-src/gtest/gtest.h | 20061 +++++++++++++++++++ src/c-ares/test/gmock-1.7.0/gtest/CHANGES | 157 + src/c-ares/test/gmock-1.7.0/gtest/CMakeLists.txt | 252 + src/c-ares/test/gmock-1.7.0/gtest/CONTRIBUTORS | 37 + src/c-ares/test/gmock-1.7.0/gtest/LICENSE | 28 + src/c-ares/test/gmock-1.7.0/gtest/Makefile.am | 306 + src/c-ares/test/gmock-1.7.0/gtest/README | 435 + .../test/gmock-1.7.0/gtest/build-aux/config.h.in | 69 + .../gmock-1.7.0/gtest/cmake/internal_utils.cmake | 227 + .../test/gmock-1.7.0/gtest/codegear/gtest.cbproj | 138 + .../gmock-1.7.0/gtest/codegear/gtest.groupproj | 54 + .../test/gmock-1.7.0/gtest/codegear/gtest_all.cc | 38 + .../test/gmock-1.7.0/gtest/codegear/gtest_link.cc | 40 + .../gmock-1.7.0/gtest/codegear/gtest_main.cbproj | 82 + .../gtest/codegear/gtest_unittest.cbproj | 88 + src/c-ares/test/gmock-1.7.0/gtest/configure.ac | 68 + .../gmock-1.7.0/gtest/fused-src/gtest/gtest-all.cc | 9592 +++++++++ .../test/gmock-1.7.0/gtest/fused-src/gtest/gtest.h | 20061 +++++++++++++++++++ .../gtest/fused-src/gtest/gtest_main.cc | 38 + .../gtest/include/gtest/gtest-death-test.h | 294 + .../gtest/include/gtest/gtest-message.h | 250 + .../gtest/include/gtest/gtest-param-test.h | 1421 ++ .../gtest/include/gtest/gtest-param-test.h.pump | 487 + .../gtest/include/gtest/gtest-printers.h | 855 + .../gmock-1.7.0/gtest/include/gtest/gtest-spi.h | 232 + .../gtest/include/gtest/gtest-test-part.h | 179 + .../gtest/include/gtest/gtest-typed-test.h | 259 + .../test/gmock-1.7.0/gtest/include/gtest/gtest.h | 2291 +++ .../gtest/include/gtest/gtest_pred_impl.h | 358 + .../gmock-1.7.0/gtest/include/gtest/gtest_prod.h | 58 + .../gtest/internal/gtest-death-test-internal.h | 319 + .../gtest/include/gtest/internal/gtest-filepath.h | 206 + .../gtest/include/gtest/internal/gtest-internal.h | 1158 ++ .../include/gtest/internal/gtest-linked_ptr.h | 233 + .../gtest/internal/gtest-param-util-generated.h | 5143 +++++ .../internal/gtest-param-util-generated.h.pump | 301 + .../include/gtest/internal/gtest-param-util.h | 619 + .../gtest/include/gtest/internal/gtest-port.h | 1947 ++ .../gtest/include/gtest/internal/gtest-string.h | 167 + .../gtest/include/gtest/internal/gtest-tuple.h | 1012 + .../include/gtest/internal/gtest-tuple.h.pump | 339 + .../gtest/include/gtest/internal/gtest-type-util.h | 3331 +++ .../include/gtest/internal/gtest-type-util.h.pump | 297 + .../test/gmock-1.7.0/gtest/m4/acx_pthread.m4 | 363 + src/c-ares/test/gmock-1.7.0/gtest/m4/gtest.m4 | 74 + .../test/gmock-1.7.0/gtest/msvc/gtest-md.sln | 45 + .../test/gmock-1.7.0/gtest/msvc/gtest-md.vcproj | 126 + src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest.sln | 45 + .../test/gmock-1.7.0/gtest/msvc/gtest.vcproj | 126 + .../gmock-1.7.0/gtest/msvc/gtest_main-md.vcproj | 129 + .../test/gmock-1.7.0/gtest/msvc/gtest_main.vcproj | 129 + .../gtest/msvc/gtest_prod_test-md.vcproj | 164 + .../gmock-1.7.0/gtest/msvc/gtest_prod_test.vcproj | 164 + .../gtest/msvc/gtest_unittest-md.vcproj | 147 + .../gmock-1.7.0/gtest/msvc/gtest_unittest.vcproj | 147 + .../test/gmock-1.7.0/gtest/samples/prime_tables.h | 123 + .../test/gmock-1.7.0/gtest/samples/sample1.cc | 68 + .../test/gmock-1.7.0/gtest/samples/sample1.h | 43 + .../gmock-1.7.0/gtest/samples/sample10_unittest.cc | 144 + .../gmock-1.7.0/gtest/samples/sample1_unittest.cc | 153 + .../test/gmock-1.7.0/gtest/samples/sample2.cc | 56 + .../test/gmock-1.7.0/gtest/samples/sample2.h | 85 + .../gmock-1.7.0/gtest/samples/sample2_unittest.cc | 109 + .../test/gmock-1.7.0/gtest/samples/sample3-inl.h | 172 + .../gmock-1.7.0/gtest/samples/sample3_unittest.cc | 151 + .../test/gmock-1.7.0/gtest/samples/sample4.cc | 46 + .../test/gmock-1.7.0/gtest/samples/sample4.h | 53 + .../gmock-1.7.0/gtest/samples/sample4_unittest.cc | 45 + .../gmock-1.7.0/gtest/samples/sample5_unittest.cc | 199 + .../gmock-1.7.0/gtest/samples/sample6_unittest.cc | 224 + .../gmock-1.7.0/gtest/samples/sample7_unittest.cc | 130 + .../gmock-1.7.0/gtest/samples/sample8_unittest.cc | 173 + .../gmock-1.7.0/gtest/samples/sample9_unittest.cc | 160 + .../gmock-1.7.0/gtest/scripts/fuse_gtest_files.py | 250 + .../gtest/scripts/gen_gtest_pred_impl.py | 730 + .../test/gmock-1.7.0/gtest/scripts/gtest-config.in | 274 + src/c-ares/test/gmock-1.7.0/gtest/scripts/pump.py | 855 + src/c-ares/test/gmock-1.7.0/gtest/src/gtest-all.cc | 48 + .../test/gmock-1.7.0/gtest/src/gtest-death-test.cc | 1344 ++ .../test/gmock-1.7.0/gtest/src/gtest-filepath.cc | 382 + .../gmock-1.7.0/gtest/src/gtest-internal-inl.h | 1218 ++ .../test/gmock-1.7.0/gtest/src/gtest-port.cc | 805 + .../test/gmock-1.7.0/gtest/src/gtest-printers.cc | 363 + .../test/gmock-1.7.0/gtest/src/gtest-test-part.cc | 110 + .../test/gmock-1.7.0/gtest/src/gtest-typed-test.cc | 110 + src/c-ares/test/gmock-1.7.0/gtest/src/gtest.cc | 5015 +++++ .../test/gmock-1.7.0/gtest/src/gtest_main.cc | 38 + .../gtest/test/gtest-death-test_ex_test.cc | 93 + .../gtest/test/gtest-death-test_test.cc | 1367 ++ .../gmock-1.7.0/gtest/test/gtest-filepath_test.cc | 680 + .../gtest/test/gtest-linked_ptr_test.cc | 154 + .../gmock-1.7.0/gtest/test/gtest-listener_test.cc | 310 + .../gmock-1.7.0/gtest/test/gtest-message_test.cc | 159 + .../gmock-1.7.0/gtest/test/gtest-options_test.cc | 215 + .../gtest/test/gtest-param-test2_test.cc | 65 + .../gtest/test/gtest-param-test_test.cc | 904 + .../gmock-1.7.0/gtest/test/gtest-param-test_test.h | 57 + .../test/gmock-1.7.0/gtest/test/gtest-port_test.cc | 1253 ++ .../gmock-1.7.0/gtest/test/gtest-printers_test.cc | 1566 ++ .../gmock-1.7.0/gtest/test/gtest-test-part_test.cc | 208 + .../gmock-1.7.0/gtest/test/gtest-tuple_test.cc | 320 + .../gtest/test/gtest-typed-test2_test.cc | 45 + .../gtest/test/gtest-typed-test_test.cc | 360 + .../gmock-1.7.0/gtest/test/gtest-typed-test_test.h | 66 + .../gtest/test/gtest-unittest-api_test.cc | 341 + .../test/gmock-1.7.0/gtest/test/gtest_all_test.cc | 47 + .../gtest/test/gtest_break_on_failure_unittest.py | 212 + .../gtest/test/gtest_break_on_failure_unittest_.cc | 88 + .../gtest/test/gtest_catch_exceptions_test.py | 237 + .../gtest/test/gtest_catch_exceptions_test_.cc | 311 + .../gmock-1.7.0/gtest/test/gtest_color_test.py | 130 + .../gmock-1.7.0/gtest/test/gtest_color_test_.cc | 71 + .../gmock-1.7.0/gtest/test/gtest_env_var_test.py | 103 + .../gmock-1.7.0/gtest/test/gtest_env_var_test_.cc | 126 + .../gtest/test/gtest_environment_test.cc | 192 + .../gtest/test/gtest_filter_unittest.py | 633 + .../gtest/test/gtest_filter_unittest_.cc | 140 + .../test/gmock-1.7.0/gtest/test/gtest_help_test.py | 172 + .../gmock-1.7.0/gtest/test/gtest_help_test_.cc | 46 + .../gtest/test/gtest_list_tests_unittest.py | 207 + .../gtest/test/gtest_list_tests_unittest_.cc | 157 + .../gmock-1.7.0/gtest/test/gtest_main_unittest.cc | 45 + .../gtest/test/gtest_no_test_unittest.cc | 56 + .../gmock-1.7.0/gtest/test/gtest_output_test.py | 335 + .../gmock-1.7.0/gtest/test/gtest_output_test_.cc | 1034 + .../gtest/test/gtest_output_test_golden_lin.txt | 720 + .../gtest/test/gtest_pred_impl_unittest.cc | 2427 +++ .../gtest/test/gtest_premature_exit_test.cc | 141 + .../test/gmock-1.7.0/gtest/test/gtest_prod_test.cc | 57 + .../gmock-1.7.0/gtest/test/gtest_repeat_test.cc | 253 + .../gmock-1.7.0/gtest/test/gtest_shuffle_test.py | 325 + .../gmock-1.7.0/gtest/test/gtest_shuffle_test_.cc | 103 + .../gtest/test/gtest_sole_header_test.cc | 57 + .../gmock-1.7.0/gtest/test/gtest_stress_test.cc | 256 + .../gmock-1.7.0/gtest/test/gtest_test_utils.py | 320 + .../gtest/test/gtest_throw_on_failure_ex_test.cc | 92 + .../gtest/test/gtest_throw_on_failure_test.py | 171 + .../gtest/test/gtest_throw_on_failure_test_.cc | 72 + .../gtest/test/gtest_uninitialized_test.py | 70 + .../gtest/test/gtest_uninitialized_test_.cc | 43 + .../test/gmock-1.7.0/gtest/test/gtest_unittest.cc | 7415 +++++++ .../gtest/test/gtest_xml_outfile1_test_.cc | 49 + .../gtest/test/gtest_xml_outfile2_test_.cc | 49 + .../gtest/test/gtest_xml_outfiles_test.py | 132 + .../gtest/test/gtest_xml_output_unittest.py | 307 + .../gtest/test/gtest_xml_output_unittest_.cc | 181 + .../gmock-1.7.0/gtest/test/gtest_xml_test_utils.py | 194 + .../test/gmock-1.7.0/gtest/test/production.cc | 36 + .../test/gmock-1.7.0/gtest/test/production.h | 55 + .../gtest/xcode/Config/DebugProject.xcconfig | 30 + .../gtest/xcode/Config/FrameworkTarget.xcconfig | 17 + .../gtest/xcode/Config/General.xcconfig | 41 + .../gtest/xcode/Config/ReleaseProject.xcconfig | 32 + .../xcode/Config/StaticLibraryTarget.xcconfig | 18 + .../gtest/xcode/Config/TestTarget.xcconfig | 8 + .../gmock-1.7.0/gtest/xcode/Resources/Info.plist | 30 + .../gtest/xcode/Samples/FrameworkSample/Info.plist | 28 + .../WidgetFramework.xcodeproj/project.pbxproj | 457 + .../xcode/Samples/FrameworkSample/runtests.sh | 62 + .../gtest/xcode/Samples/FrameworkSample/widget.cc | 63 + .../gtest/xcode/Samples/FrameworkSample/widget.h | 59 + .../xcode/Samples/FrameworkSample/widget_test.cc | 68 + .../gmock-1.7.0/gtest/xcode/Scripts/runtests.sh | 65 + .../gtest/xcode/Scripts/versiongenerate.py | 100 + .../gtest/xcode/gtest.xcodeproj/project.pbxproj | 1135 ++ .../test/gmock-1.7.0/include/gmock/gmock-actions.h | 1078 + .../include/gmock/gmock-cardinalities.h | 147 + .../include/gmock/gmock-generated-actions.h | 2415 +++ .../include/gmock/gmock-generated-actions.h.pump | 821 + .../gmock/gmock-generated-function-mockers.h | 991 + .../gmock/gmock-generated-function-mockers.h.pump | 265 + .../include/gmock/gmock-generated-matchers.h | 2190 ++ .../include/gmock/gmock-generated-matchers.h.pump | 674 + .../include/gmock/gmock-generated-nice-strict.h | 397 + .../gmock/gmock-generated-nice-strict.h.pump | 161 + .../gmock-1.7.0/include/gmock/gmock-matchers.h | 3986 ++++ .../gmock-1.7.0/include/gmock/gmock-more-actions.h | 233 + .../include/gmock/gmock-more-matchers.h | 58 + .../include/gmock/gmock-spec-builders.h | 1792 ++ src/c-ares/test/gmock-1.7.0/include/gmock/gmock.h | 94 + .../internal/gmock-generated-internal-utils.h | 279 + .../internal/gmock-generated-internal-utils.h.pump | 136 + .../include/gmock/internal/gmock-internal-utils.h | 498 + .../include/gmock/internal/gmock-port.h | 78 + src/c-ares/test/gmock-1.7.0/msvc/2005/gmock.sln | 32 + src/c-ares/test/gmock-1.7.0/msvc/2005/gmock.vcproj | 191 + .../gmock-1.7.0/msvc/2005/gmock_config.vsprops | 15 + .../test/gmock-1.7.0/msvc/2005/gmock_main.vcproj | 187 + .../test/gmock-1.7.0/msvc/2005/gmock_test.vcproj | 201 + src/c-ares/test/gmock-1.7.0/msvc/2010/gmock.sln | 32 + .../test/gmock-1.7.0/msvc/2010/gmock.vcxproj | 82 + .../test/gmock-1.7.0/msvc/2010/gmock_config.props | 19 + .../test/gmock-1.7.0/msvc/2010/gmock_main.vcxproj | 88 + .../test/gmock-1.7.0/msvc/2010/gmock_test.vcxproj | 101 + .../test/gmock-1.7.0/scripts/fuse_gmock_files.py | 240 + .../test/gmock-1.7.0/scripts/generator/LICENSE | 203 + .../test/gmock-1.7.0/scripts/generator/README | 35 + .../gmock-1.7.0/scripts/generator/README.cppclean | 115 + .../gmock-1.7.0/scripts/generator/cpp/__init__.py | 0 .../test/gmock-1.7.0/scripts/generator/cpp/ast.py | 1723 ++ .../scripts/generator/cpp/gmock_class.py | 226 + .../gmock-1.7.0/scripts/generator/cpp/keywords.py | 59 + .../gmock-1.7.0/scripts/generator/cpp/tokenize.py | 287 + .../gmock-1.7.0/scripts/generator/cpp/utils.py | 41 + .../gmock-1.7.0/scripts/generator/gmock_gen.py | 31 + .../test/gmock-1.7.0/scripts/gmock-config.in | 303 + src/c-ares/test/gmock-1.7.0/src/gmock-all.cc | 47 + .../test/gmock-1.7.0/src/gmock-cardinalities.cc | 156 + .../test/gmock-1.7.0/src/gmock-internal-utils.cc | 174 + src/c-ares/test/gmock-1.7.0/src/gmock-matchers.cc | 498 + .../test/gmock-1.7.0/src/gmock-spec-builders.cc | 813 + src/c-ares/test/gmock-1.7.0/src/gmock.cc | 182 + src/c-ares/test/gmock-1.7.0/src/gmock_main.cc | 54 + .../test/gmock-1.7.0/test/gmock-actions_test.cc | 1256 ++ .../gmock-1.7.0/test/gmock-cardinalities_test.cc | 428 + .../test/gmock-generated-actions_test.cc | 1225 ++ .../test/gmock-generated-function-mockers_test.cc | 588 + .../test/gmock-generated-internal-utils_test.cc | 127 + .../test/gmock-generated-matchers_test.cc | 1289 ++ .../gmock-1.7.0/test/gmock-internal-utils_test.cc | 649 + .../test/gmock-1.7.0/test/gmock-matchers_test.cc | 5247 +++++ .../gmock-1.7.0/test/gmock-more-actions_test.cc | 705 + .../gmock-1.7.0/test/gmock-nice-strict_test.cc | 424 + .../test/gmock-1.7.0/test/gmock-port_test.cc | 43 + .../gmock-1.7.0/test/gmock-spec-builders_test.cc | 2613 +++ src/c-ares/test/gmock-1.7.0/test/gmock_all_test.cc | 48 + src/c-ares/test/gmock-1.7.0/test/gmock_ex_test.cc | 78 + .../test/gmock-1.7.0/test/gmock_leak_test.py | 108 + .../test/gmock-1.7.0/test/gmock_leak_test_.cc | 100 + .../test/gmock-1.7.0/test/gmock_link2_test.cc | 40 + .../test/gmock-1.7.0/test/gmock_link_test.cc | 40 + src/c-ares/test/gmock-1.7.0/test/gmock_link_test.h | 669 + .../test/gmock-1.7.0/test/gmock_output_test.py | 180 + .../test/gmock-1.7.0/test/gmock_output_test_.cc | 291 + .../gmock-1.7.0/test/gmock_output_test_golden.txt | 310 + .../test/gmock-1.7.0/test/gmock_stress_test.cc | 322 + src/c-ares/test/gmock-1.7.0/test/gmock_test.cc | 255 + .../test/gmock-1.7.0/test/gmock_test_utils.py | 112 + 354 files changed, 192135 insertions(+) create mode 100644 src/c-ares/test/.gitignore create mode 100644 src/c-ares/test/Makefile.am create mode 100644 src/c-ares/test/Makefile.inc create mode 100644 src/c-ares/test/Makefile.m32 create mode 100644 src/c-ares/test/Makefile.msvc create mode 100644 src/c-ares/test/README.md create mode 100644 src/c-ares/test/ares-fuzz.c create mode 100644 src/c-ares/test/ares-test-fuzz-name.c create mode 100644 src/c-ares/test/ares-test-fuzz.c create mode 100644 src/c-ares/test/ares-test-init.cc create mode 100644 src/c-ares/test/ares-test-internal.cc create mode 100644 src/c-ares/test/ares-test-live.cc create mode 100644 src/c-ares/test/ares-test-main.cc create mode 100644 src/c-ares/test/ares-test-misc.cc create mode 100644 src/c-ares/test/ares-test-mock.cc create mode 100644 src/c-ares/test/ares-test-ns.cc create mode 100644 src/c-ares/test/ares-test-parse-a.cc create mode 100644 src/c-ares/test/ares-test-parse-aaaa.cc create mode 100644 src/c-ares/test/ares-test-parse-mx.cc create mode 100644 src/c-ares/test/ares-test-parse-naptr.cc create mode 100644 src/c-ares/test/ares-test-parse-ns.cc create mode 100644 src/c-ares/test/ares-test-parse-ptr.cc create mode 100644 src/c-ares/test/ares-test-parse-soa.cc create mode 100644 src/c-ares/test/ares-test-parse-srv.cc create mode 100644 src/c-ares/test/ares-test-parse-txt.cc create mode 100644 src/c-ares/test/ares-test-parse.cc create mode 100644 src/c-ares/test/ares-test.cc create mode 100644 src/c-ares/test/ares-test.h create mode 100755 src/c-ares/test/buildconf create mode 100644 src/c-ares/test/configure.ac create mode 100644 src/c-ares/test/dns-dump.cc create mode 100644 src/c-ares/test/dns-proto-test.cc create mode 100644 src/c-ares/test/dns-proto.cc create mode 100644 src/c-ares/test/dns-proto.h create mode 100755 src/c-ares/test/fuzzcheck.sh create mode 100644 src/c-ares/test/fuzzinput/004a216d3cff18b0c5c6b68b807f1529 create mode 100644 src/c-ares/test/fuzzinput/00539467ca159b36aea95e61f9729115 create mode 100644 src/c-ares/test/fuzzinput/00e846db8f43f2f507cd1666ed5a753e create mode 100644 src/c-ares/test/fuzzinput/0177b7566f08c013699eaea9a77abeb3 create mode 100644 src/c-ares/test/fuzzinput/020a4fa317715bfdb236ed13751e6b65 create mode 100644 src/c-ares/test/fuzzinput/0310f2e81bea31f4fe3f330872a877dd create mode 100644 src/c-ares/test/fuzzinput/0449be67df1730b2d0887d412a9b7cc4 create mode 100644 src/c-ares/test/fuzzinput/0449dd14f7aa94bf0d716bfe09b287a8 create mode 100644 src/c-ares/test/fuzzinput/04c93cdf7208979aa4df80a3a0d5a2d8 create mode 100644 src/c-ares/test/fuzzinput/0567e7171e08e75f3f91c4ca74c17adc create mode 100644 src/c-ares/test/fuzzinput/05ba948578a397e9cbc6a7b3e78622fa create mode 100644 src/c-ares/test/fuzzinput/060afe5ed25f3e2e86167e545f27edca create mode 100644 src/c-ares/test/fuzzinput/06d47d3681493f1b1d41236f460d896f create mode 100644 src/c-ares/test/fuzzinput/0724a810b0e131c2fddb6de9003b9064 create mode 100644 src/c-ares/test/fuzzinput/0b5279148826f5b962bcf1896bdb4ede create mode 100644 src/c-ares/test/fuzzinput/114048c0f6b10bdc67ce9166405d195e create mode 100644 src/c-ares/test/fuzzinput/11b8464a0ef8735d202955c34c36b0c7 create mode 100644 src/c-ares/test/fuzzinput/11cb626f1668c7b41954ce7d768fe528 create mode 100644 src/c-ares/test/fuzzinput/14b133bf18125b75a1976fa63a1df6b7 create mode 100644 src/c-ares/test/fuzzinput/153c6b3afa8faa03c8bc28f936a6d4cf create mode 100644 src/c-ares/test/fuzzinput/182cad2a342ed7317b7c21a5d17020d1 create mode 100644 src/c-ares/test/fuzzinput/1c61a61bb7057b52c5b15188345a5238 create mode 100644 src/c-ares/test/fuzzinput/1dbe2cf62ed2e4fa1c3cb473f08710b5 create mode 100644 src/c-ares/test/fuzzinput/21199be504fcfece5c7096ee0dbba507 create mode 100644 src/c-ares/test/fuzzinput/21891480074b5635dbbe7137bdcabccd create mode 100644 src/c-ares/test/fuzzinput/233aea42e15aa73e131eefabf16088c9 create mode 100644 src/c-ares/test/fuzzinput/24660d4e7ac7aa21d600ea7a3d198bbb create mode 100644 src/c-ares/test/fuzzinput/25589deb55c08429345f289d1c9b0254 create mode 100644 src/c-ares/test/fuzzinput/2573bd823e4da11f727a17f8e1f35c26 create mode 100644 src/c-ares/test/fuzzinput/276f12da56866273e76059ad0e7be97e create mode 100644 src/c-ares/test/fuzzinput/29198a2e380cb19babec9e02116d213e create mode 100644 src/c-ares/test/fuzzinput/2c94ba9434b1a1b9396fc5364f101363 create mode 100644 src/c-ares/test/fuzzinput/2d578c357dc2f5e02dc55cddb30641d1 create mode 100644 src/c-ares/test/fuzzinput/2dff6cc5a223e67fde9e5e79af456992 create mode 100644 src/c-ares/test/fuzzinput/2f103b1f9477f2d8934bd84328d51c75 create mode 100644 src/c-ares/test/fuzzinput/31cd3a8413de13d9624adbb1613784bf create mode 100644 src/c-ares/test/fuzzinput/36415bdf1d180098fe6234b4186e69f3 create mode 100644 src/c-ares/test/fuzzinput/3a04a80f0242e8dff0cd732e7c4767da create mode 100644 src/c-ares/test/fuzzinput/44d0f973b7b0fb3e4a07770c943dcd5a create mode 100644 src/c-ares/test/fuzzinput/50bc00daa0ddcd6cfb2b5d9f62c81f47 create mode 100644 src/c-ares/test/fuzzinput/51ed2d1fb77b3078b54e94e85606b7df create mode 100644 src/c-ares/test/fuzzinput/5c5e0e899cf2e7d053a9e45fb76f6e5a create mode 100644 src/c-ares/test/fuzzinput/70152ed033f139443fbfb1b858bb3b1b create mode 100644 src/c-ares/test/fuzzinput/7030ca2b24e5a7f9dd8f62096a48eb33 create mode 100644 src/c-ares/test/fuzzinput/71eec1a0ef2d25bb9e2ef17f23be7e9e create mode 100644 src/c-ares/test/fuzzinput/7a6b0177210ea4ef40b254daf99393c5 create mode 100644 src/c-ares/test/fuzzinput/7f1567733711ffb61839621af0cbfa33 create mode 100644 src/c-ares/test/fuzzinput/850c6d57c5bb7be8205fc2438d14d7e5 create mode 100644 src/c-ares/test/fuzzinput/a5c8cd2784a5792b9e91c2d7895b3b34 create mode 100644 src/c-ares/test/fuzzinput/a9135cdc7151d023300ff194bad90af9 create mode 100644 src/c-ares/test/fuzzinput/af2597e8ac7dec1e8b4a47518312912a create mode 100644 src/c-ares/test/fuzzinput/answer_a create mode 100644 src/c-ares/test/fuzzinput/answer_aaaa create mode 100644 src/c-ares/test/fuzzinput/b3f53ef826b831bb09dd25c7f5960249 create mode 100644 src/c-ares/test/fuzzinput/cda0f8751f5c4993974c2b549d29bcc8 create mode 100644 src/c-ares/test/fuzzinput/ce6c26c0e469339873d0e7f616ab0945 create mode 100644 src/c-ares/test/fuzzinput/e4dd7e7c2dd4ed7c2e17a6af5d04f9c9 create mode 100644 src/c-ares/test/fuzzinput/ed50ed8ee36230a5a69746ad830437e5 create mode 100644 src/c-ares/test/fuzzinput/f1b900d50806021953321c3b604ee497 create mode 100644 src/c-ares/test/fuzzinput/f89f6c8176b564a7dd646f14305573ce create mode 100644 src/c-ares/test/fuzzinput/f9ad508d2dbd08d3aaaabc7d1174677d create mode 100644 src/c-ares/test/fuzznames/name01 create mode 100644 src/c-ares/test/fuzznames/name02 create mode 100644 src/c-ares/test/fuzznames/name03 create mode 100644 src/c-ares/test/fuzznames/name04 create mode 100644 src/c-ares/test/fuzznames/name05 create mode 100644 src/c-ares/test/fuzznames/name06 create mode 100644 src/c-ares/test/fuzznames/name07 create mode 100644 src/c-ares/test/fuzznames/name08 create mode 100644 src/c-ares/test/fuzznames/name09 create mode 100644 src/c-ares/test/gmock-1.7.0/CHANGES create mode 100644 src/c-ares/test/gmock-1.7.0/CMakeLists.txt create mode 100644 src/c-ares/test/gmock-1.7.0/CONTRIBUTORS create mode 100644 src/c-ares/test/gmock-1.7.0/LICENSE create mode 100644 src/c-ares/test/gmock-1.7.0/Makefile.am create mode 100644 src/c-ares/test/gmock-1.7.0/README create mode 100644 src/c-ares/test/gmock-1.7.0/build-aux/config.h.in create mode 100644 src/c-ares/test/gmock-1.7.0/configure.ac create mode 100644 src/c-ares/test/gmock-1.7.0/fused-src/gmock-gtest-all.cc create mode 100644 src/c-ares/test/gmock-1.7.0/fused-src/gmock/gmock.h create mode 100644 src/c-ares/test/gmock-1.7.0/fused-src/gmock_main.cc create mode 100644 src/c-ares/test/gmock-1.7.0/fused-src/gtest/gtest.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/CHANGES create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/CMakeLists.txt create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/CONTRIBUTORS create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/LICENSE create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/Makefile.am create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/README create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/build-aux/config.h.in create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/cmake/internal_utils.cmake create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/codegear/gtest.cbproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/codegear/gtest.groupproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/codegear/gtest_all.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/codegear/gtest_link.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/codegear/gtest_main.cbproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/codegear/gtest_unittest.cbproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/configure.ac create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/fused-src/gtest/gtest-all.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/fused-src/gtest/gtest.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/fused-src/gtest/gtest_main.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-death-test.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-message.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-param-test.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-param-test.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-printers.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-spi.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-test-part.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest-typed-test.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest_pred_impl.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/gtest_prod.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-death-test-internal.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-filepath.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-internal.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-linked_ptr.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-param-util-generated.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-param-util-generated.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-param-util.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-port.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-string.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-tuple.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-tuple.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-type-util.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/include/gtest/internal/gtest-type-util.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/m4/acx_pthread.m4 create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/m4/gtest.m4 create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest-md.sln create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest-md.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest.sln create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest_main-md.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest_main.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest_prod_test-md.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest_prod_test.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest_unittest-md.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/msvc/gtest_unittest.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/prime_tables.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample1.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample1.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample10_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample1_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample2.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample2.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample2_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample3-inl.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample3_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample4.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample4.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample4_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample5_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample6_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample7_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample8_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/samples/sample9_unittest.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/scripts/fuse_gtest_files.py create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/scripts/gen_gtest_pred_impl.py create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/scripts/gtest-config.in create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/scripts/pump.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-all.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-death-test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-filepath.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-internal-inl.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-port.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-printers.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-test-part.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest-typed-test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/src/gtest_main.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-death-test_ex_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-death-test_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-filepath_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-linked_ptr_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-listener_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-message_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-options_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-param-test2_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-param-test_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-param-test_test.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-port_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-printers_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-test-part_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-tuple_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-typed-test2_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-typed-test_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-typed-test_test.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest-unittest-api_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_all_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_break_on_failure_unittest.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_break_on_failure_unittest_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_catch_exceptions_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_catch_exceptions_test_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_color_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_color_test_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_env_var_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_env_var_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_environment_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_filter_unittest.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_filter_unittest_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_help_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_help_test_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_list_tests_unittest.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_list_tests_unittest_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_main_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_no_test_unittest.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_output_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_output_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_output_test_golden_lin.txt create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_pred_impl_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_premature_exit_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_prod_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_repeat_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_shuffle_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_shuffle_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_sole_header_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_stress_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_test_utils.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_throw_on_failure_ex_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_throw_on_failure_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_throw_on_failure_test_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_uninitialized_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_uninitialized_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_unittest.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_xml_outfile1_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_xml_outfile2_test_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_xml_outfiles_test.py create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_xml_output_unittest.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_xml_output_unittest_.cc create mode 100755 src/c-ares/test/gmock-1.7.0/gtest/test/gtest_xml_test_utils.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/production.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/test/production.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Config/DebugProject.xcconfig create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Config/FrameworkTarget.xcconfig create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Config/General.xcconfig create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Config/ReleaseProject.xcconfig create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Config/StaticLibraryTarget.xcconfig create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Config/TestTarget.xcconfig create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Resources/Info.plist create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Samples/FrameworkSample/Info.plist create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Samples/FrameworkSample/runtests.sh create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Samples/FrameworkSample/widget.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Samples/FrameworkSample/widget.h create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Samples/FrameworkSample/widget_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Scripts/runtests.sh create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/Scripts/versiongenerate.py create mode 100644 src/c-ares/test/gmock-1.7.0/gtest/xcode/gtest.xcodeproj/project.pbxproj create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-actions.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-cardinalities.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-actions.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-actions.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-function-mockers.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-function-mockers.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-matchers.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-matchers.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-nice-strict.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-generated-nice-strict.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-matchers.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-more-actions.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-more-matchers.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock-spec-builders.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/gmock.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/internal/gmock-generated-internal-utils.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/internal/gmock-generated-internal-utils.h.pump create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/internal/gmock-internal-utils.h create mode 100644 src/c-ares/test/gmock-1.7.0/include/gmock/internal/gmock-port.h create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2005/gmock.sln create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2005/gmock.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2005/gmock_config.vsprops create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2005/gmock_main.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2005/gmock_test.vcproj create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2010/gmock.sln create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2010/gmock.vcxproj create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2010/gmock_config.props create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2010/gmock_main.vcxproj create mode 100644 src/c-ares/test/gmock-1.7.0/msvc/2010/gmock_test.vcxproj create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/fuse_gmock_files.py create mode 100644 src/c-ares/test/gmock-1.7.0/scripts/generator/LICENSE create mode 100644 src/c-ares/test/gmock-1.7.0/scripts/generator/README create mode 100644 src/c-ares/test/gmock-1.7.0/scripts/generator/README.cppclean create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/cpp/__init__.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/cpp/ast.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/cpp/gmock_class.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/cpp/keywords.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/cpp/tokenize.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/cpp/utils.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/generator/gmock_gen.py create mode 100755 src/c-ares/test/gmock-1.7.0/scripts/gmock-config.in create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock-all.cc create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock-cardinalities.cc create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock-internal-utils.cc create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock-matchers.cc create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock-spec-builders.cc create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock.cc create mode 100644 src/c-ares/test/gmock-1.7.0/src/gmock_main.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-actions_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-cardinalities_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-generated-actions_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-generated-function-mockers_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-generated-internal-utils_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-generated-matchers_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-internal-utils_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-matchers_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-more-actions_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-nice-strict_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-port_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock-spec-builders_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_all_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_ex_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/test/gmock_leak_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_leak_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_link2_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_link_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_link_test.h create mode 100755 src/c-ares/test/gmock-1.7.0/test/gmock_output_test.py create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_output_test_.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_output_test_golden.txt create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_stress_test.cc create mode 100644 src/c-ares/test/gmock-1.7.0/test/gmock_test.cc create mode 100755 src/c-ares/test/gmock-1.7.0/test/gmock_test_utils.py (limited to 'src/c-ares/test') diff --git a/src/c-ares/test/.gitignore b/src/c-ares/test/.gitignore new file mode 100644 index 000000000..96dd15fc2 --- /dev/null +++ b/src/c-ares/test/.gitignore @@ -0,0 +1,19 @@ +*.o +libgtest.a +libgmock.a +arestest +aresfuzz +aresfuzzname +arestest.log +arestest.trs +fuzzcheck.sh.log +fuzzcheck.sh.trs +test-suite.log +fuzzoutput +config.h.in +config.h +dnsdump +ares-libfuzzer +ares-libfuzzer-name +libFuzzer.a +Fuzzer \ No newline at end of file diff --git a/src/c-ares/test/Makefile.am b/src/c-ares/test/Makefile.am new file mode 100644 index 000000000..b758a7d76 --- /dev/null +++ b/src/c-ares/test/Makefile.am @@ -0,0 +1,92 @@ +# Where to find the c-ares source code; needed because the tests use library-internal headers +ARES_SRC_DIR = .. +# Where to find the built c-ares static library +ARES_BLD_DIR = .. +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I ../m4 +GMOCK_DIR = gmock-1.7.0 +GTEST_DIR = $(GMOCK_DIR)/gtest +# Note use of -isystem to force use of local gMock/gTest even if there's an installed version. +CPPFLAGS += -I$(ARES_SRC_DIR) -isystem $(GTEST_DIR)/include -isystem $(GMOCK_DIR)/include +CXXFLAGS += -Wall $(PTHREAD_CFLAGS) + +# Makefile.inc provides the TESTSOURCES, TESTHEADERS and FUZZSOURCES defines +include Makefile.inc + +TESTS = arestest fuzzcheck.sh + +noinst_PROGRAMS = arestest aresfuzz aresfuzzname dnsdump +arestest_SOURCES = $(TESTSOURCES) $(TESTHEADERS) +arestest_LDADD = libgmock.la libgtest.la $(ARES_BLD_DIR)/libcares.la $(PTHREAD_LIBS) + +# Not interested in coverage of test code, but linking the test binary needs the coverage option +@CODE_COVERAGE_RULES@ +arestest_LDFLAGS = $(CODE_COVERAGE_LDFLAGS) + +noinst_LTLIBRARIES = libgmock.la libgtest.la + +libgmock_la_SOURCES = $(GMOCK_DIR)/src/gmock-all.cc \ + $(GMOCK_DIR)/src/gmock-cardinalities.cc \ + $(GMOCK_DIR)/src/gmock-internal-utils.cc \ + $(GMOCK_DIR)/src/gmock-matchers.cc \ + $(GMOCK_DIR)/src/gmock-spec-builders.cc \ + $(GMOCK_DIR)/src/gmock.cc \ + $(GMOCK_DIR)/include/gmock/gmock-cardinalities.h \ + $(GMOCK_DIR)/include/gmock/gmock-generated-matchers.h \ + $(GMOCK_DIR)/include/gmock/gmock-more-actions.h \ + $(GMOCK_DIR)/include/gmock/gmock-actions.h \ + $(GMOCK_DIR)/include/gmock/gmock-generated-actions.h \ + $(GMOCK_DIR)/include/gmock/gmock-matchers.h \ + $(GMOCK_DIR)/include/gmock/gmock-generated-function-mockers.h \ + $(GMOCK_DIR)/include/gmock/gmock.h \ + $(GMOCK_DIR)/include/gmock/gmock-generated-nice-strict.h \ + $(GMOCK_DIR)/include/gmock/gmock-spec-builders.h \ + $(GMOCK_DIR)/include/gmock/internal/gmock-internal-utils.h \ + $(GMOCK_DIR)/include/gmock/internal/gmock-generated-internal-utils.h \ + $(GMOCK_DIR)/include/gmock/internal/gmock-port.h \ + $(GMOCK_DIR)/include/gmock/gmock-more-matchers.h + +libgmock_la_CPPFLAGS = -isystem $(GTEST_DIR)/include -I$(GTEST_DIR) -isystem $(GMOCK_DIR)/include -I$(GMOCK_DIR) + +libgtest_la_SOURCES = $(GTEST_DIR)/src/gtest-all.cc \ + $(GTEST_DIR)/src/gtest.cc \ + $(GTEST_DIR)/src/gtest-death-test.cc \ + $(GTEST_DIR)/src/gtest-filepath.cc \ + $(GTEST_DIR)/src/gtest-port.cc \ + $(GTEST_DIR)/src/gtest-printers.cc \ + $(GTEST_DIR)/src/gtest-test-part.cc \ + $(GTEST_DIR)/src/gtest-typed-test.cc \ + $(GTEST_DIR)/src/gtest-internal-inl.h \ + $(GTEST_DIR)/include/gtest/gtest-test-part.h \ + $(GTEST_DIR)/include/gtest/gtest-param-test.h \ + $(GTEST_DIR)/include/gtest/gtest-printers.h \ + $(GTEST_DIR)/include/gtest/gtest-message.h \ + $(GTEST_DIR)/include/gtest/gtest.h \ + $(GTEST_DIR)/include/gtest/gtest_pred_impl.h \ + $(GTEST_DIR)/include/gtest/gtest-death-test.h \ + $(GTEST_DIR)/include/gtest/gtest-spi.h \ + $(GTEST_DIR)/include/gtest/gtest-typed-test.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-filepath.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-param-util-generated.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-type-util.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-string.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-port.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-param-util.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-death-test-internal.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-linked_ptr.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-internal.h \ + $(GTEST_DIR)/include/gtest/internal/gtest-tuple.h \ + $(GTEST_DIR)/include/gtest/gtest_prod.h + +libgtest_la_CPPFLAGS = -isystem $(GTEST_DIR)/include -I$(GTEST_DIR) -isystem $(GMOCK_DIR)/include -I$(GMOCK_DIR) + +aresfuzz_SOURCES = $(FUZZSOURCES) +aresfuzz_LDADD = $(ARES_BLD_DIR)/libcares.la + +aresfuzzname_SOURCES = $(FUZZNAMESOURCES) +aresfuzzname_LDADD = $(ARES_BLD_DIR)/libcares.la + +dnsdump_SOURCES = $(DUMPSOURCES) +dnsdump_LDADD = $(ARES_BLD_DIR)/libcares.la + +test: check diff --git a/src/c-ares/test/Makefile.inc b/src/c-ares/test/Makefile.inc new file mode 100644 index 000000000..add55341b --- /dev/null +++ b/src/c-ares/test/Makefile.inc @@ -0,0 +1,32 @@ +TESTSOURCES = ares-test-main.cc \ + ares-test-init.cc \ + ares-test.cc \ + ares-test-ns.cc \ + ares-test-parse.cc \ + ares-test-parse-a.cc \ + ares-test-parse-aaaa.cc \ + ares-test-parse-mx.cc \ + ares-test-parse-naptr.cc \ + ares-test-parse-ns.cc \ + ares-test-parse-ptr.cc \ + ares-test-parse-soa.cc \ + ares-test-parse-srv.cc \ + ares-test-parse-txt.cc \ + ares-test-misc.cc \ + ares-test-live.cc \ + ares-test-mock.cc \ + ares-test-internal.cc \ + dns-proto.cc \ + dns-proto-test.cc + +TESTHEADERS = ares-test.h \ + dns-proto.h + +FUZZSOURCES = ares-test-fuzz.c \ + ares-fuzz.c + +FUZZNAMESOURCES = ares-test-fuzz-name.c \ + ares-fuzz.c + +DUMPSOURCES = dns-proto.cc \ + dns-dump.cc diff --git a/src/c-ares/test/Makefile.m32 b/src/c-ares/test/Makefile.m32 new file mode 100644 index 000000000..4c97674ca --- /dev/null +++ b/src/c-ares/test/Makefile.m32 @@ -0,0 +1,48 @@ +############################################################# +# +## Makefile for building arestest.exe with MingW32 (GCC-3.2) +## Use: make -f Makefile.m32 +# +######################################################## +CXX = g++ +LD = g++ + +# Where to find the c-ares source code; needed because the tests use library-internal headers +ARES_SRC_DIR = .. +# Where to find the built c-ares static library +ARES_BLD_DIR = .. +ARESLIB = $(ARES_BLD_DIR)/libcares.a +GMOCK_DIR = gmock-1.7.0 +GTEST_DIR = $(GMOCK_DIR)/gtest +CPPFLAGS = -I$(ARES_SRC_DIR) -I$(GTEST_DIR)/include -I$(GMOCK_DIR)/include -DCARES_STATICLIB +CXXFLAGS = -Wall $(PTHREAD_CFLAGS) -std=gnu++11 +LDFLAGS = +LDLIBS = -lwsock32 + +# Makefile.inc provides the TESTSOURCES and TESTHEADERS defines +include Makefile.inc + +OBJS := $(patsubst %.cc,%.o,$(strip $(TESTSOURCES))) + +all: arestest.exe + +arestest.exe: $(OBJS) gmock-all.o gtest-all.o + $(LD) $(LDFLAGS) -o $@ $^ -L$(ARES_BLD_DIR) -lcares $(LDLIBS) + +$(OBJS): $(TESTHEADERS) + +.cc.o: + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< +gmock-all.o: $(GMOCK_DIR)/src/gmock-all.cc + $(CXX) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(CPPFLAGS) $(CXXFLAGS) -c $< +gtest-all.o: $(GTEST_DIR)/src/gtest-all.cc + $(CXX) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(CPPFLAGS) $(CXXFLAGS) -c $< + +test: arestest.exe + ./arestest.exe +vtest: arestest.exe + ./arestest.exe -v + +clean: + $(RM) $(OBJS) gmock-all.o gtest-all.o arestest.exe + diff --git a/src/c-ares/test/Makefile.msvc b/src/c-ares/test/Makefile.msvc new file mode 100644 index 000000000..d0745e967 --- /dev/null +++ b/src/c-ares/test/Makefile.msvc @@ -0,0 +1,304 @@ +# Permission to use, copy, modify, and distribute this +# software and its documentation for any purpose and without +# fee is hereby granted, provided that the above copyright +# notice appear in all copies and that both that copyright +# notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in +# advertising or publicity pertaining to distribution of the +# software without specific, written prior permission. +# M.I.T. makes no representations about the suitability of +# this software for any purpose. It is provided "as is" +# without express or implied warranty. + +# ------------------------------------------------------------------------------ +# +# Makefile for building c-ares test suite with MSVC. +# +# Usage: nmake /f makefile.msvc CFG= +# +# can be one of: [ lib-release | lib-debug | dll-release | dll-debug } +# can be one of: [ all | arestest | clean } +# +# If is not specified then all targets are built. +# If is not specified then lib-debug will be assumed. +# +# This makefile must be processed from the subdir where it is located. +# +# All results are generated below a subdirectory named msvcXXX. +# +# ------------------------------------------------------------------------------ + + +# ------------------------------------------------ +# c-ares static and dynamic libraries common base +# file names for release and debug configurations +# ------------------------------------------------ + +LIBNAME = cares +STA_LIB_REL = lib$(LIBNAME) +DYN_LIB_REL = $(LIBNAME) +STA_LIB_DBG = $(STA_LIB_REL)d +DYN_LIB_DBG = $(DYN_LIB_REL)d + +# ------------------------------------------- +# Base names for c-ares DLL import libraries +# ------------------------------------------- + +IMP_LIB_REL = $(DYN_LIB_REL) +IMP_LIB_DBG = $(DYN_LIB_DBG) + +# -------------------------- +# Runtime library selection +# -------------------------- + +RTLIB = /MD +RTLIBD = /MDd + +!IF "$(RTLIBCFG)" == "static" +RTLIB = /MT +RTLIBD = /MTd +!ENDIF + +# -------------------------------------------------------- +# Detect compiler version. +# -------------------------------------------------------- +!INCLUDE ..\msvc_ver.inc + +# --------------------------------------------------------- +# Verify that current subdir is below the c-ares source one +# --------------------------------------------------------- + +!IF ! EXIST(..\ares_init.c) +! MESSAGE Can not process Makefile.msvc from outside of c-ares test subdirectory. +! MESSAGE Change to the subdirectory where Makefile.msvc is found, and try again. +! ERROR See previous message. +!ENDIF + +# ------------------------------------------------------------------ +# Base subdir is the common root from which other subdirs will hang, +# the name depends on MSVC version being used when building c-ares. +# ------------------------------------------------------------------ + +BASE_DIR = .\$(CC_VERS_STR) +# Look for a built library of the same configuration in the directory above. +LIB_BASE_DIR = ..\$(CC_VERS_STR) + +# ---------------------------------------- +# Subdir holding sources for all projects +# ---------------------------------------- + +SRCDIR = . + +# ------------------------- +# Configuration validation +# ------------------------- + +!IF "$(CFG)" == "" +CFG = lib-debug +!ENDIF + +VALID_CFGSET = FALSE +!IF "$(CFG)" == "lib-release" || "$(CFG)" == "lib-debug" || \ + "$(CFG)" == "dll-release" || "$(CFG)" == "dll-debug" +VALID_CFGSET = TRUE +!ENDIF + +!IF "$(VALID_CFGSET)" == "FALSE" +! MESSAGE MSVC c-ares makefile +! MESSAGE +! MESSAGE Usage: nmake /f makefile.msvc CFG= +! MESSAGE +! MESSAGE can be one of: [ lib-release | lib-debug | dll-release | dll-debug } +! MESSAGE can be one of: [ all | arestest | clean } +! MESSAGE +! MESSAGE If is not specified then all targets are built. +! MESSAGE If is not specified then lib-debug will be assumed. +! MESSAGE +! ERROR Choose a valid configuration. +!ENDIF + +# -------------------------------------------------------- +# Project subdirs independent of configuration being used +# -------------------------------------------------------- + +PROG_DIR = $(BASE_DIR)\arestest +LIB_DIR = $(LIB_BASE_DIR)\cares + +GMOCK_DIR = gmock-1.7.0 +GTEST_DIR = $(GMOCK_DIR)\gtest +GMOCK_SRC_DIR = $(GMOCK_DIR)\src +GTEST_SRC_DIR = $(GTEST_DIR)\src + +# --------------------------------------------------- +# Subdirs which are configuration dependent are only +# defined when a valid configuration has been given. +# --------------------------------------------------- + +PROG_OUTDIR = $(PROG_DIR)\$(CFG) +PROG_OBJDIR = $(PROG_OUTDIR)\obj +LIB_OUTDIR = $(LIB_DIR)\$(CFG) + + +# ------------------------------------- +# TCP/IP stack settings +# ------------------------------------- +CFLAGS = /DWIN32 +EX_LIBS_REL = ws2_32.lib advapi32.lib kernel32.lib +EX_LIBS_DBG = ws2_32.lib advapi32.lib kernel32.lib + +# ----------------------------------------- +# Switches that depend on compiler version +# ----------------------------------------- + +!IF $(CC_VERS_NUM) == 60 +PDB_NONE = /pdb:none +PDBTYPE_CONSOLIDATE = /pdbtype:consolidate +!ELSE +!UNDEF PDB_NONE +!UNDEF PDBTYPE_CONSOLIDATE +!ENDIF + +!IF $(CC_VERS_NUM) <= 70 +RT_ERROR_CHECKING = /GZ +!ELSE +RT_ERROR_CHECKING = /RTCsu +!ENDIF + +# ---------------------------- +# Assorted commands and flags +# ---------------------------- + +CC_CMD_REL = cl.exe /nologo $(RTLIB) /DNDEBUG /O2 /D_CRT_NONSTDC_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS +CC_CMD_DBG = cl.exe /nologo $(RTLIBD) /D_DEBUG /Od /Zi /D_CRT_NONSTDC_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS $(RT_ERROR_CHECKING) +CC_CFLAGS = $(CFLAGS) /I. /I.. /I $(GTEST_DIR)/include /I $(GMOCK_DIR)/include /W3 /EHsc /FD + +RC_CMD_REL = rc.exe /l 0x409 /d "NDEBUG" +RC_CMD_DBG = rc.exe /l 0x409 /d "_DEBUG" + +LINK_CMD_LIB = link.exe /lib /nologo +LINK_CMD_DLL = link.exe /dll /nologo /incremental:no /fixed:no +LINK_CMD_EXE = link.exe /nologo /incremental:no /fixed:no /subsystem:console + +LINK_CMD_EXE_REL = $(LINK_CMD_EXE) /release $(PDB_NONE) +LINK_CMD_EXE_DBG = $(LINK_CMD_EXE) /debug $(PDBTYPE_CONSOLIDATE) + +# --------------------------------- +# Configuration dependent settings +# --------------------------------- + +!IF "$(CFG)" == "lib-release" +CARES_TARGET = $(STA_LIB_REL).lib +CARES_CFLAGS = /DCARES_BUILDING_LIBRARY /DCARES_STATICLIB +CARES_LFLAGS = +SPROG_CFLAGS = /DCARES_STATICLIB +SPROG_LFLAGS = /libpath:$(LIB_OUTDIR) $(EX_LIBS_REL) $(STA_LIB_REL).lib +CARES_LINK = $(LINK_CMD_LIB) +SPROG_LINK = $(LINK_CMD_EXE_REL) +CC_CMD = $(CC_CMD_REL) +!ENDIF + +!IF "$(CFG)" == "lib-debug" +CARES_TARGET = $(STA_LIB_DBG).lib +CARES_CFLAGS = /DCARES_BUILDING_LIBRARY /DCARES_STATICLIB /DDEBUGBUILD +CARES_LFLAGS = +SPROG_CFLAGS = /DCARES_STATICLIB +SPROG_LFLAGS = /libpath:$(LIB_OUTDIR) $(EX_LIBS_DBG) $(STA_LIB_DBG).lib +CARES_LINK = $(LINK_CMD_LIB) +SPROG_LINK = $(LINK_CMD_EXE_DBG) +CC_CMD = $(CC_CMD_DBG) +!ENDIF + +!IF "$(CFG)" == "dll-release" +CARES_TARGET = $(DYN_LIB_REL).dll +CARES_CFLAGS = /DCARES_BUILDING_LIBRARY +CARES_LFLAGS = /release $(EX_LIBS_REL) /implib:$(PROG_OUTDIR)\$(IMP_LIB_REL).lib $(PDB_NONE) +SPROG_CFLAGS = +SPROG_LFLAGS = /libpath:$(LIB_OUTDIR) $(EX_LIBS_REL) $(IMP_LIB_REL).lib +CARES_LINK = $(LINK_CMD_DLL) +SPROG_LINK = $(LINK_CMD_EXE_REL) +CC_CMD = $(CC_CMD_REL) +USE_RES_FILE = TRUE +RC_CMD = $(RC_CMD_REL) +!ENDIF + +!IF "$(CFG)" == "dll-debug" +CARES_TARGET = $(DYN_LIB_DBG).dll +CARES_CFLAGS = /DCARES_BUILDING_LIBRARY /DDEBUGBUILD +CARES_LFLAGS = /debug $(EX_LIBS_DBG) /implib:$(PROG_OUTDIR)\$(IMP_LIB_DBG).lib /pdb:$(PROG_OUTDIR)\$(DYN_LIB_DBG).pdb $(PDBTYPE_CONSOLIDATE) +SPROG_CFLAGS = +SPROG_LFLAGS = /libpath:$(LIB_OUTDIR) $(EX_LIBS_DBG) $(IMP_LIB_DBG).lib +CARES_LINK = $(LINK_CMD_DLL) +SPROG_LINK = $(LINK_CMD_EXE_DBG) +CC_CMD = $(CC_CMD_DBG) +USE_RES_FILE = TRUE +RC_CMD = $(RC_CMD_DBG) +!ENDIF + +# -------------------------------------------- +# Makefile.inc provides lists of source files +# -------------------------------------------- + +!INCLUDE .\Makefile.inc + +# ---------------------------- +# Build lists of object files +# ---------------------------- + +!IF [ECHO PROG_OBJS=^$(PROG_OBJDIR)\$(TESTSOURCES: = $(PROG_OBJDIR^)\) > .\prog_objs.inc] == 0 +!INCLUDE .\prog_objs.inc +!IF [DEL .\prog_objs.inc] +!ENDIF +!ELSE +!ERROR Problem generating PROG_OBJS list. +!ENDIF +PROG_OBJS = $(PROG_OBJS:.cc=.obj) +GTEST_OBJ = $(PROG_OBJDIR)\gtest-all.obj +GMOCK_OBJ = $(PROG_OBJDIR)\gmock-all.obj + +# -------------------------------- +# Only our custom inference rules +# -------------------------------- + +.SUFFIXES: +.SUFFIXES: .cc + +{$(SRCDIR)}.cc{$(PROG_OBJDIR)}.obj: + $(CC_CMD) $(CC_CFLAGS) $(SPROG_CFLAGS) /Fo$@ /Fd$(@D)\ /c $< +{$(GMOCK_SRC_DIR)}.cc{$(PROG_OBJDIR)}.obj: + $(CC_CMD) $(CC_CFLAGS) $(SPROG_CFLAGS) /I $(GMOCK_DIR) /Fo$@ /Fd$(@D)\ /c $< +{$(GTEST_SRC_DIR)}.cc{$(PROG_OBJDIR)}.obj: + $(CC_CMD) $(CC_CFLAGS) $(SPROG_CFLAGS) /I $(GTEST_DIR) /Fo$@ /Fd$(@D)\ /c $< + + +# --------------------------------------------------------------------- +# Main targets +# --------------------------------------------------------------------- + +ALL: arestest + @ + +test: arestest + $(PROG_OUTDIR)\arestest +vtest: arestest + $(PROG_OUTDIR)\arestest -v + +arestest: $(TESTSOURCES) $(PROB_OUTDIR) $(PROG_OBJDIR) $(PROG_OBJS) $(GTEST_OBJ) $(GMOCK_OBJ) + $(SPROG_LINK) $(SPROG_LFLAGS) /out:$(PROG_OUTDIR)\arestest.exe $(PROG_OBJS) $(GTEST_OBJ) $(GMOCK_OBJ) + @if exist $(PROG_OUTDIR)\arestest.exe.manifest mt -nologo -manifest $(PROG_OUTDIR)\arestest.exe.manifest -outputresource:$(PROG_OUTDIR)\arestest.exe;1 + +$(PROG_OUTDIR): $(PROG_DIR) + @if not exist $(PROG_OUTDIR) mkdir $(PROG_OUTDIR) + +$(PROG_OBJDIR): $(PROG_OUTDIR) + @if not exist $(PROG_OBJDIR) mkdir $(PROG_OBJDIR) + +clean: + @-RMDIR /S /Q $(PROG_OUTDIR) >NUL 2>&1 + +$(BASE_DIR): + @if not exist $(BASE_DIR) mkdir $(BASE_DIR) + +$(PROG_DIR): $(BASE_DIR) + @if not exist $(PROG_DIR) mkdir $(PROG_DIR) + +# End of Makefile.msvc diff --git a/src/c-ares/test/README.md b/src/c-ares/test/README.md new file mode 100644 index 000000000..988abb674 --- /dev/null +++ b/src/c-ares/test/README.md @@ -0,0 +1,161 @@ +c-ares Unit Test Suite +====================== + + +This directory holds unit tests for the c-ares library. To build the tests: + + - Build the main c-ares library first, in the directory above this. To + enable tests of internal functions, configure the library build to expose + hidden symbols with `./configure --disable-symbol-hiding`. + - Generate a `configure` file by running `autoreconf -iv` (which requires + a local installation of + [autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html)). + - `./configure` + - `make` + - Run the tests with `./arestest`, or `./arestest -v` for extra debug info. + +Points to note: + + - The tests are written in C++11, and so need a C++ compiler that supports + this. To avoid adding this as a requirement for the library, the + configuration and build of the tests is independent from the library. + - The tests include some live queries, which will fail when run on a machine + without internet connectivity. To skip live tests, run with + `./arestest --gtest_filter=-*.Live*`. + - The tests include queries of a mock DNS server. This server listens on port + 5300 by default, but the port can be changed with the `-p 5300` option to + `arestest`. + + +Test Types +---------- + +The test suite includes various different types of test. + + - There are live tests (`ares-test-live.cc`), which assume that the + current machine has a valid DNS setup and connection to the + internet; these tests issue queries for real domains but don't + particularly check what gets returned. The tests will fail on + an offline machine. + - There are some mock tests (`ares-test-mock.cc`) that set up a fake DNS + server and inject its port into the c-ares library configuration. + These tests allow specific response messages to be crafted and + injected, and so are likely to be used for many more tests in + future. + - To make this generation/injection easier, the `dns-proto.h` + file includes C++ helper classes for building DNS packets. + - Other library entrypoints that don't require network activity + (e.g. `ares_parse_*_reply`) are tested directly. + - A couple of the tests use a helper method of the test fixture to + inject memory allocation failures, using a recent change to the + c-ares library that allows override of `malloc`/`free`. + - There are some tests of the internal entrypoints of the library + (`ares-test-internal.c`), but these are only enabled if the library + was configured with `--disable-symbol-hiding` and/or + `--enable-expose-statics`. + - There is also an entrypoint to allow Clang's + [libfuzzer](http://llvm.org/docs/LibFuzzer.html) to drive + the packet parsing code in `ares_parse_*_reply`, together with a + standalone wrapper for it (`./aresfuzz`) to allow use of command + line fuzzers (such as [afl-fuzz](http://lcamtuf.coredump.cx/afl/)) + for further [fuzz testing](#fuzzing). + + +Code Coverage Information +------------------------- + +To generate code coverage information: + + - Configure both the library and the tests with `./configure + --enable-code-coverage` before building. This requires the relevant code + coverage tools ([gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html), + [lcov](http://ltp.sourceforge.net/coverage/lcov.php)) to be installed locally. + - Run the tests with `test/arestest`. + - Generate code coverage output with `make code-coverage-capture` in the + library directory (i.e. not in `test/`). + + +Fuzzing +------- + +### libFuzzer + +To fuzz the packet parsing code with libFuzzer, follow the main +[libFuzzer build instructions](http://llvm.org/docs/LibFuzzer.html#building): + + - Configure the c-ares library and test suite with a recent Clang and a sanitizer, for example: + + ```console + % export CFLAGS="-fsanitize=address -fsanitize-coverage=edge" + % export CC=clang + % ./configure --disable-shared && make + ``` + - Download and build the libFuzzer code: + + ```console + % cd test + % svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer + % clang++ -c -g -O2 -std=c++11 Fuzzer/*.cpp -IFuzzer + % ar ruv libFuzzer.a Fuzzer*.o + ``` + - Link each of the fuzzer entrypoints in with `ares-fuzz.cc`: + + ``` + % $CC $CFLAGS -I.. -c ares-test-fuzz.c + % $CC $CFLAGS -I.. -c ares-test-fuzz-name.c + % clang++ $CFLAGS ares-test-fuzz.o ../.libs/libcares.a libFuzzer.a -o ares-libfuzzer + % clang++ $CFLAGS ares-test-fuzz-name.o ../.libs/libcares.a libFuzzer.a -o ares-libfuzzer-name + ``` + - Run the fuzzer using the starting corpus with: + + ```console + % ./ares-libfuzzer fuzzinput/ # OR + % ./ares-libfuzzer-name fuzznames/ + ``` + +### AFL + +To fuzz using AFL, follow the +[AFL quick start guide](http://lcamtuf.coredump.cx/afl/QuickStartGuide.txt): + + - Download and build AFL. + - Configure the c-ares library and test tool to use AFL's compiler wrappers: + + ```console + % export CC=$AFLDIR/afl-gcc + % ./configure --disable-shared && make + % cd test && ./configure && make aresfuzz aresfuzzname + ``` + + - Run the AFL fuzzer against the starting corpus: + + ```console + % mkdir fuzzoutput + % $AFLDIR/afl-fuzz -i fuzzinput -o fuzzoutput -- ./aresfuzz # OR + % $AFLDIR/afl-fuzz -i fuzznames -o fuzzoutput -- ./aresfuzzname + ``` + +### AFL Persistent Mode + +If a recent version of Clang is available, AFL can use its built-in compiler +instrumentation; this configuration also allows the use of a (much) faster +persistent mode, where multiple fuzz inputs are run for each process invocation. + + - Download and build a recent AFL, and run `make` in the `llvm_mode` + subdirectory to ensure that `afl-clang-fast` gets built. + - Configure the c-ares library and test tool to use AFL's clang wrappers that + use compiler instrumentation: + + ```console + % export CC=$AFLDIR/afl-clang-fast + % ./configure --disable-shared && make + % cd test && ./configure && make aresfuzz + ``` + + - Run the AFL fuzzer (in persistent mode) against the starting corpus: + + ```console + % mkdir fuzzoutput + % $AFLDIR/afl-fuzz -i fuzzinput -o fuzzoutput -- ./aresfuzz + ``` + diff --git a/src/c-ares/test/ares-fuzz.c b/src/c-ares/test/ares-fuzz.c new file mode 100644 index 000000000..92761286b --- /dev/null +++ b/src/c-ares/test/ares-fuzz.c @@ -0,0 +1,58 @@ +/* + * General driver to allow command-line fuzzer (i.e. afl) to + * exercise the libFuzzer entrypoint. + */ + +#include +#include +#include +#include +#include +#include + +#define kMaxAflInputSize (1 << 20) +static unsigned char afl_buffer[kMaxAflInputSize]; + +#ifdef __AFL_LOOP +/* If we are built with afl-clang-fast, use persistent mode */ +#define KEEP_FUZZING(count) __AFL_LOOP(1000) +#else +/* If we are built with afl-clang, execute each input once */ +#define KEEP_FUZZING(count) ((count) < 1) +#endif + +/* In ares-test-fuzz.c: */ +int LLVMFuzzerTestOneInput(const unsigned char *data, unsigned long size); + +static void ProcessFile(int fd) { + ssize_t count = read(fd, afl_buffer, kMaxAflInputSize); + /* + * Make a copy of the data so that it's not part of a larger + * buffer (where buffer overflows would go unnoticed). + */ + unsigned char *copied_data = (unsigned char *)malloc(count); + LLVMFuzzerTestOneInput(copied_data, count); + free(copied_data); +} + +int main(int argc, char *argv[]) { + if (argc == 1) { + int count = 0; + while (KEEP_FUZZING(count)) { + ProcessFile(fileno(stdin)); + count++; + } + } else { + int ii; + for (ii = 1; ii < argc; ++ii) { + int fd = open(argv[ii], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open '%s'\n", argv[ii]); + continue; + } + ProcessFile(fd); + close(fd); + } + } + return 0; +} diff --git a/src/c-ares/test/ares-test-fuzz-name.c b/src/c-ares/test/ares-test-fuzz-name.c new file mode 100644 index 000000000..378da3739 --- /dev/null +++ b/src/c-ares/test/ares-test-fuzz-name.c @@ -0,0 +1,23 @@ +#include +#include +#include + +#include "ares.h" +// Include ares internal file for DNS protocol constants +#include "nameser.h" + +// Entrypoint for Clang's libfuzzer, exercising query creation. +int LLVMFuzzerTestOneInput(const unsigned char *data, + unsigned long size) { + // Null terminate the data. + char *name = malloc(size + 1); + name[size] = '\0'; + memcpy(name, data, size); + + unsigned char *buf = NULL; + int buflen = 0; + ares_create_query(name, ns_c_in, ns_t_aaaa, 1234, 0, &buf, &buflen, 1024); + free(buf); + free(name); + return 0; +} diff --git a/src/c-ares/test/ares-test-fuzz.c b/src/c-ares/test/ares-test-fuzz.c new file mode 100644 index 000000000..2b5c16151 --- /dev/null +++ b/src/c-ares/test/ares-test-fuzz.c @@ -0,0 +1,46 @@ +#include + +#include "ares.h" + +// Entrypoint for Clang's libfuzzer +int LLVMFuzzerTestOneInput(const unsigned char *data, + unsigned long size) { + // Feed the data into each of the ares_parse_*_reply functions. + struct hostent *host = NULL; + struct ares_addrttl info[5]; + int count = 5; + ares_parse_a_reply(data, size, &host, info, &count); + if (host) ares_free_hostent(host); + + host = NULL; + struct ares_addr6ttl info6[5]; + count = 5; + ares_parse_aaaa_reply(data, size, &host, info6, &count); + if (host) ares_free_hostent(host); + + host = NULL; + unsigned char addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + ares_parse_ptr_reply(data, size, addrv4, sizeof(addrv4), AF_INET, &host); + if (host) ares_free_hostent(host); + + host = NULL; + ares_parse_ns_reply(data, size, &host); + if (host) ares_free_hostent(host); + + struct ares_srv_reply* srv = NULL; + ares_parse_srv_reply(data, size, &srv); + if (srv) ares_free_data(srv); + + struct ares_mx_reply* mx = NULL; + ares_parse_mx_reply(data, size, &mx); + if (mx) ares_free_data(mx); + + struct ares_txt_reply* txt = NULL; + ares_parse_txt_reply(data, size, &txt); + if (txt) ares_free_data(txt); + + struct ares_soa_reply* soa = NULL; + ares_parse_soa_reply(data, size, &soa); + if (soa) ares_free_data(soa); + return 0; +} diff --git a/src/c-ares/test/ares-test-init.cc b/src/c-ares/test/ares-test-init.cc new file mode 100644 index 000000000..66570ace5 --- /dev/null +++ b/src/c-ares/test/ares-test-init.cc @@ -0,0 +1,595 @@ +#include "ares-test.h" + +// library initialization is only needed for windows builds +#ifdef WIN32 +#define EXPECTED_NONINIT ARES_ENOTINITIALIZED +#else +#define EXPECTED_NONINIT ARES_SUCCESS +#endif + +namespace ares { +namespace test { + +TEST(LibraryInit, Basic) { + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); + EXPECT_EQ(ARES_SUCCESS, ares_library_init(ARES_LIB_INIT_ALL)); + EXPECT_EQ(ARES_SUCCESS, ares_library_initialized()); + ares_library_cleanup(); + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); +} + +TEST(LibraryInit, UnexpectedCleanup) { + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); + ares_library_cleanup(); + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); +} + +TEST(LibraryInit, DISABLED_InvalidParam) { + // TODO: police flags argument to ares_library_init() + EXPECT_EQ(ARES_EBADQUERY, ares_library_init(ARES_LIB_INIT_ALL << 2)); + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); + ares_library_cleanup(); +} + +TEST(LibraryInit, Nested) { + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); + EXPECT_EQ(ARES_SUCCESS, ares_library_init(ARES_LIB_INIT_ALL)); + EXPECT_EQ(ARES_SUCCESS, ares_library_initialized()); + EXPECT_EQ(ARES_SUCCESS, ares_library_init(ARES_LIB_INIT_ALL)); + EXPECT_EQ(ARES_SUCCESS, ares_library_initialized()); + ares_library_cleanup(); + EXPECT_EQ(ARES_SUCCESS, ares_library_initialized()); + ares_library_cleanup(); + EXPECT_EQ(EXPECTED_NONINIT, ares_library_initialized()); +} + +TEST(LibraryInit, BasicChannelInit) { + EXPECT_EQ(ARES_SUCCESS, ares_library_init(ARES_LIB_INIT_ALL)); + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + EXPECT_NE(nullptr, channel); + ares_destroy(channel); + ares_library_cleanup(); +} + +TEST_F(LibraryTest, OptionsChannelInit) { + struct ares_options opts = {0}; + int optmask = 0; + opts.flags = ARES_FLAG_USEVC | ARES_FLAG_PRIMARY; + optmask |= ARES_OPT_FLAGS; + opts.timeout = 2000; + optmask |= ARES_OPT_TIMEOUTMS; + opts.tries = 2; + optmask |= ARES_OPT_TRIES; + opts.ndots = 4; + optmask |= ARES_OPT_NDOTS; + opts.udp_port = 54; + optmask |= ARES_OPT_UDP_PORT; + opts.tcp_port = 54; + optmask |= ARES_OPT_TCP_PORT; + opts.socket_send_buffer_size = 514; + optmask |= ARES_OPT_SOCK_SNDBUF; + opts.socket_receive_buffer_size = 514; + optmask |= ARES_OPT_SOCK_RCVBUF; + opts.ednspsz = 1280; + optmask |= ARES_OPT_EDNSPSZ; + opts.nservers = 2; + opts.servers = (struct in_addr *)malloc(opts.nservers * sizeof(struct in_addr)); + opts.servers[0].s_addr = htonl(0x01020304); + opts.servers[1].s_addr = htonl(0x02030405); + optmask |= ARES_OPT_SERVERS; + opts.ndomains = 2; + opts.domains = (char **)malloc(opts.ndomains * sizeof(char *)); + opts.domains[0] = strdup("example.com"); + opts.domains[1] = strdup("example2.com"); + optmask |= ARES_OPT_DOMAINS; + opts.lookups = strdup("b"); + optmask |= ARES_OPT_LOOKUPS; + optmask |= ARES_OPT_ROTATE; + + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); + EXPECT_NE(nullptr, channel); + + ares_channel channel2 = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_dup(&channel2, channel)); + + struct ares_options opts2 = {0}; + int optmask2 = 0; + EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel2, &opts2, &optmask2)); + + // Note that not all opts-settable fields are saved (e.g. + // ednspsz, socket_{send,receive}_buffer_size). + EXPECT_EQ(opts.flags, opts2.flags); + EXPECT_EQ(opts.timeout, opts2.timeout); + EXPECT_EQ(opts.tries, opts2.tries); + EXPECT_EQ(opts.ndots, opts2.ndots); + EXPECT_EQ(opts.udp_port, opts2.udp_port); + EXPECT_EQ(opts.tcp_port, opts2.tcp_port); + EXPECT_EQ(1, opts2.nservers); // Truncated by ARES_FLAG_PRIMARY + EXPECT_EQ(opts.servers[0].s_addr, opts2.servers[0].s_addr); + EXPECT_EQ(opts.ndomains, opts2.ndomains); + EXPECT_EQ(std::string(opts.domains[0]), std::string(opts2.domains[0])); + EXPECT_EQ(std::string(opts.domains[1]), std::string(opts2.domains[1])); + EXPECT_EQ(std::string(opts.lookups), std::string(opts2.lookups)); + + ares_destroy_options(&opts); + ares_destroy_options(&opts2); + ares_destroy(channel); + ares_destroy(channel2); +} + +TEST_F(LibraryTest, ChannelAllocFail) { + ares_channel channel; + for (int ii = 1; ii <= 25; ii++) { + ClearFails(); + SetAllocFail(ii); + channel = nullptr; + int rc = ares_init(&channel); + // The number of allocations depends on local environment, so don't expect ENOMEM. + if (rc == ARES_ENOMEM) { + EXPECT_EQ(nullptr, channel); + } else { + ares_destroy(channel); + } + } +} + +TEST_F(LibraryTest, OptionsChannelAllocFail) { + struct ares_options opts = {0}; + int optmask = 0; + opts.flags = ARES_FLAG_USEVC; + optmask |= ARES_OPT_FLAGS; + opts.timeout = 2; + optmask |= ARES_OPT_TIMEOUT; + opts.tries = 2; + optmask |= ARES_OPT_TRIES; + opts.ndots = 4; + optmask |= ARES_OPT_NDOTS; + opts.udp_port = 54; + optmask |= ARES_OPT_UDP_PORT; + opts.tcp_port = 54; + optmask |= ARES_OPT_TCP_PORT; + opts.socket_send_buffer_size = 514; + optmask |= ARES_OPT_SOCK_SNDBUF; + opts.socket_receive_buffer_size = 514; + optmask |= ARES_OPT_SOCK_RCVBUF; + opts.ednspsz = 1280; + optmask |= ARES_OPT_EDNSPSZ; + opts.nservers = 2; + opts.servers = (struct in_addr *)malloc(opts.nservers * sizeof(struct in_addr)); + opts.servers[0].s_addr = htonl(0x01020304); + opts.servers[1].s_addr = htonl(0x02030405); + optmask |= ARES_OPT_SERVERS; + opts.ndomains = 2; + opts.domains = (char **)malloc(opts.ndomains * sizeof(char *)); + opts.domains[0] = strdup("example.com"); + opts.domains[1] = strdup("example2.com"); + optmask |= ARES_OPT_DOMAINS; + opts.lookups = strdup("b"); + optmask |= ARES_OPT_LOOKUPS; + optmask |= ARES_OPT_ROTATE; + + ares_channel channel = nullptr; + for (int ii = 1; ii <= 8; ii++) { + ClearFails(); + SetAllocFail(ii); + int rc = ares_init_options(&channel, &opts, optmask); + if (rc == ARES_ENOMEM) { + EXPECT_EQ(nullptr, channel); + } else { + EXPECT_EQ(ARES_SUCCESS, rc); + ares_destroy(channel); + channel = nullptr; + } + } + ClearFails(); + + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); + EXPECT_NE(nullptr, channel); + + // Add some servers and a sortlist for flavour. + EXPECT_EQ(ARES_SUCCESS, + ares_set_servers_csv(channel, "1.2.3.4,0102:0304:0506:0708:0910:1112:1314:1516,2.3.4.5")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel, "1.2.3.4 2.3.4.5")); + + ares_channel channel2 = nullptr; + for (int ii = 1; ii <= 18; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_dup(&channel2, channel)) << ii; + EXPECT_EQ(nullptr, channel2) << ii; + } + + struct ares_options opts2; + int optmask2 = 0; + for (int ii = 1; ii <= 6; ii++) { + memset(&opts2, 0, sizeof(opts2)); + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_save_options(channel, &opts2, &optmask2)) << ii; + // May still have allocations even after ARES_ENOMEM return code. + ares_destroy_options(&opts2); + } + ares_destroy_options(&opts); + ares_destroy(channel); +} + +TEST_F(LibraryTest, FailChannelInit) { + EXPECT_EQ(ARES_SUCCESS, + ares_library_init_mem(ARES_LIB_INIT_ALL, + &LibraryTest::amalloc, + &LibraryTest::afree, + &LibraryTest::arealloc)); + SetAllocFail(1); + ares_channel channel = nullptr; + EXPECT_EQ(ARES_ENOMEM, ares_init(&channel)); + EXPECT_EQ(nullptr, channel); + ares_library_cleanup(); +} + +#ifndef WIN32 +TEST_F(LibraryTest, EnvInit) { + ares_channel channel = nullptr; + EnvValue v1("LOCALDOMAIN", "this.is.local"); + EnvValue v2("RES_OPTIONS", "options debug ndots:3 retry:3 rotate retrans:2"); + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + ares_destroy(channel); +} + +TEST_F(LibraryTest, EnvInitAllocFail) { + ares_channel channel; + EnvValue v1("LOCALDOMAIN", "this.is.local"); + EnvValue v2("RES_OPTIONS", "options debug ndots:3 retry:3 rotate retrans:2"); + for (int ii = 1; ii <= 10; ii++) { + ClearFails(); + SetAllocFail(ii); + channel = nullptr; + int rc = ares_init(&channel); + if (rc == ARES_SUCCESS) { + ares_destroy(channel); + } else { + EXPECT_EQ(ARES_ENOMEM, rc); + } + } +} +#endif + +TEST_F(DefaultChannelTest, SetAddresses) { + ares_set_local_ip4(channel_, 0x01020304); + byte addr6[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; + ares_set_local_ip6(channel_, addr6); + ares_set_local_dev(channel_, "dummy"); +} + +TEST_F(DefaultChannelTest, SetSortlistFailures) { + EXPECT_EQ(ARES_ENODATA, ares_set_sortlist(nullptr, "1.2.3.4")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "xyzzy ; lwk")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "xyzzy ; 0x123")); +} + +TEST_F(DefaultChannelTest, SetSortlistVariants) { + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "1.2.3.4")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "1.2.3.4 ; 2.3.4.5")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "129.1.1.1")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "192.1.1.1")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "224.1.1.1")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "225.1.1.1")); +} + +TEST_F(DefaultChannelTest, SetSortlistAllocFail) { + for (int ii = 1; ii <= 3; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_set_sortlist(channel_, "12.13.0.0/16 1234::5678/40 1.2.3.4")) << ii; + } +} + +#ifdef USE_WINSOCK +TEST(Init, NoLibraryInit) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_ENOTINITIALIZED, ares_init(&channel)); +} +#endif + +#ifdef HAVE_CONTAINER +// These tests rely on the ability of non-root users to create a chroot +// using Linux namespaces. + + +// The library uses a variety of information sources to initialize a channel, +// in particular to determine: +// - search: the search domains to use +// - servers: the name servers to use +// - lookup: whether to check files or DNS or both (e.g. "fb") +// - options: various resolver options +// - sortlist: the order of preference for IP addresses +// +// The first source from the following list is used: +// - init_by_options(): explicitly specified values in struct ares_options +// - init_by_environment(): values from the environment: +// - LOCALDOMAIN -> search (single value) +// - RES_OPTIONS -> options +// - init_by_resolv_conf(): values from various config files: +// - /etc/resolv.conf -> search, lookup, servers, sortlist, options +// - /etc/nsswitch.conf -> lookup +// - /etc/host.conf -> lookup +// - /etc/svc.conf -> lookup +// - init_by_defaults(): fallback values: +// - gethostname(3) -> domain +// - "fb" -> lookup + +NameContentList filelist = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" + "sortlist 1.2.3.4/16 2.3.4.5\n" + "search first.com second.com\n"}, + {"/etc/hosts", "3.4.5.6 ahostname.com\n"}, + {"/etc/nsswitch.conf", "hosts: files\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerChannelInit, + "myhostname", "mydomainname.org", filelist) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + std::vector actual = GetNameServers(channel); + std::vector expected = {"1.2.3.4"}; + EXPECT_EQ(expected, actual); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(2, opts.ndomains); + EXPECT_EQ(std::string("first.com"), std::string(opts.domains[0])); + EXPECT_EQ(std::string("second.com"), std::string(opts.domains[1])); + ares_destroy_options(&opts); + + HostResult result; + ares_gethostbyname(channel, "ahostname.com", AF_INET, HostCallback, &result); + ProcessWork(channel, NoExtraFDs, nullptr); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'ahostname.com' aliases=[] addrs=[3.4.5.6]}", ss.str()); + + ares_destroy(channel); + return HasFailure(); +} + +CONTAINED_TEST_F(LibraryTest, ContainerSortlistOptionInit, + "myhostname", "mydomainname.org", filelist) { + ares_channel channel = nullptr; + struct ares_options opts = {0}; + int optmask = 0; + optmask |= ARES_OPT_SORTLIST; + opts.nsort = 0; + // Explicitly specifying an empty sortlist in the options should override the + // environment. + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(0, opts.nsort); + EXPECT_EQ(nullptr, opts.sortlist); + EXPECT_EQ(ARES_OPT_SORTLIST, (optmask & ARES_OPT_SORTLIST)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +NameContentList fullresolv = { + {"/etc/resolv.conf", " nameserver 1.2.3.4 \n" + "search first.com second.com\n" + "lookup bind\n" + "options debug ndots:5\n" + "sortlist 1.2.3.4/16 2.3.4.5\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerFullResolvInit, + "myhostname", "mydomainname.org", fullresolv) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(std::string("b"), std::string(opts.lookups)); + EXPECT_EQ(5, opts.ndots); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +NameContentList hostconf = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" + "sortlist1.2.3.4\n" // malformed line + "search first.com second.com\n"}, + {"/etc/host.conf", "order bind hosts\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerHostConfInit, + "myhostname", "mydomainname.org", hostconf) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(std::string("bf"), std::string(opts.lookups)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +NameContentList svcconf = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" + "search first.com second.com\n"}, + {"/etc/svc.conf", "hosts= bind\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerSvcConfInit, + "myhostname", "mydomainname.org", svcconf) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(std::string("b"), std::string(opts.lookups)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +// Failures when expected config filenames are inaccessible. +class MakeUnreadable { + public: + explicit MakeUnreadable(const std::string& filename) + : filename_(filename) { + chmod(filename_.c_str(), 0000); + } + ~MakeUnreadable() { chmod(filename_.c_str(), 0644); } + private: + std::string filename_; +}; + +CONTAINED_TEST_F(LibraryTest, ContainerResolvConfNotReadable, + "myhostname", "mydomainname.org", filelist) { + ares_channel channel = nullptr; + MakeUnreadable hide("/etc/resolv.conf"); + // Unavailable /etc/resolv.conf falls back to defaults + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + return HasFailure(); +} +CONTAINED_TEST_F(LibraryTest, ContainerNsswitchConfNotReadable, + "myhostname", "mydomainname.org", filelist) { + ares_channel channel = nullptr; + // Unavailable /etc/nsswitch.conf falls back to defaults. + MakeUnreadable hide("/etc/nsswitch.conf"); + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(std::string("fb"), std::string(opts.lookups)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} +CONTAINED_TEST_F(LibraryTest, ContainerHostConfNotReadable, + "myhostname", "mydomainname.org", hostconf) { + ares_channel channel = nullptr; + // Unavailable /etc/host.conf falls back to defaults. + MakeUnreadable hide("/etc/host.conf"); + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + ares_destroy(channel); + return HasFailure(); +} +CONTAINED_TEST_F(LibraryTest, ContainerSvcConfNotReadable, + "myhostname", "mydomainname.org", svcconf) { + ares_channel channel = nullptr; + // Unavailable /etc/svc.conf falls back to defaults. + MakeUnreadable hide("/etc/svc.conf"); + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + ares_destroy(channel); + return HasFailure(); +} + +NameContentList rotateenv = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" + "search first.com second.com\n" + "options rotate\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerRotateInit, + "myhostname", "mydomainname.org", rotateenv) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(ARES_OPT_ROTATE, (optmask & ARES_OPT_ROTATE)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +CONTAINED_TEST_F(LibraryTest, ContainerRotateOverride, + "myhostname", "mydomainname.org", rotateenv) { + ares_channel channel = nullptr; + struct ares_options opts = {0}; + int optmask = ARES_OPT_NOROTATE; + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); + + optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(ARES_OPT_NOROTATE, (optmask & ARES_OPT_NOROTATE)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +NameContentList multiresolv = { + {"/etc/resolv.conf", " nameserver 1::2 ; ;;\n" + " domain first.com\n"}, + {"/etc/nsswitch.conf", "hosts: files\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerMultiResolvInit, + "myhostname", "mydomainname.org", multiresolv) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + std::vector actual = GetNameServers(channel); + std::vector expected = {"0001:0000:0000:0000:0000:0000:0000:0002"}; + EXPECT_EQ(expected, actual); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(1, opts.ndomains); + EXPECT_EQ(std::string("first.com"), std::string(opts.domains[0])); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +NameContentList systemdresolv = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" + "domain first.com\n"}, + {"/etc/nsswitch.conf", "hosts: junk resolve files\n"}}; +CONTAINED_TEST_F(LibraryTest, ContainerSystemdResolvInit, + "myhostname", "mydomainname.org", systemdresolv) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(std::string("bf"), std::string(opts.lookups)); + ares_destroy_options(&opts); + + ares_destroy(channel); + return HasFailure(); +} + +NameContentList empty = {}; // no files +CONTAINED_TEST_F(LibraryTest, ContainerEmptyInit, + "host.domain.org", "domain.org", empty) { + ares_channel channel = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel)); + std::vector actual = GetNameServers(channel); + std::vector expected = {"127.0.0.1"}; + EXPECT_EQ(expected, actual); + + struct ares_options opts; + int optmask = 0; + ares_save_options(channel, &opts, &optmask); + EXPECT_EQ(1, opts.ndomains); + EXPECT_EQ(std::string("domain.org"), std::string(opts.domains[0])); + EXPECT_EQ(std::string("fb"), std::string(opts.lookups)); + ares_destroy_options(&opts); + + + ares_destroy(channel); + return HasFailure(); +} + +#endif + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-internal.cc b/src/c-ares/test/ares-test-internal.cc new file mode 100644 index 000000000..ad1a2c9ab --- /dev/null +++ b/src/c-ares/test/ares-test-internal.cc @@ -0,0 +1,458 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include +#include + +extern "C" { +// Remove command-line defines of package variables for the test project... +#undef PACKAGE_NAME +#undef PACKAGE_BUGREPORT +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +// ... so we can include the library's config without symbol redefinitions. +#include "ares_setup.h" +#include "ares_nowarn.h" +#include "ares_inet_net_pton.h" +#include "ares_data.h" +#include "ares_private.h" +#include "bitncmp.h" + +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_SYS_UIO_H +# include +#endif +} + +#include +#include + +namespace ares { +namespace test { + +#ifndef CARES_SYMBOL_HIDING +void CheckPtoN4(int size, unsigned int value, const char *input) { + struct in_addr a4; + a4.s_addr = 0; + uint32_t expected = htonl(value); + EXPECT_EQ(size, ares_inet_net_pton(AF_INET, input, &a4, sizeof(a4))) + << " for input " << input; + EXPECT_EQ(expected, a4.s_addr) << " for input " << input; +} +#endif + +TEST_F(LibraryTest, InetPtoN) { + struct in_addr a4; + struct in6_addr a6; + +#ifndef CARES_SYMBOL_HIDING + uint32_t expected; + + CheckPtoN4(4 * 8, 0x01020304, "1.2.3.4"); + CheckPtoN4(4 * 8, 0x81010101, "129.1.1.1"); + CheckPtoN4(4 * 8, 0xC0010101, "192.1.1.1"); + CheckPtoN4(4 * 8, 0xE0010101, "224.1.1.1"); + CheckPtoN4(4 * 8, 0xE1010101, "225.1.1.1"); + CheckPtoN4(4, 0xE0000000, "224"); + CheckPtoN4(4 * 8, 0xFD000000, "253"); + CheckPtoN4(4 * 8, 0xF0010101, "240.1.1.1"); + CheckPtoN4(4 * 8, 0x02030405, "02.3.4.5"); + CheckPtoN4(3 * 8, 0x01020304, "1.2.3.4/24"); + CheckPtoN4(3 * 8, 0x01020300, "1.2.3/24"); + CheckPtoN4(2 * 8, 0xa0000000, "0xa"); + CheckPtoN4(0, 0x02030405, "2.3.4.5/000"); + CheckPtoN4(1 * 8, 0x01020000, "1.2/8"); + CheckPtoN4(2 * 8, 0x01020000, "0x0102/16"); + CheckPtoN4(4 * 8, 0x02030405, "02.3.4.5"); + + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "::", &a6, sizeof(a6))); + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "::1", &a6, sizeof(a6))); + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "1234:5678::", &a6, sizeof(a6))); + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "12:34::ff", &a6, sizeof(a6))); + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4", &a6, sizeof(a6))); + EXPECT_EQ(23, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4/23", &a6, sizeof(a6))); + EXPECT_EQ(3 * 8, ares_inet_net_pton(AF_INET6, "12:34::ff/24", &a6, sizeof(a6))); + EXPECT_EQ(0, ares_inet_net_pton(AF_INET6, "12:34::ff/0", &a6, sizeof(a6))); + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "12:34::ffff:0.2", &a6, sizeof(a6))); + EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234", &a6, sizeof(a6))); + + // Various malformed versions + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, " ", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x ", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "x0", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0xXYZZY", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "xyzzy", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET+AF_INET6, "1.2.3.4", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "257.2.3.4", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "002.3.4.x", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "00.3.4.x", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.x", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5.6", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5.6/12", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4:5", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5/120", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5/1x", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5/x", &a4, sizeof(a4))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/240", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/02", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/2y", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/y", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":x", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ": :1234", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "::12345", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234::2345:3456::0011", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234::", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1.2.3.4", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":1234:1234:1234:1234:1234:1234:1234:1234", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":1234:1234:1234:1234:1234:1234:1234:1234:", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:5678", &a6, sizeof(a6))); + // TODO(drysdale): check whether the next two tests should give -1. + EXPECT_EQ(0, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:5678:5678", &a6, sizeof(a6))); + EXPECT_EQ(0, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:5678:5678:5678", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:257.2.3.4", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:002.2.3.4", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4.5.6", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4.5", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.z", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3001.4", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3..4", &a6, sizeof(a6))); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.", &a6, sizeof(a6))); + + // Hex constants are allowed. + EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x01020304", &a4, sizeof(a4))); + expected = htonl(0x01020304); + EXPECT_EQ(expected, a4.s_addr); + EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x0a0b0c0d", &a4, sizeof(a4))); + expected = htonl(0x0a0b0c0d); + EXPECT_EQ(expected, a4.s_addr); + EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x0A0B0C0D", &a4, sizeof(a4))); + expected = htonl(0x0a0b0c0d); + EXPECT_EQ(expected, a4.s_addr); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x0xyz", &a4, sizeof(a4))); + EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x1122334", &a4, sizeof(a4))); + expected = htonl(0x11223340); + EXPECT_EQ(expected, a4.s_addr); // huh? + + // No room, no room. + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "1.2.3.4", &a4, sizeof(a4) - 1)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff", &a6, sizeof(a6) - 1)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x01020304", &a4, 2)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x01020304", &a4, 0)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x0a0b0c0d", &a4, 0)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x0xyz", &a4, 0)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x1122334", &a4, sizeof(a4) - 1)); + EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "253", &a4, sizeof(a4) - 1)); +#endif + + EXPECT_EQ(1, ares_inet_pton(AF_INET, "1.2.3.4", &a4)); + EXPECT_EQ(1, ares_inet_pton(AF_INET6, "12:34::ff", &a6)); + EXPECT_EQ(1, ares_inet_pton(AF_INET6, "12:34::ffff:1.2.3.4", &a6)); + EXPECT_EQ(0, ares_inet_pton(AF_INET, "xyzzy", &a4)); + EXPECT_EQ(-1, ares_inet_pton(AF_INET+AF_INET6, "1.2.3.4", &a4)); +} + +TEST_F(LibraryTest, FreeCorruptData) { + // ares_free_data(p) expects that there is a type field and a marker + // field in the memory before p. Feed it incorrect versions of each. + struct ares_data *data = (struct ares_data *)malloc(sizeof(struct ares_data)); + void* p = &(data->data); + + // Invalid type + data->type = (ares_datatype)99; + data->mark = ARES_DATATYPE_MARK; + ares_free_data(p); + + // Invalid marker + data->type = (ares_datatype)ARES_DATATYPE_MX_REPLY; + data->mark = ARES_DATATYPE_MARK + 1; + ares_free_data(p); + + // Null pointer + ares_free_data(nullptr); + + free(data); +} + +#ifndef CARES_SYMBOL_HIDING +TEST(LibraryInit, StrdupFailures) { + EXPECT_EQ(ARES_SUCCESS, ares_library_init(ARES_LIB_INIT_ALL)); + char* copy = ares_strdup("string"); + EXPECT_NE(nullptr, copy); + free(copy); + ares_library_cleanup(); +} + +TEST_F(LibraryTest, StrdupFailures) { + SetAllocFail(1); + char* copy = ares_strdup("string"); + EXPECT_EQ(nullptr, copy); +} + +TEST_F(LibraryTest, MallocDataFail) { + EXPECT_EQ(nullptr, ares_malloc_data((ares_datatype)99)); + SetAllocSizeFail(sizeof(struct ares_data)); + EXPECT_EQ(nullptr, ares_malloc_data(ARES_DATATYPE_MX_REPLY)); +} + +TEST(Misc, Bitncmp) { + byte a[4] = {0x80, 0x01, 0x02, 0x03}; + byte b[4] = {0x80, 0x01, 0x02, 0x04}; + byte c[4] = {0x01, 0xFF, 0x80, 0x02}; + EXPECT_GT(0, ares__bitncmp(a, b, sizeof(a)*8)); + EXPECT_LT(0, ares__bitncmp(b, a, sizeof(a)*8)); + EXPECT_EQ(0, ares__bitncmp(a, a, sizeof(a)*8)); + + for (int ii = 1; ii < (3*8+5); ii++) { + EXPECT_EQ(0, ares__bitncmp(a, b, ii)); + EXPECT_EQ(0, ares__bitncmp(b, a, ii)); + EXPECT_LT(0, ares__bitncmp(a, c, ii)); + EXPECT_GT(0, ares__bitncmp(c, a, ii)); + } + + // Last byte differs at 5th bit + EXPECT_EQ(0, ares__bitncmp(a, b, 3*8 + 3)); + EXPECT_EQ(0, ares__bitncmp(a, b, 3*8 + 4)); + EXPECT_EQ(0, ares__bitncmp(a, b, 3*8 + 5)); + EXPECT_GT(0, ares__bitncmp(a, b, 3*8 + 6)); + EXPECT_GT(0, ares__bitncmp(a, b, 3*8 + 7)); +} + +TEST_F(LibraryTest, Casts) { + ssize_t ssz = 100; + unsigned int u = 100; + int i = 100; + long l = 100; + + unsigned int ru = aresx_sztoui(ssz); + EXPECT_EQ(u, ru); + int ri = aresx_sztosi(ssz); + EXPECT_EQ(i, ri); + + ri = aresx_sltosi(l); + EXPECT_EQ(l, (long)ri); +} + +TEST_F(LibraryTest, ReadLine) { + TempFile temp("abcde\n0123456789\nXYZ\n012345678901234567890\n\n"); + FILE *fp = fopen(temp.filename(), "r"); + size_t bufsize = 4; + char *buf = (char *)malloc(bufsize); + + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("abcde", std::string(buf)); + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("0123456789", std::string(buf)); + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("XYZ", std::string(buf)); + SetAllocFail(1); + EXPECT_EQ(ARES_ENOMEM, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ(nullptr, buf); + + fclose(fp); + free(buf); +} + +TEST_F(LibraryTest, ReadLineNoBuf) { + TempFile temp("abcde\n0123456789\nXYZ\n012345678901234567890"); + FILE *fp = fopen(temp.filename(), "r"); + size_t bufsize = 0; + char *buf = nullptr; + + SetAllocFail(1); + EXPECT_EQ(ARES_ENOMEM, ares__read_line(fp, &buf, &bufsize)); + + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("abcde", std::string(buf)); + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("0123456789", std::string(buf)); + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("XYZ", std::string(buf)); + EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize)); + EXPECT_EQ("012345678901234567890", std::string(buf)); + + fclose(fp); + free(buf); +} + +TEST(Misc, GetHostent) { + TempFile hostsfile("1.2.3.4 example.com \n" + " 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n" + "#comment\n" + "4.5.6.7\n" + "1.3.5.7 \n" + "::1 ipv6.com"); + struct hostent *host = nullptr; + FILE *fp = fopen(hostsfile.filename(), "r"); + ASSERT_NE(nullptr, fp); + EXPECT_EQ(ARES_EBADFAMILY, ares__get_hostent(fp, AF_INET+AF_INET6, &host)); + rewind(fp); + + EXPECT_EQ(ARES_SUCCESS, ares__get_hostent(fp, AF_INET, &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss1; + ss1 << HostEnt(host); + EXPECT_EQ("{'example.com' aliases=[] addrs=[1.2.3.4]}", ss1.str()); + ares_free_hostent(host); + host = nullptr; + + EXPECT_EQ(ARES_SUCCESS, ares__get_hostent(fp, AF_INET, &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss2; + ss2 << HostEnt(host); + EXPECT_EQ("{'google.com' aliases=[www.google.com, www2.google.com] addrs=[2.3.4.5]}", ss2.str()); + ares_free_hostent(host); + host = nullptr; + + EXPECT_EQ(ARES_EOF, ares__get_hostent(fp, AF_INET, &host)); + + rewind(fp); + EXPECT_EQ(ARES_SUCCESS, ares__get_hostent(fp, AF_INET6, &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss3; + ss3 << HostEnt(host); + EXPECT_EQ("{'ipv6.com' aliases=[] addrs=[0000:0000:0000:0000:0000:0000:0000:0001]}", ss3.str()); + ares_free_hostent(host); + host = nullptr; + EXPECT_EQ(ARES_EOF, ares__get_hostent(fp, AF_INET6, &host)); + fclose(fp); +} + +TEST_F(LibraryTest, GetHostentAllocFail) { + TempFile hostsfile("1.2.3.4 example.com alias1 alias2\n"); + struct hostent *host = nullptr; + FILE *fp = fopen(hostsfile.filename(), "r"); + ASSERT_NE(nullptr, fp); + + for (int ii = 1; ii <= 8; ii++) { + rewind(fp); + ClearFails(); + SetAllocFail(ii); + host = nullptr; + EXPECT_EQ(ARES_ENOMEM, ares__get_hostent(fp, AF_INET, &host)) << ii; + } + fclose(fp); +} +#endif + +#ifdef CARES_EXPOSE_STATICS +// These tests access internal static functions from the library, which +// are only exposed when CARES_EXPOSE_STATICS has been configured. As such +// they are tightly couple to the internal library implementation details. +extern "C" char *ares_striendstr(const char*, const char*); +TEST_F(LibraryTest, Striendstr) { + EXPECT_EQ(nullptr, ares_striendstr("abc", "12345")); + EXPECT_NE(nullptr, ares_striendstr("abc12345", "12345")); + EXPECT_NE(nullptr, ares_striendstr("abcxyzzy", "XYZZY")); + EXPECT_NE(nullptr, ares_striendstr("xyzzy", "XYZZY")); + EXPECT_EQ(nullptr, ares_striendstr("xyxzy", "XYZZY")); + EXPECT_NE(nullptr, ares_striendstr("", "")); + const char *str = "plugh"; + EXPECT_NE(nullptr, ares_striendstr(str, str)); +} +extern "C" int single_domain(ares_channel, const char*, char**); +TEST_F(DefaultChannelTest, SingleDomain) { + TempFile aliases("www www.google.com\n"); + EnvValue with_env("HOSTALIASES", aliases.filename()); + + SetAllocSizeFail(128); + char *ptr = nullptr; + EXPECT_EQ(ARES_ENOMEM, single_domain(channel_, "www", &ptr)); + + channel_->flags |= ARES_FLAG_NOSEARCH|ARES_FLAG_NOALIASES; + EXPECT_EQ(ARES_SUCCESS, single_domain(channel_, "www", &ptr)); + EXPECT_EQ("www", std::string(ptr)); + free(ptr); + ptr = nullptr; + + SetAllocFail(1); + EXPECT_EQ(ARES_ENOMEM, single_domain(channel_, "www", &ptr)); + EXPECT_EQ(nullptr, ptr); +} +#endif + +TEST_F(DefaultChannelTest, SaveInvalidChannel) { + int saved = channel_->nservers; + channel_->nservers = -1; + struct ares_options opts; + int optmask = 0; + EXPECT_EQ(ARES_ENODATA, ares_save_options(channel_, &opts, &optmask)); + channel_->nservers = saved; +} + +// TODO: This should not really be in this file, but we need ares config +// flags, and here they are available. +const struct ares_socket_functions VirtualizeIO::default_functions = { + [](int af, int type, int protocol, void *) { + auto s = ::socket(af, type, protocol); + if (s < 0) { + return s; + } + auto res = [s] { + // transposed from ares-process, simplified non-block setter. +#if defined(USE_BLOCKING_SOCKETS) + return 0; /* returns success */ +#elif defined(HAVE_FCNTL_O_NONBLOCK) + /* most recent unix versions */ + int flags; + flags = fcntl(s, F_GETFL, 0); + return fcntl(s, F_SETFL, flags | O_NONBLOCK); +#elif defined(HAVE_IOCTL_FIONBIO) + /* older unix versions */ + int flags = 1; + return ioctl(s, FIONBIO, &flags); +#elif defined(HAVE_IOCTLSOCKET_FIONBIO) +#ifdef WATT32 + char flags = 1; +#else + /* Windows */ + unsigned long flags = 1UL; +#endif + return ioctlsocket(s, FIONBIO, &flags); +#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) + /* Amiga */ + long flags = 1L; + return IoctlSocket(s, FIONBIO, flags); +#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) + /* BeOS */ + long b = 1L; + return setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +#else +# error "no non-blocking method was found/used/set" +#endif + }(); + if (res != 0) { + ::close(s); + return -1; + } + return s; + }, + [](ares_socket_t s, void * p) { + return ::close(s); + }, + [](ares_socket_t s, const struct sockaddr * addr, socklen_t len, void *) { + return ::connect(s, addr, len); + }, + [](ares_socket_t s, void * dst, size_t len, int flags, struct sockaddr * addr, socklen_t * alen, void *) { +#ifdef HAVE_RECVFROM + return ::recvfrom(s, dst, len, flags, addr, alen); +#else + return sread(s, dst, len); +#endif + }, + [](ares_socket_t s, const struct iovec * vec, int len, void *) { + return :: writev(s, vec, len); + } +}; + + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-live.cc b/src/c-ares/test/ares-test-live.cc new file mode 100644 index 000000000..0bd4a3987 --- /dev/null +++ b/src/c-ares/test/ares-test-live.cc @@ -0,0 +1,633 @@ +// This file includes tests that attempt to do real lookups +// of DNS names using the local machine's live infrastructure. +// As a result, we don't check the results very closely, to allow +// for varying local configurations. + +#include "ares-test.h" + +#ifdef HAVE_NETDB_H +#include +#endif + +namespace ares { +namespace test { + +// Use the address of Google's public DNS servers as example addresses that are +// likely to be accessible everywhere/everywhen. +unsigned char gdns_addr4[4] = {0x08, 0x08, 0x08, 0x08}; +unsigned char gdns_addr6[16] = {0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}; + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByNameV4) { + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_LT(0, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET, result.host_.addrtype_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByNameV6) { + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_LT(0, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET6, result.host_.addrtype_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByAddrV4) { + HostResult result; + ares_gethostbyaddr(channel_, gdns_addr4, sizeof(gdns_addr4), AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_LT(0, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET, result.host_.addrtype_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByAddrV6) { + HostResult result; + ares_gethostbyaddr(channel_, gdns_addr6, sizeof(gdns_addr6), AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_LT(0, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET6, result.host_.addrtype_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetHostByNameFile) { + struct hostent *host = nullptr; + + // Still need a channel even to query /etc/hosts. + EXPECT_EQ(ARES_ENOTFOUND, + ares_gethostbyname_file(nullptr, "localhost", AF_INET, &host)); + + int rc = ares_gethostbyname_file(channel_, "bogus.mcname", AF_INET, &host); + EXPECT_EQ(nullptr, host); + EXPECT_EQ(ARES_ENOTFOUND, rc); + + rc = ares_gethostbyname_file(channel_, "localhost", AF_INET, &host); + if (rc == ARES_SUCCESS) { + EXPECT_NE(nullptr, host); + ares_free_hostent(host); + } +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostByNameV4) { + HostResult result; + ares_gethostbyname(channel_, "localhost", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + if ((result.status_ != ARES_ENOTFOUND) && (result.status_ != ARES_ECONNREFUSED)) { + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ(1, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET, result.host_.addrtype_); + EXPECT_NE(std::string::npos, result.host_.name_.find("localhost")); + } +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostByNameV6) { + HostResult result; + ares_gethostbyname(channel_, "localhost", AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + if (result.status_ == ARES_SUCCESS) { + EXPECT_EQ(1, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET6, result.host_.addrtype_); + std::stringstream ss; + ss << HostEnt(result.host_); + EXPECT_NE(std::string::npos, result.host_.name_.find("localhost")); + } +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostByNameIPV4) { + HostResult result; + ares_gethostbyname(channel_, "127.0.0.1", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ(1, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET, result.host_.addrtype_); + std::stringstream ss; + ss << HostEnt(result.host_); + EXPECT_EQ("{'127.0.0.1' aliases=[] addrs=[127.0.0.1]}", ss.str()); +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostByNameIPV6) { + HostResult result; + ares_gethostbyname(channel_, "::1", AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + if (result.status_ != ARES_ENOTFOUND) { + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ(1, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET6, result.host_.addrtype_); + std::stringstream ss; + ss << HostEnt(result.host_); + EXPECT_EQ("{'::1' aliases=[] addrs=[0000:0000:0000:0000:0000:0000:0000:0001]}", ss.str()); + } +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostFailFamily) { + HostResult result; + ares_gethostbyname(channel_, "127.0.0.1", AF_INET+AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTIMP, result.status_); +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostByAddrV4) { + HostResult result; + struct in_addr addr; + addr.s_addr = htonl(INADDR_LOOPBACK); + ares_gethostbyaddr(channel_, &addr, sizeof(addr), AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + if (result.status_ != ARES_ENOTFOUND) { + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_LT(0, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET, result.host_.addrtype_); + EXPECT_NE(std::string::npos, + result.host_.name_.find("localhost")); + } +} + +TEST_P(DefaultChannelModeTest, LiveGetLocalhostByAddrV6) { + HostResult result; + struct in6_addr addr; + memset(&addr, 0, sizeof(addr)); + addr.s6_addr[15] = 1; // in6addr_loopback + ares_gethostbyaddr(channel_, &addr, sizeof(addr), AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + if (result.status_ != ARES_ENOTFOUND) { + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_LT(0, (int)result.host_.addrs_.size()); + EXPECT_EQ(AF_INET6, result.host_.addrtype_); + EXPECT_NE(std::string::npos, + result.host_.name_.find("localhost")); + } +} + +TEST_P(DefaultChannelModeTest, LiveGetHostByAddrFailFamily) { + HostResult result; + unsigned char addr[4] = {8, 8, 8, 8}; + ares_gethostbyaddr(channel_, addr, sizeof(addr), AF_INET6+AF_INET, + HostCallback, &result); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTIMP, result.status_); +} + +TEST_P(DefaultChannelModeTest, LiveGetHostByAddrFailAddrSize) { + HostResult result; + unsigned char addr[4] = {8, 8, 8, 8}; + ares_gethostbyaddr(channel_, addr, sizeof(addr) - 1, AF_INET, + HostCallback, &result); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTIMP, result.status_); +} + +TEST_P(DefaultChannelModeTest, LiveGetHostByAddrFailAlloc) { + HostResult result; + unsigned char addr[4] = {8, 8, 8, 8}; + SetAllocFail(1); + ares_gethostbyaddr(channel_, addr, sizeof(addr), AF_INET, + HostCallback, &result); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOMEM, result.status_); +} + +INSTANTIATE_TEST_CASE_P(Modes, DefaultChannelModeTest, + ::testing::Values("f", "b", "fb", "bf")); + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchA) { + SearchResult result; + ares_search(channel_, "www.youtube.com.", ns_c_in, ns_t_a, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchEmptyA) { + SearchResult result; + ares_search(channel_, "", ns_c_in, ns_t_a, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_NE(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchNS) { + SearchResult result; + ares_search(channel_, "google.com.", ns_c_in, ns_t_ns, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchMX) { + SearchResult result; + ares_search(channel_, "google.com.", ns_c_in, ns_t_mx, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchTXT) { + SearchResult result; + ares_search(channel_, "google.com.", ns_c_in, ns_t_txt, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchSOA) { + SearchResult result; + ares_search(channel_, "google.com.", ns_c_in, ns_t_soa, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchSRV) { + SearchResult result; + ares_search(channel_, "_imap._tcp.gmail.com.", ns_c_in, ns_t_srv, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveSearchANY) { + SearchResult result; + ares_search(channel_, "google.com.", ns_c_in, ns_t_any, + SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV4) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(53); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "8.8.8.8:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV4NoPort) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(0); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "8.8.8.8:0 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV4UnassignedPort) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(4); // Unassigned at IANA + sockaddr.sin_addr.s_addr = htonl(0x08080808); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "8.8.8.8:4 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV6Both) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + memcpy(sockaddr.sin6_addr.s6_addr, gdns_addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_TCP|ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_NOFQDN, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "[2001:4860:4860::8888]:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV6Neither) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + memcpy(sockaddr.sin6_addr.s6_addr, gdns_addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_TCP|ARES_NI_NOFQDN, // Neither specified => assume lookup host. + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "[2001:4860:4860::8888]:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV4Numeric) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(53); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_TCP|ARES_NI_NUMERICHOST, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("8.8.8.8", result.node_); + if (verbose) std::cerr << "8.8.8.8:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV6Numeric) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + memcpy(sockaddr.sin6_addr.s6_addr, gdns_addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_DCCP|ARES_NI_NUMERICHOST, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("2001:4860:4860::8888%0", result.node_); + if (verbose) std::cerr << "[2001:4860:4860::8888]:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV6LinkLocal) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + unsigned char addr6[16] = {0xfe, 0x80, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04}; + memcpy(sockaddr.sin6_addr.s6_addr, addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_DCCP|ARES_NI_NUMERICHOST, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("fe80:102:102::304%0", result.node_); + if (verbose) std::cerr << "[fe80:102:102::304]:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV4NotFound) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(4); // Port 4 unassigned at IANA + // RFC5737 says 192.0.2.0 should not be used publically. + sockaddr.sin_addr.s_addr = htonl(0xC0000200); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("192.0.2.0", result.node_); + if (verbose) std::cerr << "192.0.2.0:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV4NotFoundFail) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(53); + // RFC5737 says 192.0.2.0 should not be used publically. + sockaddr.sin_addr.s_addr = htonl(0xC0000200); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP|ARES_NI_NAMEREQD, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTFOUND, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoV6NotFound) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + // 2001:db8::/32 is only supposed to be used in documentation. + unsigned char addr6[16] = {0x20, 0x01, 0x0d, 0xb8, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04}; + memcpy(sockaddr.sin6_addr.s6_addr, addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("2001:db8:102::304%0", result.node_); + if (verbose) std::cerr << "[2001:db8:102::304]:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInvalidFamily) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6 + AF_INET; + sockaddr.sin6_port = htons(53); + memcpy(sockaddr.sin6_addr.s6_addr, gdns_addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTIMP, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInvalidFlags) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + memcpy(sockaddr.sin6_addr.s6_addr, gdns_addr6, 16); + // Ask for both a name-required, and a numeric host. + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP|ARES_NI_NUMERICHOST|ARES_NI_NAMEREQD, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EBADFLAGS, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetServiceInfo) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(53); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + // Just look up service info + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPSERVICE|ARES_NI_SCTP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("", result.node_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetServiceInfoNumeric) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(53); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + // Just look up service info + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPSERVICE|ARES_NI_SCTP|ARES_NI_NUMERICSERV, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + EXPECT_EQ("", result.node_); + EXPECT_EQ("53", result.service_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, LiveGetNameInfoAllocFail) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(53); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + SetAllocFail(1); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOMEM, result.status_); +} + +VIRT_NONVIRT_TEST_F(DefaultChannelTest, GetSock) { + ares_socket_t socks[3] = {-1, -1, -1}; + int bitmask = ares_getsock(channel_, socks, 3); + EXPECT_EQ(0, bitmask); + bitmask = ares_getsock(channel_, nullptr, 0); + EXPECT_EQ(0, bitmask); + + // Ask again with a pending query. + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + bitmask = ares_getsock(channel_, socks, 3); + EXPECT_NE(0, bitmask); + bitmask = ares_getsock(channel_, nullptr, 0); + EXPECT_EQ(0, bitmask); + + Process(); +} + +TEST_F(LibraryTest, GetTCPSock) { + ares_channel channel; + struct ares_options opts = {0}; + opts.tcp_port = 53; + opts.flags = ARES_FLAG_USEVC; + int optmask = ARES_OPT_TCP_PORT | ARES_OPT_FLAGS; + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); + EXPECT_NE(nullptr, channel); + + ares_socket_t socks[3] = {-1, -1, -1}; + int bitmask = ares_getsock(channel, socks, 3); + EXPECT_EQ(0, bitmask); + bitmask = ares_getsock(channel, nullptr, 0); + EXPECT_EQ(0, bitmask); + + // Ask again with a pending query. + HostResult result; + ares_gethostbyname(channel, "www.google.com.", AF_INET, HostCallback, &result); + bitmask = ares_getsock(channel, socks, 3); + EXPECT_NE(0, bitmask); + bitmask = ares_getsock(channel, nullptr, 0); + EXPECT_EQ(0, bitmask); + + ProcessWork(channel, NoExtraFDs, nullptr); + + ares_destroy(channel); +} + +TEST_F(DefaultChannelTest, VerifySocketFunctionCallback) { + VirtualizeIO vio(channel_); + + auto my_functions = VirtualizeIO::default_functions; + size_t count = 0; + + my_functions.asocket = [](int af, int type, int protocol, void * p) { + EXPECT_NE(nullptr, p); + (*reinterpret_cast(p))++; + return ::socket(af, type, protocol); + }; + + ares_set_socket_functions(channel_, &my_functions, &count); + + { + count = 0; + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_NE(0, count); + } + + { + count = 0; + ares_channel copy; + EXPECT_EQ(ARES_SUCCESS, ares_dup(©, channel_)); + + HostResult result; + ares_gethostbyname(copy, "www.google.com.", AF_INET, HostCallback, &result); + ProcessWork(copy, NoExtraFDs, nullptr); + EXPECT_TRUE(result.done_); + ares_destroy(copy); + EXPECT_NE(0, count); + } + +} + + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-main.cc b/src/c-ares/test/ares-test-main.cc new file mode 100644 index 000000000..f7e90cb49 --- /dev/null +++ b/src/c-ares/test/ares-test-main.cc @@ -0,0 +1,37 @@ +#include +#include + +#include "ares-test.h" + +int main(int argc, char* argv[]) { + std::vector gtest_argv = {argv[0]}; + for (int ii = 1; ii < argc; ii++) { + if (strcmp(argv[ii], "-v") == 0) { + ares::test::verbose = true; + } else if ((strcmp(argv[ii], "-p") == 0) && (ii + 1 < argc)) { + ii++; + ares::test::mock_port = atoi(argv[ii]); + } else { + gtest_argv.push_back(argv[ii]); + } + } + int gtest_argc = gtest_argv.size(); + gtest_argv.push_back(nullptr); + ::testing::InitGoogleTest(>est_argc, gtest_argv.data()); + +#ifdef WIN32 + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(wVersionRequested, &wsaData); +#else + signal(SIGPIPE, SIG_IGN); +#endif + + int rc = RUN_ALL_TESTS(); + +#ifdef WIN32 + WSACleanup(); +#endif + + return rc; +} diff --git a/src/c-ares/test/ares-test-misc.cc b/src/c-ares/test/ares-test-misc.cc new file mode 100644 index 000000000..16142fe72 --- /dev/null +++ b/src/c-ares/test/ares-test-misc.cc @@ -0,0 +1,485 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(DefaultChannelTest, GetServers) { + std::vector servers = GetNameServers(channel_); + if (verbose) { + for (const std::string& server : servers) { + std::cerr << "Nameserver: " << server << std::endl; + } + } +} + +TEST_F(DefaultChannelTest, GetServersFailures) { + EXPECT_EQ(ARES_SUCCESS, + ares_set_servers_csv(channel_, "1.2.3.4,2.3.4.5")); + struct ares_addr_node* servers = nullptr; + SetAllocFail(1); + EXPECT_EQ(ARES_ENOMEM, ares_get_servers(channel_, &servers)); + SetAllocFail(2); + EXPECT_EQ(ARES_ENOMEM, ares_get_servers(channel_, &servers)); + EXPECT_EQ(ARES_ENODATA, ares_get_servers(nullptr, &servers)); +} + +TEST_F(DefaultChannelTest, SetServers) { + EXPECT_EQ(ARES_SUCCESS, ares_set_servers(channel_, nullptr)); + std::vector empty; + EXPECT_EQ(empty, GetNameServers(channel_)); + + struct ares_addr_node server1; + struct ares_addr_node server2; + server1.next = &server2; + server1.family = AF_INET; + server1.addr.addr4.s_addr = htonl(0x01020304); + server2.next = nullptr; + server2.family = AF_INET; + server2.addr.addr4.s_addr = htonl(0x02030405); + EXPECT_EQ(ARES_ENODATA, ares_set_servers(nullptr, &server1)); + + EXPECT_EQ(ARES_SUCCESS, ares_set_servers(channel_, &server1)); + std::vector expected = {"1.2.3.4", "2.3.4.5"}; + EXPECT_EQ(expected, GetNameServers(channel_)); +} + +TEST_F(DefaultChannelTest, SetServersPorts) { + EXPECT_EQ(ARES_SUCCESS, ares_set_servers_ports(channel_, nullptr)); + std::vector empty; + EXPECT_EQ(empty, GetNameServers(channel_)); + + struct ares_addr_port_node server1; + struct ares_addr_port_node server2; + server1.next = &server2; + server1.family = AF_INET; + server1.addr.addr4.s_addr = htonl(0x01020304); + server1.udp_port = 111; + server1.tcp_port = 111; + server2.next = nullptr; + server2.family = AF_INET; + server2.addr.addr4.s_addr = htonl(0x02030405); + server2.udp_port = 0; + server2.tcp_port = 0;; + EXPECT_EQ(ARES_ENODATA, ares_set_servers_ports(nullptr, &server1)); + + EXPECT_EQ(ARES_SUCCESS, ares_set_servers_ports(channel_, &server1)); + std::vector expected = {"1.2.3.4:111", "2.3.4.5"}; + EXPECT_EQ(expected, GetNameServers(channel_)); +} + +TEST_F(DefaultChannelTest, SetServersCSV) { + EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "1.2.3.4")); + EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "xyzzy,plugh")); + EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "256.1.2.3")); + EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "1.2.3.4.5")); + EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "1:2:3:4:5")); + + EXPECT_EQ(ARES_SUCCESS, + ares_set_servers_csv(channel_, "1.2.3.4,0102:0304:0506:0708:0910:1112:1314:1516,2.3.4.5")); + std::vector expected = {"1.2.3.4", "0102:0304:0506:0708:0910:1112:1314:1516", "2.3.4.5"}; + EXPECT_EQ(expected, GetNameServers(channel_)); + + // Same, with spaces + EXPECT_EQ(ARES_EBADSTR, + ares_set_servers_csv(channel_, "1.2.3.4 , 0102:0304:0506:0708:0910:1112:1314:1516, 2.3.4.5")); + + // Same, with ports + EXPECT_EQ(ARES_SUCCESS, + ares_set_servers_csv(channel_, "1.2.3.4:54,[0102:0304:0506:0708:0910:1112:1314:1516]:80,2.3.4.5:55")); + EXPECT_EQ(expected, GetNameServers(channel_)); + EXPECT_EQ(ARES_SUCCESS, + ares_set_servers_ports_csv(channel_, "1.2.3.4:54,[0102:0304:0506:0708:0910:1112:1314:1516]:80,2.3.4.5:55")); + std::vector expected2 = {"1.2.3.4:54", "[0102:0304:0506:0708:0910:1112:1314:1516]:80", "2.3.4.5:55"}; + EXPECT_EQ(expected2, GetNameServers(channel_)); + + // Should survive duplication + ares_channel channel2; + EXPECT_EQ(ARES_SUCCESS, ares_dup(&channel2, channel_)); + EXPECT_EQ(expected2, GetNameServers(channel2)); + ares_destroy(channel2); + + // Allocation failure cases + for (int fail = 1; fail <= 5; fail++) { + SetAllocFail(fail); + EXPECT_EQ(ARES_ENOMEM, + ares_set_servers_csv(channel_, "1.2.3.4,0102:0304:0506:0708:0910:1112:1314:1516,2.3.4.5")); + } + + // Blank servers + EXPECT_EQ(ARES_SUCCESS, ares_set_servers_csv(channel_, "")); + std::vector none; + EXPECT_EQ(none, GetNameServers(channel_)); + + EXPECT_EQ(ARES_EBADSTR, ares_set_servers_csv(channel_, "2.3.4.5,1.2.3.4:,3.4.5.6")); + EXPECT_EQ(ARES_EBADSTR, ares_set_servers_csv(channel_, "2.3.4.5,1.2.3.4:Z,3.4.5.6")); +} + +TEST_F(DefaultChannelTest, TimeoutValue) { + struct timeval tinfo; + tinfo.tv_sec = 0; + tinfo.tv_usec = 0; + struct timeval tmax; + tmax.tv_sec = 0; + tmax.tv_usec = 10; + struct timeval* pt; + + // No timers => get max back. + pt = ares_timeout(channel_, &tmax, &tinfo); + EXPECT_EQ(&tmax, pt); + EXPECT_EQ(0, pt->tv_sec); + EXPECT_EQ(10, pt->tv_usec); + + pt = ares_timeout(channel_, nullptr, &tinfo); + EXPECT_EQ(nullptr, pt); + + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + + // Now there's a timer running. + pt = ares_timeout(channel_, &tmax, &tinfo); + EXPECT_EQ(&tmax, pt); + EXPECT_EQ(0, pt->tv_sec); + EXPECT_EQ(10, pt->tv_usec); + + tmax.tv_sec = 100; + pt = ares_timeout(channel_, &tmax, &tinfo); + EXPECT_EQ(&tinfo, pt); + + pt = ares_timeout(channel_, nullptr, &tinfo); + EXPECT_EQ(&tinfo, pt); + + Process(); +} + +TEST_F(LibraryTest, InetNtoP) { + struct in_addr addr; + addr.s_addr = htonl(0x01020304); + char buffer[256]; + EXPECT_EQ(buffer, ares_inet_ntop(AF_INET, &addr, buffer, sizeof(buffer))); + EXPECT_EQ("1.2.3.4", std::string(buffer)); +} + +TEST_F(LibraryTest, Mkquery) { + byte* p; + int len; + ares_mkquery("example.com", ns_c_in, ns_t_a, 0x1234, 0, &p, &len); + std::vector data(p, p + len); + ares_free_string(p); + + std::string actual = PacketToString(data); + DNSPacket pkt; + pkt.set_qid(0x1234).add_question(new DNSQuestion("example.com", ns_t_a)); + std::string expected = PacketToString(pkt.data()); + EXPECT_EQ(expected, actual); +} + +TEST_F(LibraryTest, CreateQuery) { + byte* p; + int len; + EXPECT_EQ(ARES_SUCCESS, + ares_create_query("exam\\@le.com", ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 0)); + std::vector data(p, p + len); + ares_free_string(p); + + std::string actual = PacketToString(data); + DNSPacket pkt; + pkt.set_qid(0x1234).add_question(new DNSQuestion("exam@le.com", ns_t_a)); + std::string expected = PacketToString(pkt.data()); + EXPECT_EQ(expected, actual); +} + +TEST_F(LibraryTest, CreateQueryTrailingEscapedDot) { + byte* p; + int len; + EXPECT_EQ(ARES_SUCCESS, + ares_create_query("example.com\\.", ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 0)); + std::vector data(p, p + len); + ares_free_string(p); + + std::string actual = PacketToString(data); + EXPECT_EQ("REQ QRY Q:{'example.com\\.' IN A}", actual); +} + +TEST_F(LibraryTest, CreateQueryNameTooLong) { + byte* p; + int len; + EXPECT_EQ(ARES_EBADNAME, + ares_create_query( + "a1234567890123456789.b1234567890123456789.c1234567890123456789.d1234567890123456789." + "a1234567890123456789.b1234567890123456789.c1234567890123456789.d1234567890123456789." + "a1234567890123456789.b1234567890123456789.c1234567890123456789.d1234567890123456789." + "x1234567890123456789.y1234567890123456789.", + ns_c_in, ns_t_a, 0x1234, 0, &p, &len, 0)); +} + +TEST_F(LibraryTest, CreateQueryFailures) { + byte* p; + int len; + // RC1035 has a 255 byte limit on names. + std::string longname; + for (int ii = 0; ii < 17; ii++) { + longname += "fedcba9876543210"; + } + p = nullptr; + EXPECT_EQ(ARES_EBADNAME, + ares_create_query(longname.c_str(), ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 0)); + if (p) ares_free_string(p); + + SetAllocFail(1); + + p = nullptr; + EXPECT_EQ(ARES_ENOMEM, + ares_create_query("example.com", ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 0)); + if (p) ares_free_string(p); + + // 63-char limit on a single label + std::string longlabel = "a.a123456789b123456789c123456789d123456789e123456789f123456789g123456789.org"; + p = nullptr; + EXPECT_EQ(ARES_EBADNAME, + ares_create_query(longlabel.c_str(), ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 0)); + if (p) ares_free_string(p); + + // Empty non-terminal label + p = nullptr; + EXPECT_EQ(ARES_EBADNAME, + ares_create_query("example..com", ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 0)); + if (p) ares_free_string(p); +} + +TEST_F(DefaultChannelTest, SendFailure) { + unsigned char buf[2]; + SearchResult result; + ares_send(channel_, buf, sizeof(buf), SearchCallback, &result); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EBADQUERY, result.status_); +} + +std::string ExpandName(const std::vector& data, int offset, + long *enclen) { + char *name = nullptr; + int rc = ares_expand_name(data.data() + offset, data.data(), data.size(), + &name, enclen); + EXPECT_EQ(ARES_SUCCESS, rc); + std::string result; + if (rc == ARES_SUCCESS) { + result = name; + } else { + result = ""; + } + free(name); + return result; +} + +TEST_F(LibraryTest, ExpandName) { + long enclen; + std::vector data1 = {1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f', 0}; + EXPECT_EQ("a.bc.def", ExpandName(data1, 0, &enclen)); + EXPECT_EQ(data1.size(), enclen); + + std::vector data2 = {0}; + EXPECT_EQ("", ExpandName(data2, 0, &enclen)); + EXPECT_EQ(1, enclen); + + // Complete name indirection + std::vector data3 = {0x12, 0x23, + 3, 'd', 'e', 'f', 0, + 0xC0, 2}; + EXPECT_EQ("def", ExpandName(data3, 2, &enclen)); + EXPECT_EQ(5, enclen); + EXPECT_EQ("def", ExpandName(data3, 7, &enclen)); + EXPECT_EQ(2, enclen); + + // One label then indirection + std::vector data4 = {0x12, 0x23, + 3, 'd', 'e', 'f', 0, + 1, 'a', 0xC0, 2}; + EXPECT_EQ("def", ExpandName(data4, 2, &enclen)); + EXPECT_EQ(5, enclen); + EXPECT_EQ("a.def", ExpandName(data4, 7, &enclen)); + EXPECT_EQ(4, enclen); + + // Two labels then indirection + std::vector data5 = {0x12, 0x23, + 3, 'd', 'e', 'f', 0, + 1, 'a', 1, 'b', 0xC0, 2}; + EXPECT_EQ("def", ExpandName(data5, 2, &enclen)); + EXPECT_EQ(5, enclen); + EXPECT_EQ("a.b.def", ExpandName(data5, 7, &enclen)); + EXPECT_EQ(6, enclen); + + // Empty name, indirection to empty name + std::vector data6 = {0x12, 0x23, + 0, + 0xC0, 2}; + EXPECT_EQ("", ExpandName(data6, 2, &enclen)); + EXPECT_EQ(1, enclen); + EXPECT_EQ("", ExpandName(data6, 3, &enclen)); + EXPECT_EQ(2, enclen); +} + +TEST_F(LibraryTest, ExpandNameFailure) { + std::vector data1 = {0x03, 'c', 'o', 'm', 0x00}; + char *name = nullptr; + long enclen; + SetAllocFail(1); + EXPECT_EQ(ARES_ENOMEM, + ares_expand_name(data1.data(), data1.data(), data1.size(), + &name, &enclen)); + + // Empty packet + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data1.data(), data1.data(), 0, &name, &enclen)); + + // Start beyond enclosing data + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data1.data() + data1.size(), data1.data(), data1.size(), + &name, &enclen)); + + // Length beyond size of enclosing data + std::vector data2a = {0x13, 'c', 'o', 'm', 0x00}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data2a.data(), data2a.data(), data2a.size(), + &name, &enclen)); + std::vector data2b = {0x1}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data2b.data(), data2b.data(), data2b.size(), + &name, &enclen)); + std::vector data2c = {0xC0}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data2c.data(), data2c.data(), data2c.size(), + &name, &enclen)); + + // Indirection beyond enclosing data + std::vector data3a = {0xC0, 0x02}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data3a.data(), data3a.data(), data3a.size(), + &name, &enclen)); + std::vector data3b = {0xC0, 0x0A, 'c', 'o', 'm', 0x00}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data3b.data(), data3b.data(), data3b.size(), + &name, &enclen)); + + // Invalid top bits in label length + std::vector data4 = {0x03, 'c', 'o', 'm', 0x00, 0x80, 0x00}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data4.data() + 5, data4.data(), data4.size(), + &name, &enclen)); + + // Label too long: 64-byte label, with invalid top 2 bits of length (01). + std::vector data5 = {0x40, + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + 0x00}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data5.data(), data5.data(), data5.size(), + &name, &enclen)) << name; + + // Incomplete indirect length + std::vector data6 = {0x03, 'c', 'o', 'm', 0x00, 0xC0}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data6.data() + 5, data6.data(), data6.size(), + &name, &enclen)); + + // Indirection loops + std::vector data7 = {0xC0, 0x02, 0xC0, 0x00}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data7.data(), data7.data(), data7.size(), + &name, &enclen)); + std::vector data8 = {3, 'd', 'e', 'f', 0xC0, 0x08, 0x00, 0x00, + 3, 'a', 'b', 'c', 0xC0, 0x00}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data8.data(), data8.data(), data8.size(), + &name, &enclen)); + std::vector data9 = {0x12, 0x23, // start 2 bytes in + 3, 'd', 'e', 'f', 0xC0, 0x02}; + EXPECT_EQ(ARES_EBADNAME, + ares_expand_name(data9.data() + 2, data9.data(), data9.size(), + &name, &enclen)); +} + +TEST_F(LibraryTest, CreateEDNSQuery) { + byte* p; + int len; + EXPECT_EQ(ARES_SUCCESS, + ares_create_query("example.com", ns_c_in, ns_t_a, 0x1234, 0, + &p, &len, 1280)); + std::vector data(p, p + len); + ares_free_string(p); + + std::string actual = PacketToString(data); + DNSPacket pkt; + pkt.set_qid(0x1234).add_question(new DNSQuestion("example.com", ns_t_a)) + .add_additional(new DNSOptRR(0, 1280)); + std::string expected = PacketToString(pkt.data()); + EXPECT_EQ(expected, actual); +} + +TEST_F(LibraryTest, CreateRootQuery) { + byte* p; + int len; + ares_create_query(".", ns_c_in, ns_t_a, 0x1234, 0, &p, &len, 0); + std::vector data(p, p + len); + ares_free_string(p); + + std::string actual = PacketToString(data); + DNSPacket pkt; + pkt.set_qid(0x1234).add_question(new DNSQuestion("", ns_t_a)); + std::string expected = PacketToString(pkt.data()); + EXPECT_EQ(expected, actual); +} + +TEST_F(LibraryTest, Version) { + // Assume linked to same version + EXPECT_EQ(std::string(ARES_VERSION_STR), + std::string(ares_version(nullptr))); + int version; + ares_version(&version); + EXPECT_EQ(ARES_VERSION, version); +} + +TEST_F(LibraryTest, Strerror) { + EXPECT_EQ("Successful completion", + std::string(ares_strerror(ARES_SUCCESS))); + EXPECT_EQ("DNS query cancelled", + std::string(ares_strerror(ARES_ECANCELLED))); + EXPECT_EQ("unknown", + std::string(ares_strerror(99))); +} + +TEST_F(LibraryTest, ExpandString) { + std::vector s1 = { 3, 'a', 'b', 'c'}; + char* result = nullptr; + long len; + EXPECT_EQ(ARES_SUCCESS, + ares_expand_string(s1.data(), s1.data(), s1.size(), + (unsigned char**)&result, &len)); + EXPECT_EQ("abc", std::string(result)); + EXPECT_EQ(1 + 3, len); // amount of data consumed includes 1 byte len + free(result); + result = nullptr; + EXPECT_EQ(ARES_EBADSTR, + ares_expand_string(s1.data() + 1, s1.data(), s1.size(), + (unsigned char**)&result, &len)); + EXPECT_EQ(ARES_EBADSTR, + ares_expand_string(s1.data() + 4, s1.data(), s1.size(), + (unsigned char**)&result, &len)); + SetAllocSizeFail(3 + 1); + EXPECT_EQ(ARES_ENOMEM, + ares_expand_string(s1.data(), s1.data(), s1.size(), + (unsigned char**)&result, &len)); +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-mock.cc b/src/c-ares/test/ares-test-mock.cc new file mode 100644 index 000000000..2cbe6b4a1 --- /dev/null +++ b/src/c-ares/test/ares-test-mock.cc @@ -0,0 +1,1161 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +using testing::InvokeWithoutArgs; +using testing::DoAll; + +namespace ares { +namespace test { + +TEST_P(MockChannelTest, Basic) { + std::vector reply = { + 0x00, 0x00, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // 1 question + 0x00, 0x01, // 1 answer RRs + 0x00, 0x00, // 0 authority RRs + 0x00, 0x00, // 0 additional RRs + // Question + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + 0x00, 0x00, 0x01, 0x00, // TTL + 0x00, 0x04, // rdata length + 0x01, 0x02, 0x03, 0x04 + }; + + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReplyData(&server_, reply)); + + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +// UDP only so mock server doesn't get confused by concatenated requests +TEST_P(MockUDPChannelTest, ParallelLookups) { + DNSPacket rsp1; + rsp1.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp1)); + DNSPacket rsp2; + rsp2.set_response().set_aa() + .add_question(new DNSQuestion("www.example.com", ns_t_a)) + .add_answer(new DNSARR("www.example.com", 100, {1, 2, 3, 4})); + ON_CALL(server_, OnRequest("www.example.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp2)); + + HostResult result1; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result1); + HostResult result2; + ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result2); + HostResult result3; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result3); + Process(); + EXPECT_TRUE(result1.done_); + EXPECT_TRUE(result2.done_); + EXPECT_TRUE(result3.done_); + std::stringstream ss1; + ss1 << result1.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss1.str()); + std::stringstream ss2; + ss2 << result2.host_; + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[1.2.3.4]}", ss2.str()); + std::stringstream ss3; + ss3 << result3.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss3.str()); +} + +// UDP to TCP specific test +TEST_P(MockUDPChannelTest, TruncationRetry) { + DNSPacket rsptruncated; + rsptruncated.set_response().set_aa().set_tc() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + DNSPacket rspok; + rspok.set_response() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {1, 2, 3, 4})); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsptruncated)) + .WillOnce(SetReply(&server_, &rspok)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +static int sock_cb_count = 0; +static int SocketConnectCallback(ares_socket_t fd, int type, void *data) { + int rc = *(int*)data; + if (verbose) std::cerr << "SocketConnectCallback(" << fd << ") invoked" << std::endl; + sock_cb_count++; + return rc; +} + +TEST_P(MockChannelTest, SockCallback) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5})); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + + // Get notified of new sockets + int rc = ARES_SUCCESS; + ares_set_socket_callback(channel_, SocketConnectCallback, &rc); + + HostResult result; + sock_cb_count = 0; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_EQ(1, sock_cb_count); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, SockFailCallback) { + // Notification of new sockets gives an error. + int rc = -1; + ares_set_socket_callback(channel_, SocketConnectCallback, &rc); + + HostResult result; + sock_cb_count = 0; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_LT(1, sock_cb_count); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +static int sock_config_cb_count = 0; +static int SocketConfigureCallback(ares_socket_t fd, int type, void *data) { + int rc = *(int*)data; + if (verbose) std::cerr << "SocketConfigureCallback(" << fd << ") invoked" << std::endl; + sock_config_cb_count++; + return rc; +} + +TEST_P(MockChannelTest, SockConfigureCallback) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5})); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + + // Get notified of new sockets + int rc = ARES_SUCCESS; + ares_set_socket_configure_callback(channel_, SocketConfigureCallback, &rc); + + HostResult result; + sock_config_cb_count = 0; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_EQ(1, sock_config_cb_count); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, SockConfigureFailCallback) { + // Notification of new sockets gives an error. + int rc = -1; + ares_set_socket_configure_callback(channel_, SocketConfigureCallback, &rc); + + HostResult result; + sock_config_cb_count = 0; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_LT(1, sock_config_cb_count); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +// TCP only to prevent retries +TEST_P(MockTCPChannelTest, MalformedResponse) { + std::vector one = {0x01}; + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReplyData(&server_, one)); + + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ETIMEOUT, result.status_); +} + +TEST_P(MockTCPChannelTest, FormErrResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_formerr); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EFORMERR, result.status_); +} + +TEST_P(MockTCPChannelTest, ServFailResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_servfail); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + // ARES_FLAG_NOCHECKRESP not set, so SERVFAIL consumed + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +TEST_P(MockTCPChannelTest, NotImplResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_notimpl); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + // ARES_FLAG_NOCHECKRESP not set, so NOTIMPL consumed + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +TEST_P(MockTCPChannelTest, RefusedResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_refused); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + // ARES_FLAG_NOCHECKRESP not set, so REFUSED consumed + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +TEST_P(MockTCPChannelTest, YXDomainResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_yxdomain); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENODATA, result.status_); +} + +class MockExtraOptsTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface< std::pair > { + public: + MockExtraOptsTest() + : MockChannelOptsTest(1, GetParam().first, GetParam().second, + FillOptions(&opts_), + ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {} + static struct ares_options* FillOptions(struct ares_options * opts) { + memset(opts, 0, sizeof(struct ares_options)); + // Set a few options that affect socket communications + opts->socket_send_buffer_size = 514; + opts->socket_receive_buffer_size = 514; + return opts; + } + private: + struct ares_options opts_; +}; + +TEST_P(MockExtraOptsTest, SimpleQuery) { + ares_set_local_ip4(channel_, 0x7F000001); + byte addr6[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + ares_set_local_ip6(channel_, addr6); + ares_set_local_dev(channel_, "dummy"); + + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp)); + + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +class MockFlagsChannelOptsTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface< std::pair > { + public: + MockFlagsChannelOptsTest(int flags) + : MockChannelOptsTest(1, GetParam().first, GetParam().second, + FillOptions(&opts_, flags), ARES_OPT_FLAGS) {} + static struct ares_options* FillOptions(struct ares_options * opts, int flags) { + memset(opts, 0, sizeof(struct ares_options)); + opts->flags = flags; + return opts; + } + private: + struct ares_options opts_; +}; + +class MockNoCheckRespChannelTest : public MockFlagsChannelOptsTest { + public: + MockNoCheckRespChannelTest() : MockFlagsChannelOptsTest(ARES_FLAG_NOCHECKRESP) {} +}; + +TEST_P(MockNoCheckRespChannelTest, ServFailResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_servfail); + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ESERVFAIL, result.status_); +} + +TEST_P(MockNoCheckRespChannelTest, NotImplResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_notimpl); + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTIMP, result.status_); +} + +TEST_P(MockNoCheckRespChannelTest, RefusedResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_refused); + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EREFUSED, result.status_); +} + +class MockEDNSChannelTest : public MockFlagsChannelOptsTest { + public: + MockEDNSChannelTest() : MockFlagsChannelOptsTest(ARES_FLAG_EDNS) {} +}; + +TEST_P(MockEDNSChannelTest, RetryWithoutEDNS) { + DNSPacket rspfail; + rspfail.set_response().set_aa().set_rcode(ns_r_servfail) + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + DNSPacket rspok; + rspok.set_response() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {1, 2, 3, 4})); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rspfail)) + .WillOnce(SetReply(&server_, &rspok)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +TEST_P(MockChannelTest, SearchDomains) { + DNSPacket nofirst; + nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillByDefault(SetReply(&server_, &nosecond)); + DNSPacket yesthird; + yesthird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)) + .add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesthird)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +// Relies on retries so is UDP-only +TEST_P(MockUDPChannelTest, SearchDomainsWithResentReply) { + DNSPacket nofirst; + nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + EXPECT_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillOnce(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + EXPECT_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillOnce(SetReply(&server_, &nosecond)); + DNSPacket yesthird; + yesthird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)) + .add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5})); + // Before sending the real answer, resend an earlier reply + EXPECT_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillOnce(DoAll(SetReply(&server_, &nofirst), + SetReplyQID(&server_, 123))) + .WillOnce(DoAll(SetReply(&server_, &yesthird), + SetReplyQID(&server_, -1))); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, SearchDomainsBare) { + DNSPacket nofirst; + nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillByDefault(SetReply(&server_, &nosecond)); + DNSPacket nothird; + nothird.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.third.gov", ns_t_a)); + ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillByDefault(SetReply(&server_, ¬hird)); + DNSPacket yesbare; + yesbare.set_response().set_aa() + .add_question(new DNSQuestion("www", ns_t_a)) + .add_answer(new DNSARR("www", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesbare)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, SearchNoDataThenSuccess) { + // First two search domains recognize the name but have no A records. + DNSPacket nofirst; + nofirst.set_response().set_aa() + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa() + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillByDefault(SetReply(&server_, &nosecond)); + DNSPacket yesthird; + yesthird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)) + .add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesthird)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, SearchNoDataThenNoDataBare) { + // First two search domains recognize the name but have no A records. + DNSPacket nofirst; + nofirst.set_response().set_aa() + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa() + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillByDefault(SetReply(&server_, &nosecond)); + DNSPacket nothird; + nothird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)); + ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillByDefault(SetReply(&server_, ¬hird)); + DNSPacket nobare; + nobare.set_response().set_aa() + .add_question(new DNSQuestion("www", ns_t_a)); + ON_CALL(server_, OnRequest("www", ns_t_a)) + .WillByDefault(SetReply(&server_, &nobare)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENODATA, result.status_); +} + +TEST_P(MockChannelTest, SearchNoDataThenFail) { + // First two search domains recognize the name but have no A records. + DNSPacket nofirst; + nofirst.set_response().set_aa() + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa() + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillByDefault(SetReply(&server_, &nosecond)); + DNSPacket nothird; + nothird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)); + ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillByDefault(SetReply(&server_, ¬hird)); + DNSPacket nobare; + nobare.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www", ns_t_a)); + ON_CALL(server_, OnRequest("www", ns_t_a)) + .WillByDefault(SetReply(&server_, &nobare)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENODATA, result.status_); +} + +TEST_P(MockChannelTest, SearchAllocFailure) { + SearchResult result; + SetAllocFail(1); + ares_search(channel_, "fully.qualified.", ns_c_in, ns_t_a, SearchCallback, &result); + /* Already done */ + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOMEM, result.status_); +} + +TEST_P(MockChannelTest, SearchHighNdots) { + DNSPacket nobare; + nobare.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("a.b.c.w.w.w", ns_t_a)); + ON_CALL(server_, OnRequest("a.b.c.w.w.w", ns_t_a)) + .WillByDefault(SetReply(&server_, &nobare)); + DNSPacket yesfirst; + yesfirst.set_response().set_aa() + .add_question(new DNSQuestion("a.b.c.w.w.w.first.com", ns_t_a)) + .add_answer(new DNSARR("a.b.c.w.w.w.first.com", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("a.b.c.w.w.w.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesfirst)); + + SearchResult result; + ares_search(channel_, "a.b.c.w.w.w", ns_c_in, ns_t_a, SearchCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + std::stringstream ss; + ss << PacketToString(result.data_); + EXPECT_EQ("RSP QRY AA NOERROR Q:{'a.b.c.w.w.w.first.com' IN A} " + "A:{'a.b.c.w.w.w.first.com' IN A TTL=512 2.3.4.5}", + ss.str()); +} + +TEST_P(MockChannelTest, UnspecifiedFamilyV6) { + DNSPacket rsp6; + rsp6.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSAaaaRR("example.com", 100, + {0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03})); + ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) + .WillByDefault(SetReply(&server_, &rsp6)); + + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + // Default to IPv6 when both are available. + EXPECT_EQ("{'example.com' aliases=[] addrs=[2121:0000:0000:0000:0000:0000:0000:0303]}", ss.str()); +} + +TEST_P(MockChannelTest, UnspecifiedFamilyV4) { + DNSPacket rsp6; + rsp6.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)); + ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) + .WillByDefault(SetReply(&server_, &rsp6)); + DNSPacket rsp4; + rsp4.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)) + .add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("example.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp4)); + + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, UnspecifiedFamilyNoData) { + DNSPacket rsp6; + rsp6.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSCnameRR("example.com", 100, "elsewhere.com")); + ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) + .WillByDefault(SetReply(&server_, &rsp6)); + DNSPacket rsp4; + rsp4.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)); + ON_CALL(server_, OnRequest("example.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp4)); + + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'' aliases=[] addrs=[]}", ss.str()); +} + +TEST_P(MockChannelTest, UnspecifiedFamilyCname6A4) { + DNSPacket rsp6; + rsp6.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSCnameRR("example.com", 100, "elsewhere.com")); + ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) + .WillByDefault(SetReply(&server_, &rsp6)); + DNSPacket rsp4; + rsp4.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)) + .add_answer(new DNSARR("example.com", 100, {1, 2, 3, 4})); + ON_CALL(server_, OnRequest("example.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp4)); + + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_UNSPEC, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'example.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +TEST_P(MockChannelTest, ExplicitIP) { + HostResult result; + ares_gethostbyname(channel_, "1.2.3.4", AF_INET, HostCallback, &result); + EXPECT_TRUE(result.done_); // Immediate return + EXPECT_EQ(ARES_SUCCESS, result.status_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'1.2.3.4' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +TEST_P(MockChannelTest, ExplicitIPAllocFail) { + HostResult result; + SetAllocSizeFail(strlen("1.2.3.4") + 1); + ares_gethostbyname(channel_, "1.2.3.4", AF_INET, HostCallback, &result); + EXPECT_TRUE(result.done_); // Immediate return + EXPECT_EQ(ARES_ENOMEM, result.status_); +} + +TEST_P(MockChannelTest, SortListV4) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)) + .add_answer(new DNSARR("example.com", 100, {22, 23, 24, 25})) + .add_answer(new DNSARR("example.com", 100, {12, 13, 14, 15})) + .add_answer(new DNSARR("example.com", 100, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("example.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &rsp)); + + { + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "12.13.0.0/255.255.0.0 1234::5678")); + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'example.com' aliases=[] addrs=[12.13.14.15, 22.23.24.25, 2.3.4.5]}", ss.str()); + } + { + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "2.3.0.0/16 130.140.150.160/26")); + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'example.com' aliases=[] addrs=[2.3.4.5, 22.23.24.25, 12.13.14.15]}", ss.str()); + } + struct ares_options options; + memset(&options, 0, sizeof(options)); + int optmask = 0; + EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel_, &options, &optmask)); + EXPECT_TRUE((optmask & ARES_OPT_SORTLIST) == ARES_OPT_SORTLIST); + ares_destroy_options(&options); +} + +TEST_P(MockChannelTest, SortListV6) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSAaaaRR("example.com", 100, + {0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02})) + .add_answer(new DNSAaaaRR("example.com", 100, + {0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03})); + ON_CALL(server_, OnRequest("example.com", ns_t_aaaa)) + .WillByDefault(SetReply(&server_, &rsp)); + + { + ares_set_sortlist(channel_, "1111::/16 2.3.0.0/255.255.0.0"); + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'example.com' aliases=[] addrs=[1111:0000:0000:0000:0000:0000:0000:0202, " + "2121:0000:0000:0000:0000:0000:0000:0303]}", ss.str()); + } + { + ares_set_sortlist(channel_, "2121::/8"); + HostResult result; + ares_gethostbyname(channel_, "example.com.", AF_INET6, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'example.com' aliases=[] addrs=[2121:0000:0000:0000:0000:0000:0000:0303, " + "1111:0000:0000:0000:0000:0000:0000:0202]}", ss.str()); + } +} + +// Relies on retries so is UDP-only +TEST_P(MockUDPChannelTest, SearchDomainsAllocFail) { + DNSPacket nofirst; + nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + ON_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillByDefault(SetReply(&server_, &nosecond)); + DNSPacket yesthird; + yesthird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)) + .add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesthird)); + + // Fail a variety of different memory allocations, and confirm + // that the operation either fails with ENOMEM or succeeds + // with the expected result. + const int kCount = 34; + HostResult results[kCount]; + for (int ii = 1; ii <= kCount; ii++) { + HostResult* result = &(results[ii - 1]); + ClearFails(); + SetAllocFail(ii); + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, result); + Process(); + EXPECT_TRUE(result->done_); + if (result->status_ == ARES_SUCCESS) { + std::stringstream ss; + ss << result->host_; + EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()) << " failed alloc #" << ii; + if (verbose) std::cerr << "Succeeded despite failure of alloc #" << ii << std::endl; + } + } + + // Explicitly destroy the channel now, so that the HostResult objects + // are still valid (in case any pending work refers to them). + ares_destroy(channel_); + channel_ = nullptr; +} + +// Relies on retries so is UDP-only +TEST_P(MockUDPChannelTest, Resend) { + std::vector nothing; + DNSPacket reply; + reply.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04})); + + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReplyData(&server_, nothing)) + .WillOnce(SetReplyData(&server_, nothing)) + .WillOnce(SetReply(&server_, &reply)); + + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(2, result.timeouts_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +TEST_P(MockChannelTest, CancelImmediate) { + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + ares_cancel(channel_); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ECANCELLED, result.status_); + EXPECT_EQ(0, result.timeouts_); +} + +// Relies on retries so is UDP-only +TEST_P(MockUDPChannelTest, CancelLater) { + std::vector nothing; + + // On second request, cancel the channel. + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReplyData(&server_, nothing)) + .WillOnce(CancelChannel(&server_, channel_)); + + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ECANCELLED, result.status_); + EXPECT_EQ(0, result.timeouts_); +} + +TEST_P(MockChannelTest, GetHostByNameDestroyAbsolute) { + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + + ares_destroy(channel_); + channel_ = nullptr; + + EXPECT_TRUE(result.done_); // Synchronous + EXPECT_EQ(ARES_EDESTRUCTION, result.status_); + EXPECT_EQ(0, result.timeouts_); +} + +TEST_P(MockChannelTest, GetHostByNameDestroyRelative) { + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + + ares_destroy(channel_); + channel_ = nullptr; + + EXPECT_TRUE(result.done_); // Synchronous + EXPECT_EQ(ARES_EDESTRUCTION, result.status_); + EXPECT_EQ(0, result.timeouts_); +} + +TEST_P(MockChannelTest, GetHostByAddrDestroy) { + unsigned char gdns_addr4[4] = {0x08, 0x08, 0x08, 0x08}; + HostResult result; + ares_gethostbyaddr(channel_, gdns_addr4, sizeof(gdns_addr4), AF_INET, HostCallback, &result); + + ares_destroy(channel_); + channel_ = nullptr; + + EXPECT_TRUE(result.done_); // Synchronous + EXPECT_EQ(ARES_EDESTRUCTION, result.status_); + EXPECT_EQ(0, result.timeouts_); +} + +#ifndef WIN32 +TEST_P(MockChannelTest, HostAlias) { + DNSPacket reply; + reply.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 0x0100, {0x01, 0x02, 0x03, 0x04})); + ON_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &reply)); + + TempFile aliases("\n\n# www commentedout\nwww www.google.com\n"); + EnvValue with_env("HOSTALIASES", aliases.filename()); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); +} + +TEST_P(MockChannelTest, HostAliasMissing) { + DNSPacket yesfirst; + yesfirst.set_response().set_aa() + .add_question(new DNSQuestion("www.first.com", ns_t_a)) + .add_answer(new DNSARR("www.first.com", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesfirst)); + + TempFile aliases("\n\n# www commentedout\nww www.google.com\n"); + EnvValue with_env("HOSTALIASES", aliases.filename()); + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.first.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, HostAliasMissingFile) { + DNSPacket yesfirst; + yesfirst.set_response().set_aa() + .add_question(new DNSQuestion("www.first.com", ns_t_a)) + .add_answer(new DNSARR("www.first.com", 0x0200, {2, 3, 4, 5})); + ON_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillByDefault(SetReply(&server_, &yesfirst)); + + EnvValue with_env("HOSTALIASES", "bogus.mcfile"); + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.first.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +TEST_P(MockChannelTest, HostAliasUnreadable) { + TempFile aliases("www www.google.com\n"); + chmod(aliases.filename(), 0); + EnvValue with_env("HOSTALIASES", aliases.filename()); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EFILE, result.status_); + chmod(aliases.filename(), 0777); +} +#endif + +class MockMultiServerChannelTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface< std::pair > { + public: + MockMultiServerChannelTest(bool rotate) + : MockChannelOptsTest(3, GetParam().first, GetParam().second, nullptr, rotate ? ARES_OPT_ROTATE : 0) {} + void CheckExample() { + HostResult result; + ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); + } +}; + +class RotateMultiMockTest : public MockMultiServerChannelTest { + public: + RotateMultiMockTest() : MockMultiServerChannelTest(true) {} +}; + +class NoRotateMultiMockTest : public MockMultiServerChannelTest { + public: + NoRotateMultiMockTest() : MockMultiServerChannelTest(false) {} +}; + + +TEST_P(RotateMultiMockTest, ThirdServer) { + DNSPacket servfailrsp; + servfailrsp.set_response().set_aa().set_rcode(ns_r_servfail) + .add_question(new DNSQuestion("www.example.com", ns_t_a)); + DNSPacket notimplrsp; + notimplrsp.set_response().set_aa().set_rcode(ns_r_notimpl) + .add_question(new DNSQuestion("www.example.com", ns_t_a)); + DNSPacket okrsp; + okrsp.set_response().set_aa() + .add_question(new DNSQuestion("www.example.com", ns_t_a)) + .add_answer(new DNSARR("www.example.com", 100, {2,3,4,5})); + + EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); + EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); + EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[2].get(), &okrsp)); + CheckExample(); + + // Second time around, starts from server [1]. + EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[1].get(), &servfailrsp)); + EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[2].get(), ¬implrsp)); + EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[0].get(), &okrsp)); + CheckExample(); + + // Third time around, starts from server [2]. + EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[2].get(), &servfailrsp)); + EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[0].get(), ¬implrsp)); + EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[1].get(), &okrsp)); + CheckExample(); +} + +TEST_P(NoRotateMultiMockTest, ThirdServer) { + DNSPacket servfailrsp; + servfailrsp.set_response().set_aa().set_rcode(ns_r_servfail) + .add_question(new DNSQuestion("www.example.com", ns_t_a)); + DNSPacket notimplrsp; + notimplrsp.set_response().set_aa().set_rcode(ns_r_notimpl) + .add_question(new DNSQuestion("www.example.com", ns_t_a)); + DNSPacket okrsp; + okrsp.set_response().set_aa() + .add_question(new DNSQuestion("www.example.com", ns_t_a)) + .add_answer(new DNSARR("www.example.com", 100, {2,3,4,5})); + + EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); + EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); + EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[2].get(), &okrsp)); + CheckExample(); + + // Second time around, still starts from server [0]. + EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); + EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); + EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[2].get(), &okrsp)); + CheckExample(); + + // Third time around, still starts from server [0]. + EXPECT_CALL(*servers_[0], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[0].get(), &servfailrsp)); + EXPECT_CALL(*servers_[1], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[1].get(), ¬implrsp)); + EXPECT_CALL(*servers_[2], OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(servers_[2].get(), &okrsp)); + CheckExample(); +} + + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockChannelTest, + ::testing::Values(std::make_pair(AF_INET, false), + std::make_pair(AF_INET, true), + std::make_pair(AF_INET6, false), + std::make_pair(AF_INET6, true))); + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockUDPChannelTest, + ::testing::Values(AF_INET, AF_INET6)); + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockTCPChannelTest, + ::testing::Values(AF_INET, AF_INET6)); + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockExtraOptsTest, + ::testing::Values(std::make_pair(AF_INET, false), + std::make_pair(AF_INET, true), + std::make_pair(AF_INET6, false), + std::make_pair(AF_INET6, true))); + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockNoCheckRespChannelTest, + ::testing::Values(std::make_pair(AF_INET, false), + std::make_pair(AF_INET, true), + std::make_pair(AF_INET6, false), + std::make_pair(AF_INET6, true))); + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockEDNSChannelTest, + ::testing::Values(std::make_pair(AF_INET, false), + std::make_pair(AF_INET, true), + std::make_pair(AF_INET6, false), + std::make_pair(AF_INET6, true))); + +INSTANTIATE_TEST_CASE_P(TransportModes, RotateMultiMockTest, + ::testing::Values(std::make_pair(AF_INET, false), + std::make_pair(AF_INET, true), + std::make_pair(AF_INET6, false), + std::make_pair(AF_INET6, true))); + +INSTANTIATE_TEST_CASE_P(TransportModes, NoRotateMultiMockTest, + ::testing::Values(std::make_pair(AF_INET, false), + std::make_pair(AF_INET, true), + std::make_pair(AF_INET6, false), + std::make_pair(AF_INET6, true))); + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-ns.cc b/src/c-ares/test/ares-test-ns.cc new file mode 100644 index 000000000..c3c455214 --- /dev/null +++ b/src/c-ares/test/ares-test-ns.cc @@ -0,0 +1,199 @@ +#include "ares-test.h" + +#ifdef HAVE_CONTAINER + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace ares { +namespace test { + +namespace { + +struct ContainerInfo { + ContainerFilesystem* fs_; + std::string hostname_; + std::string domainname_; + VoidToIntFn fn_; +}; + +int EnterContainer(void *data) { + ContainerInfo *container = (ContainerInfo*)data; + + if (verbose) { + std::cerr << "Running function in container {chroot='" + << container->fs_->root() << "', hostname='" << container->hostname_ + << "', domainname='" << container->domainname_ << "'}" + << std::endl; + } + + // Ensure we are apparently root before continuing. + int count = 10; + while (getuid() != 0 && count > 0) { + usleep(100000); + count--; + } + if (getuid() != 0) { + std::cerr << "Child in user namespace has uid " << getuid() << std::endl; + return -1; + } + if (!container->fs_->mountpt().empty()) { + // We want to bind mount this inside the specified directory. + std::string innerdir = container->fs_->root() + container->fs_->mountpt(); + if (verbose) std::cerr << " mount --bind " << container->fs_->mountpt() + << " " << innerdir << std::endl; + int rc = mount(container->fs_->mountpt().c_str(), innerdir.c_str(), + "none", MS_BIND, 0); + if (rc != 0) { + std::cerr << "Warning: failed to bind mount " << container->fs_->mountpt() << " at " + << innerdir << ", errno=" << errno << std::endl; + } + } + + // Move into the specified directory. + if (chdir(container->fs_->root().c_str()) != 0) { + std::cerr << "Failed to chdir('" << container->fs_->root() + << "'), errno=" << errno << std::endl; + return -1; + } + // And make it the new root directory; + char buffer[PATH_MAX + 1]; + if (getcwd(buffer, PATH_MAX) == NULL) { + std::cerr << "failed to retrieve cwd, errno=" << errno << std::endl; + return -1; + } + buffer[PATH_MAX] = '\0'; + if (chroot(buffer) != 0) { + std::cerr << "chroot('" << buffer << "') failed, errno=" << errno << std::endl; + return -1; + } + + // Set host/domainnames if specified + if (!container->hostname_.empty()) { + if (sethostname(container->hostname_.c_str(), + container->hostname_.size()) != 0) { + std::cerr << "Failed to sethostname('" << container->hostname_ + << "'), errno=" << errno << std::endl; + return -1; + } + } + if (!container->domainname_.empty()) { + if (setdomainname(container->domainname_.c_str(), + container->domainname_.size()) != 0) { + std::cerr << "Failed to setdomainname('" << container->domainname_ + << "'), errno=" << errno << std::endl; + return -1; + } + } + + return container->fn_(); +} + +} // namespace + +// Run a function while: +// - chroot()ed into a particular directory +// - having a specified hostname/domainname + +int RunInContainer(ContainerFilesystem* fs, const std::string& hostname, + const std::string& domainname, VoidToIntFn fn) { + const int stack_size = 1024 * 1024; + std::vector stack(stack_size, 0); + ContainerInfo container = {fs, hostname, domainname, fn}; + + // Start a child process in a new user and UTS namespace + pid_t child = clone(EnterContainer, stack.data() + stack_size, + CLONE_VM|CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWUTS|SIGCHLD, + (void *)&container); + if (child < 0) { + std::cerr << "Failed to clone(), errno=" << errno << std::endl; + return -1; + } + + // Build the UID map that makes us look like root inside the namespace. + std::stringstream mapfiless; + mapfiless << "/proc/" << child << "/uid_map"; + std::string mapfile = mapfiless.str(); + int fd = open(mapfile.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644); + if (fd < 0) { + std::cerr << "Failed to create '" << mapfile << "'" << std::endl; + return -1; + } + std::stringstream contentss; + contentss << "0 " << getuid() << " 1" << std::endl; + std::string content = contentss.str(); + int rc = write(fd, content.c_str(), content.size()); + if (rc != (int)content.size()) { + std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl; + } + close(fd); + + // Wait for the child process and retrieve its status. + int status; + waitpid(child, &status, 0); + if (rc <= 0) { + std::cerr << "Failed to waitpid(" << child << ")" << std::endl; + return -1; + } + if (!WIFEXITED(status)) { + std::cerr << "Child " << child << " did not exit normally" << std::endl; + return -1; + } + return status; +} + +ContainerFilesystem::ContainerFilesystem(NameContentList files, const std::string& mountpt) { + rootdir_ = TempNam(nullptr, "ares-chroot"); + mkdir(rootdir_.c_str(), 0755); + dirs_.push_front(rootdir_); + for (const auto& nc : files) { + std::string fullpath = rootdir_ + nc.first; + int idx = fullpath.rfind('/'); + std::string dir = fullpath.substr(0, idx); + EnsureDirExists(dir); + files_.push_back(std::unique_ptr( + new TransientFile(fullpath, nc.second))); + } + if (!mountpt.empty()) { + char buffer[PATH_MAX + 1]; + if (realpath(mountpt.c_str(), buffer)) { + mountpt_ = buffer; + std::string fullpath = rootdir_ + mountpt_; + EnsureDirExists(fullpath); + } + } +} + +ContainerFilesystem::~ContainerFilesystem() { + files_.clear(); + for (const std::string& dir : dirs_) { + rmdir(dir.c_str()); + } +} + +void ContainerFilesystem::EnsureDirExists(const std::string& dir) { + if (std::find(dirs_.begin(), dirs_.end(), dir) != dirs_.end()) { + return; + } + size_t idx = dir.rfind('/'); + if (idx != std::string::npos) { + std::string prevdir = dir.substr(0, idx); + EnsureDirExists(prevdir); + } + // Ensure this directory is in the list before its ancestors. + mkdir(dir.c_str(), 0755); + dirs_.push_front(dir); +} + +} // namespace test +} // namespace ares + +#endif diff --git a/src/c-ares/test/ares-test-parse-a.cc b/src/c-ares/test/ares-test-parse-a.cc new file mode 100644 index 000000000..77d9591c2 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-a.cc @@ -0,0 +1,362 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseAReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)) + .add_answer(new DNSARR("example.com", 0x01020304, {2,3,4,5})); + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + }; + EXPECT_EQ(data, pkt.data()); + struct hostent *host = nullptr; + struct ares_addrttl info[5]; + int count = 5; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ(0x01020304, info[0].ttl); + unsigned long expected_addr = htonl(0x02030405); + EXPECT_EQ(expected_addr, info[0].ipaddr.s_addr); + EXPECT_EQ("2.3.4.5", AddressToString(&(info[0].ipaddr), 4)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); + ares_free_hostent(host); + + // Repeat without providing a hostent + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + nullptr, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ(0x01020304, info[0].ttl); + EXPECT_EQ(expected_addr, info[0].ipaddr.s_addr); + EXPECT_EQ("2.3.4.5", AddressToString(&(info[0].ipaddr), 4)); +} + +TEST_F(LibraryTest, ParseMalformedAReply) { + std::vector data = { + 0x12, 0x34, // [0:2) qid + 0x84, // [2] response + query + AA + not-TC + not-RD + 0x00, // [3] not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // [4:6) num questions + 0x00, 0x01, // [6:8) num answer RRs + 0x00, 0x00, // [8:10) num authority RRs + 0x00, 0x00, // [10:12) num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', // [12:20) + 0x03, 'c', 'o', 'm', // [20,24) + 0x00, // [24] + 0x00, 0x01, // [25:26) type A + 0x00, 0x01, // [27:29) class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', // [29:37) + 0x03, 'c', 'o', 'm', // [37:41) + 0x00, // [41] + 0x00, 0x01, // [42:44) RR type + 0x00, 0x01, // [44:46) class IN + 0x01, 0x02, 0x03, 0x04, // [46:50) TTL + 0x00, 0x04, // [50:52) rdata length + 0x02, 0x03, 0x04, 0x05, // [52,56) + }; + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + + // Invalid RR-len. + std::vector invalid_rrlen(data); + invalid_rrlen[51] = 180; + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(invalid_rrlen.data(), invalid_rrlen.size(), + &host, info, &count)); + + // Truncate mid-question. + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), 26, + &host, info, &count)); + + // Truncate mid-answer. + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), 42, + &host, info, &count)); +} + +TEST_F(LibraryTest, ParseAReplyNoData) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_ENODATA, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(0, count); + EXPECT_EQ(nullptr, host); + + // Again but with a CNAME. + pkt.add_answer(new DNSCnameRR("example.com", 200, "c.example.com")); + EXPECT_EQ(ARES_ENODATA, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(0, count); + EXPECT_EQ(nullptr, host); +} + +TEST_F(LibraryTest, ParseAReplyVariantA) { + DNSPacket pkt; + pkt.set_qid(6366).set_rd().set_ra() + .add_question(new DNSQuestion("mit.edu", ns_t_a)) + .add_answer(new DNSARR("mit.edu", 52, {18,7,22,69})) + .add_auth(new DNSNsRR("mit.edu", 292, "W20NS.mit.edu")) + .add_auth(new DNSNsRR("mit.edu", 292, "BITSY.mit.edu")) + .add_auth(new DNSNsRR("mit.edu", 292, "STRAWB.mit.edu")) + .add_additional(new DNSARR("STRAWB.mit.edu", 292, {18,71,0,151})); + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + std::vector data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ("18.7.22.69", AddressToString(&(info[0].ipaddr), 4)); + EXPECT_EQ(52, info[0].ttl); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseAReplyJustCname) { + DNSPacket pkt; + pkt.set_qid(6366).set_rd().set_ra() + .add_question(new DNSQuestion("mit.edu", ns_t_a)) + .add_answer(new DNSCnameRR("mit.edu", 52, "other.mit.edu")); + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + std::vector data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(0, count); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'other.mit.edu' aliases=[mit.edu] addrs=[]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseAReplyVariantCname) { + DNSPacket pkt; + pkt.set_qid(6366).set_rd().set_ra() + .add_question(new DNSQuestion("query.example.com", ns_t_a)) + .add_answer(new DNSCnameRR("query.example.com", 200, "redirect.query.example.com")) + .add_answer(new DNSARR("redirect.query.example.com", 300, {129,97,123,22})) + .add_auth(new DNSNsRR("example.com", 218, "aa.ns1.example.com")) + .add_auth(new DNSNsRR("example.com", 218, "ns2.example.com")) + .add_auth(new DNSNsRR("example.com", 218, "ns3.example.com")) + .add_auth(new DNSNsRR("example.com", 218, "ns4.example.com")) + .add_additional(new DNSARR("aa.ns1.example.com", 218, {129,97,1,1})) + .add_additional(new DNSARR("ns2.example.com", 218, {129,97,1,2})) + .add_additional(new DNSARR("ns3.example.com", 218, {129,97,1,3})) + .add_additional(new DNSARR("ns4.example.com", 218, {129,97,1,4})); + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + std::vector data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ("129.97.123.22", AddressToString(&(info[0].ipaddr), 4)); + // TTL is reduced to match CNAME's. + EXPECT_EQ(200, info[0].ttl); + ares_free_hostent(host); + + // Repeat parsing without places to put the results. + count = 0; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + nullptr, info, &count)); +} + +TEST_F(LibraryTest, ParseAReplyVariantCnameChain) { + DNSPacket pkt; + pkt.set_qid(6366).set_rd().set_ra() + .add_question(new DNSQuestion("c1.localhost", ns_t_a)) + .add_answer(new DNSCnameRR("c1.localhost", 604800, "c2.localhost")) + .add_answer(new DNSCnameRR("c2.localhost", 604800, "c3.localhost")) + .add_answer(new DNSCnameRR("c3.localhost", 604800, "c4.localhost")) + .add_answer(new DNSARR("c4.localhost", 604800, {8,8,8,8})) + .add_auth(new DNSNsRR("localhost", 604800, "localhost")) + .add_additional(new DNSARR("localhost", 604800, {127,0,0,1})) + .add_additional(new DNSAaaaRR("localhost", 604800, + {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})); + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + std::vector data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ("8.8.8.8", AddressToString(&(info[0].ipaddr), 4)); + EXPECT_EQ(604800, info[0].ttl); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, DISABLED_ParseAReplyVariantCnameLast) { + DNSPacket pkt; + pkt.set_qid(6366).set_rd().set_ra() + .add_question(new DNSQuestion("query.example.com", ns_t_a)) + .add_answer(new DNSARR("redirect.query.example.com", 300, {129,97,123,221})) + .add_answer(new DNSARR("redirect.query.example.com", 300, {129,97,123,222})) + .add_answer(new DNSARR("redirect.query.example.com", 300, {129,97,123,223})) + .add_answer(new DNSARR("redirect.query.example.com", 300, {129,97,123,224})) + .add_answer(new DNSCnameRR("query.example.com", 60, "redirect.query.example.com")) + .add_additional(new DNSTxtRR("query.example.com", 60, {"text record"})); + struct hostent *host = nullptr; + struct ares_addrttl info[8]; + int count = 8; + std::vector data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(4, count); + EXPECT_EQ("129.97.123.221", AddressToString(&(info[0].ipaddr), 4)); + EXPECT_EQ("129.97.123.222", AddressToString(&(info[1].ipaddr), 4)); + EXPECT_EQ("129.97.123.223", AddressToString(&(info[2].ipaddr), 4)); + EXPECT_EQ("129.97.123.224", AddressToString(&(info[3].ipaddr), 4)); + EXPECT_EQ(300, info[0].ttl); + EXPECT_EQ(300, info[1].ttl); + EXPECT_EQ(300, info[2].ttl); + EXPECT_EQ(300, info[3].ttl); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseAReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)) + .add_answer(new DNSARR("example.com", 100, {0x02, 0x03, 0x04, 0x05})); + std::vector data; + + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.add_question(new DNSQuestion("example.com", ns_t_a)); + + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_a)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_a)); + +#ifdef DISABLED + // Not a response. + pkt.set_response(false); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.set_response(true); + + // Bad return code. + pkt.set_rcode(ns_r_formerr); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.set_rcode(ns_r_noerror); +#endif + + // Two questions + pkt.add_question(new DNSQuestion("example.com", ns_t_a)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_a)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.answers_.clear(); + pkt.add_answer(new DNSARR("example.com", 100, {0x02, 0x03, 0x04, 0x05})); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.add_answer(new DNSARR("example.com", 100, {0x02, 0x03, 0x04, 0x05})); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), len, + &host, info, &count)); + EXPECT_EQ(nullptr, host); + EXPECT_EQ(ARES_EBADRESP, ares_parse_a_reply(data.data(), len, + nullptr, info, &count)); + } +} + +TEST_F(LibraryTest, ParseAReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_a)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSARR("c.example.com", 500, {0x02, 0x03, 0x04, 0x05})); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + + for (int ii = 1; ii <= 8; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)) << ii; + EXPECT_EQ(nullptr, host); + } +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-aaaa.cc b/src/c-ares/test/ares-test-parse-aaaa.cc new file mode 100644 index 000000000..9d0457e68 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-aaaa.cc @@ -0,0 +1,191 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseAaaaReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSAaaaRR("example.com", 100, + {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04})); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + struct ares_addr6ttl info[5]; + int count = 5; + EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ(100, info[0].ttl); + EXPECT_EQ(0x01, info[0].ip6addr._S6_un._S6_u8[0]); + EXPECT_EQ(0x02, info[0].ip6addr._S6_un._S6_u8[4]); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'example.com' aliases=[] addrs=[0101:0101:0202:0202:0303:0303:0404:0404]}", ss.str()); + ares_free_hostent(host); + + // Repeat without providing places to put the results + count = 0; + EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), data.size(), + nullptr, info, &count)); +} + +TEST_F(LibraryTest, ParseAaaaReplyCname) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSCnameRR("example.com", 50, "c.example.com")) + .add_answer(new DNSAaaaRR("c.example.com", 100, + {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04})); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + struct ares_addr6ttl info[5]; + int count = 5; + EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + // CNAME TTL overrides AAAA TTL. + EXPECT_EQ(50, info[0].ttl); + EXPECT_EQ(0x01, info[0].ip6addr._S6_un._S6_u8[0]); + EXPECT_EQ(0x02, info[0].ip6addr._S6_un._S6_u8[4]); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'c.example.com' aliases=[example.com] addrs=[0101:0101:0202:0202:0303:0303:0404:0404]}", ss.str()); + ares_free_hostent(host); + + // Repeat without providing a hostent + count = 5; + EXPECT_EQ(ARES_SUCCESS, ares_parse_aaaa_reply(data.data(), data.size(), + nullptr, info, &count)); + EXPECT_EQ(1, count); + EXPECT_EQ(50, info[0].ttl); + EXPECT_EQ(0x01, info[0].ip6addr._S6_un._S6_u8[0]); + EXPECT_EQ(0x02, info[0].ip6addr._S6_un._S6_u8[4]); +} + +TEST_F(LibraryTest, ParseAaaaReplyNoData) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + struct ares_addr6ttl info[2]; + int count = 2; + EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(0, count); + EXPECT_EQ(nullptr, host); + + // Again but with a CNAME. + pkt.add_answer(new DNSCnameRR("example.com", 200, "c.example.com")); + EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(0, count); + EXPECT_EQ(nullptr, host); +} + +TEST_F(LibraryTest, ParseAaaaReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSAaaaRR("example.com", 100, + {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04})); + std::vector data; + + struct hostent *host = nullptr; + struct ares_addr6ttl info[2]; + int count = 2; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.add_question(new DNSQuestion("example.com", ns_t_aaaa)); + + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_aaaa)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_aaaa)); + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", ns_t_aaaa)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_aaaa)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.answers_.clear(); + pkt.add_answer(new DNSAaaaRR("example.com", 100, + {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04})); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(nullptr, host); + pkt.add_answer(new DNSAaaaRR("example.com", 100, + {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04})); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), len, + &host, info, &count)); + EXPECT_EQ(nullptr, host); + EXPECT_EQ(ARES_EBADRESP, ares_parse_aaaa_reply(data.data(), len, + nullptr, info, &count)); + } +} + +TEST_F(LibraryTest, ParseAaaaReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_aaaa)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSAaaaRR("c.example.com", 100, + {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04})); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + struct ares_addr6ttl info[2]; + int count = 2; + + for (int ii = 1; ii <= 8; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_aaaa_reply(data.data(), data.size(), + &host, info, &count)) << ii; + EXPECT_EQ(nullptr, host); + } +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-mx.cc b/src/c-ares/test/ares-test-parse-mx.cc new file mode 100644 index 000000000..37324a6d4 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-mx.cc @@ -0,0 +1,141 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseMxReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")) + .add_answer(new DNSMxRR("example.com", 100, 200, "mx2.example.com")); + std::vector data = pkt.data(); + + struct ares_mx_reply* mx = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_mx_reply(data.data(), data.size(), &mx)); + ASSERT_NE(nullptr, mx); + EXPECT_EQ("mx1.example.com", std::string(mx->host)); + EXPECT_EQ(100, mx->priority); + + struct ares_mx_reply* mx2 = mx->next; + ASSERT_NE(nullptr, mx2); + EXPECT_EQ("mx2.example.com", std::string(mx2->host)); + EXPECT_EQ(200, mx2->priority); + EXPECT_EQ(nullptr, mx2->next); + + ares_free_data(mx); +} + +TEST_F(LibraryTest, ParseMxReplyMalformed) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x0F, // type MX + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x0F, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x01, // rdata length -- too short + 0x02, + }; + + struct ares_mx_reply* mx = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_mx_reply(data.data(), data.size(), &mx)); + ASSERT_EQ(nullptr, mx); +} + + +TEST_F(LibraryTest, ParseMxReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + std::vector data; + struct ares_mx_reply* mx = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_mx)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_mx_reply(data.data(), data.size(), &mx)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); + + // Wrong sort of answer. + // TODO(drysdale): check if this should be ARES_ENODATA? + pkt.answers_.clear(); + pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_mx_reply(data.data(), data.size(), &mx)); + EXPECT_EQ(nullptr, mx); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + int rc = ares_parse_mx_reply(data.data(), len, &mx); + EXPECT_EQ(nullptr, mx); + EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME); + } +} + +TEST_F(LibraryTest, ParseMxReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSMxRR("c.example.com", 100, 100, "mx1.example.com")); + std::vector data = pkt.data(); + struct ares_mx_reply* mx = nullptr; + + for (int ii = 1; ii <= 5; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_mx_reply(data.data(), data.size(), &mx)) << ii; + } +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-naptr.cc b/src/c-ares/test/ares-test-parse-naptr.cc new file mode 100644 index 000000000..40013cbda --- /dev/null +++ b/src/c-ares/test/ares-test-parse-naptr.cc @@ -0,0 +1,119 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseNaptrReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_naptr)) + .add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")) + .add_answer(new DNSNaptrRR("example.com", 0x0010, + 11, 21, "SP", "service2", "regexp2", "replace2")); + std::vector data = pkt.data(); + + struct ares_naptr_reply* naptr = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + ASSERT_NE(nullptr, naptr); + EXPECT_EQ("SP", std::string((char*)naptr->flags)); + EXPECT_EQ("service", std::string((char*)naptr->service)); + EXPECT_EQ("regexp", std::string((char*)naptr->regexp)); + EXPECT_EQ("replace", std::string((char*)naptr->replacement)); + EXPECT_EQ(10, naptr->order); + EXPECT_EQ(20, naptr->preference); + + struct ares_naptr_reply* naptr2 = naptr->next; + ASSERT_NE(nullptr, naptr2); + EXPECT_EQ("SP", std::string((char*)naptr2->flags)); + EXPECT_EQ("service2", std::string((char*)naptr2->service)); + EXPECT_EQ("regexp2", std::string((char*)naptr2->regexp)); + EXPECT_EQ("replace2", std::string((char*)naptr2->replacement)); + EXPECT_EQ(11, naptr2->order); + EXPECT_EQ(21, naptr2->preference); + EXPECT_EQ(nullptr, naptr2->next); + + ares_free_data(naptr); +} + +TEST_F(LibraryTest, ParseNaptrReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_naptr)) + .add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")); + std::vector data; + struct ares_naptr_reply* naptr = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.add_question(new DNSQuestion("example.com", ns_t_naptr)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_naptr)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_naptr)); +#endif + + // Two questions + pkt.add_question(new DNSQuestion("example.com", ns_t_naptr)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_naptr)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + EXPECT_EQ(nullptr, naptr); + pkt.answers_.clear(); + pkt.add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + pkt.add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + int rc = ares_parse_naptr_reply(data.data(), len, &naptr); + EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME); + } +} + +TEST_F(LibraryTest, ParseNaptrReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_naptr)) + .add_answer(new DNSNaptrRR("example.com", 100, + 10, 20, "SP", "service", "regexp", "replace")) + .add_answer(new DNSNaptrRR("example.com", 0x0010, + 11, 21, "SP", "service2", "regexp2", "replace2")); + std::vector data = pkt.data(); + struct ares_naptr_reply* naptr = nullptr; + + for (int ii = 1; ii <= 13; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_naptr_reply(data.data(), data.size(), &naptr)); + } +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-ns.cc b/src/c-ares/test/ares-test-parse-ns.cc new file mode 100644 index 000000000..cd6531896 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-ns.cc @@ -0,0 +1,119 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseNsReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_ns)) + .add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_ns_reply(data.data(), data.size(), &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'example.com' aliases=[ns.example.com] addrs=[]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseNsReplyMultiple) { + DNSPacket pkt; + pkt.set_qid(10501).set_response().set_rd().set_ra() + .add_question(new DNSQuestion("google.com", ns_t_ns)) + .add_answer(new DNSNsRR("google.com", 59, "ns1.google.com")) + .add_answer(new DNSNsRR("google.com", 59, "ns2.google.com")) + .add_answer(new DNSNsRR("google.com", 59, "ns3.google.com")) + .add_answer(new DNSNsRR("google.com", 59, "ns4.google.com")) + .add_additional(new DNSARR("ns4.google.com", 247, {216,239,38,10})) + .add_additional(new DNSARR("ns2.google.com", 247, {216,239,34,10})) + .add_additional(new DNSARR("ns1.google.com", 247, {216,239,32,10})) + .add_additional(new DNSARR("ns3.google.com", 247, {216,239,36,10})); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_ns_reply(data.data(), data.size(), &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'google.com' aliases=[ns1.google.com, ns2.google.com, ns3.google.com, ns4.google.com] addrs=[]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseNsReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_ns)) + .add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + std::vector data; + struct hostent *host = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_ns_reply(data.data(), data.size(), &host)); + pkt.add_question(new DNSQuestion("example.com", ns_t_ns)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_ns)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_ns_reply(data.data(), data.size(), &host)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_ns)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", ns_t_ns)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_ns_reply(data.data(), data.size(), &host)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_ns)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_ns_reply(data.data(), data.size(), &host)); + pkt.answers_.clear(); + pkt.add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_ns_reply(data.data(), data.size(), &host)); + pkt.add_answer(new DNSNsRR("example.com", 100, "ns.example.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, ares_parse_ns_reply(data.data(), len, &host)); + } +} + +TEST_F(LibraryTest, ParseNsReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_ns)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSNsRR("c.example.com", 100, "ns.example.com")); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + + for (int ii = 1; ii <= 8; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_ns_reply(data.data(), data.size(), &host)) << ii; + } +} + + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-ptr.cc b/src/c-ares/test/ares-test-parse-ptr.cc new file mode 100644 index 000000000..c769b29c6 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-ptr.cc @@ -0,0 +1,249 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParsePtrReplyOK) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'other.com' aliases=[other.com] addrs=[16.32.48.64]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParsePtrReplyCname) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSCnameRR("64.48.32.16.in-addr.arpa", 50, "64.48.32.8.in-addr.arpa")) + .add_answer(new DNSPtrRR("64.48.32.8.in-addr.arpa", 100, "other.com")); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'other.com' aliases=[other.com] addrs=[16.32.48.64]}", ss.str()); + ares_free_hostent(host); +} + + +struct DNSMalformedCnameRR : public DNSCnameRR { + DNSMalformedCnameRR(const std::string& name, int ttl, const std::string& other) + : DNSCnameRR(name, ttl, other) {} + std::vector data() const { + std::vector data = DNSRR::data(); + std::vector encname = EncodeString(other_); + encname[0] = encname[0] + 63; // invalid label length + int len = encname.size(); + PushInt16(&data, len); + data.insert(data.end(), encname.begin(), encname.end()); + return data; + } +}; + +TEST_F(LibraryTest, ParsePtrReplyMalformedCname) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSMalformedCnameRR("64.48.32.16.in-addr.arpa", 50, "64.48.32.8.in-addr.arpa")) + .add_answer(new DNSPtrRR("64.48.32.8.in-addr.arpa", 100, "other.com")); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + ASSERT_EQ(nullptr, host); +} + +TEST_F(LibraryTest, ParseManyPtrReply) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "main.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other1.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other2.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other3.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other4.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other5.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other6.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other7.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other8.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other9.com")); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + ASSERT_NE(nullptr, host); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParsePtrReplyAdditional) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 55, "other.com")) + .add_auth(new DNSNsRR("16.in-addr.arpa", 234, "ns1.other.com")) + .add_auth(new DNSNsRR("16.in-addr.arpa", 234, "bb.ns2.other.com")) + .add_auth(new DNSNsRR("16.in-addr.arpa", 234, "ns3.other.com")) + .add_additional(new DNSARR("ns1.other.com", 229, {10,20,30,41})) + .add_additional(new DNSARR("bb.ns2.other.com", 229, {10,20,30,42})) + .add_additional(new DNSARR("ns3.other.com", 229, {10,20,30,43})); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'other.com' aliases=[other.com] addrs=[16.32.48.64]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParsePtrReplyErrors) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + std::vector data; + struct hostent *host = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)); + + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("99.48.32.16.in-addr.arpa", ns_t_ptr)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + EXPECT_EQ(nullptr, host); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)); + + // Two questions. + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + EXPECT_EQ(nullptr, host); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + EXPECT_EQ(nullptr, host); + pkt.answers_.clear(); + pkt.add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)); + EXPECT_EQ(nullptr, host); + pkt.add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, ares_parse_ptr_reply(data.data(), len, + addrv4, sizeof(addrv4), AF_INET, &host)); + EXPECT_EQ(nullptr, host); + } + + // Truncated packets with CNAME. + pkt.add_answer(new DNSCnameRR("64.48.32.16.in-addr.arpa", 50, "64.48.32.8.in-addr.arpa")); + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, ares_parse_ptr_reply(data.data(), len, + addrv4, sizeof(addrv4), AF_INET, &host)); + EXPECT_EQ(nullptr, host); + } +} + +TEST_F(LibraryTest, ParsePtrReplyAllocFailSome) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "main.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other1.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other2.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other3.com")); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + + for (int ii = 1; ii <= 18; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host)) << ii; + } +} + +TEST_F(LibraryTest, ParsePtrReplyAllocFailMany) { + byte addrv4[4] = {0x10, 0x20, 0x30, 0x40}; + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "main.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other1.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other2.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other3.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other4.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other5.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other6.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other7.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other8.com")) + .add_answer(new DNSPtrRR("64.48.32.16.in-addr.arpa", 100, "other9.com")); + std::vector data = pkt.data(); + struct hostent *host = nullptr; + + for (int ii = 1; ii <= 63; ii++) { + ClearFails(); + SetAllocFail(ii); + int rc = ares_parse_ptr_reply(data.data(), data.size(), + addrv4, sizeof(addrv4), AF_INET, &host); + if (rc != ARES_ENOMEM) { + EXPECT_EQ(ARES_SUCCESS, rc); + ares_free_hostent(host); + host = nullptr; + } + } +} + + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-soa.cc b/src/c-ares/test/ares-test-parse-soa.cc new file mode 100644 index 000000000..afa4b7ab9 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-soa.cc @@ -0,0 +1,108 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseSoaReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_soa)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data = pkt.data(); + + struct ares_soa_reply* soa = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_soa_reply(data.data(), data.size(), &soa)); + ASSERT_NE(nullptr, soa); + EXPECT_EQ("soa1.example.com", std::string(soa->nsname)); + EXPECT_EQ("fred.example.com", std::string(soa->hostmaster)); + EXPECT_EQ(1, soa->serial); + EXPECT_EQ(2, soa->refresh); + EXPECT_EQ(3, soa->retry); + EXPECT_EQ(4, soa->expire); + EXPECT_EQ(5, soa->minttl); + ares_free_data(soa); +} + +TEST_F(LibraryTest, ParseSoaReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_soa)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data; + struct ares_soa_reply* soa = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.add_question(new DNSQuestion("example.com", ns_t_soa)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_soa)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_soa)); +#endif + + // Two questions + pkt.add_question(new DNSQuestion("example.com", ns_t_soa)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_soa)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.answers_.clear(); + pkt.add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_soa_reply(data.data(), data.size(), &soa)); + pkt.add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + EXPECT_EQ(ARES_EBADRESP, ares_parse_soa_reply(data.data(), len, &soa)); + } +} + +TEST_F(LibraryTest, ParseSoaReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_soa)) + .add_answer(new DNSSoaRR("example.com", 100, + "soa1.example.com", "fred.example.com", + 1, 2, 3, 4, 5)); + std::vector data = pkt.data(); + struct ares_soa_reply* soa = nullptr; + + for (int ii = 1; ii <= 5; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_soa_reply(data.data(), data.size(), &soa)) << ii; + } +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-srv.cc b/src/c-ares/test/ares-test-parse-srv.cc new file mode 100644 index 000000000..cc651d6d4 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-srv.cc @@ -0,0 +1,288 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseSrvReplyOK) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_srv)) + .add_answer(new DNSSrvRR("example.com", 100, 10, 20, 30, "srv.example.com")) + .add_answer(new DNSSrvRR("example.com", 100, 11, 21, 31, "srv2.example.com")); + std::vector data = pkt.data(); + + struct ares_srv_reply* srv = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv)); + ASSERT_NE(nullptr, srv); + + EXPECT_EQ("srv.example.com", std::string(srv->host)); + EXPECT_EQ(10, srv->priority); + EXPECT_EQ(20, srv->weight); + EXPECT_EQ(30, srv->port); + + struct ares_srv_reply* srv2 = srv->next; + ASSERT_NE(nullptr, srv2); + EXPECT_EQ("srv2.example.com", std::string(srv2->host)); + EXPECT_EQ(11, srv2->priority); + EXPECT_EQ(21, srv2->weight); + EXPECT_EQ(31, srv2->port); + EXPECT_EQ(nullptr, srv2->next); + + ares_free_data(srv); +} + +TEST_F(LibraryTest, ParseSrvReplySingle) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.abc.def.com", ns_t_srv)) + .add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else1.where.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else2.where.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else3.where.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else4.where.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else5.where.com")) + .add_additional(new DNSARR("else2.where.com", 42, {172,19,0,1})) + .add_additional(new DNSARR("else5.where.com", 42, {172,19,0,2})); + std::vector data = pkt.data(); + + struct ares_srv_reply* srv = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv)); + ASSERT_NE(nullptr, srv); + + EXPECT_EQ("example.abc.def.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(10, srv->weight); + EXPECT_EQ(8160, srv->port); + EXPECT_EQ(nullptr, srv->next); + + ares_free_data(srv); +} + +TEST_F(LibraryTest, ParseSrvReplyMalformed) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x21, // type SRV + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x21, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length -- too short + 0x02, 0x03, 0x04, 0x05, + }; + + struct ares_srv_reply* srv = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_srv_reply(data.data(), data.size(), &srv)); + ASSERT_EQ(nullptr, srv); +} + +TEST_F(LibraryTest, ParseSrvReplyMultiple) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_ra().set_rd() + .add_question(new DNSQuestion("srv.example.com", ns_t_srv)) + .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 6789, "a1.srv.example.com")) + .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 4567, "a2.srv.example.com")) + .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 5678, "a3.srv.example.com")) + .add_auth(new DNSNsRR("example.com", 300, "ns1.example.com")) + .add_auth(new DNSNsRR("example.com", 300, "ns2.example.com")) + .add_auth(new DNSNsRR("example.com", 300, "ns3.example.com")) + .add_additional(new DNSARR("a1.srv.example.com", 300, {172,19,1,1})) + .add_additional(new DNSARR("a2.srv.example.com", 300, {172,19,1,2})) + .add_additional(new DNSARR("a3.srv.example.com", 300, {172,19,1,3})) + .add_additional(new DNSARR("n1.example.com", 300, {172,19,0,1})) + .add_additional(new DNSARR("n2.example.com", 300, {172,19,0,2})) + .add_additional(new DNSARR("n3.example.com", 300, {172,19,0,3})); + std::vector data = pkt.data(); + + struct ares_srv_reply* srv0 = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv0)); + ASSERT_NE(nullptr, srv0); + struct ares_srv_reply* srv = srv0; + + EXPECT_EQ("a1.srv.example.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(5, srv->weight); + EXPECT_EQ(6789, srv->port); + EXPECT_NE(nullptr, srv->next); + srv = srv->next; + + EXPECT_EQ("a2.srv.example.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(5, srv->weight); + EXPECT_EQ(4567, srv->port); + EXPECT_NE(nullptr, srv->next); + srv = srv->next; + + EXPECT_EQ("a3.srv.example.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(5, srv->weight); + EXPECT_EQ(5678, srv->port); + EXPECT_EQ(nullptr, srv->next); + + ares_free_data(srv0); +} + +TEST_F(LibraryTest, ParseSrvReplyCname) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.abc.def.com", ns_t_srv)) + .add_answer(new DNSCnameRR("example.abc.def.com", 300, "cname.abc.def.com")) + .add_answer(new DNSSrvRR("cname.abc.def.com", 300, 0, 10, 1234, "srv.abc.def.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else1.where.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else2.where.com")) + .add_auth(new DNSNsRR("abc.def.com", 44, "else3.where.com")) + .add_additional(new DNSARR("example.abc.def.com", 300, {172,19,0,1})) + .add_additional(new DNSARR("else1.where.com", 42, {172,19,0,1})) + .add_additional(new DNSARR("else2.where.com", 42, {172,19,0,2})) + .add_additional(new DNSARR("else3.where.com", 42, {172,19,0,3})); + std::vector data = pkt.data(); + + struct ares_srv_reply* srv = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv)); + ASSERT_NE(nullptr, srv); + + EXPECT_EQ("srv.abc.def.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(10, srv->weight); + EXPECT_EQ(1234, srv->port); + EXPECT_EQ(nullptr, srv->next); + + ares_free_data(srv); +} + +TEST_F(LibraryTest, ParseSrvReplyCnameMultiple) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_ra().set_rd() + .add_question(new DNSQuestion("query.example.com", ns_t_srv)) + .add_answer(new DNSCnameRR("query.example.com", 300, "srv.example.com")) + .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 6789, "a1.srv.example.com")) + .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 4567, "a2.srv.example.com")) + .add_answer(new DNSSrvRR("srv.example.com", 300, 0, 5, 5678, "a3.srv.example.com")) + .add_auth(new DNSNsRR("example.com", 300, "ns1.example.com")) + .add_auth(new DNSNsRR("example.com", 300, "ns2.example.com")) + .add_auth(new DNSNsRR("example.com", 300, "ns3.example.com")) + .add_additional(new DNSARR("a1.srv.example.com", 300, {172,19,1,1})) + .add_additional(new DNSARR("a2.srv.example.com", 300, {172,19,1,2})) + .add_additional(new DNSARR("a3.srv.example.com", 300, {172,19,1,3})) + .add_additional(new DNSARR("n1.example.com", 300, {172,19,0,1})) + .add_additional(new DNSARR("n2.example.com", 300, {172,19,0,2})) + .add_additional(new DNSARR("n3.example.com", 300, {172,19,0,3})); + std::vector data = pkt.data(); + + struct ares_srv_reply* srv0 = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv0)); + ASSERT_NE(nullptr, srv0); + struct ares_srv_reply* srv = srv0; + + EXPECT_EQ("a1.srv.example.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(5, srv->weight); + EXPECT_EQ(6789, srv->port); + EXPECT_NE(nullptr, srv->next); + srv = srv->next; + + EXPECT_EQ("a2.srv.example.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(5, srv->weight); + EXPECT_EQ(4567, srv->port); + EXPECT_NE(nullptr, srv->next); + srv = srv->next; + + EXPECT_EQ("a3.srv.example.com", std::string(srv->host)); + EXPECT_EQ(0, srv->priority); + EXPECT_EQ(5, srv->weight); + EXPECT_EQ(5678, srv->port); + EXPECT_EQ(nullptr, srv->next); + + ares_free_data(srv0); +} + +TEST_F(LibraryTest, ParseSrvReplyErrors) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.abc.def.com", ns_t_srv)) + .add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + std::vector data; + struct ares_srv_reply* srv = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_srv_reply(data.data(), data.size(), &srv)); + pkt.add_question(new DNSQuestion("example.abc.def.com", ns_t_srv)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_srv)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_srv_reply(data.data(), data.size(), &srv)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_srv)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.abc.def.com", ns_t_srv)); + data = pkt.data(); + EXPECT_EQ(ARES_EBADRESP, ares_parse_srv_reply(data.data(), data.size(), &srv)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", ns_t_ptr)); + + // Wrong sort of answer. + pkt.answers_.clear(); + pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com")); + data = pkt.data(); + EXPECT_EQ(ARES_SUCCESS, ares_parse_srv_reply(data.data(), data.size(), &srv)); + EXPECT_EQ(nullptr, srv); + pkt.answers_.clear(); + pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_srv_reply(data.data(), data.size(), &srv)); + pkt.add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + + // Truncated packets. + data = pkt.data(); + for (size_t len = 1; len < data.size(); len++) { + int rc = ares_parse_srv_reply(data.data(), len, &srv); + EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME); + } +} + +TEST_F(LibraryTest, ParseSrvReplyAllocFail) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.abc.def.com", ns_t_srv)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSSrvRR("example.abc.def.com", 180, 0, 10, 8160, "example.abc.def.com")); + std::vector data = pkt.data(); + struct ares_srv_reply* srv = nullptr; + + for (int ii = 1; ii <= 5; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_srv_reply(data.data(), data.size(), &srv)) << ii; + } +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse-txt.cc b/src/c-ares/test/ares-test-parse-txt.cc new file mode 100644 index 000000000..8aaaaa3b4 --- /dev/null +++ b/src/c-ares/test/ares-test-parse-txt.cc @@ -0,0 +1,266 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseTxtReplyOK) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b("ABC\0ABC", 7); + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_NE(nullptr, txt); + EXPECT_EQ(std::vector(expected1.data(), expected1.data() + expected1.size()), + std::vector(txt->txt, txt->txt + txt->length)); + + struct ares_txt_reply* txt2 = txt->next; + ASSERT_NE(nullptr, txt2); + EXPECT_EQ(std::vector(expected2a.data(), expected2a.data() + expected2a.size()), + std::vector(txt2->txt, txt2->txt + txt2->length)); + + struct ares_txt_reply* txt3 = txt2->next; + ASSERT_NE(nullptr, txt3); + EXPECT_EQ(std::vector(expected2b.data(), expected2b.data() + expected2b.size()), + std::vector(txt3->txt, txt3->txt + txt3->length)); + EXPECT_EQ(nullptr, txt3->next); + + ares_free_data(txt); +} + +TEST_F(LibraryTest, ParseTxtExtReplyOK) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b("ABC\0ABC", 7); + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + + struct ares_txt_ext* txt = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_parse_txt_reply_ext(data.data(), data.size(), &txt)); + ASSERT_NE(nullptr, txt); + EXPECT_EQ(std::vector(expected1.data(), expected1.data() + expected1.size()), + std::vector(txt->txt, txt->txt + txt->length)); + EXPECT_EQ(1, txt->record_start); + + struct ares_txt_ext* txt2 = txt->next; + ASSERT_NE(nullptr, txt2); + EXPECT_EQ(std::vector(expected2a.data(), expected2a.data() + expected2a.size()), + std::vector(txt2->txt, txt2->txt + txt2->length)); + EXPECT_EQ(1, txt2->record_start); + + struct ares_txt_ext* txt3 = txt2->next; + ASSERT_NE(nullptr, txt3); + EXPECT_EQ(std::vector(expected2b.data(), expected2b.data() + expected2b.size()), + std::vector(txt3->txt, txt3->txt + txt3->length)); + EXPECT_EQ(nullptr, txt3->next); + EXPECT_EQ(0, txt3->record_start); + + ares_free_data(txt); +} + +TEST_F(LibraryTest, ParseTxtMalformedReply1) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x03, // rdata length + 0x12, 'a', 'b', // invalid length + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + +TEST_F(LibraryTest, ParseTxtMalformedReply2) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + // truncated + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + +TEST_F(LibraryTest, ParseTxtMalformedReply3) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x13, // rdata length INVALID + 0x02, 'a', 'b', + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + +TEST_F(LibraryTest, ParseTxtMalformedReply4) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, // TRUNCATED + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + +TEST_F(LibraryTest, ParseTxtReplyErrors) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b = "txt2b"; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + struct ares_txt_reply* txt = nullptr; + + // No question. + pkt.questions_.clear(); + data = pkt.data(); + txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + EXPECT_EQ(nullptr, txt); + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); + +#ifdef DISABLED + // Question != answer + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("Axample.com", ns_t_txt)); + data = pkt.data(); + EXPECT_EQ(ARES_ENODATA, ares_parse_txt_reply(data.data(), data.size(), &txt)); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_txt)); +#endif + + // Two questions. + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); + data = pkt.data(); + txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + EXPECT_EQ(nullptr, txt); + pkt.questions_.clear(); + pkt.add_question(new DNSQuestion("example.com", ns_t_mx)); + + // No answer. + pkt.answers_.clear(); + data = pkt.data(); + txt = nullptr; + EXPECT_EQ(ARES_ENODATA, ares_parse_txt_reply(data.data(), data.size(), &txt)); + EXPECT_EQ(nullptr, txt); + pkt.add_answer(new DNSTxtRR("example.com", 100, {expected1})); + + // Truncated packets. + for (size_t len = 1; len < data.size(); len++) { + txt = nullptr; + EXPECT_NE(ARES_SUCCESS, ares_parse_txt_reply(data.data(), len, &txt)); + EXPECT_EQ(nullptr, txt); + } +} + +TEST_F(LibraryTest, ParseTxtReplyAllocFail) { + DNSPacket pkt; + std::string expected1 = "txt1.example.com"; + std::string expected2a = "txt2a"; + std::string expected2b = "txt2b"; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com", ns_t_mx)) + .add_answer(new DNSCnameRR("example.com", 300, "c.example.com")) + .add_answer(new DNSTxtRR("c.example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("c.example.com", 100, {expected1})) + .add_answer(new DNSTxtRR("c.example.com", 100, {expected2a, expected2b})); + std::vector data = pkt.data(); + struct ares_txt_reply* txt = nullptr; + + for (int ii = 1; ii <= 13; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_parse_txt_reply(data.data(), data.size(), &txt)) << ii; + } +} + + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test-parse.cc b/src/c-ares/test/ares-test-parse.cc new file mode 100644 index 000000000..4c54fc990 --- /dev/null +++ b/src/c-ares/test/ares-test-parse.cc @@ -0,0 +1,219 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include +#include + +namespace ares { +namespace test { + +TEST_F(LibraryTest, ParseRootName) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion(".", ns_t_a)) + .add_answer(new DNSARR(".", 100, {0x02, 0x03, 0x04, 0x05})); + std::vector data = pkt.data(); + + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'' aliases=[] addrs=[2.3.4.5]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseIndirectRootName) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0xC0, 0x04, // weird: pointer to a random zero earlier in the message + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer 1 + 0xC0, 0x04, + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + }; + + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'' aliases=[] addrs=[2.3.4.5]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseEscapedName) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x05, 'a', '\\', 'b', '.', 'c', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer 1 + 0x05, 'a', '\\', 'b', '.', 'c', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + }; + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + EXPECT_EQ(1, count); + HostEnt hent(host); + std::stringstream ss; + ss << hent; + // The printable name is expanded with escapes. + EXPECT_EQ(11, hent.name_.size()); + EXPECT_EQ('a', hent.name_[0]); + EXPECT_EQ('\\', hent.name_[1]); + EXPECT_EQ('\\', hent.name_[2]); + EXPECT_EQ('b', hent.name_[3]); + EXPECT_EQ('\\', hent.name_[4]); + EXPECT_EQ('.', hent.name_[5]); + EXPECT_EQ('c', hent.name_[6]); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParsePartialCompressedName) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x03, 'w', 'w', 'w', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer 1 + 0x03, 'w', 'w', 'w', + 0xc0, 0x10, // offset 16 + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + }; + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseFullyCompressedName) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x03, 'w', 'w', 'w', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer 1 + 0xc0, 0x0c, // offset 12 + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + }; + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); + ares_free_hostent(host); +} + +TEST_F(LibraryTest, ParseFullyCompressedName2) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0xC0, 0x12, // pointer to later in message + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Answer 1 + 0x03, 'w', 'w', 'w', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + }; + struct hostent *host = nullptr; + struct ares_addrttl info[2]; + int count = 2; + EXPECT_EQ(ARES_SUCCESS, ares_parse_a_reply(data.data(), data.size(), + &host, info, &count)); + ASSERT_NE(nullptr, host); + std::stringstream ss; + ss << HostEnt(host); + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); + ares_free_hostent(host); +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test.cc b/src/c-ares/test/ares-test.cc new file mode 100644 index 000000000..f08d3db56 --- /dev/null +++ b/src/c-ares/test/ares-test.cc @@ -0,0 +1,677 @@ +#include "ares-test.h" +#include "dns-proto.h" + +// Include ares internal files for DNS protocol details +#include "nameser.h" +#include "ares_dns.h" + +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#include + +#include +#include + +#ifdef WIN32 +#define BYTE_CAST (char *) +#define sclose(x) closesocket(x) +#define mkdir_(d, p) mkdir(d) +#else +#define BYTE_CAST +#define sclose(x) close(x) +#define mkdir_(d, p) mkdir(d, p) +#endif + +namespace ares { +namespace test { + +bool verbose = false; +int mock_port = 5300; + +unsigned long long LibraryTest::fails_ = 0; +std::map LibraryTest::size_fails_; + +void ProcessWork(ares_channel channel, + std::function()> get_extrafds, + std::function process_extra) { + int nfds, count; + fd_set readers, writers; + struct timeval tv; + while (true) { + // Retrieve the set of file descriptors that the library wants us to monitor. + FD_ZERO(&readers); + FD_ZERO(&writers); + nfds = ares_fds(channel, &readers, &writers); + if (nfds == 0) // no work left to do in the library + return; + + // Add in the extra FDs if present. + std::set extrafds = get_extrafds(); + for (int extrafd : extrafds) { + FD_SET(extrafd, &readers); + if (extrafd >= nfds) { + nfds = extrafd + 1; + } + } + + // Wait for activity or timeout. + tv.tv_sec = 0; + tv.tv_usec = 100000; // 100ms + count = select(nfds, &readers, &writers, nullptr, &tv); + if (count < 0) { + fprintf(stderr, "select() failed, errno %d\n", errno); + return; + } + + // Let the library process any activity. + ares_process(channel, &readers, &writers); + + // Let the provided callback process any activity on the extra FD. + for (int extrafd : extrafds) { + if (FD_ISSET(extrafd, &readers)) { + process_extra(extrafd); + } + } + } +} + +// static +void LibraryTest::SetAllocFail(int nth) { + assert(nth > 0); + assert(nth <= (int)(8 * sizeof(fails_))); + fails_ |= (1LL << (nth - 1)); +} + +// static +void LibraryTest::SetAllocSizeFail(size_t size) { + size_fails_[size]++; +} + +// static +void LibraryTest::ClearFails() { + fails_ = 0; + size_fails_.clear(); +} + + +// static +bool LibraryTest::ShouldAllocFail(size_t size) { + bool fail = (fails_ & 0x01); + fails_ >>= 1; + if (size_fails_[size] > 0) { + size_fails_[size]--; + fail = true; + } + return fail; +} + +// static +void* LibraryTest::amalloc(size_t size) { + if (ShouldAllocFail(size)) { + if (verbose) std::cerr << "Failing malloc(" << size << ") request" << std::endl; + return nullptr; + } else { + return malloc(size); + } +} + +// static +void* LibraryTest::arealloc(void *ptr, size_t size) { + if (ShouldAllocFail(size)) { + if (verbose) std::cerr << "Failing realloc(" << ptr << ", " << size << ") request" << std::endl; + return nullptr; + } else { + return realloc(ptr, size); + } +} + +// static +void LibraryTest::afree(void *ptr) { + free(ptr); +} + +std::set NoExtraFDs() { + return std::set(); +} + +void DefaultChannelTest::Process() { + ProcessWork(channel_, NoExtraFDs, nullptr); +} + +void DefaultChannelModeTest::Process() { + ProcessWork(channel_, NoExtraFDs, nullptr); +} + +MockServer::MockServer(int family, int port, int tcpport) + : udpport_(port), tcpport_(tcpport ? tcpport : udpport_), qid_(-1) { + // Create a TCP socket to receive data on. + tcpfd_ = socket(family, SOCK_STREAM, 0); + EXPECT_NE(-1, tcpfd_); + int optval = 1; + setsockopt(tcpfd_, SOL_SOCKET, SO_REUSEADDR, + BYTE_CAST &optval , sizeof(int)); + // Send TCP data right away. + setsockopt(tcpfd_, IPPROTO_TCP, TCP_NODELAY, + BYTE_CAST &optval , sizeof(int)); + + // Create a UDP socket to receive data on. + udpfd_ = socket(family, SOCK_DGRAM, 0); + EXPECT_NE(-1, udpfd_); + + // Bind the sockets to the given port. + if (family == AF_INET) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(tcpport_); + int tcprc = bind(tcpfd_, (struct sockaddr*)&addr, sizeof(addr)); + EXPECT_EQ(0, tcprc) << "Failed to bind AF_INET to TCP port " << tcpport_; + addr.sin_port = htons(udpport_); + int udprc = bind(udpfd_, (struct sockaddr*)&addr, sizeof(addr)); + EXPECT_EQ(0, udprc) << "Failed to bind AF_INET to UDP port " << udpport_; + } else { + EXPECT_EQ(AF_INET6, family); + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + memset(&addr.sin6_addr, 0, sizeof(addr.sin6_addr)); // in6addr_any + addr.sin6_port = htons(tcpport_); + int tcprc = bind(tcpfd_, (struct sockaddr*)&addr, sizeof(addr)); + EXPECT_EQ(0, tcprc) << "Failed to bind AF_INET6 to TCP port " << tcpport_; + addr.sin6_port = htons(udpport_); + int udprc = bind(udpfd_, (struct sockaddr*)&addr, sizeof(addr)); + EXPECT_EQ(0, udprc) << "Failed to bind AF_INET6 to UDP port " << udpport_; + } + if (verbose) std::cerr << "Configured " + << (family == AF_INET ? "IPv4" : "IPv6") + << " mock server with TCP socket " << tcpfd_ + << " on port " << tcpport_ + << " and UDP socket " << udpfd_ + << " on port " << udpport_ << std::endl; + + // For TCP, also need to listen for connections. + EXPECT_EQ(0, listen(tcpfd_, 5)) << "Failed to listen for TCP connections"; +} + +MockServer::~MockServer() { + for (int fd : connfds_) { + sclose(fd); + } + sclose(tcpfd_); + sclose(udpfd_); +} + +void MockServer::ProcessFD(int fd) { + if (fd != tcpfd_ && fd != udpfd_ && connfds_.find(fd) == connfds_.end()) { + // Not one of our FDs. + return; + } + if (fd == tcpfd_) { + int connfd = accept(tcpfd_, NULL, NULL); + if (connfd < 0) { + std::cerr << "Error accepting connection on fd " << fd << std::endl; + } else { + connfds_.insert(connfd); + } + return; + } + + // Activity on a data-bearing file descriptor. + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + byte buffer[2048]; + int len = recvfrom(fd, BYTE_CAST buffer, sizeof(buffer), 0, + (struct sockaddr *)&addr, &addrlen); + byte* data = buffer; + if (fd != udpfd_) { + if (len == 0) { + connfds_.erase(std::find(connfds_.begin(), connfds_.end(), fd)); + sclose(fd); + return; + } + if (len < 2) { + std::cerr << "Packet too short (" << len << ")" << std::endl; + return; + } + int tcplen = (data[0] << 8) + data[1]; + data += 2; + len -= 2; + if (tcplen != len) { + std::cerr << "Warning: TCP length " << tcplen + << " doesn't match remaining data length " << len << std::endl; + } + } + + // Assume the packet is a well-formed DNS request and extract the request + // details. + if (len < NS_HFIXEDSZ) { + std::cerr << "Packet too short (" << len << ")" << std::endl; + return; + } + int qid = DNS_HEADER_QID(data); + if (DNS_HEADER_QR(data) != 0) { + std::cerr << "Not a request" << std::endl; + return; + } + if (DNS_HEADER_OPCODE(data) != ns_o_query) { + std::cerr << "Not a query (opcode " << DNS_HEADER_OPCODE(data) + << ")" << std::endl; + return; + } + if (DNS_HEADER_QDCOUNT(data) != 1) { + std::cerr << "Unexpected question count (" << DNS_HEADER_QDCOUNT(data) + << ")" << std::endl; + return; + } + byte* question = data + 12; + int qlen = len - 12; + + char *name = nullptr; + long enclen; + ares_expand_name(question, data, len, &name, &enclen); + if (!name) { + std::cerr << "Failed to retrieve name" << std::endl; + return; + } + qlen -= enclen; + question += enclen; + std::string namestr(name); + free(name); + + if (qlen < 4) { + std::cerr << "Unexpected question size (" << qlen + << " bytes after name)" << std::endl; + return; + } + if (DNS_QUESTION_CLASS(question) != ns_c_in) { + std::cerr << "Unexpected question class (" << DNS_QUESTION_CLASS(question) + << ")" << std::endl; + return; + } + int rrtype = DNS_QUESTION_TYPE(question); + + if (verbose) { + std::vector req(data, data + len); + std::cerr << "received " << (fd == udpfd_ ? "UDP" : "TCP") << " request " << PacketToString(req) + << " on port " << (fd == udpfd_ ? udpport_ : tcpport_) << std::endl; + std::cerr << "ProcessRequest(" << qid << ", '" << namestr + << "', " << RRTypeToString(rrtype) << ")" << std::endl; + } + ProcessRequest(fd, &addr, addrlen, qid, namestr, rrtype); +} + +std::set MockServer::fds() const { + std::set result = connfds_; + result.insert(tcpfd_); + result.insert(udpfd_); + return result; +} + +void MockServer::ProcessRequest(int fd, struct sockaddr_storage* addr, int addrlen, + int qid, const std::string& name, int rrtype) { + // Before processing, let gMock know the request is happening. + OnRequest(name, rrtype); + + if (reply_.size() == 0) { + return; + } + + // Make a local copy of the current pending reply. + std::vector reply = reply_; + + if (qid_ >= 0) { + // Use the explicitly specified query ID. + qid = qid_; + } + if (reply.size() >= 2) { + // Overwrite the query ID if space to do so. + reply[0] = (byte)((qid >> 8) & 0xff); + reply[1] = (byte)(qid & 0xff); + } + if (verbose) std::cerr << "sending reply " << PacketToString(reply) + << " on port " << ((fd == udpfd_) ? udpport_ : tcpport_) << std::endl; + + // Prefix with 2-byte length if TCP. + if (fd != udpfd_) { + int len = reply.size(); + std::vector vlen = {(byte)((len & 0xFF00) >> 8), (byte)(len & 0xFF)}; + reply.insert(reply.begin(), vlen.begin(), vlen.end()); + // Also, don't bother with the destination address. + addr = nullptr; + addrlen = 0; + } + + int rc = sendto(fd, BYTE_CAST reply.data(), reply.size(), 0, + (struct sockaddr *)addr, addrlen); + if (rc < static_cast(reply.size())) { + std::cerr << "Failed to send full reply, rc=" << rc << std::endl; + } +} + +// static +MockChannelOptsTest::NiceMockServers MockChannelOptsTest::BuildServers(int count, int family, int base_port) { + NiceMockServers servers; + assert(count > 0); + for (int ii = 0; ii < count; ii++) { + std::unique_ptr server(new NiceMockServer(family, base_port + ii)); + servers.push_back(std::move(server)); + } + return servers; +} + +MockChannelOptsTest::MockChannelOptsTest(int count, + int family, + bool force_tcp, + struct ares_options* givenopts, + int optmask) + : servers_(BuildServers(count, family, mock_port)), + server_(*servers_[0].get()), channel_(nullptr) { + // Set up channel options. + struct ares_options opts; + if (givenopts) { + memcpy(&opts, givenopts, sizeof(opts)); + } else { + memset(&opts, 0, sizeof(opts)); + } + + // Point the library at the first mock server by default (overridden below). + opts.udp_port = mock_port; + optmask |= ARES_OPT_UDP_PORT; + opts.tcp_port = mock_port; + optmask |= ARES_OPT_TCP_PORT; + + // If not already overridden, set short-ish timeouts. + if (!(optmask & (ARES_OPT_TIMEOUTMS|ARES_OPT_TIMEOUT))) { + opts.timeout = 1500; + optmask |= ARES_OPT_TIMEOUTMS; + } + // If not already overridden, set 3 retries. + if (!(optmask & ARES_OPT_TRIES)) { + opts.tries = 3; + optmask |= ARES_OPT_TRIES; + } + // If not already overridden, set search domains. + const char *domains[3] = {"first.com", "second.org", "third.gov"}; + if (!(optmask & ARES_OPT_DOMAINS)) { + opts.ndomains = 3; + opts.domains = (char**)domains; + optmask |= ARES_OPT_DOMAINS; + } + if (force_tcp) { + opts.flags |= ARES_FLAG_USEVC; + optmask |= ARES_OPT_FLAGS; + } + + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel_, &opts, optmask)); + EXPECT_NE(nullptr, channel_); + + // Set up servers after construction so we can set individual ports + struct ares_addr_port_node* prev = nullptr; + struct ares_addr_port_node* first = nullptr; + for (const auto& server : servers_) { + struct ares_addr_port_node* node = (struct ares_addr_port_node*)malloc(sizeof(*node)); + if (prev) { + prev->next = node; + } else { + first = node; + } + node->next = nullptr; + node->family = family; + node->udp_port = server->udpport(); + node->tcp_port = server->tcpport(); + if (family == AF_INET) { + node->addr.addr4.s_addr = htonl(0x7F000001); + } else { + memset(&node->addr.addr6, 0, sizeof(node->addr.addr6)); + node->addr.addr6._S6_un._S6_u8[15] = 1; + } + prev = node; + } + EXPECT_EQ(ARES_SUCCESS, ares_set_servers_ports(channel_, first)); + + while (first) { + prev = first; + first = first->next; + free(prev); + } + if (verbose) { + std::cerr << "Configured library with servers:"; + std::vector servers = GetNameServers(channel_); + for (const auto& server : servers) { + std::cerr << " " << server; + } + std::cerr << std::endl; + } +} + +MockChannelOptsTest::~MockChannelOptsTest() { + if (channel_) { + ares_destroy(channel_); + } + channel_ = nullptr; +} + +std::set MockChannelOptsTest::fds() const { + std::set fds; + for (const auto& server : servers_) { + std::set serverfds = server->fds(); + fds.insert(serverfds.begin(), serverfds.end()); + } + return fds; +} + +void MockChannelOptsTest::ProcessFD(int fd) { + for (auto& server : servers_) { + server->ProcessFD(fd); + } +} + +void MockChannelOptsTest::Process() { + using namespace std::placeholders; + ProcessWork(channel_, + std::bind(&MockChannelOptsTest::fds, this), + std::bind(&MockChannelOptsTest::ProcessFD, this, _1)); +} + +std::ostream& operator<<(std::ostream& os, const HostResult& result) { + os << '{'; + if (result.done_) { + os << StatusToString(result.status_) << " " << result.host_; + } else { + os << "(incomplete)"; + } + os << '}'; + return os; +} + +HostEnt::HostEnt(const struct hostent *hostent) : addrtype_(-1) { + if (!hostent) + return; + if (hostent->h_name) + name_ = hostent->h_name; + if (hostent->h_aliases) { + char** palias = hostent->h_aliases; + while (*palias != nullptr) { + aliases_.push_back(*palias); + palias++; + } + } + addrtype_ = hostent->h_addrtype; + if (hostent->h_addr_list) { + char** paddr = hostent->h_addr_list; + while (*paddr != nullptr) { + std::string addr = AddressToString(*paddr, hostent->h_length); + addrs_.push_back(addr); + paddr++; + } + } +} + +std::ostream& operator<<(std::ostream& os, const HostEnt& host) { + os << '{'; + os << "'" << host.name_ << "' " + << "aliases=["; + for (size_t ii = 0; ii < host.aliases_.size(); ii++) { + if (ii > 0) os << ", "; + os << host.aliases_[ii]; + } + os << "] "; + os << "addrs=["; + for (size_t ii = 0; ii < host.addrs_.size(); ii++) { + if (ii > 0) os << ", "; + os << host.addrs_[ii]; + } + os << "]"; + os << '}'; + return os; +} + +void HostCallback(void *data, int status, int timeouts, + struct hostent *hostent) { + EXPECT_NE(nullptr, data); + HostResult* result = reinterpret_cast(data); + result->done_ = true; + result->status_ = status; + result->timeouts_ = timeouts; + result->host_ = HostEnt(hostent); + if (verbose) std::cerr << "HostCallback(" << *result << ")" << std::endl; +} + +std::ostream& operator<<(std::ostream& os, const SearchResult& result) { + os << '{'; + if (result.done_) { + os << StatusToString(result.status_) << " " << PacketToString(result.data_); + } else { + os << "(incomplete)"; + } + os << '}'; + return os; +} + +void SearchCallback(void *data, int status, int timeouts, + unsigned char *abuf, int alen) { + EXPECT_NE(nullptr, data); + SearchResult* result = reinterpret_cast(data); + result->done_ = true; + result->status_ = status; + result->timeouts_ = timeouts; + result->data_.assign(abuf, abuf + alen); + if (verbose) std::cerr << "SearchCallback(" << *result << ")" << std::endl; +} + +std::ostream& operator<<(std::ostream& os, const NameInfoResult& result) { + os << '{'; + if (result.done_) { + os << StatusToString(result.status_) << " " << result.node_ << " " << result.service_; + } else { + os << "(incomplete)"; + } + os << '}'; + return os; +} + +void NameInfoCallback(void *data, int status, int timeouts, + char *node, char *service) { + EXPECT_NE(nullptr, data); + NameInfoResult* result = reinterpret_cast(data); + result->done_ = true; + result->status_ = status; + result->timeouts_ = timeouts; + result->node_ = std::string(node ? node : ""); + result->service_ = std::string(service ? service : ""); + if (verbose) std::cerr << "NameInfoCallback(" << *result << ")" << std::endl; +} + +std::vector GetNameServers(ares_channel channel) { + struct ares_addr_port_node* servers = nullptr; + EXPECT_EQ(ARES_SUCCESS, ares_get_servers_ports(channel, &servers)); + struct ares_addr_port_node* server = servers; + std::vector results; + while (server) { + std::stringstream ss; + switch (server->family) { + case AF_INET: + ss << AddressToString((char*)&server->addr.addr4, 4); + break; + case AF_INET6: + if (server->udp_port != 0) { + ss << '['; + } + ss << AddressToString((char*)&server->addr.addr6, 16); + if (server->udp_port != 0) { + ss << ']'; + } + break; + default: + results.push_back(""); + break; + } + if (server->udp_port != 0) { + ss << ":" << server->udp_port; + } + results.push_back(ss.str()); + server = server->next; + } + if (servers) ares_free_data(servers); + return results; +} + +TransientDir::TransientDir(const std::string& dirname) : dirname_(dirname) { + if (mkdir_(dirname_.c_str(), 0755) != 0) { + std::cerr << "Failed to create subdirectory '" << dirname_ << "'" << std::endl; + } +} + +TransientDir::~TransientDir() { + rmdir(dirname_.c_str()); +} + +TransientFile::TransientFile(const std::string& filename, + const std::string& contents) + : filename_(filename) { + FILE *f = fopen(filename.c_str(), "w"); + if (f == nullptr) { + std::cerr << "Error: failed to create '" << filename << "'" << std::endl; + return; + } + int rc = fwrite(contents.data(), 1, contents.size(), f); + if (rc != (int)contents.size()) { + std::cerr << "Error: failed to write contents of '" << filename << "'" << std::endl; + } + fclose(f); +} + +TransientFile::~TransientFile() { + unlink(filename_.c_str()); +} + +std::string TempNam(const char *dir, const char *prefix) { + char *p = tempnam(dir, prefix); + std::string result(p); + free(p); + return result; +} + +TempFile::TempFile(const std::string& contents) + : TransientFile(TempNam(nullptr, "ares"), contents) { + +} + +VirtualizeIO::VirtualizeIO(ares_channel c) + : channel_(c) +{ + ares_set_socket_functions(channel_, &default_functions, 0); +} + +VirtualizeIO::~VirtualizeIO() { + ares_set_socket_functions(channel_, 0, 0); +} + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/ares-test.h b/src/c-ares/test/ares-test.h new file mode 100644 index 000000000..282544269 --- /dev/null +++ b/src/c-ares/test/ares-test.h @@ -0,0 +1,424 @@ +// -*- mode: c++ -*- +#ifndef ARES_TEST_H +#define ARES_TEST_H + +#include "ares.h" + +#include "dns-proto.h" + +// Include ares internal file for DNS protocol constants +#include "nameser.h" + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if defined(HAVE_USER_NAMESPACE) && defined(HAVE_UTS_NAMESPACE) +#define HAVE_CONTAINER +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ares { + +typedef unsigned char byte; + +namespace test { + +extern bool verbose; +extern int mock_port; + +// Process all pending work on ares-owned file descriptors, plus +// optionally the given set-of-FDs + work function. +void ProcessWork(ares_channel channel, + std::function()> get_extrafds, + std::function process_extra); +std::set NoExtraFDs(); + +// Test fixture that ensures library initialization, and allows +// memory allocations to be failed. +class LibraryTest : public ::testing::Test { + public: + LibraryTest() { + EXPECT_EQ(ARES_SUCCESS, + ares_library_init_mem(ARES_LIB_INIT_ALL, + &LibraryTest::amalloc, + &LibraryTest::afree, + &LibraryTest::arealloc)); + } + ~LibraryTest() { + ares_library_cleanup(); + ClearFails(); + } + // Set the n-th malloc call (of any size) from the library to fail. + // (nth == 1 means the next call) + static void SetAllocFail(int nth); + // Set the next malloc call for the given size to fail. + static void SetAllocSizeFail(size_t size); + // Remove any pending alloc failures. + static void ClearFails(); + + static void *amalloc(size_t size); + static void* arealloc(void *ptr, size_t size); + static void afree(void *ptr); + private: + static bool ShouldAllocFail(size_t size); + static unsigned long long fails_; + static std::map size_fails_; +}; + +// Test fixture that uses a default channel. +class DefaultChannelTest : public LibraryTest { + public: + DefaultChannelTest() : channel_(nullptr) { + EXPECT_EQ(ARES_SUCCESS, ares_init(&channel_)); + EXPECT_NE(nullptr, channel_); + } + + ~DefaultChannelTest() { + ares_destroy(channel_); + channel_ = nullptr; + } + + // Process all pending work on ares-owned file descriptors. + void Process(); + + protected: + ares_channel channel_; +}; + +// Test fixture that uses a default channel with the specified lookup mode. +class DefaultChannelModeTest + : public LibraryTest, + public ::testing::WithParamInterface { + public: + DefaultChannelModeTest() : channel_(nullptr) { + struct ares_options opts = {0}; + opts.lookups = strdup(GetParam().c_str()); + int optmask = ARES_OPT_LOOKUPS; + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel_, &opts, optmask)); + EXPECT_NE(nullptr, channel_); + free(opts.lookups); + } + + ~DefaultChannelModeTest() { + ares_destroy(channel_); + channel_ = nullptr; + } + + // Process all pending work on ares-owned file descriptors. + void Process(); + + protected: + ares_channel channel_; +}; + +// Mock DNS server to allow responses to be scripted by tests. +class MockServer { + public: + MockServer(int family, int port, int tcpport = 0); + ~MockServer(); + + // Mock method indicating the processing of a particular + // request. + MOCK_METHOD2(OnRequest, void(const std::string& name, int rrtype)); + + // Set the reply to be sent next; the query ID field will be overwritten + // with the value from the request. + void SetReplyData(const std::vector& reply) { reply_ = reply; } + void SetReply(const DNSPacket* reply) { SetReplyData(reply->data()); } + void SetReplyQID(int qid) { qid_ = qid; } + + // The set of file descriptors that the server handles. + std::set fds() const; + + // Process activity on a file descriptor. + void ProcessFD(int fd); + + // Ports the server is responding to + int udpport() const { return udpport_; } + int tcpport() const { return tcpport_; } + + private: + void ProcessRequest(int fd, struct sockaddr_storage* addr, int addrlen, + int qid, const std::string& name, int rrtype); + + int udpport_; + int tcpport_; + int udpfd_; + int tcpfd_; + std::set connfds_; + std::vector reply_; + int qid_; +}; + +// Test fixture that uses a mock DNS server. +class MockChannelOptsTest : public LibraryTest { + public: + MockChannelOptsTest(int count, int family, bool force_tcp, struct ares_options* givenopts, int optmask); + ~MockChannelOptsTest(); + + // Process all pending work on ares-owned and mock-server-owned file descriptors. + void Process(); + + protected: + // NiceMockServer doesn't complain about uninteresting calls. + typedef testing::NiceMock NiceMockServer; + typedef std::vector< std::unique_ptr > NiceMockServers; + + std::set fds() const; + void ProcessFD(int fd); + + static NiceMockServers BuildServers(int count, int family, int base_port); + + NiceMockServers servers_; + // Convenience reference to first server. + NiceMockServer& server_; + ares_channel channel_; +}; + +class MockChannelTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface< std::pair > { + public: + MockChannelTest() : MockChannelOptsTest(1, GetParam().first, GetParam().second, nullptr, 0) {} +}; + +class MockUDPChannelTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface { + public: + MockUDPChannelTest() : MockChannelOptsTest(1, GetParam(), false, nullptr, 0) {} +}; + +class MockTCPChannelTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface { + public: + MockTCPChannelTest() : MockChannelOptsTest(1, GetParam(), true, nullptr, 0) {} +}; + +// gMock action to set the reply for a mock server. +ACTION_P2(SetReplyData, mockserver, data) { + mockserver->SetReplyData(data); +} +ACTION_P2(SetReply, mockserver, reply) { + mockserver->SetReply(reply); +} +ACTION_P2(SetReplyQID, mockserver, qid) { + mockserver->SetReplyQID(qid); +} +// gMock action to cancel a channel. +ACTION_P2(CancelChannel, mockserver, channel) { + ares_cancel(channel); +} + +// C++ wrapper for struct hostent. +struct HostEnt { + HostEnt() : addrtype_(-1) {} + HostEnt(const struct hostent* hostent); + std::string name_; + std::vector aliases_; + int addrtype_; // AF_INET or AF_INET6 + std::vector addrs_; +}; +std::ostream& operator<<(std::ostream& os, const HostEnt& result); + +// Structure that describes the result of an ares_host_callback invocation. +struct HostResult { + // Whether the callback has been invoked. + bool done_; + // Explicitly provided result information. + int status_; + int timeouts_; + // Contents of the hostent structure, if provided. + HostEnt host_; +}; +std::ostream& operator<<(std::ostream& os, const HostResult& result); + +// Structure that describes the result of an ares_callback invocation. +struct SearchResult { + // Whether the callback has been invoked. + bool done_; + // Explicitly provided result information. + int status_; + int timeouts_; + std::vector data_; +}; +std::ostream& operator<<(std::ostream& os, const SearchResult& result); + +// Structure that describes the result of an ares_nameinfo_callback invocation. +struct NameInfoResult { + // Whether the callback has been invoked. + bool done_; + // Explicitly provided result information. + int status_; + int timeouts_; + std::string node_; + std::string service_; +}; +std::ostream& operator<<(std::ostream& os, const NameInfoResult& result); + +// Standard implementation of ares callbacks that fill out the corresponding +// structures. +void HostCallback(void *data, int status, int timeouts, + struct hostent *hostent); +void SearchCallback(void *data, int status, int timeouts, + unsigned char *abuf, int alen); +void NameInfoCallback(void *data, int status, int timeouts, + char *node, char *service); + +// Retrieve the name servers used by a channel. +std::vector GetNameServers(ares_channel channel); + + +// RAII class to temporarily create a directory of a given name. +class TransientDir { + public: + TransientDir(const std::string& dirname); + ~TransientDir(); + + private: + std::string dirname_; +}; + +// C++ wrapper around tempnam() +std::string TempNam(const char *dir, const char *prefix); + +// RAII class to temporarily create file of a given name and contents. +class TransientFile { + public: + TransientFile(const std::string &filename, const std::string &contents); + ~TransientFile(); + + protected: + std::string filename_; +}; + +// RAII class for a temporary file with the given contents. +class TempFile : public TransientFile { + public: + TempFile(const std::string& contents); + const char* filename() const { return filename_.c_str(); } +}; + +#ifndef WIN32 +// RAII class for a temporary environment variable value. +class EnvValue { + public: + EnvValue(const char *name, const char *value) : name_(name), restore_(false) { + char *original = getenv(name); + if (original) { + restore_ = true; + original_ = original; + } + setenv(name_.c_str(), value, 1); + } + ~EnvValue() { + if (restore_) { + setenv(name_.c_str(), original_.c_str(), 1); + } else { + unsetenv(name_.c_str()); + } + } + private: + std::string name_; + bool restore_; + std::string original_; +}; +#endif + + +#ifdef HAVE_CONTAINER +// Linux-specific functionality for running code in a container, implemented +// in ares-test-ns.cc +typedef std::function VoidToIntFn; +typedef std::vector> NameContentList; + +class ContainerFilesystem { + public: + ContainerFilesystem(NameContentList files, const std::string& mountpt); + ~ContainerFilesystem(); + std::string root() const { return rootdir_; }; + std::string mountpt() const { return mountpt_; }; + private: + void EnsureDirExists(const std::string& dir); + std::string rootdir_; + std::string mountpt_; + std::list dirs_; + std::vector> files_; +}; + +int RunInContainer(ContainerFilesystem* fs, const std::string& hostname, + const std::string& domainname, VoidToIntFn fn); + +#define ICLASS_NAME(casename, testname) Contained##casename##_##testname +#define CONTAINED_TEST_F(casename, testname, hostname, domainname, files) \ + class ICLASS_NAME(casename, testname) : public casename { \ + public: \ + ICLASS_NAME(casename, testname)() {} \ + static int InnerTestBody(); \ + }; \ + TEST_F(ICLASS_NAME(casename, testname), _) { \ + ContainerFilesystem chroot(files, ".."); \ + VoidToIntFn fn(ICLASS_NAME(casename, testname)::InnerTestBody); \ + EXPECT_EQ(0, RunInContainer(&chroot, hostname, domainname, fn)); \ + } \ + int ICLASS_NAME(casename, testname)::InnerTestBody() + +#endif + +/* Assigns virtual IO functions to a channel. These functions simply call + * the actual system functions. + */ +class VirtualizeIO { +public: + VirtualizeIO(ares_channel); + ~VirtualizeIO(); + + static const ares_socket_functions default_functions; +private: + ares_channel channel_; +}; + +/* + * Slightly white-box macro to generate two runs for a given test case: + * One with no modifications, and one with all IO functions set to use + * the virtual io structure. + * Since no magic socket setup or anything is done in the latter case + * this should probably only be used for test with very vanilla IO + * requirements. + */ +#define VCLASS_NAME(casename, testname) Virt##casename##_##testname +#define VIRT_NONVIRT_TEST_F(casename, testname) \ + class VCLASS_NAME(casename, testname) : public casename { \ + public: \ + VCLASS_NAME(casename, testname)() {} \ + void InnerTestBody(); \ + }; \ + GTEST_TEST_(casename, testname, VCLASS_NAME(casename, testname), \ + ::testing::internal::GetTypeId()) { \ + InnerTestBody(); \ + } \ + GTEST_TEST_(casename, testname##_virtualized, \ + VCLASS_NAME(casename, testname), \ + ::testing::internal::GetTypeId()) { \ + VirtualizeIO vio(channel_); \ + InnerTestBody(); \ + } \ + void VCLASS_NAME(casename, testname)::InnerTestBody() + + +} // namespace test +} // namespace ares + +#endif diff --git a/src/c-ares/test/buildconf b/src/c-ares/test/buildconf new file mode 100755 index 000000000..5a9d7a346 --- /dev/null +++ b/src/c-ares/test/buildconf @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf -iv \ No newline at end of file diff --git a/src/c-ares/test/configure.ac b/src/c-ares/test/configure.ac new file mode 100644 index 000000000..577ce5c53 --- /dev/null +++ b/src/c-ares/test/configure.ac @@ -0,0 +1,22 @@ +AC_PREREQ(2.57) +AC_INIT([c-ares-test],[-],[-]) +AC_CONFIG_SRCDIR([ares-test.cc]) +AC_CONFIG_MACRO_DIR([../m4]) + +AM_INIT_AUTOMAKE([no-define]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl Checks for programs. +AC_PROG_CXX +AX_CXX_COMPILE_STDCXX_11([noext],[mandatory]) +LT_INIT +AC_SUBST(LIBTOOL_DEPS) +AX_PTHREAD +AX_CODE_COVERAGE +AX_CHECK_USER_NAMESPACE +AX_CHECK_UTS_NAMESPACE + +AC_CHECK_HEADERS(netdb.h netinet/tcp.h) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/src/c-ares/test/dns-dump.cc b/src/c-ares/test/dns-dump.cc new file mode 100644 index 000000000..023de3697 --- /dev/null +++ b/src/c-ares/test/dns-dump.cc @@ -0,0 +1,35 @@ +#include +#include +#include + +#include +#include + +#include "dns-proto.h" + +namespace ares { + +static void ShowFile(const char* filename) { + int fd = open(filename, O_RDONLY); + if (fd < 0) { + std::cerr << "Failed to open '" << filename << "'" << std::endl; + return; + } + std::vector contents; + while (true) { + unsigned char buffer[1024]; + int len = read(fd, buffer, sizeof(buffer)); + if (len <= 0) break; + contents.insert(contents.end(), buffer, buffer + len); + } + std::cout << PacketToString(contents) << std::endl; +} + +} // namespace ares + +int main(int argc, char* argv[]) { + for (int ii = 1; ii < argc; ++ii) { + ares::ShowFile(argv[ii]); + } +} + diff --git a/src/c-ares/test/dns-proto-test.cc b/src/c-ares/test/dns-proto-test.cc new file mode 100644 index 000000000..0c36a0c92 --- /dev/null +++ b/src/c-ares/test/dns-proto-test.cc @@ -0,0 +1,131 @@ +#include "ares-test.h" +#include "dns-proto.h" + +#include + +namespace ares { +namespace test { + +TEST(DNSProto, EncodeQuestions) { + DNSPacket pkt; + pkt.set_qid(0x1234).set_response().set_aa() + .add_question(new DNSQuestion("example.com.", ns_t_a)) + .add_question(new DNSQuestion("www.example.com", ns_t_aaaa, ns_c_chaos)); + + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x02, // num questions + 0x00, 0x00, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // type A + 0x00, 0x01, // class IN + // Question 2 + 0x03, 'w', 'w', 'w', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x1C, // type AAAA = 28 + 0x00, 0x03, // class CHAOS = 3 + }; + EXPECT_EQ(data, pkt.data()); +} + +TEST(DNSProto, EncodeSingleNameAnswers) { + DNSPacket pkt; + pkt.qid_ = 0x1234; + pkt.response_ = true; + pkt.aa_ = true; + pkt.opcode_ = ns_o_query; + pkt.add_answer(new DNSCnameRR("example.com", 0x01020304, "other.com.")); + pkt.add_auth(new DNSPtrRR("www.example.com", 0x01020304, "www.other.com")); + + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x00, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x01, // num authority RRs + 0x00, 0x00, // num additional RRs + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x05, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x0B, // rdata length + 0x05, 'o', 't', 'h', 'e', 'r', + 0x03, 'c', 'o', 'm', + 0x00, + // Authority 1 + 0x03, 'w', 'w', 'w', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x0c, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x0F, // rdata length + 0x03, 'w', 'w', 'w', + 0x05, 'o', 't', 'h', 'e', 'r', + 0x03, 'c', 'o', 'm', + 0x00, + }; + EXPECT_EQ(data, pkt.data()); +} + +TEST(DNSProto, EncodeAddressAnswers) { + DNSPacket pkt; + pkt.qid_ = 0x1234; + pkt.response_ = true; + pkt.aa_ = true; + pkt.opcode_ = ns_o_query; + std::vector addrv4 = {0x02, 0x03, 0x04, 0x05}; + pkt.add_answer(new DNSARR("example.com", 0x01020304, addrv4)); + byte addrv6[16] = {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}; + pkt.add_additional(new DNSAaaaRR("www.example.com", 0x01020304, addrv6, 16)); + + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x00, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x01, // num additional RRs + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x04, // rdata length + 0x02, 0x03, 0x04, 0x05, + // Additional 1 + 0x03, 'w', 'w', 'w', + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x1c, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x10, // rdata length + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04 + }; + EXPECT_EQ(data, pkt.data()); +} + + +} // namespace test +} // namespace ares diff --git a/src/c-ares/test/dns-proto.cc b/src/c-ares/test/dns-proto.cc new file mode 100644 index 000000000..8eb7f2e12 --- /dev/null +++ b/src/c-ares/test/dns-proto.cc @@ -0,0 +1,637 @@ +#include "dns-proto.h" + +// Include ares internal file for DNS protocol details +#include "ares.h" +#include "ares_dns.h" + +#include +#include + +#include + +namespace ares { + +std::string HexDump(std::vector data) { + std::stringstream ss; + for (size_t ii = 0; ii < data.size(); ii++) { + char buffer[2 + 1]; + sprintf(buffer, "%02x", data[ii]); + ss << buffer; + } + return ss.str(); +} + +std::string HexDump(const byte *data, int len) { + return HexDump(std::vector(data, data + len)); +} + +std::string HexDump(const char *data, int len) { + return HexDump(reinterpret_cast(data), len); +} + +std::string StatusToString(int status) { + switch (status) { + case ARES_SUCCESS: return "ARES_SUCCESS"; + case ARES_ENODATA: return "ARES_ENODATA"; + case ARES_EFORMERR: return "ARES_EFORMERR"; + case ARES_ESERVFAIL: return "ARES_ESERVFAIL"; + case ARES_ENOTFOUND: return "ARES_ENOTFOUND"; + case ARES_ENOTIMP: return "ARES_ENOTIMP"; + case ARES_EREFUSED: return "ARES_EREFUSED"; + case ARES_EBADQUERY: return "ARES_EBADQUERY"; + case ARES_EBADNAME: return "ARES_EBADNAME"; + case ARES_EBADFAMILY: return "ARES_EBADFAMILY"; + case ARES_EBADRESP: return "ARES_EBADRESP"; + case ARES_ECONNREFUSED: return "ARES_ECONNREFUSED"; + case ARES_ETIMEOUT: return "ARES_ETIMEOUT"; + case ARES_EOF: return "ARES_EOF"; + case ARES_EFILE: return "ARES_EFILE"; + case ARES_ENOMEM: return "ARES_ENOMEM"; + case ARES_EDESTRUCTION: return "ARES_EDESTRUCTION"; + case ARES_EBADSTR: return "ARES_EBADSTR"; + case ARES_EBADFLAGS: return "ARES_EBADFLAGS"; + case ARES_ENONAME: return "ARES_ENONAME"; + case ARES_EBADHINTS: return "ARES_EBADHINTS"; + case ARES_ENOTINITIALIZED: return "ARES_ENOTINITIALIZED"; + case ARES_ELOADIPHLPAPI: return "ARES_ELOADIPHLPAPI"; + case ARES_EADDRGETNETWORKPARAMS: return "ARES_EADDRGETNETWORKPARAMS"; + case ARES_ECANCELLED: return "ARES_ECANCELLED"; + default: return "UNKNOWN"; + } +} + +std::string RcodeToString(int rcode) { + switch (rcode) { + case ns_r_noerror: return "NOERROR"; + case ns_r_formerr: return "FORMERR"; + case ns_r_servfail: return "SERVFAIL"; + case ns_r_nxdomain: return "NXDOMAIN"; + case ns_r_notimpl: return "NOTIMPL"; + case ns_r_refused: return "REFUSED"; + case ns_r_yxdomain: return "YXDOMAIN"; + case ns_r_yxrrset: return "YXRRSET"; + case ns_r_nxrrset: return "NXRRSET"; + case ns_r_notauth: return "NOTAUTH"; + case ns_r_notzone: return "NOTZONE"; + case ns_r_badsig: return "BADSIG"; + case ns_r_badkey: return "BADKEY"; + case ns_r_badtime: return "BADTIME"; + default: return "UNKNOWN"; + } +} + +std::string RRTypeToString(int rrtype) { + switch (rrtype) { + case ns_t_a: return "A"; + case ns_t_ns: return "NS"; + case ns_t_md: return "MD"; + case ns_t_mf: return "MF"; + case ns_t_cname: return "CNAME"; + case ns_t_soa: return "SOA"; + case ns_t_mb: return "MB"; + case ns_t_mg: return "MG"; + case ns_t_mr: return "MR"; + case ns_t_null: return "NULL"; + case ns_t_wks: return "WKS"; + case ns_t_ptr: return "PTR"; + case ns_t_hinfo: return "HINFO"; + case ns_t_minfo: return "MINFO"; + case ns_t_mx: return "MX"; + case ns_t_txt: return "TXT"; + case ns_t_rp: return "RP"; + case ns_t_afsdb: return "AFSDB"; + case ns_t_x25: return "X25"; + case ns_t_isdn: return "ISDN"; + case ns_t_rt: return "RT"; + case ns_t_nsap: return "NSAP"; + case ns_t_nsap_ptr: return "NSAP_PTR"; + case ns_t_sig: return "SIG"; + case ns_t_key: return "KEY"; + case ns_t_px: return "PX"; + case ns_t_gpos: return "GPOS"; + case ns_t_aaaa: return "AAAA"; + case ns_t_loc: return "LOC"; + case ns_t_nxt: return "NXT"; + case ns_t_eid: return "EID"; + case ns_t_nimloc: return "NIMLOC"; + case ns_t_srv: return "SRV"; + case ns_t_atma: return "ATMA"; + case ns_t_naptr: return "NAPTR"; + case ns_t_kx: return "KX"; + case ns_t_cert: return "CERT"; + case ns_t_a6: return "A6"; + case ns_t_dname: return "DNAME"; + case ns_t_sink: return "SINK"; + case ns_t_opt: return "OPT"; + case ns_t_apl: return "APL"; + case ns_t_ds: return "DS"; + case ns_t_sshfp: return "SSHFP"; + case ns_t_rrsig: return "RRSIG"; + case ns_t_nsec: return "NSEC"; + case ns_t_dnskey: return "DNSKEY"; + case ns_t_tkey: return "TKEY"; + case ns_t_tsig: return "TSIG"; + case ns_t_ixfr: return "IXFR"; + case ns_t_axfr: return "AXFR"; + case ns_t_mailb: return "MAILB"; + case ns_t_maila: return "MAILA"; + case ns_t_any: return "ANY"; + case ns_t_zxfr: return "ZXFR"; + case ns_t_max: return "MAX"; + default: return "UNKNOWN"; + } +} + +std::string ClassToString(int qclass) { + switch (qclass) { + case ns_c_in: return "IN"; + case ns_c_chaos: return "CHAOS"; + case ns_c_hs: return "HESIOD"; + case ns_c_none: return "NONE"; + case ns_c_any: return "ANY"; + default: return "UNKNOWN"; + } +} + +std::string AddressToString(const void* vaddr, int len) { + const byte* addr = reinterpret_cast(vaddr); + std::stringstream ss; + if (len == 4) { + char buffer[4*4 + 3 + 1]; + sprintf(buffer, "%u.%u.%u.%u", + (unsigned char)addr[0], + (unsigned char)addr[1], + (unsigned char)addr[2], + (unsigned char)addr[3]); + ss << buffer; + } else if (len == 16) { + for (int ii = 0; ii < 16; ii+=2) { + if (ii > 0) ss << ':'; + char buffer[4 + 1]; + sprintf(buffer, "%02x%02x", (unsigned char)addr[ii], (unsigned char)addr[ii+1]); + ss << buffer; + } + } else { + ss << "!" << HexDump(addr, len) << "!"; + } + return ss.str(); +} + +std::string PacketToString(const std::vector& packet) { + const byte* data = packet.data(); + int len = packet.size(); + std::stringstream ss; + if (len < NS_HFIXEDSZ) { + ss << "(too short, len " << len << ")"; + return ss.str(); + } + ss << ((DNS_HEADER_QR(data) == 0) ? "REQ " : "RSP "); + switch (DNS_HEADER_OPCODE(data)) { + case ns_o_query: ss << "QRY "; break; + case ns_o_iquery: ss << "IQRY "; break; + case ns_o_status: ss << "STATUS "; break; + case ns_o_notify: ss << "NOTIFY "; break; + case ns_o_update: ss << "UPDATE "; break; + default: ss << "UNKNOWN(" << DNS_HEADER_OPCODE(data) << ") "; break; + } + if (DNS_HEADER_AA(data)) ss << "AA "; + if (DNS_HEADER_TC(data)) ss << "TC "; + if (DNS_HEADER_RD(data)) ss << "RD "; + if (DNS_HEADER_RA(data)) ss << "RA "; + if (DNS_HEADER_Z(data)) ss << "Z "; + if (DNS_HEADER_QR(data) == 1) ss << RcodeToString(DNS_HEADER_RCODE(data)); + + int nquestions = DNS_HEADER_QDCOUNT(data); + int nanswers = DNS_HEADER_ANCOUNT(data); + int nauths = DNS_HEADER_NSCOUNT(data); + int nadds = DNS_HEADER_ARCOUNT(data); + + const byte* pq = data + NS_HFIXEDSZ; + len -= NS_HFIXEDSZ; + for (int ii = 0; ii < nquestions; ii++) { + ss << " Q:" << QuestionToString(packet, &pq, &len); + } + const byte* prr = pq; + for (int ii = 0; ii < nanswers; ii++) { + ss << " A:" << RRToString(packet, &prr, &len); + } + for (int ii = 0; ii < nauths; ii++) { + ss << " AUTH:" << RRToString(packet, &prr, &len); + } + for (int ii = 0; ii < nadds; ii++) { + ss << " ADD:" << RRToString(packet, &prr, &len); + } + return ss.str(); +} + +std::string QuestionToString(const std::vector& packet, + const byte** data, int* len) { + std::stringstream ss; + ss << "{"; + if (*len < NS_QFIXEDSZ) { + ss << "(too short, len " << *len << ")"; + return ss.str(); + } + + char *name = nullptr; + long enclen; + int rc = ares_expand_name(*data, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + return ss.str(); + } + if (enclen > *len) { + ss << "(error, encoded name len " << enclen << "bigger than remaining data " << *len << " bytes)"; + return ss.str(); + } + *len -= enclen; + *data += enclen; + ss << "'" << name << "' "; + free(name); + if (*len < NS_QFIXEDSZ) { + ss << "(too short, len left " << *len << ")"; + return ss.str(); + } + ss << ClassToString(DNS_QUESTION_CLASS(*data)) << " "; + ss << RRTypeToString(DNS_QUESTION_TYPE(*data)); + *data += NS_QFIXEDSZ; + *len -= NS_QFIXEDSZ; + ss << "}"; + return ss.str(); +} + +std::string RRToString(const std::vector& packet, + const byte** data, int* len) { + std::stringstream ss; + ss << "{"; + if (*len < NS_RRFIXEDSZ) { + ss << "too short, len " << *len << ")"; + return ss.str(); + } + + char *name = nullptr; + long enclen; + int rc = ares_expand_name(*data, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + return ss.str(); + } + if (enclen > *len) { + ss << "(error, encoded name len " << enclen << "bigger than remaining data " << *len << " bytes)"; + return ss.str(); + } + *len -= enclen; + *data += enclen; + ss << "'" << name << "' "; + free(name); + name = nullptr; + + if (*len < NS_RRFIXEDSZ) { + ss << "(too short, len left " << *len << ")"; + return ss.str(); + } + int rrtype = DNS_RR_TYPE(*data); + if (rrtype == ns_t_opt) { + ss << "MAXUDP=" << DNS_RR_CLASS(*data) << " "; + ss << RRTypeToString(rrtype) << " "; + ss << "RCODE2=" << DNS_RR_TTL(*data); + } else { + ss << ClassToString(DNS_RR_CLASS(*data)) << " "; + ss << RRTypeToString(rrtype) << " "; + ss << "TTL=" << DNS_RR_TTL(*data); + } + int rdatalen = DNS_RR_LEN(*data); + + *data += NS_RRFIXEDSZ; + *len -= NS_RRFIXEDSZ; + if (*len < rdatalen) { + ss << "(RR too long at " << rdatalen << ", len left " << *len << ")"; + } else { + switch (rrtype) { + case ns_t_a: + case ns_t_aaaa: + ss << " " << AddressToString(*data, rdatalen); + break; + case ns_t_txt: { + const byte* p = *data; + while (p < (*data + rdatalen)) { + int len = *p++; + if ((p + len) <= (*data + rdatalen)) { + std::string txt(p, p + len); + ss << " " << len << ":'" << txt << "'"; + } else { + ss << "(string too long)"; + } + p += len; + } + break; + } + case ns_t_cname: + case ns_t_ns: + case ns_t_ptr: { + int rc = ares_expand_name(*data, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + break; + } + ss << " '" << name << "'"; + free(name); + break; + } + case ns_t_mx: + if (rdatalen > 2) { + int rc = ares_expand_name(*data + 2, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + break; + } + ss << " " << DNS__16BIT(*data) << " '" << name << "'"; + free(name); + } else { + ss << "(RR too short)"; + } + break; + case ns_t_srv: { + if (rdatalen > 6) { + const byte* p = *data; + unsigned long prio = DNS__16BIT(p); + unsigned long weight = DNS__16BIT(p + 2); + unsigned long port = DNS__16BIT(p + 4); + p += 6; + int rc = ares_expand_name(p, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + break; + } + ss << prio << " " << weight << " " << port << " '" << name << "'"; + free(name); + } else { + ss << "(RR too short)"; + } + break; + } + case ns_t_soa: { + const byte* p = *data; + int rc = ares_expand_name(p, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + break; + } + ss << " '" << name << "'"; + free(name); + p += enclen; + rc = ares_expand_name(p, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + break; + } + ss << " '" << name << "'"; + free(name); + p += enclen; + if ((p + 20) <= (*data + rdatalen)) { + unsigned long serial = DNS__32BIT(p); + unsigned long refresh = DNS__32BIT(p + 4); + unsigned long retry = DNS__32BIT(p + 8); + unsigned long expire = DNS__32BIT(p + 12); + unsigned long minimum = DNS__32BIT(p + 16); + ss << " " << serial << " " << refresh << " " << retry << " " << expire << " " << minimum; + } else { + ss << "(RR too short)"; + } + break; + } + case ns_t_naptr: { + if (rdatalen > 7) { + const byte* p = *data; + unsigned long order = DNS__16BIT(p); + unsigned long pref = DNS__16BIT(p + 2); + p += 4; + ss << order << " " << pref; + + int len = *p++; + std::string flags(p, p + len); + ss << " " << flags; + p += len; + + len = *p++; + std::string service(p, p + len); + ss << " '" << service << "'"; + p += len; + + len = *p++; + std::string regexp(p, p + len); + ss << " '" << regexp << "'"; + p += len; + + int rc = ares_expand_name(p, packet.data(), packet.size(), &name, &enclen); + if (rc != ARES_SUCCESS) { + ss << "(error from ares_expand_name)"; + break; + } + ss << " '" << name << "'"; + free(name); + } else { + ss << "(RR too short)"; + } + break; + } + default: + ss << " " << HexDump(*data, rdatalen); + break; + } + } + *data += rdatalen; + *len -= rdatalen; + + ss << "}"; + return ss.str(); +} + +void PushInt32(std::vector* data, int value) { + data->push_back((value & 0xff000000) >> 24); + data->push_back((value & 0x00ff0000) >> 16); + data->push_back((value & 0x0000ff00) >> 8); + data->push_back(value & 0x000000ff); +} + +void PushInt16(std::vector* data, int value) { + data->push_back((value & 0xff00) >> 8); + data->push_back(value & 0x00ff); +} + +std::vector EncodeString(const std::string& name) { + std::vector data; + std::stringstream ss(name); + std::string label; + // TODO: cope with escapes + while (std::getline(ss, label, '.')) { + data.push_back(label.length()); + data.insert(data.end(), label.begin(), label.end()); + } + data.push_back(0); + return data; +} + +std::vector DNSQuestion::data() const { + std::vector data; + std::vector encname = EncodeString(name_); + data.insert(data.end(), encname.begin(), encname.end()); + PushInt16(&data, rrtype_); + PushInt16(&data, qclass_); + return data; +} + +std::vector DNSRR::data() const { + std::vector data = DNSQuestion::data(); + PushInt32(&data, ttl_); + return data; +} + +std::vector DNSSingleNameRR::data() const { + std::vector data = DNSRR::data(); + std::vector encname = EncodeString(other_); + int len = encname.size(); + PushInt16(&data, len); + data.insert(data.end(), encname.begin(), encname.end()); + return data; +} + +std::vector DNSTxtRR::data() const { + std::vector data = DNSRR::data(); + int len = 0; + for (const std::string& txt : txt_) { + len += (1 + txt.size()); + } + PushInt16(&data, len); + for (const std::string& txt : txt_) { + data.push_back(txt.size()); + data.insert(data.end(), txt.begin(), txt.end()); + } + return data; +} + +std::vector DNSMxRR::data() const { + std::vector data = DNSRR::data(); + std::vector encname = EncodeString(other_); + int len = 2 + encname.size(); + PushInt16(&data, len); + PushInt16(&data, pref_); + data.insert(data.end(), encname.begin(), encname.end()); + return data; +} + +std::vector DNSSrvRR::data() const { + std::vector data = DNSRR::data(); + std::vector encname = EncodeString(target_); + int len = 6 + encname.size(); + PushInt16(&data, len); + PushInt16(&data, prio_); + PushInt16(&data, weight_); + PushInt16(&data, port_); + data.insert(data.end(), encname.begin(), encname.end()); + return data; +} + +std::vector DNSAddressRR::data() const { + std::vector data = DNSRR::data(); + int len = addr_.size(); + PushInt16(&data, len); + data.insert(data.end(), addr_.begin(), addr_.end()); + return data; +} + +std::vector DNSSoaRR::data() const { + std::vector data = DNSRR::data(); + std::vector encname1 = EncodeString(nsname_); + std::vector encname2 = EncodeString(rname_); + int len = encname1.size() + encname2.size() + 5*4; + PushInt16(&data, len); + data.insert(data.end(), encname1.begin(), encname1.end()); + data.insert(data.end(), encname2.begin(), encname2.end()); + PushInt32(&data, serial_); + PushInt32(&data, refresh_); + PushInt32(&data, retry_); + PushInt32(&data, expire_); + PushInt32(&data, minimum_); + return data; +} + +std::vector DNSOptRR::data() const { + std::vector data = DNSRR::data(); + int len = 0; + for (const DNSOption& opt : opts_) { + len += (4 + opt.data_.size()); + } + PushInt16(&data, len); + for (const DNSOption& opt : opts_) { + PushInt16(&data, opt.code_); + PushInt16(&data, opt.data_.size()); + data.insert(data.end(), opt.data_.begin(), opt.data_.end()); + } + return data; +} + +std::vector DNSNaptrRR::data() const { + std::vector data = DNSRR::data(); + std::vector encname = EncodeString(replacement_); + int len = (4 + 1 + flags_.size() + 1 + service_.size() + 1 + regexp_.size() + encname.size()); + PushInt16(&data, len); + PushInt16(&data, order_); + PushInt16(&data, pref_); + data.push_back(flags_.size()); + data.insert(data.end(), flags_.begin(), flags_.end()); + data.push_back(service_.size()); + data.insert(data.end(), service_.begin(), service_.end()); + data.push_back(regexp_.size()); + data.insert(data.end(), regexp_.begin(), regexp_.end()); + data.insert(data.end(), encname.begin(), encname.end()); + return data; +} + +std::vector DNSPacket::data() const { + std::vector data; + PushInt16(&data, qid_); + byte b = 0x00; + if (response_) b |= 0x80; + b |= ((opcode_ & 0x0f) << 3); + if (aa_) b |= 0x04; + if (tc_) b |= 0x02; + if (rd_) b |= 0x01; + data.push_back(b); + b = 0x00; + if (ra_) b |= 0x80; + if (z_) b |= 0x40; + if (ad_) b |= 0x20; + if (cd_) b |= 0x10; + b |= (rcode_ & 0x0f); + data.push_back(b); + + int count = questions_.size(); + PushInt16(&data, count); + count = answers_.size(); + PushInt16(&data, count); + count = auths_.size(); + PushInt16(&data, count); + count = adds_.size(); + PushInt16(&data, count); + + for (const std::unique_ptr& question : questions_) { + std::vector qdata = question->data(); + data.insert(data.end(), qdata.begin(), qdata.end()); + } + for (const std::unique_ptr& rr : answers_) { + std::vector rrdata = rr->data(); + data.insert(data.end(), rrdata.begin(), rrdata.end()); + } + for (const std::unique_ptr& rr : auths_) { + std::vector rrdata = rr->data(); + data.insert(data.end(), rrdata.begin(), rrdata.end()); + } + for (const std::unique_ptr& rr : adds_) { + std::vector rrdata = rr->data(); + data.insert(data.end(), rrdata.begin(), rrdata.end()); + } + return data; +} + +} // namespace ares diff --git a/src/c-ares/test/dns-proto.h b/src/c-ares/test/dns-proto.h new file mode 100644 index 000000000..346711dfe --- /dev/null +++ b/src/c-ares/test/dns-proto.h @@ -0,0 +1,242 @@ +// -*- mode: c++ -*- +#ifndef DNS_PROTO_H +#define DNS_PROTO_H +// Utilities for processing DNS packet contents + +// Include ares internal file for DNS protocol constants +#include "nameser.h" + +#include +#include +#include + +namespace ares { + +typedef unsigned char byte; + +std::string HexDump(std::vector data); +std::string HexDump(const byte *data, int len); +std::string HexDump(const char *data, int len); + +std::string StatusToString(int status); +std::string RcodeToString(int rcode); +std::string RRTypeToString(int rrtype); +std::string ClassToString(int qclass); +std::string AddressToString(const void* addr, int len); + +// Convert DNS protocol data to strings. +// Note that these functions are not defensive; they assume +// a validly formatted input, and so should not be used on +// externally-determined inputs. +std::string PacketToString(const std::vector& packet); +std::string QuestionToString(const std::vector& packet, + const byte** data, int* len); +std::string RRToString(const std::vector& packet, + const byte** data, int* len); + + +// Manipulate DNS protocol data. +void PushInt32(std::vector* data, int value); +void PushInt16(std::vector* data, int value); +std::vector EncodeString(const std::string& name); + +struct DNSQuestion { + DNSQuestion(const std::string& name, ns_type rrtype, ns_class qclass) + : name_(name), rrtype_(rrtype), qclass_(qclass) {} + DNSQuestion(const std::string& name, ns_type rrtype) + : name_(name), rrtype_(rrtype), qclass_(ns_c_in) {} + virtual ~DNSQuestion() {} + virtual std::vector data() const; + std::string name_; + ns_type rrtype_; + ns_class qclass_; +}; + +struct DNSRR : public DNSQuestion { + DNSRR(const std::string& name, ns_type rrtype, ns_class qclass, int ttl) + : DNSQuestion(name, rrtype, qclass), ttl_(ttl) {} + DNSRR(const std::string& name, ns_type rrtype, int ttl) + : DNSQuestion(name, rrtype), ttl_(ttl) {} + virtual ~DNSRR() {} + virtual std::vector data() const = 0; + int ttl_; +}; + +struct DNSAddressRR : public DNSRR { + DNSAddressRR(const std::string& name, ns_type rrtype, int ttl, + const byte* addr, int addrlen) + : DNSRR(name, rrtype, ttl), addr_(addr, addr + addrlen) {} + DNSAddressRR(const std::string& name, ns_type rrtype, int ttl, + const std::vector& addr) + : DNSRR(name, rrtype, ttl), addr_(addr) {} + virtual std::vector data() const; + std::vector addr_; +}; + +struct DNSARR : public DNSAddressRR { + DNSARR(const std::string& name, int ttl, const byte* addr, int addrlen) + : DNSAddressRR(name, ns_t_a, ttl, addr, addrlen) {} + DNSARR(const std::string& name, int ttl, const std::vector& addr) + : DNSAddressRR(name, ns_t_a, ttl, addr) {} +}; + +struct DNSAaaaRR : public DNSAddressRR { + DNSAaaaRR(const std::string& name, int ttl, const byte* addr, int addrlen) + : DNSAddressRR(name, ns_t_aaaa, ttl, addr, addrlen) {} + DNSAaaaRR(const std::string& name, int ttl, const std::vector& addr) + : DNSAddressRR(name, ns_t_aaaa, ttl, addr) {} +}; + +struct DNSSingleNameRR : public DNSRR { + DNSSingleNameRR(const std::string& name, ns_type rrtype, int ttl, + const std::string& other) + : DNSRR(name, rrtype, ttl), other_(other) {} + virtual std::vector data() const; + std::string other_; +}; + +struct DNSCnameRR : public DNSSingleNameRR { + DNSCnameRR(const std::string& name, int ttl, const std::string& other) + : DNSSingleNameRR(name, ns_t_cname, ttl, other) {} +}; + +struct DNSNsRR : public DNSSingleNameRR { + DNSNsRR(const std::string& name, int ttl, const std::string& other) + : DNSSingleNameRR(name, ns_t_ns, ttl, other) {} +}; + +struct DNSPtrRR : public DNSSingleNameRR { + DNSPtrRR(const std::string& name, int ttl, const std::string& other) + : DNSSingleNameRR(name, ns_t_ptr, ttl, other) {} +}; + +struct DNSTxtRR : public DNSRR { + DNSTxtRR(const std::string& name, int ttl, const std::vector& txt) + : DNSRR(name, ns_t_txt, ttl), txt_(txt) {} + virtual std::vector data() const; + std::vector txt_; +}; + +struct DNSMxRR : public DNSRR { + DNSMxRR(const std::string& name, int ttl, int pref, const std::string& other) + : DNSRR(name, ns_t_mx, ttl), pref_(pref), other_(other) {} + virtual std::vector data() const; + int pref_; + std::string other_; +}; + +struct DNSSrvRR : public DNSRR { + DNSSrvRR(const std::string& name, int ttl, + int prio, int weight, int port, const std::string& target) + : DNSRR(name, ns_t_srv, ttl), prio_(prio), weight_(weight), port_(port), target_(target) {} + virtual std::vector data() const; + int prio_; + int weight_; + int port_; + std::string target_; +}; + +struct DNSSoaRR : public DNSRR { + DNSSoaRR(const std::string& name, int ttl, + const std::string& nsname, const std::string& rname, + int serial, int refresh, int retry, int expire, int minimum) + : DNSRR(name, ns_t_soa, ttl), nsname_(nsname), rname_(rname), + serial_(serial), refresh_(refresh), retry_(retry), + expire_(expire), minimum_(minimum) {} + virtual std::vector data() const; + std::string nsname_; + std::string rname_; + int serial_; + int refresh_; + int retry_; + int expire_; + int minimum_; +}; + +struct DNSNaptrRR : public DNSRR { + DNSNaptrRR(const std::string& name, int ttl, + int order, int pref, + const std::string& flags, + const std::string& service, + const std::string& regexp, + const std::string& replacement) + : DNSRR(name, ns_t_naptr, ttl), order_(order), pref_(pref), + flags_(flags), service_(service), regexp_(regexp), replacement_(replacement) {} + virtual std::vector data() const; + int order_; + int pref_; + std::string flags_; + std::string service_; + std::string regexp_; + std::string replacement_; +}; + +struct DNSOption { + int code_; + std::vector data_; +}; + +struct DNSOptRR : public DNSRR { + DNSOptRR(int extrcode, int udpsize) + : DNSRR("", ns_t_opt, static_cast(udpsize), extrcode) {} + virtual std::vector data() const; + std::vector opts_; +}; + +struct DNSPacket { + DNSPacket() + : qid_(0), response_(false), opcode_(ns_o_query), + aa_(false), tc_(false), rd_(false), ra_(false), + z_(false), ad_(false), cd_(false), rcode_(ns_r_noerror) {} + // Convenience functions that take ownership of given pointers. + DNSPacket& add_question(DNSQuestion *q) { + questions_.push_back(std::unique_ptr(q)); + return *this; + } + DNSPacket& add_answer(DNSRR *q) { + answers_.push_back(std::unique_ptr(q)); + return *this; + } + DNSPacket& add_auth(DNSRR *q) { + auths_.push_back(std::unique_ptr(q)); + return *this; + } + DNSPacket& add_additional(DNSRR *q) { + adds_.push_back(std::unique_ptr(q)); + return *this; + } + // Chainable setters. + DNSPacket& set_qid(int qid) { qid_ = qid; return *this; } + DNSPacket& set_response(bool v = true) { response_ = v; return *this; } + DNSPacket& set_aa(bool v = true) { aa_ = v; return *this; } + DNSPacket& set_tc(bool v = true) { tc_ = v; return *this; } + DNSPacket& set_rd(bool v = true) { rd_ = v; return *this; } + DNSPacket& set_ra(bool v = true) { ra_ = v; return *this; } + DNSPacket& set_z(bool v = true) { z_ = v; return *this; } + DNSPacket& set_ad(bool v = true) { ad_ = v; return *this; } + DNSPacket& set_cd(bool v = true) { cd_ = v; return *this; } + DNSPacket& set_rcode(ns_rcode rcode) { rcode_ = rcode; return *this; } + + // Return the encoded packet. + std::vector data() const; + + int qid_; + bool response_; + ns_opcode opcode_; + bool aa_; + bool tc_; + bool rd_; + bool ra_; + bool z_; + bool ad_; + bool cd_; + ns_rcode rcode_; + std::vector> questions_; + std::vector> answers_; + std::vector> auths_; + std::vector> adds_; +}; + +} // namespace ares + +#endif diff --git a/src/c-ares/test/fuzzcheck.sh b/src/c-ares/test/fuzzcheck.sh new file mode 100755 index 000000000..d54f13ff9 --- /dev/null +++ b/src/c-ares/test/fuzzcheck.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# Check that all of the base fuzzing corpus parse without errors +./aresfuzz fuzzinput/* diff --git a/src/c-ares/test/fuzzinput/004a216d3cff18b0c5c6b68b807f1529 b/src/c-ares/test/fuzzinput/004a216d3cff18b0c5c6b68b807f1529 new file mode 100644 index 000000000..129a65ad1 Binary files /dev/null and b/src/c-ares/test/fuzzinput/004a216d3cff18b0c5c6b68b807f1529 differ diff --git a/src/c-ares/test/fuzzinput/00539467ca159b36aea95e61f9729115 b/src/c-ares/test/fuzzinput/00539467ca159b36aea95e61f9729115 new file mode 100644 index 000000000..f3f09a2b9 Binary files /dev/null and b/src/c-ares/test/fuzzinput/00539467ca159b36aea95e61f9729115 differ diff --git a/src/c-ares/test/fuzzinput/00e846db8f43f2f507cd1666ed5a753e b/src/c-ares/test/fuzzinput/00e846db8f43f2f507cd1666ed5a753e new file mode 100644 index 000000000..c629df3f1 Binary files /dev/null and b/src/c-ares/test/fuzzinput/00e846db8f43f2f507cd1666ed5a753e differ diff --git a/src/c-ares/test/fuzzinput/0177b7566f08c013699eaea9a77abeb3 b/src/c-ares/test/fuzzinput/0177b7566f08c013699eaea9a77abeb3 new file mode 100644 index 000000000..ddf98c615 Binary files /dev/null and b/src/c-ares/test/fuzzinput/0177b7566f08c013699eaea9a77abeb3 differ diff --git a/src/c-ares/test/fuzzinput/020a4fa317715bfdb236ed13751e6b65 b/src/c-ares/test/fuzzinput/020a4fa317715bfdb236ed13751e6b65 new file mode 100644 index 000000000..d6ad27464 Binary files /dev/null and b/src/c-ares/test/fuzzinput/020a4fa317715bfdb236ed13751e6b65 differ diff --git a/src/c-ares/test/fuzzinput/0310f2e81bea31f4fe3f330872a877dd b/src/c-ares/test/fuzzinput/0310f2e81bea31f4fe3f330872a877dd new file mode 100644 index 000000000..8b0617d75 Binary files /dev/null and b/src/c-ares/test/fuzzinput/0310f2e81bea31f4fe3f330872a877dd differ diff --git a/src/c-ares/test/fuzzinput/0449be67df1730b2d0887d412a9b7cc4 b/src/c-ares/test/fuzzinput/0449be67df1730b2d0887d412a9b7cc4 new file mode 100644 index 000000000..33bb3a3af Binary files /dev/null and b/src/c-ares/test/fuzzinput/0449be67df1730b2d0887d412a9b7cc4 differ diff --git a/src/c-ares/test/fuzzinput/0449dd14f7aa94bf0d716bfe09b287a8 b/src/c-ares/test/fuzzinput/0449dd14f7aa94bf0d716bfe09b287a8 new file mode 100644 index 000000000..5cb2e68b1 Binary files /dev/null and b/src/c-ares/test/fuzzinput/0449dd14f7aa94bf0d716bfe09b287a8 differ diff --git a/src/c-ares/test/fuzzinput/04c93cdf7208979aa4df80a3a0d5a2d8 b/src/c-ares/test/fuzzinput/04c93cdf7208979aa4df80a3a0d5a2d8 new file mode 100644 index 000000000..20e6fb0ff Binary files /dev/null and b/src/c-ares/test/fuzzinput/04c93cdf7208979aa4df80a3a0d5a2d8 differ diff --git a/src/c-ares/test/fuzzinput/0567e7171e08e75f3f91c4ca74c17adc b/src/c-ares/test/fuzzinput/0567e7171e08e75f3f91c4ca74c17adc new file mode 100644 index 000000000..b59a14597 Binary files /dev/null and b/src/c-ares/test/fuzzinput/0567e7171e08e75f3f91c4ca74c17adc differ diff --git a/src/c-ares/test/fuzzinput/05ba948578a397e9cbc6a7b3e78622fa b/src/c-ares/test/fuzzinput/05ba948578a397e9cbc6a7b3e78622fa new file mode 100644 index 000000000..7e8e92a60 Binary files /dev/null and b/src/c-ares/test/fuzzinput/05ba948578a397e9cbc6a7b3e78622fa differ diff --git a/src/c-ares/test/fuzzinput/060afe5ed25f3e2e86167e545f27edca b/src/c-ares/test/fuzzinput/060afe5ed25f3e2e86167e545f27edca new file mode 100644 index 000000000..38b48cebc Binary files /dev/null and b/src/c-ares/test/fuzzinput/060afe5ed25f3e2e86167e545f27edca differ diff --git a/src/c-ares/test/fuzzinput/06d47d3681493f1b1d41236f460d896f b/src/c-ares/test/fuzzinput/06d47d3681493f1b1d41236f460d896f new file mode 100644 index 000000000..7570c154c Binary files /dev/null and b/src/c-ares/test/fuzzinput/06d47d3681493f1b1d41236f460d896f differ diff --git a/src/c-ares/test/fuzzinput/0724a810b0e131c2fddb6de9003b9064 b/src/c-ares/test/fuzzinput/0724a810b0e131c2fddb6de9003b9064 new file mode 100644 index 000000000..40a08d261 Binary files /dev/null and b/src/c-ares/test/fuzzinput/0724a810b0e131c2fddb6de9003b9064 differ diff --git a/src/c-ares/test/fuzzinput/0b5279148826f5b962bcf1896bdb4ede b/src/c-ares/test/fuzzinput/0b5279148826f5b962bcf1896bdb4ede new file mode 100644 index 000000000..5d16d5780 Binary files /dev/null and b/src/c-ares/test/fuzzinput/0b5279148826f5b962bcf1896bdb4ede differ diff --git a/src/c-ares/test/fuzzinput/114048c0f6b10bdc67ce9166405d195e b/src/c-ares/test/fuzzinput/114048c0f6b10bdc67ce9166405d195e new file mode 100644 index 000000000..a4c35463c Binary files /dev/null and b/src/c-ares/test/fuzzinput/114048c0f6b10bdc67ce9166405d195e differ diff --git a/src/c-ares/test/fuzzinput/11b8464a0ef8735d202955c34c36b0c7 b/src/c-ares/test/fuzzinput/11b8464a0ef8735d202955c34c36b0c7 new file mode 100644 index 000000000..84c7db26c Binary files /dev/null and b/src/c-ares/test/fuzzinput/11b8464a0ef8735d202955c34c36b0c7 differ diff --git a/src/c-ares/test/fuzzinput/11cb626f1668c7b41954ce7d768fe528 b/src/c-ares/test/fuzzinput/11cb626f1668c7b41954ce7d768fe528 new file mode 100644 index 000000000..c99a7c6a5 Binary files /dev/null and b/src/c-ares/test/fuzzinput/11cb626f1668c7b41954ce7d768fe528 differ diff --git a/src/c-ares/test/fuzzinput/14b133bf18125b75a1976fa63a1df6b7 b/src/c-ares/test/fuzzinput/14b133bf18125b75a1976fa63a1df6b7 new file mode 100644 index 000000000..37a9e8e6d Binary files /dev/null and b/src/c-ares/test/fuzzinput/14b133bf18125b75a1976fa63a1df6b7 differ diff --git a/src/c-ares/test/fuzzinput/153c6b3afa8faa03c8bc28f936a6d4cf b/src/c-ares/test/fuzzinput/153c6b3afa8faa03c8bc28f936a6d4cf new file mode 100644 index 000000000..8115f93c4 Binary files /dev/null and b/src/c-ares/test/fuzzinput/153c6b3afa8faa03c8bc28f936a6d4cf differ diff --git a/src/c-ares/test/fuzzinput/182cad2a342ed7317b7c21a5d17020d1 b/src/c-ares/test/fuzzinput/182cad2a342ed7317b7c21a5d17020d1 new file mode 100644 index 000000000..2f7db026e Binary files /dev/null and b/src/c-ares/test/fuzzinput/182cad2a342ed7317b7c21a5d17020d1 differ diff --git a/src/c-ares/test/fuzzinput/1c61a61bb7057b52c5b15188345a5238 b/src/c-ares/test/fuzzinput/1c61a61bb7057b52c5b15188345a5238 new file mode 100644 index 000000000..9343c19a9 Binary files /dev/null and b/src/c-ares/test/fuzzinput/1c61a61bb7057b52c5b15188345a5238 differ diff --git a/src/c-ares/test/fuzzinput/1dbe2cf62ed2e4fa1c3cb473f08710b5 b/src/c-ares/test/fuzzinput/1dbe2cf62ed2e4fa1c3cb473f08710b5 new file mode 100644 index 000000000..3283d9793 Binary files /dev/null and b/src/c-ares/test/fuzzinput/1dbe2cf62ed2e4fa1c3cb473f08710b5 differ diff --git a/src/c-ares/test/fuzzinput/21199be504fcfece5c7096ee0dbba507 b/src/c-ares/test/fuzzinput/21199be504fcfece5c7096ee0dbba507 new file mode 100644 index 000000000..4978f793c Binary files /dev/null and b/src/c-ares/test/fuzzinput/21199be504fcfece5c7096ee0dbba507 differ diff --git a/src/c-ares/test/fuzzinput/21891480074b5635dbbe7137bdcabccd b/src/c-ares/test/fuzzinput/21891480074b5635dbbe7137bdcabccd new file mode 100644 index 000000000..1ddf16bfd Binary files /dev/null and b/src/c-ares/test/fuzzinput/21891480074b5635dbbe7137bdcabccd differ diff --git a/src/c-ares/test/fuzzinput/233aea42e15aa73e131eefabf16088c9 b/src/c-ares/test/fuzzinput/233aea42e15aa73e131eefabf16088c9 new file mode 100644 index 000000000..706bb1194 Binary files /dev/null and b/src/c-ares/test/fuzzinput/233aea42e15aa73e131eefabf16088c9 differ diff --git a/src/c-ares/test/fuzzinput/24660d4e7ac7aa21d600ea7a3d198bbb b/src/c-ares/test/fuzzinput/24660d4e7ac7aa21d600ea7a3d198bbb new file mode 100644 index 000000000..227d627f9 Binary files /dev/null and b/src/c-ares/test/fuzzinput/24660d4e7ac7aa21d600ea7a3d198bbb differ diff --git a/src/c-ares/test/fuzzinput/25589deb55c08429345f289d1c9b0254 b/src/c-ares/test/fuzzinput/25589deb55c08429345f289d1c9b0254 new file mode 100644 index 000000000..87c6e571d Binary files /dev/null and b/src/c-ares/test/fuzzinput/25589deb55c08429345f289d1c9b0254 differ diff --git a/src/c-ares/test/fuzzinput/2573bd823e4da11f727a17f8e1f35c26 b/src/c-ares/test/fuzzinput/2573bd823e4da11f727a17f8e1f35c26 new file mode 100644 index 000000000..64de596b7 Binary files /dev/null and b/src/c-ares/test/fuzzinput/2573bd823e4da11f727a17f8e1f35c26 differ diff --git a/src/c-ares/test/fuzzinput/276f12da56866273e76059ad0e7be97e b/src/c-ares/test/fuzzinput/276f12da56866273e76059ad0e7be97e new file mode 100644 index 000000000..ec1c4800d Binary files /dev/null and b/src/c-ares/test/fuzzinput/276f12da56866273e76059ad0e7be97e differ diff --git a/src/c-ares/test/fuzzinput/29198a2e380cb19babec9e02116d213e b/src/c-ares/test/fuzzinput/29198a2e380cb19babec9e02116d213e new file mode 100644 index 000000000..2357986b2 Binary files /dev/null and b/src/c-ares/test/fuzzinput/29198a2e380cb19babec9e02116d213e differ diff --git a/src/c-ares/test/fuzzinput/2c94ba9434b1a1b9396fc5364f101363 b/src/c-ares/test/fuzzinput/2c94ba9434b1a1b9396fc5364f101363 new file mode 100644 index 000000000..0f7d5d151 Binary files /dev/null and b/src/c-ares/test/fuzzinput/2c94ba9434b1a1b9396fc5364f101363 differ diff --git a/src/c-ares/test/fuzzinput/2d578c357dc2f5e02dc55cddb30641d1 b/src/c-ares/test/fuzzinput/2d578c357dc2f5e02dc55cddb30641d1 new file mode 100644 index 000000000..ec48abca2 Binary files /dev/null and b/src/c-ares/test/fuzzinput/2d578c357dc2f5e02dc55cddb30641d1 differ diff --git a/src/c-ares/test/fuzzinput/2dff6cc5a223e67fde9e5e79af456992 b/src/c-ares/test/fuzzinput/2dff6cc5a223e67fde9e5e79af456992 new file mode 100644 index 000000000..74455d8f3 Binary files /dev/null and b/src/c-ares/test/fuzzinput/2dff6cc5a223e67fde9e5e79af456992 differ diff --git a/src/c-ares/test/fuzzinput/2f103b1f9477f2d8934bd84328d51c75 b/src/c-ares/test/fuzzinput/2f103b1f9477f2d8934bd84328d51c75 new file mode 100644 index 000000000..d757d1df7 Binary files /dev/null and b/src/c-ares/test/fuzzinput/2f103b1f9477f2d8934bd84328d51c75 differ diff --git a/src/c-ares/test/fuzzinput/31cd3a8413de13d9624adbb1613784bf b/src/c-ares/test/fuzzinput/31cd3a8413de13d9624adbb1613784bf new file mode 100644 index 000000000..3fb4e483b Binary files /dev/null and b/src/c-ares/test/fuzzinput/31cd3a8413de13d9624adbb1613784bf differ diff --git a/src/c-ares/test/fuzzinput/36415bdf1d180098fe6234b4186e69f3 b/src/c-ares/test/fuzzinput/36415bdf1d180098fe6234b4186e69f3 new file mode 100644 index 000000000..91326f99e Binary files /dev/null and b/src/c-ares/test/fuzzinput/36415bdf1d180098fe6234b4186e69f3 differ diff --git a/src/c-ares/test/fuzzinput/3a04a80f0242e8dff0cd732e7c4767da b/src/c-ares/test/fuzzinput/3a04a80f0242e8dff0cd732e7c4767da new file mode 100644 index 000000000..dec08a1b9 Binary files /dev/null and b/src/c-ares/test/fuzzinput/3a04a80f0242e8dff0cd732e7c4767da differ diff --git a/src/c-ares/test/fuzzinput/44d0f973b7b0fb3e4a07770c943dcd5a b/src/c-ares/test/fuzzinput/44d0f973b7b0fb3e4a07770c943dcd5a new file mode 100644 index 000000000..719509558 Binary files /dev/null and b/src/c-ares/test/fuzzinput/44d0f973b7b0fb3e4a07770c943dcd5a differ diff --git a/src/c-ares/test/fuzzinput/50bc00daa0ddcd6cfb2b5d9f62c81f47 b/src/c-ares/test/fuzzinput/50bc00daa0ddcd6cfb2b5d9f62c81f47 new file mode 100644 index 000000000..0513462f9 Binary files /dev/null and b/src/c-ares/test/fuzzinput/50bc00daa0ddcd6cfb2b5d9f62c81f47 differ diff --git a/src/c-ares/test/fuzzinput/51ed2d1fb77b3078b54e94e85606b7df b/src/c-ares/test/fuzzinput/51ed2d1fb77b3078b54e94e85606b7df new file mode 100644 index 000000000..1174bd252 Binary files /dev/null and b/src/c-ares/test/fuzzinput/51ed2d1fb77b3078b54e94e85606b7df differ diff --git a/src/c-ares/test/fuzzinput/5c5e0e899cf2e7d053a9e45fb76f6e5a b/src/c-ares/test/fuzzinput/5c5e0e899cf2e7d053a9e45fb76f6e5a new file mode 100644 index 000000000..3849134e5 Binary files /dev/null and b/src/c-ares/test/fuzzinput/5c5e0e899cf2e7d053a9e45fb76f6e5a differ diff --git a/src/c-ares/test/fuzzinput/70152ed033f139443fbfb1b858bb3b1b b/src/c-ares/test/fuzzinput/70152ed033f139443fbfb1b858bb3b1b new file mode 100644 index 000000000..0228e9ddc Binary files /dev/null and b/src/c-ares/test/fuzzinput/70152ed033f139443fbfb1b858bb3b1b differ diff --git a/src/c-ares/test/fuzzinput/7030ca2b24e5a7f9dd8f62096a48eb33 b/src/c-ares/test/fuzzinput/7030ca2b24e5a7f9dd8f62096a48eb33 new file mode 100644 index 000000000..74b567a53 Binary files /dev/null and b/src/c-ares/test/fuzzinput/7030ca2b24e5a7f9dd8f62096a48eb33 differ diff --git a/src/c-ares/test/fuzzinput/71eec1a0ef2d25bb9e2ef17f23be7e9e b/src/c-ares/test/fuzzinput/71eec1a0ef2d25bb9e2ef17f23be7e9e new file mode 100644 index 000000000..9d8d01b59 Binary files /dev/null and b/src/c-ares/test/fuzzinput/71eec1a0ef2d25bb9e2ef17f23be7e9e differ diff --git a/src/c-ares/test/fuzzinput/7a6b0177210ea4ef40b254daf99393c5 b/src/c-ares/test/fuzzinput/7a6b0177210ea4ef40b254daf99393c5 new file mode 100644 index 000000000..5a5983872 Binary files /dev/null and b/src/c-ares/test/fuzzinput/7a6b0177210ea4ef40b254daf99393c5 differ diff --git a/src/c-ares/test/fuzzinput/7f1567733711ffb61839621af0cbfa33 b/src/c-ares/test/fuzzinput/7f1567733711ffb61839621af0cbfa33 new file mode 100644 index 000000000..f42cb4fd5 Binary files /dev/null and b/src/c-ares/test/fuzzinput/7f1567733711ffb61839621af0cbfa33 differ diff --git a/src/c-ares/test/fuzzinput/850c6d57c5bb7be8205fc2438d14d7e5 b/src/c-ares/test/fuzzinput/850c6d57c5bb7be8205fc2438d14d7e5 new file mode 100644 index 000000000..bfa5ec764 Binary files /dev/null and b/src/c-ares/test/fuzzinput/850c6d57c5bb7be8205fc2438d14d7e5 differ diff --git a/src/c-ares/test/fuzzinput/a5c8cd2784a5792b9e91c2d7895b3b34 b/src/c-ares/test/fuzzinput/a5c8cd2784a5792b9e91c2d7895b3b34 new file mode 100644 index 000000000..3ded23312 Binary files /dev/null and b/src/c-ares/test/fuzzinput/a5c8cd2784a5792b9e91c2d7895b3b34 differ diff --git a/src/c-ares/test/fuzzinput/a9135cdc7151d023300ff194bad90af9 b/src/c-ares/test/fuzzinput/a9135cdc7151d023300ff194bad90af9 new file mode 100644 index 000000000..8b2816324 Binary files /dev/null and b/src/c-ares/test/fuzzinput/a9135cdc7151d023300ff194bad90af9 differ diff --git a/src/c-ares/test/fuzzinput/af2597e8ac7dec1e8b4a47518312912a b/src/c-ares/test/fuzzinput/af2597e8ac7dec1e8b4a47518312912a new file mode 100644 index 000000000..b01f0bdb9 Binary files /dev/null and b/src/c-ares/test/fuzzinput/af2597e8ac7dec1e8b4a47518312912a differ diff --git a/src/c-ares/test/fuzzinput/answer_a b/src/c-ares/test/fuzzinput/answer_a new file mode 100644 index 000000000..57840a144 Binary files /dev/null and b/src/c-ares/test/fuzzinput/answer_a differ diff --git a/src/c-ares/test/fuzzinput/answer_aaaa b/src/c-ares/test/fuzzinput/answer_aaaa new file mode 100644 index 000000000..517b35753 Binary files /dev/null and b/src/c-ares/test/fuzzinput/answer_aaaa differ diff --git a/src/c-ares/test/fuzzinput/b3f53ef826b831bb09dd25c7f5960249 b/src/c-ares/test/fuzzinput/b3f53ef826b831bb09dd25c7f5960249 new file mode 100644 index 000000000..5590d978d Binary files /dev/null and b/src/c-ares/test/fuzzinput/b3f53ef826b831bb09dd25c7f5960249 differ diff --git a/src/c-ares/test/fuzzinput/cda0f8751f5c4993974c2b549d29bcc8 b/src/c-ares/test/fuzzinput/cda0f8751f5c4993974c2b549d29bcc8 new file mode 100644 index 000000000..5bdd3ff49 Binary files /dev/null and b/src/c-ares/test/fuzzinput/cda0f8751f5c4993974c2b549d29bcc8 differ diff --git a/src/c-ares/test/fuzzinput/ce6c26c0e469339873d0e7f616ab0945 b/src/c-ares/test/fuzzinput/ce6c26c0e469339873d0e7f616ab0945 new file mode 100644 index 000000000..90abda46c Binary files /dev/null and b/src/c-ares/test/fuzzinput/ce6c26c0e469339873d0e7f616ab0945 differ diff --git a/src/c-ares/test/fuzzinput/e4dd7e7c2dd4ed7c2e17a6af5d04f9c9 b/src/c-ares/test/fuzzinput/e4dd7e7c2dd4ed7c2e17a6af5d04f9c9 new file mode 100644 index 000000000..952abc840 Binary files /dev/null and b/src/c-ares/test/fuzzinput/e4dd7e7c2dd4ed7c2e17a6af5d04f9c9 differ diff --git a/src/c-ares/test/fuzzinput/ed50ed8ee36230a5a69746ad830437e5 b/src/c-ares/test/fuzzinput/ed50ed8ee36230a5a69746ad830437e5 new file mode 100644 index 000000000..b663782f2 Binary files /dev/null and b/src/c-ares/test/fuzzinput/ed50ed8ee36230a5a69746ad830437e5 differ diff --git a/src/c-ares/test/fuzzinput/f1b900d50806021953321c3b604ee497 b/src/c-ares/test/fuzzinput/f1b900d50806021953321c3b604ee497 new file mode 100644 index 000000000..84edf9615 Binary files /dev/null and b/src/c-ares/test/fuzzinput/f1b900d50806021953321c3b604ee497 differ diff --git a/src/c-ares/test/fuzzinput/f89f6c8176b564a7dd646f14305573ce b/src/c-ares/test/fuzzinput/f89f6c8176b564a7dd646f14305573ce new file mode 100644 index 000000000..62f0f66b4 Binary files /dev/null and b/src/c-ares/test/fuzzinput/f89f6c8176b564a7dd646f14305573ce differ diff --git a/src/c-ares/test/fuzzinput/f9ad508d2dbd08d3aaaabc7d1174677d b/src/c-ares/test/fuzzinput/f9ad508d2dbd08d3aaaabc7d1174677d new file mode 100644 index 000000000..277849194 Binary files /dev/null and b/src/c-ares/test/fuzzinput/f9ad508d2dbd08d3aaaabc7d1174677d differ diff --git a/src/c-ares/test/fuzznames/name01 b/src/c-ares/test/fuzznames/name01 new file mode 100644 index 000000000..5a4321e28 --- /dev/null +++ b/src/c-ares/test/fuzznames/name01 @@ -0,0 +1 @@ +normal.name \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name02 b/src/c-ares/test/fuzznames/name02 new file mode 100644 index 000000000..88c4144ed --- /dev/null +++ b/src/c-ares/test/fuzznames/name02 @@ -0,0 +1 @@ +singlelabel \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name03 b/src/c-ares/test/fuzznames/name03 new file mode 100644 index 000000000..5c3cd14ec --- /dev/null +++ b/src/c-ares/test/fuzznames/name03 @@ -0,0 +1 @@ +www.labelismuchtoolong012345678901234567890123456789012345678901234567890123456789.com diff --git a/src/c-ares/test/fuzznames/name04 b/src/c-ares/test/fuzznames/name04 new file mode 100644 index 000000000..721b071e5 --- /dev/null +++ b/src/c-ares/test/fuzznames/name04 @@ -0,0 +1 @@ +labelwithescaped\.dot.dot \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name05 b/src/c-ares/test/fuzznames/name05 new file mode 100644 index 000000000..3507d96cc --- /dev/null +++ b/src/c-ares/test/fuzznames/name05 @@ -0,0 +1 @@ +escaped.dot.at.end\. \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name06 b/src/c-ares/test/fuzznames/name06 new file mode 100644 index 000000000..fff8ae29f --- /dev/null +++ b/src/c-ares/test/fuzznames/name06 @@ -0,0 +1 @@ +absolute.name. \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name07 b/src/c-ares/test/fuzznames/name07 new file mode 100644 index 000000000..57ac8e06c --- /dev/null +++ b/src/c-ares/test/fuzznames/name07 @@ -0,0 +1 @@ +empty..label \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name08 b/src/c-ares/test/fuzznames/name08 new file mode 100644 index 000000000..a887115d7 --- /dev/null +++ b/src/c-ares/test/fuzznames/name08 @@ -0,0 +1 @@ +utf8.££.data.com \ No newline at end of file diff --git a/src/c-ares/test/fuzznames/name09 b/src/c-ares/test/fuzznames/name09 new file mode 100644 index 000000000..a4ce6e7eb --- /dev/null +++ b/src/c-ares/test/fuzznames/name09 @@ -0,0 +1 @@ +astral.plane.utf8.name.𐀀.org \ No newline at end of file diff --git a/src/c-ares/test/gmock-1.7.0/CHANGES b/src/c-ares/test/gmock-1.7.0/CHANGES new file mode 100644 index 000000000..d6f2f760e --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/CHANGES @@ -0,0 +1,126 @@ +Changes for 1.7.0: + +* All new improvements in Google Test 1.7.0. +* New feature: matchers DoubleNear(), FloatNear(), + NanSensitiveDoubleNear(), NanSensitiveFloatNear(), + UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(), + WhenSortedBy(), IsEmpty(), and SizeIs(). +* Improvement: Google Mock can now be built as a DLL. +* Improvement: when compiled by a C++11 compiler, matchers AllOf() + and AnyOf() can accept an arbitrary number of matchers. +* Improvement: when compiled by a C++11 compiler, matchers + ElementsAreArray() can accept an initializer list. +* Improvement: when exceptions are enabled, a mock method with no + default action now throws instead crashing the test. +* Improvement: added class testing::StringMatchResultListener to aid + definition of composite matchers. +* Improvement: function return types used in MOCK_METHOD*() macros can + now contain unprotected commas. +* Improvement (potentially breaking): EXPECT_THAT() and ASSERT_THAT() + are now more strict in ensuring that the value type and the matcher + type are compatible, catching potential bugs in tests. +* Improvement: Pointee() now works on an optional. +* Improvement: the ElementsAreArray() matcher can now take a vector or + iterator range as input, and makes a copy of its input elements + before the conversion to a Matcher. +* Improvement: the Google Mock Generator can now generate mocks for + some class templates. +* Bug fix: mock object destruction triggerred by another mock object's + destruction no longer hangs. +* Improvement: Google Mock Doctor works better with newer Clang and + GCC now. +* Compatibility fixes. +* Bug/warning fixes. + +Changes for 1.6.0: + +* Compilation is much faster and uses much less memory, especially + when the constructor and destructor of a mock class are moved out of + the class body. +* New matchers: Pointwise(), Each(). +* New actions: ReturnPointee() and ReturnRefOfCopy(). +* CMake support. +* Project files for Visual Studio 2010. +* AllOf() and AnyOf() can handle up-to 10 arguments now. +* Google Mock doctor understands Clang error messages now. +* SetArgPointee<> now accepts string literals. +* gmock_gen.py handles storage specifier macros and template return + types now. +* Compatibility fixes. +* Bug fixes and implementation clean-ups. +* Potentially incompatible changes: disables the harmful 'make install' + command in autotools. + +Potentially breaking changes: + +* The description string for MATCHER*() changes from Python-style + interpolation to an ordinary C++ string expression. +* SetArgumentPointee is deprecated in favor of SetArgPointee. +* Some non-essential project files for Visual Studio 2005 are removed. + +Changes for 1.5.0: + + * New feature: Google Mock can be safely used in multi-threaded tests + on platforms having pthreads. + * New feature: function for printing a value of arbitrary type. + * New feature: function ExplainMatchResult() for easy definition of + composite matchers. + * The new matcher API lets user-defined matchers generate custom + explanations more directly and efficiently. + * Better failure messages all around. + * NotNull() and IsNull() now work with smart pointers. + * Field() and Property() now work when the matcher argument is a pointer + passed by reference. + * Regular expression matchers on all platforms. + * Added GCC 4.0 support for Google Mock Doctor. + * Added gmock_all_test.cc for compiling most Google Mock tests + in a single file. + * Significantly cleaned up compiler warnings. + * Bug fixes, better test coverage, and implementation clean-ups. + + Potentially breaking changes: + + * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher() + need to be updated after upgrading to Google Mock 1.5.0; matchers defined + using MATCHER or MATCHER_P* aren't affected. + * Dropped support for 'make install'. + +Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of +Google Test): + + * Works in more environments: Symbian and minGW, Visual C++ 7.1. + * Lighter weight: comes with our own implementation of TR1 tuple (no + more dependency on Boost!). + * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks. + * New feature: ACTION_TEMPLATE for defining templatized actions. + * New feature: the .After() clause for specifying expectation order. + * New feature: the .With() clause for for specifying inter-argument + constraints. + * New feature: actions ReturnArg(), ReturnNew(...), and + DeleteArg(). + * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(), + and Contains(). + * New feature: utility class MockFunction, useful for checkpoints, etc. + * New feature: functions Value(x, m) and SafeMatcherCast(m). + * New feature: copying a mock object is rejected at compile time. + * New feature: a script for fusing all Google Mock and Google Test + source files for easy deployment. + * Improved the Google Mock doctor to diagnose more diseases. + * Improved the Google Mock generator script. + * Compatibility fixes for Mac OS X and gcc. + * Bug fixes and implementation clean-ups. + +Changes for 1.1.0: + + * New feature: ability to use Google Mock with any testing framework. + * New feature: macros for easily defining new matchers + * New feature: macros for easily defining new actions. + * New feature: more container matchers. + * New feature: actions for accessing function arguments and throwing + exceptions. + * Improved the Google Mock doctor script for diagnosing compiler errors. + * Bug fixes and implementation clean-ups. + +Changes for 1.0.0: + + * Initial Open Source release of Google Mock diff --git a/src/c-ares/test/gmock-1.7.0/CMakeLists.txt b/src/c-ares/test/gmock-1.7.0/CMakeLists.txt new file mode 100644 index 000000000..572d0444b --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/CMakeLists.txt @@ -0,0 +1,171 @@ +######################################################################## +# CMake build script for Google Mock. +# +# To run the tests for Google Mock itself on Linux, use 'make test' or +# ctest. You can select which tests to run using 'ctest -R regex'. +# For more options, run 'ctest --help'. + +# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to +# make it prominent in the GUI. +option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) + +option(gmock_build_tests "Build all of Google Mock's own tests." OFF) + +# A directory to find Google Test sources. +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gtest/CMakeLists.txt") + set(gtest_dir gtest) +else() + set(gtest_dir ../gtest) +endif() + +# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build(). +include("${gtest_dir}/cmake/hermetic_build.cmake" OPTIONAL) + +if (COMMAND pre_project_set_up_hermetic_build) + # Google Test also calls hermetic setup functions from add_subdirectory, + # although its changes will not affect things at the current scope. + pre_project_set_up_hermetic_build() +endif() + +######################################################################## +# +# Project-wide settings + +# Name of the project. +# +# CMake files in this project can refer to the root source directory +# as ${gmock_SOURCE_DIR} and to the root binary directory as +# ${gmock_BINARY_DIR}. +# Language "C" is required for find_package(Threads). +project(gmock CXX C) +cmake_minimum_required(VERSION 2.6.2) + +if (COMMAND set_up_hermetic_build) + set_up_hermetic_build() +endif() + +# Instructs CMake to process Google Test's CMakeLists.txt and add its +# targets to the current scope. We are placing Google Test's binary +# directory in a subdirectory of our own as VC compilation may break +# if they are the same (the default). +add_subdirectory("${gtest_dir}" "${gmock_BINARY_DIR}/gtest") + +# Although Google Test's CMakeLists.txt calls this function, the +# changes there don't affect the current scope. Therefore we have to +# call it again here. +config_compiler_and_linker() # from ${gtest_dir}/cmake/internal_utils.cmake + +# Adds Google Mock's and Google Test's header directories to the search path. +include_directories("${gmock_SOURCE_DIR}/include" + "${gmock_SOURCE_DIR}" + "${gtest_SOURCE_DIR}/include" + # This directory is needed to build directly from Google + # Test sources. + "${gtest_SOURCE_DIR}") + +######################################################################## +# +# Defines the gmock & gmock_main libraries. User tests should link +# with one of them. + +# Google Mock libraries. We build them using more strict warnings than what +# are used for other targets, to ensure that Google Mock can be compiled by +# a user aggressive about warnings. +cxx_library(gmock + "${cxx_strict}" + "${gtest_dir}/src/gtest-all.cc" + src/gmock-all.cc) + +cxx_library(gmock_main + "${cxx_strict}" + "${gtest_dir}/src/gtest-all.cc" + src/gmock-all.cc + src/gmock_main.cc) + +######################################################################## +# +# Google Mock's own tests. +# +# You can skip this section if you aren't interested in testing +# Google Mock itself. +# +# The tests are not built by default. To build them, set the +# gmock_build_tests option to ON. You can do it by running ccmake +# or specifying the -Dgmock_build_tests=ON flag when running cmake. + +if (gmock_build_tests) + # This must be set in the root directory for the tests to be run by + # 'make test' or ctest. + enable_testing() + + ############################################################ + # C++ tests built with standard compiler flags. + + cxx_test(gmock-actions_test gmock_main) + cxx_test(gmock-cardinalities_test gmock_main) + cxx_test(gmock_ex_test gmock_main) + cxx_test(gmock-generated-actions_test gmock_main) + cxx_test(gmock-generated-function-mockers_test gmock_main) + cxx_test(gmock-generated-internal-utils_test gmock_main) + cxx_test(gmock-generated-matchers_test gmock_main) + cxx_test(gmock-internal-utils_test gmock_main) + cxx_test(gmock-matchers_test gmock_main) + cxx_test(gmock-more-actions_test gmock_main) + cxx_test(gmock-nice-strict_test gmock_main) + cxx_test(gmock-port_test gmock_main) + cxx_test(gmock-spec-builders_test gmock_main) + cxx_test(gmock_link_test gmock_main test/gmock_link2_test.cc) + cxx_test(gmock_test gmock_main) + + if (CMAKE_USE_PTHREADS_INIT) + cxx_test(gmock_stress_test gmock) + endif() + + # gmock_all_test is commented to save time building and running tests. + # Uncomment if necessary. + # cxx_test(gmock_all_test gmock_main) + + ############################################################ + # C++ tests built with non-standard compiler flags. + + cxx_library(gmock_main_no_exception "${cxx_no_exception}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + cxx_library(gmock_main_no_rtti "${cxx_no_rtti}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + cxx_library(gmock_main_use_own_tuple "${cxx_use_own_tuple}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + cxx_test_with_flags(gmock-more-actions_no_exception_test "${cxx_no_exception}" + gmock_main_no_exception test/gmock-more-actions_test.cc) + + cxx_test_with_flags(gmock_no_rtti_test "${cxx_no_rtti}" + gmock_main_no_rtti test/gmock-spec-builders_test.cc) + + cxx_test_with_flags(gmock_use_own_tuple_test "${cxx_use_own_tuple}" + gmock_main_use_own_tuple test/gmock-spec-builders_test.cc) + + cxx_shared_library(shared_gmock_main "${cxx_default}" + "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) + + # Tests that a binary can be built with Google Mock as a shared library. On + # some system configurations, it may not possible to run the binary without + # knowing more details about the system configurations. We do not try to run + # this binary. To get a more robust shared library coverage, configure with + # -DBUILD_SHARED_LIBS=ON. + cxx_executable_with_flags(shared_gmock_test_ "${cxx_default}" + shared_gmock_main test/gmock-spec-builders_test.cc) + set_target_properties(shared_gmock_test_ + PROPERTIES + COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") + + ############################################################ + # Python tests. + + cxx_executable(gmock_leak_test_ test gmock_main) + py_test(gmock_leak_test) + + cxx_executable(gmock_output_test_ test gmock) + py_test(gmock_output_test) +endif() diff --git a/src/c-ares/test/gmock-1.7.0/CONTRIBUTORS b/src/c-ares/test/gmock-1.7.0/CONTRIBUTORS new file mode 100644 index 000000000..6e9ae362b --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/CONTRIBUTORS @@ -0,0 +1,40 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Mocking Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Benoit Sigoure +Bogdan Piloca +Chandler Carruth +Dave MacLachlan +David Anderson +Dean Sturtevant +Gene Volovich +Hal Burch +Jeffrey Yasskin +Jim Keller +Joe Walnes +Jon Wray +Keir Mierle +Keith Ray +Kostya Serebryany +Lev Makhlis +Manuel Klimek +Mario Tanev +Mark Paskin +Markus Heule +Matthew Simmons +Mike Bland +Neal Norwitz +Nermin Ozkiranartli +Owen Carlsen +Paneendra Ba +Paul Menage +Piotr Kaminski +Russ Rufer +Sverre Sundsdal +Takeshi Yoshino +Vadim Berman +Vlad Losev +Wolfgang Klier +Zhanyong Wan diff --git a/src/c-ares/test/gmock-1.7.0/LICENSE b/src/c-ares/test/gmock-1.7.0/LICENSE new file mode 100644 index 000000000..1941a11f8 --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, 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. diff --git a/src/c-ares/test/gmock-1.7.0/Makefile.am b/src/c-ares/test/gmock-1.7.0/Makefile.am new file mode 100644 index 000000000..0eb7bd8f3 --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/Makefile.am @@ -0,0 +1,216 @@ +# Automake file + +# Nonstandard package files for distribution. +EXTRA_DIST = LICENSE + +# We may need to build our internally packaged gtest. If so, it will be +# included in the 'subdirs' variable. +SUBDIRS = $(subdirs) + +# This is generated by the configure script, so clean it for distribution. +DISTCLEANFILES = scripts/gmock-config + +# We define the global AM_CPPFLAGS as everything we compile includes from these +# directories. +AM_CPPFLAGS = $(GTEST_CPPFLAGS) -I$(srcdir)/include + +# Modifies compiler and linker flags for pthreads compatibility. +if HAVE_PTHREADS + AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1 + AM_LIBS = @PTHREAD_LIBS@ +endif + +# Build rules for libraries. +lib_LTLIBRARIES = lib/libgmock.la lib/libgmock_main.la + +lib_libgmock_la_SOURCES = src/gmock-all.cc + +pkginclude_HEADERS = \ + include/gmock/gmock-actions.h \ + include/gmock/gmock-cardinalities.h \ + include/gmock/gmock-generated-actions.h \ + include/gmock/gmock-generated-function-mockers.h \ + include/gmock/gmock-generated-matchers.h \ + include/gmock/gmock-generated-nice-strict.h \ + include/gmock/gmock-matchers.h \ + include/gmock/gmock-more-actions.h \ + include/gmock/gmock-more-matchers.h \ + include/gmock/gmock-spec-builders.h \ + include/gmock/gmock.h + +pkginclude_internaldir = $(pkgincludedir)/internal +pkginclude_internal_HEADERS = \ + include/gmock/internal/gmock-generated-internal-utils.h \ + include/gmock/internal/gmock-internal-utils.h \ + include/gmock/internal/gmock-port.h + +lib_libgmock_main_la_SOURCES = src/gmock_main.cc +lib_libgmock_main_la_LIBADD = lib/libgmock.la + +# Build rules for tests. Automake's naming for some of these variables isn't +# terribly obvious, so this is a brief reference: +# +# TESTS -- Programs run automatically by "make check" +# check_PROGRAMS -- Programs built by "make check" but not necessarily run + +TESTS= +check_PROGRAMS= +AM_LDFLAGS = $(GTEST_LDFLAGS) + +# This exercises all major components of Google Mock. It also +# verifies that libgmock works. +TESTS += test/gmock-spec-builders_test +check_PROGRAMS += test/gmock-spec-builders_test +test_gmock_spec_builders_test_SOURCES = test/gmock-spec-builders_test.cc +test_gmock_spec_builders_test_LDADD = $(GTEST_LIBS) lib/libgmock.la + +# This tests using Google Mock in multiple translation units. It also +# verifies that libgmock_main and libgmock work. +TESTS += test/gmock_link_test +check_PROGRAMS += test/gmock_link_test +test_gmock_link_test_SOURCES = \ + test/gmock_link2_test.cc \ + test/gmock_link_test.cc \ + test/gmock_link_test.h +test_gmock_link_test_LDADD = $(GTEST_LIBS) lib/libgmock_main.la lib/libgmock.la + +if HAVE_PYTHON + # Tests that fused gmock files compile and work. + TESTS += test/gmock_fused_test + check_PROGRAMS += test/gmock_fused_test + test_gmock_fused_test_SOURCES = \ + fused-src/gmock-gtest-all.cc \ + fused-src/gmock/gmock.h \ + fused-src/gmock_main.cc \ + fused-src/gtest/gtest.h \ + test/gmock_test.cc + test_gmock_fused_test_CPPFLAGS = -I"$(srcdir)/fused-src" +endif + +# Google Mock source files that we don't compile directly. +GMOCK_SOURCE_INGLUDES = \ + src/gmock-cardinalities.cc \ + src/gmock-internal-utils.cc \ + src/gmock-matchers.cc \ + src/gmock-spec-builders.cc \ + src/gmock.cc + +EXTRA_DIST += $(GMOCK_SOURCE_INGLUDES) + +# C++ tests that we don't compile using autotools. +EXTRA_DIST += \ + test/gmock-actions_test.cc \ + test/gmock_all_test.cc \ + test/gmock-cardinalities_test.cc \ + test/gmock_ex_test.cc \ + test/gmock-generated-actions_test.cc \ + test/gmock-generated-function-mockers_test.cc \ + test/gmock-generated-internal-utils_test.cc \ + test/gmock-generated-matchers_test.cc \ + test/gmock-internal-utils_test.cc \ + test/gmock-matchers_test.cc \ + test/gmock-more-actions_test.cc \ + test/gmock-nice-strict_test.cc \ + test/gmock-port_test.cc \ + test/gmock_stress_test.cc + +# Python tests, which we don't run using autotools. +EXTRA_DIST += \ + test/gmock_leak_test.py \ + test/gmock_leak_test_.cc \ + test/gmock_output_test.py \ + test/gmock_output_test_.cc \ + test/gmock_output_test_golden.txt \ + test/gmock_test_utils.py + +# Nonstandard package files for distribution. +EXTRA_DIST += \ + CHANGES \ + CONTRIBUTORS \ + make/Makefile + +# Pump scripts for generating Google Mock headers. +# TODO(chandlerc@google.com): automate the generation of *.h from *.h.pump. +EXTRA_DIST += \ + include/gmock/gmock-generated-actions.h.pump \ + include/gmock/gmock-generated-function-mockers.h.pump \ + include/gmock/gmock-generated-matchers.h.pump \ + include/gmock/gmock-generated-nice-strict.h.pump \ + include/gmock/internal/gmock-generated-internal-utils.h.pump + +# Script for fusing Google Mock and Google Test source files. +EXTRA_DIST += scripts/fuse_gmock_files.py + +# The Google Mock Generator tool from the cppclean project. +EXTRA_DIST += \ + scripts/generator/LICENSE \ + scripts/generator/README \ + scripts/generator/README.cppclean \ + scripts/generator/cpp/__init__.py \ + scripts/generator/cpp/ast.py \ + scripts/generator/cpp/gmock_class.py \ + scripts/generator/cpp/keywords.py \ + scripts/generator/cpp/tokenize.py \ + scripts/generator/cpp/utils.py \ + scripts/generator/gmock_gen.py + +# CMake scripts. +EXTRA_DIST += \ + CMakeLists.txt + +# Microsoft Visual Studio 2005 projects. +EXTRA_DIST += \ + msvc/2005/gmock.sln \ + msvc/2005/gmock.vcproj \ + msvc/2005/gmock_config.vsprops \ + msvc/2005/gmock_main.vcproj \ + msvc/2005/gmock_test.vcproj + +# Microsoft Visual Studio 2010 projects. +EXTRA_DIST += \ + msvc/2010/gmock.sln \ + msvc/2010/gmock.vcxproj \ + msvc/2010/gmock_config.props \ + msvc/2010/gmock_main.vcxproj \ + msvc/2010/gmock_test.vcxproj + +if HAVE_PYTHON +# gmock_test.cc does not really depend on files generated by the +# fused-gmock-internal rule. However, gmock_test.o does, and it is +# important to include test/gmock_test.cc as part of this rule in order to +# prevent compiling gmock_test.o until all dependent files have been +# generated. +$(test_gmock_fused_test_SOURCES): fused-gmock-internal + +# TODO(vladl@google.com): Find a way to add Google Tests's sources here. +fused-gmock-internal: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \ + $(lib_libgmock_la_SOURCES) $(GMOCK_SOURCE_INGLUDES) \ + $(lib_libgmock_main_la_SOURCES) \ + scripts/fuse_gmock_files.py + mkdir -p "$(srcdir)/fused-src" + chmod -R u+w "$(srcdir)/fused-src" + rm -f "$(srcdir)/fused-src/gtest/gtest.h" + rm -f "$(srcdir)/fused-src/gmock/gmock.h" + rm -f "$(srcdir)/fused-src/gmock-gtest-all.cc" + "$(srcdir)/scripts/fuse_gmock_files.py" "$(srcdir)/fused-src" + cp -f "$(srcdir)/src/gmock_main.cc" "$(srcdir)/fused-src" + +maintainer-clean-local: + rm -rf "$(srcdir)/fused-src" +endif + +# Death tests may produce core dumps in the build directory. In case +# this happens, clean them to keep distcleancheck happy. +CLEANFILES = core + +# Disables 'make install' as installing a compiled version of Google +# Mock can lead to undefined behavior due to violation of the +# One-Definition Rule. + +install-exec-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Mock into your build system." + false + +install-data-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Mock into your build system." + false diff --git a/src/c-ares/test/gmock-1.7.0/README b/src/c-ares/test/gmock-1.7.0/README new file mode 100644 index 000000000..ed2e69bac --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/README @@ -0,0 +1,369 @@ +Google C++ Mocking Framework +============================ + +http://code.google.com/p/googlemock/ + +Overview +-------- + +Google's framework for writing and using C++ mock classes on a variety +of platforms (Linux, Mac OS X, Windows, Windows CE, Symbian, etc). +Inspired by jMock, EasyMock, and Hamcrest, and designed with C++'s +specifics in mind, it can help you derive better designs of your +system and write better tests. + +Google Mock: + +- provides a declarative syntax for defining mocks, +- can easily define partial (hybrid) mocks, which are a cross of real + and mock objects, +- handles functions of arbitrary types and overloaded functions, +- comes with a rich set of matchers for validating function arguments, +- uses an intuitive syntax for controlling the behavior of a mock, +- does automatic verification of expectations (no record-and-replay + needed), +- allows arbitrary (partial) ordering constraints on + function calls to be expressed, +- lets a user extend it by defining new matchers and actions. +- does not use exceptions, and +- is easy to learn and use. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Please note that code under scripts/generator/ is from the cppclean +project (http://code.google.com/p/cppclean/) and under the Apache +License, which is different from Google Mock's license. + +Requirements for End Users +-------------------------- + +Google Mock is implemented on top of the Google Test C++ testing +framework (http://code.google.com/p/googletest/), and includes the +latter as part of the SVN repositary and distribution package. You +must use the bundled version of Google Test when using Google Mock, or +you may get compiler/linker errors. + +You can also easily configure Google Mock to work with another testing +framework of your choice; although it will still need Google Test as +an internal dependency. Please read +http://code.google.com/p/googlemock/wiki/ForDummies#Using_Google_Mock_with_Any_Testing_Framework +for how to do it. + +Google Mock depends on advanced C++ features and thus requires a more +modern compiler. The following are needed to use Google Mock: + +### Linux Requirements ### + +These are the base requirements to build and use Google Mock from a source +package (as described below): + + * GNU-compatible Make or "gmake" + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * C++98-standard-compliant compiler (e.g. GCC 3.4 or newer) + +### Windows Requirements ### + + * Microsoft Visual C++ 8.0 SP1 or newer + +### Mac OS X Requirements ### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +Requirements for Contributors +----------------------------- + +We welcome patches. If you plan to contribute a patch, you need to +build Google Mock and its own tests from an SVN checkout (described +below), which has further requirements: + + * Automake version 1.9 or newer + * Autoconf version 2.59 or newer + * Libtool / Libtoolize + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + +Getting the Source +------------------ + +There are two primary ways of getting Google Mock's source code: you +can download a stable source release in your preferred archive format, +or directly check out the source from our Subversion (SVN) repositary. +The SVN checkout requires a few extra steps and some extra software +packages on your system, but lets you track development and make +patches much more easily, so we highly encourage it. + +### Source Package ### + +Google Mock is released in versioned source packages which can be +downloaded from the download page [1]. Several different archive +formats are provided, but the only difference is the tools needed to +extract their contents, and the size of the resulting file. Download +whichever you are most comfortable with. + + [1] http://code.google.com/p/googlemock/downloads/list + +Once downloaded expand the archive using whichever tools you prefer +for that type. This will always result in a new directory with the +name "gmock-X.Y.Z" which contains all of the source code. Here are +some examples on Linux: + + tar -xvzf gmock-X.Y.Z.tar.gz + tar -xvjf gmock-X.Y.Z.tar.bz2 + unzip gmock-X.Y.Z.zip + +### SVN Checkout ### + +To check out the main branch (also known as the "trunk") of Google +Mock, run the following Subversion command: + + svn checkout http://googlemock.googlecode.com/svn/trunk/ gmock-svn + +If you are using a *nix system and plan to use the GNU Autotools build +system to build Google Mock (described below), you'll need to +configure it now. Otherwise you are done with getting the source +files. + +To prepare the Autotools build system, enter the target directory of +the checkout command you used ('gmock-svn') and proceed with the +following command: + + autoreconf -fvi + +Once you have completed this step, you are ready to build the library. +Note that you should only need to complete this step once. The +subsequent 'make' invocations will automatically re-generate the bits +of the build system that need to be changed. + +If your system uses older versions of the autotools, the above command +will fail. You may need to explicitly specify a version to use. For +instance, if you have both GNU Automake 1.4 and 1.9 installed and +'automake' would invoke the 1.4, use instead: + + AUTOMAKE=automake-1.9 ACLOCAL=aclocal-1.9 autoreconf -fvi + +Make sure you're using the same version of automake and aclocal. + +Setting up the Build +-------------------- + +To build Google Mock and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +### Generic Build Instructions ### + +This section shows how you can integrate Google Mock into your +existing build system. + +Suppose you put Google Mock in directory ${GMOCK_DIR} and Google Test +in ${GTEST_DIR} (the latter is ${GMOCK_DIR}/gtest by default). To +build Google Mock, create a library build target (or a project as +called by Visual Studio and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc and ${GMOCK_DIR}/src/gmock-all.cc + +with + + ${GTEST_DIR}/include and ${GMOCK_DIR}/include + +in the system header search path, and + + ${GTEST_DIR} and ${GMOCK_DIR} + +in the normal header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \ + -pthread -c ${GTEST_DIR}/src/gtest-all.cc + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \ + -pthread -c ${GMOCK_DIR}/src/gmock-all.cc + ar -rv libgmock.a gtest-all.o gmock-all.o + +(We need -pthread as Google Test and Google Mock use threads.) + +Next, you should compile your test source file with +${GTEST_DIR}/include and ${GMOCK_DIR}/include in the header search +path, and link it with gmock and any other necessary libraries: + + g++ -isystem ${GTEST_DIR}/include -isystem ${GMOCK_DIR}/include \ + -pthread path/to/your_test.cc libgmock.a -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Mock on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Mock's own tests. Instead, it just builds the Google Mock library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GMOCK_DIR}/make + make + ./gmock_test + +If you see errors, try to tweak the contents of make/Makefile to make +them go away. There are instructions in make/Makefile on how to do +it. + +### Windows ### + +The msvc/2005 directory contains VC++ 2005 projects and the msvc/2010 +directory contains VC++ 2010 projects for building Google Mock and +selected tests. + +Change to the appropriate directory and run "msbuild gmock.sln" to +build the library and tests (or open the gmock.sln in the MSVC IDE). +If you want to create your own project to use with Google Mock, you'll +have to configure it to use the gmock_config propety sheet. For that: + + * Open the Property Manager window (View | Other Windows | Property Manager) + * Right-click on your project and select "Add Existing Property Sheet..." + * Navigate to gmock_config.vsprops or gmock_config.props and select it. + * In Project Properties | Configuration Properties | General | Additional + Include Directories, type /include. + +Tweaking Google Mock +-------------------- + +Google Mock can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Mock by +defining control macros on the compiler command line. Generally, +these macros are named like GTEST_XYZ and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file ${GTEST_DIR}/include/gtest/internal/gtest-port.h. + +### Choosing a TR1 Tuple Library ### + +Google Mock uses the C++ Technical Report 1 (TR1) tuple library +heavily. Unfortunately TR1 tuple is not yet widely available with all +compilers. The good news is that Google Test 1.4.0+ implements a +subset of TR1 tuple that's enough for Google Mock's need. Google Mock +will automatically use that implementation when the compiler doesn't +provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +and Google Mock use. However, if your project already uses TR1 tuple, +you need to tell Google Test and Google Mock to use the same TR1 tuple +library the rest of your project uses, or the two tuple +implementations will clash. To do that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test, Google Mock, and +your tests. If you want to force Google Test and Google Mock to use +their own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you want to use Boost's TR1 tuple library with Google Mock, please +refer to the Boost website (http://www.boost.org/) for how to obtain +it and set it up. + +### As a Shared Library (DLL) ### + +Google Mock is compact, so most users can build and link it as a static +library for the simplicity. Google Mock can be used as a DLL, but the +same DLL must contain Google Test as well. See Google Test's README +file for instructions on how to set up necessary compiler settings. + +### Tweaking Google Mock ### + +Most of Google Test's control macros apply to Google Mock as well. +Please see file ${GTEST_DIR}/README for how to tweak them. + +Upgrading from an Earlier Version +--------------------------------- + +We strive to keep Google Mock releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Mock. + +### Upgrading from 1.1.0 or Earlier ### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "Choosing a TR1 Tuple +Library". + +### Upgrading from 1.4.0 or Earlier ### + +On platforms where the pthread library is available, Google Test and +Google Mock use it in order to be thread-safe. For this to work, you +may need to tweak your compiler and/or linker flags. Please see the +"Multi-threaded Tests" section in file ${GTEST_DIR}/README for what +you may need to do. + +If you have custom matchers defined using MatcherInterface or +MakePolymorphicMatcher(), you'll need to update their definitions to +use the new matcher API [2]. Matchers defined using MATCHER() or +MATCHER_P*() aren't affected. + + [2] http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Monomorphic_Matchers, + http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Polymorphic_Matchers + +Developing Google Mock +---------------------- + +This section discusses how to make your own changes to Google Mock. + +### Testing Google Mock Itself ### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you'll need Autotools. First, make sure you have followed +the instructions in section "SVN Checkout" to configure Google Mock. +Then, create a build output directory and enter it. Next, + + ${GMOCK_DIR}/configure # Standard GNU configure script, --help for more info + +Once you have successfully configured Google Mock, the build steps are +standard for GNU-style OSS packages. + + make # Standard makefile following GNU conventions + make check # Builds and runs all tests - all should pass. + +Note that when building your project against Google Mock, you are building +against Google Test as well. There is no need to configure Google Test +separately. + +### Regenerating Source Files ### + +Some of Google Mock's source files are generated from templates (not +in the C++ sense) using a script. A template file is named FOO.pump, +where FOO is the name of the file it will generate. For example, the +file include/gmock/gmock-generated-actions.h.pump is used to generate +gmock-generated-actions.h in the same directory. + +Normally you don't need to worry about regenerating the source files, +unless you need to modify them. In that case, you should modify the +corresponding .pump files instead and run the 'pump' script (for Pump +is Useful for Meta Programming) to regenerate them. You can find +pump.py in the ${GTEST_DIR}/scripts/ directory. Read the Pump manual +[3] for how to use it. + + [3] http://code.google.com/p/googletest/wiki/PumpManual. + +### Contributing a Patch ### + +We welcome patches. Please read the Google Mock developer's guide [4] +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + + [4] http://code.google.com/p/googlemock/wiki/DevGuide + +Happy testing! diff --git a/src/c-ares/test/gmock-1.7.0/build-aux/config.h.in b/src/c-ares/test/gmock-1.7.0/build-aux/config.h.in new file mode 100644 index 000000000..843b5b10c --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/build-aux/config.h.in @@ -0,0 +1,69 @@ +/* build-aux/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/src/c-ares/test/gmock-1.7.0/configure.ac b/src/c-ares/test/gmock-1.7.0/configure.ac new file mode 100644 index 000000000..d268d5d73 --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/configure.ac @@ -0,0 +1,146 @@ +m4_include(gtest/m4/acx_pthread.m4) + +AC_INIT([Google C++ Mocking Framework], + [1.7.0], + [googlemock@googlegroups.com], + [gmock]) + +# Provide various options to initialize the Autoconf and configure processes. +AC_PREREQ([2.59]) +AC_CONFIG_SRCDIR([./LICENSE]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_HEADERS([build-aux/config.h]) +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([scripts/gmock-config], [chmod +x scripts/gmock-config]) + +# Initialize Automake with various options. We require at least v1.9, prevent +# pedantic complaints about package files, and enable various distribution +# targets. +AM_INIT_AUTOMAKE([1.9 dist-bzip2 dist-zip foreign subdir-objects]) + +# Check for programs used in building Google Test. +AC_PROG_CC +AC_PROG_CXX +AC_LANG([C++]) +AC_PROG_LIBTOOL + +# TODO(chandlerc@google.com): Currently we aren't running the Python tests +# against the interpreter detected by AM_PATH_PYTHON, and so we condition +# HAVE_PYTHON by requiring "python" to be in the PATH, and that interpreter's +# version to be >= 2.3. This will allow the scripts to use a "/usr/bin/env" +# hashbang. +PYTHON= # We *do not* allow the user to specify a python interpreter +AC_PATH_PROG([PYTHON],[python],[:]) +AS_IF([test "$PYTHON" != ":"], + [AM_PYTHON_CHECK_VERSION([$PYTHON],[2.3],[:],[PYTHON=":"])]) +AM_CONDITIONAL([HAVE_PYTHON],[test "$PYTHON" != ":"]) + +# TODO(chandlerc@google.com) Check for the necessary system headers. + +# Configure pthreads. +AC_ARG_WITH([pthreads], + [AS_HELP_STRING([--with-pthreads], + [use pthreads (default is yes)])], + [with_pthreads=$withval], + [with_pthreads=check]) + +have_pthreads=no +AS_IF([test "x$with_pthreads" != "xno"], + [ACX_PTHREAD( + [], + [AS_IF([test "x$with_pthreads" != "xcheck"], + [AC_MSG_FAILURE( + [--with-pthreads was specified, but unable to be used])])]) + have_pthreads="$acx_pthread_ok"]) +AM_CONDITIONAL([HAVE_PTHREADS],[test "x$have_pthreads" == "xyes"]) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_LIBS) + +# GoogleMock currently has hard dependencies upon GoogleTest above and beyond +# running its own test suite, so we both provide our own version in +# a subdirectory and provide some logic to use a custom version or a system +# installed version. +AC_ARG_WITH([gtest], + [AS_HELP_STRING([--with-gtest], + [Specifies how to find the gtest package. If no + arguments are given, the default behavior, a + system installed gtest will be used if present, + and an internal version built otherwise. If a + path is provided, the gtest built or installed at + that prefix will be used.])], + [], + [with_gtest=yes]) +AC_ARG_ENABLE([external-gtest], + [AS_HELP_STRING([--disable-external-gtest], + [Disables any detection or use of a system + installed or user provided gtest. Any option to + '--with-gtest' is ignored. (Default is enabled.)]) + ], [], [enable_external_gtest=yes]) +AS_IF([test "x$with_gtest" == "xno"], + [AC_MSG_ERROR([dnl +Support for GoogleTest was explicitly disabled. Currently GoogleMock has a hard +dependency upon GoogleTest to build, please provide a version, or allow +GoogleMock to use any installed version and fall back upon its internal +version.])]) + +# Setup various GTEST variables. TODO(chandlerc@google.com): When these are +# used below, they should be used such that any pre-existing values always +# trump values we set them to, so that they can be used to selectively override +# details of the detection process. +AC_ARG_VAR([GTEST_CONFIG], + [The exact path of Google Test's 'gtest-config' script.]) +AC_ARG_VAR([GTEST_CPPFLAGS], + [C-like preprocessor flags for Google Test.]) +AC_ARG_VAR([GTEST_CXXFLAGS], + [C++ compile flags for Google Test.]) +AC_ARG_VAR([GTEST_LDFLAGS], + [Linker path and option flags for Google Test.]) +AC_ARG_VAR([GTEST_LIBS], + [Library linking flags for Google Test.]) +AC_ARG_VAR([GTEST_VERSION], + [The version of Google Test available.]) +HAVE_BUILT_GTEST="no" + +GTEST_MIN_VERSION="1.7.0" + +AS_IF([test "x${enable_external_gtest}" = "xyes"], + [# Begin filling in variables as we are able. + AS_IF([test "x${with_gtest}" != "xyes"], + [AS_IF([test -x "${with_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${with_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${with_gtest}/bin/gtest-config"]) + AS_IF([test -x "${GTEST_CONFIG}"], [], + [AC_MSG_ERROR([dnl +Unable to locate either a built or installed Google Test at '${with_gtest}'.]) + ])]) + + AS_IF([test -x "${GTEST_CONFIG}"], [], + [AC_PATH_PROG([GTEST_CONFIG], [gtest-config])]) + AS_IF([test -x "${GTEST_CONFIG}"], + [AC_MSG_CHECKING([for Google Test version >= ${GTEST_MIN_VERSION}]) + AS_IF([${GTEST_CONFIG} --min-version=${GTEST_MIN_VERSION}], + [AC_MSG_RESULT([yes]) + HAVE_BUILT_GTEST="yes"], + [AC_MSG_RESULT([no])])])]) + +AS_IF([test "x${HAVE_BUILT_GTEST}" = "xyes"], + [GTEST_CPPFLAGS=`${GTEST_CONFIG} --cppflags` + GTEST_CXXFLAGS=`${GTEST_CONFIG} --cxxflags` + GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags` + GTEST_LIBS=`${GTEST_CONFIG} --libs` + GTEST_VERSION=`${GTEST_CONFIG} --version`], + [AC_CONFIG_SUBDIRS([gtest]) + # GTEST_CONFIG needs to be executable both in a Makefile environmont and + # in a shell script environment, so resolve an absolute path for it here. + GTEST_CONFIG="`pwd -P`/gtest/scripts/gtest-config" + GTEST_CPPFLAGS='-I$(top_srcdir)/gtest/include' + GTEST_CXXFLAGS='-g' + GTEST_LDFLAGS='' + GTEST_LIBS='$(top_builddir)/gtest/lib/libgtest.la' + GTEST_VERSION="${GTEST_MIN_VERSION}"]) + +# TODO(chandlerc@google.com) Check the types, structures, and other compiler +# and architecture characteristics. + +# Output the generated files. No further autoconf macros may be used. +AC_OUTPUT diff --git a/src/c-ares/test/gmock-1.7.0/fused-src/gmock-gtest-all.cc b/src/c-ares/test/gmock-1.7.0/fused-src/gmock-gtest-all.cc new file mode 100644 index 000000000..1a63a8ce7 --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/fused-src/gmock-gtest-all.cc @@ -0,0 +1,11443 @@ +// Copyright 2008, 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. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// Copyright 2005, 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +// Copyright 2005, 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. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is +// part of Google Test's implementation; otherwise it's undefined. +#if !GTEST_IMPLEMENTATION_ +// A user is trying to include this from his code - just say no. +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include + + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool print_time_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() : caller_frame_(NULL) {} + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + +#if GTEST_HAS_PARAM_TEST + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } +#endif // GTEST_HAS_PARAM_TEST + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector test_case_indices_; + +#if GTEST_HAS_PARAM_TEST + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; +#endif // GTEST_HAS_PARAM_TEST + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +# if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +class AutoHandle { + public: + AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} + explicit AutoHandle(HANDLE handle) : handle_(handle) {} + + ~AutoHandle() { Reset(); } + + HANDLE Get() const { return handle_; } + void Reset() { Reset(INVALID_HANDLE_VALUE); } + void Reset(HANDLE handle) { + if (handle != handle_) { + if (handle_ != INVALID_HANDLE_VALUE) + ::CloseHandle(handle_); + handle_ = handle; + } + } + + private: + HANDLE handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; +# endif // GTEST_OS_WINDOWS + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise."); + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +// +// A user must call testing::InitGoogleTest() to initialize Google +// Test. g_init_gtest_count is set to the number of times +// InitGoogleTest() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +GTEST_API_ int g_init_gtest_count = 0; +static bool GTestIsInitialized() { return g_init_gtest_count != 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// Application pathname gotten in InitGoogleTest. +std::string g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return std::string(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return ""; + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return ""; +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + +# ifdef _MSC_VER + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +# pragma warning(pop) // Restores the warning state. +# else + + _ftime64(&now); + +# endif // _MSC_VER + + return static_cast(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(2 * (end - start)); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +#if GTEST_HAS_PARAM_TEST +void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors.GetString().c_str()); +} +#endif // GTEST_HAS_PARAM_TEST + +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { +#if GTEST_HAS_PARAM_TEST + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +#endif +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static std::string FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS + const bool use_color = false; +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_info.test_case_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_info.test_case_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << ms/1000.0; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + // Using non-reentrant version as localtime_r is not portable. + time_t seconds = static_cast(ms / 1000); +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe). + const struct tm* const time_struct = localtime(&seconds); // NOLINT +# pragma warning(pop) // Restores the warning state again. +#else + const struct tm* const time_struct = localtime(&seconds); // NOLINT +#endif + if (time_struct == NULL) + return ""; // Invalid ms value + + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct->tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct->tm_mday) + "T" + + String::FormatIntWidth2(time_struct->tm_hour) + ":" + + String::FormatIntWidth2(time_struct->tm_min) + ":" + + String::FormatIntWidth2(time_struct->tm_sec); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != NULL) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + + *stream << " \n"; + } + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0) + *stream << " />\n"; + else + *stream << " \n"; +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; + + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as an std::string. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; +} + +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +#if GTEST_HAS_PARAM_TEST +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} +#endif // GTEST_HAS_PARAM_TEST + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 + // (using this in initializer). + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +# pragma warning(pop) // Restores the warning state again. +#else + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +#endif // _MSC_VER + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), +#if GTEST_HAS_PARAM_TEST + parameterized_test_registry_(), + parameterized_tests_registered_(false), +#endif // GTEST_HAS_PARAM_TEST + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const std::string& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(test_case_name, + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // Makes sure InitGoogleTest() was called. + if (!GTestIsInitialized()) { + printf("%s", + "\nThis test program did NOT call ::testing::InitGoogleTest " + "before calling RUN_ALL_TESTS(). Please fix it.\n"); + return false; + } + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const std::string &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + &(current_test_info_->result_) : &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate an XML report in the given directory or with the given file\n" +" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + g_init_gtest_count++; + + // We don't want to run the initialization code twice. + if (g_init_gtest_count != 1) return; + + if (*argc <= 0) return; + + internal::g_executable_path = internal::StreamableToString(argv[0]); + +#if GTEST_HAS_DEATH_TEST + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#endif // GTEST_HAS_DEATH_TEST + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +} // namespace testing +// Copyright 2005, 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. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +#endif // GTEST_HAS_DEATH_TEST + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS +} + +# if !GTEST_OS_WINDOWS +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} +# else // We are not on Windows. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +# if !GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // !GTEST_OS_QNX + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy; + *result = (&dummy < ptr); +} + +bool StackGrowsDown() { + int dummy; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// Copyright 2008, 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. +// +// Authors: keith.ray@gmail.com (Keith Ray) + + +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kPathSeparatorString[] = "\\"; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing +// Copyright 2008, 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. +// +// Author: wan@google.com (Zhanyong Wan) + + +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include // For TerminateProcess() +#elif GTEST_OS_WINDOWS +# include +# include +#else +# include +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_QNX +# include +# include +#endif // GTEST_OS_QNX + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_MAC + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_MAC + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +#endif // _MSC_VER + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); + + // Returns the size (in bytes) of a file. + static size_t GetFileSize(FILE* file); + + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +// Returns the size (in bytes) of a file. +size_t CapturedStream::GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +std::string CapturedStream::ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector g_argvs; + +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. + +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include +#include +#include // NOLINT +#include + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast(c); + return kAsIs; + } else { + *os << "\\x" + String::FormatHexInt(static_cast(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a wchar_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo(wc, os); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << kQuoteBegin; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// Copyright 2008, 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. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing Framework (Google Test) + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? message : + std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// Copyright 2008 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. +// +// Author: wan@google.com (Zhanyong Wan) + + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +// Verifies that registered_tests match the test names in +// defined_test_names_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef ::std::set::const_iterator DefinedTestIter; + registered_ = true; + + // Skip initial whitespace in registered_tests since some + // preprocessors prefix stringizied literals with whitespace. + registered_tests = SkipSpaces(registered_tests); + + Message errors; + ::std::set tests; + for (const char* names = registered_tests; names != NULL; + names = SkipComma(names)) { + const std::string name = GetPrefixUntilComma(names); + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (name == *it) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (tests.count(*it) == 0) { + errors << "You forgot to list test " << *it << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing +// Copyright 2008, 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Mocking Framework (Google Mock) +// +// This file #includes all Google Mock implementation .cc files. The +// purpose is to allow a user to build Google Mock by compiling this +// file alone. + +// This line ensures that gmock.h can be compiled on its own, even +// when it's fused. +#include "gmock/gmock.h" + +// The following lines pull in the real gmock *.cc files. +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements cardinalities. + + +#include +#include // NOLINT +#include +#include + +namespace testing { + +namespace { + +// Implements the Between(m, n) cardinality. +class BetweenCardinalityImpl : public CardinalityInterface { + public: + BetweenCardinalityImpl(int min, int max) + : min_(min >= 0 ? min : 0), + max_(max >= min_ ? max : min_) { + std::stringstream ss; + if (min < 0) { + ss << "The invocation lower bound must be >= 0, " + << "but is actually " << min << "."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } else if (max < 0) { + ss << "The invocation upper bound must be >= 0, " + << "but is actually " << max << "."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } else if (min > max) { + ss << "The invocation upper bound (" << max + << ") must be >= the invocation lower bound (" << min + << ")."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } + } + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return min_; } + virtual int ConservativeUpperBound() const { return max_; } + + virtual bool IsSatisfiedByCallCount(int call_count) const { + return min_ <= call_count && call_count <= max_; + } + + virtual bool IsSaturatedByCallCount(int call_count) const { + return call_count >= max_; + } + + virtual void DescribeTo(::std::ostream* os) const; + + private: + const int min_; + const int max_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(BetweenCardinalityImpl); +}; + +// Formats "n times" in a human-friendly way. +inline internal::string FormatTimes(int n) { + if (n == 1) { + return "once"; + } else if (n == 2) { + return "twice"; + } else { + std::stringstream ss; + ss << n << " times"; + return ss.str(); + } +} + +// Describes the Between(m, n) cardinality in human-friendly text. +void BetweenCardinalityImpl::DescribeTo(::std::ostream* os) const { + if (min_ == 0) { + if (max_ == 0) { + *os << "never called"; + } else if (max_ == INT_MAX) { + *os << "called any number of times"; + } else { + *os << "called at most " << FormatTimes(max_); + } + } else if (min_ == max_) { + *os << "called " << FormatTimes(min_); + } else if (max_ == INT_MAX) { + *os << "called at least " << FormatTimes(min_); + } else { + // 0 < min_ < max_ < INT_MAX + *os << "called between " << min_ << " and " << max_ << " times"; + } +} + +} // Unnamed namespace + +// Describes the given call count to an ostream. +void Cardinality::DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os) { + if (actual_call_count > 0) { + *os << "called " << FormatTimes(actual_call_count); + } else { + *os << "never called"; + } +} + +// Creates a cardinality that allows at least n calls. +GTEST_API_ Cardinality AtLeast(int n) { return Between(n, INT_MAX); } + +// Creates a cardinality that allows at most n calls. +GTEST_API_ Cardinality AtMost(int n) { return Between(0, n); } + +// Creates a cardinality that allows any number of calls. +GTEST_API_ Cardinality AnyNumber() { return AtLeast(0); } + +// Creates a cardinality that allows between min and max calls. +GTEST_API_ Cardinality Between(int min, int max) { + return Cardinality(new BetweenCardinalityImpl(min, max)); +} + +// Creates a cardinality that allows exactly n calls. +GTEST_API_ Cardinality Exactly(int n) { return Between(n, n); } + +} // namespace testing +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file defines some utilities useful for implementing Google +// Mock. They are subject to change without notice, so please DO NOT +// USE THEM IN USER CODE. + + +#include +#include // NOLINT +#include + +namespace testing { +namespace internal { + +// Converts an identifier name to a space-separated list of lower-case +// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is +// treated as one word. For example, both "FooBar123" and +// "foo_bar_123" are converted to "foo bar 123". +GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name) { + string result; + char prev_char = '\0'; + for (const char* p = id_name; *p != '\0'; prev_char = *(p++)) { + // We don't care about the current locale as the input is + // guaranteed to be a valid C++ identifier name. + const bool starts_new_word = IsUpper(*p) || + (!IsAlpha(prev_char) && IsLower(*p)) || + (!IsDigit(prev_char) && IsDigit(*p)); + + if (IsAlNum(*p)) { + if (starts_new_word && result != "") + result += ' '; + result += ToLower(*p); + } + } + return result; +} + +// This class reports Google Mock failures as Google Test failures. A +// user can define another class in a similar fashion if he intends to +// use Google Mock with a testing framework other than Google Test. +class GoogleTestFailureReporter : public FailureReporterInterface { + public: + virtual void ReportFailure(FailureType type, const char* file, int line, + const string& message) { + AssertHelper(type == kFatal ? + TestPartResult::kFatalFailure : + TestPartResult::kNonFatalFailure, + file, + line, + message.c_str()) = Message(); + if (type == kFatal) { + posix::Abort(); + } + } +}; + +// Returns the global failure reporter. Will create a +// GoogleTestFailureReporter and return it the first time called. +GTEST_API_ FailureReporterInterface* GetFailureReporter() { + // Points to the global failure reporter used by Google Mock. gcc + // guarantees that the following use of failure_reporter is + // thread-safe. We may need to add additional synchronization to + // protect failure_reporter if we port Google Mock to other + // compilers. + static FailureReporterInterface* const failure_reporter = + new GoogleTestFailureReporter(); + return failure_reporter; +} + +// Protects global resources (stdout in particular) used by Log(). +static GTEST_DEFINE_STATIC_MUTEX_(g_log_mutex); + +// Returns true iff a log with the given severity is visible according +// to the --gmock_verbose flag. +GTEST_API_ bool LogIsVisible(LogSeverity severity) { + if (GMOCK_FLAG(verbose) == kInfoVerbosity) { + // Always show the log if --gmock_verbose=info. + return true; + } else if (GMOCK_FLAG(verbose) == kErrorVerbosity) { + // Always hide it if --gmock_verbose=error. + return false; + } else { + // If --gmock_verbose is neither "info" nor "error", we treat it + // as "warning" (its default value). + return severity == kWarning; + } +} + +// Prints the given message to stdout iff 'severity' >= the level +// specified by the --gmock_verbose flag. If stack_frames_to_skip >= +// 0, also prints the stack trace excluding the top +// stack_frames_to_skip frames. In opt mode, any positive +// stack_frames_to_skip is treated as 0, since we don't know which +// function calls will be inlined by the compiler and need to be +// conservative. +GTEST_API_ void Log(LogSeverity severity, + const string& message, + int stack_frames_to_skip) { + if (!LogIsVisible(severity)) + return; + + // Ensures that logs from different threads don't interleave. + MutexLock l(&g_log_mutex); + + // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is a + // macro. + + if (severity == kWarning) { + // Prints a GMOCK WARNING marker to make the warnings easily searchable. + std::cout << "\nGMOCK WARNING:"; + } + // Pre-pends a new-line to message if it doesn't start with one. + if (message.empty() || message[0] != '\n') { + std::cout << "\n"; + } + std::cout << message; + if (stack_frames_to_skip >= 0) { +#ifdef NDEBUG + // In opt mode, we have to be conservative and skip no stack frame. + const int actual_to_skip = 0; +#else + // In dbg mode, we can do what the caller tell us to do (plus one + // for skipping this function's stack frame). + const int actual_to_skip = stack_frames_to_skip + 1; +#endif // NDEBUG + + // Appends a new-line to message if it doesn't end with one. + if (!message.empty() && *message.rbegin() != '\n') { + std::cout << "\n"; + } + std::cout << "Stack trace:\n" + << ::testing::internal::GetCurrentOsStackTraceExceptTop( + ::testing::UnitTest::GetInstance(), actual_to_skip); + } + std::cout << ::std::flush; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements Matcher, Matcher, and +// utilities for defining matchers. + + +#include +#include +#include + +namespace testing { + +// Constructs a matcher that matches a const string& whose value is +// equal to s. +Matcher::Matcher(const internal::string& s) { + *this = Eq(s); +} + +// Constructs a matcher that matches a const string& whose value is +// equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(internal::string(s)); +} + +// Constructs a matcher that matches a string whose value is equal to s. +Matcher::Matcher(const internal::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a string whose value is equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(internal::string(s)); +} + +#if GTEST_HAS_STRING_PIECE_ +// Constructs a matcher that matches a const StringPiece& whose value is +// equal to s. +Matcher::Matcher(const internal::string& s) { + *this = Eq(s); +} + +// Constructs a matcher that matches a const StringPiece& whose value is +// equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(internal::string(s)); +} + +// Constructs a matcher that matches a const StringPiece& whose value is +// equal to s. +Matcher::Matcher(StringPiece s) { + *this = Eq(s.ToString()); +} + +// Constructs a matcher that matches a StringPiece whose value is equal to s. +Matcher::Matcher(const internal::string& s) { + *this = Eq(s); +} + +// Constructs a matcher that matches a StringPiece whose value is equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(internal::string(s)); +} + +// Constructs a matcher that matches a StringPiece whose value is equal to s. +Matcher::Matcher(StringPiece s) { + *this = Eq(s.ToString()); +} +#endif // GTEST_HAS_STRING_PIECE_ + +namespace internal { + +// Joins a vector of strings as if they are fields of a tuple; returns +// the joined string. +GTEST_API_ string JoinAsTuple(const Strings& fields) { + switch (fields.size()) { + case 0: + return ""; + case 1: + return fields[0]; + default: + string result = "(" + fields[0]; + for (size_t i = 1; i < fields.size(); i++) { + result += ", "; + result += fields[i]; + } + result += ")"; + return result; + } +} + +// Returns the description for a matcher defined using the MATCHER*() +// macro where the user-supplied description string is "", if +// 'negation' is false; otherwise returns the description of the +// negation of the matcher. 'param_values' contains a list of strings +// that are the print-out of the matcher's parameters. +GTEST_API_ string FormatMatcherDescription(bool negation, + const char* matcher_name, + const Strings& param_values) { + string result = ConvertIdentifierNameToWords(matcher_name); + if (param_values.size() >= 1) + result += " " + JoinAsTuple(param_values); + return negation ? "not (" + result + ")" : result; +} + +// FindMaxBipartiteMatching and its helper class. +// +// Uses the well-known Ford-Fulkerson max flow method to find a maximum +// bipartite matching. Flow is considered to be from left to right. +// There is an implicit source node that is connected to all of the left +// nodes, and an implicit sink node that is connected to all of the +// right nodes. All edges have unit capacity. +// +// Neither the flow graph nor the residual flow graph are represented +// explicitly. Instead, they are implied by the information in 'graph' and +// a vector called 'left_' whose elements are initialized to the +// value kUnused. This represents the initial state of the algorithm, +// where the flow graph is empty, and the residual flow graph has the +// following edges: +// - An edge from source to each left_ node +// - An edge from each right_ node to sink +// - An edge from each left_ node to each right_ node, if the +// corresponding edge exists in 'graph'. +// +// When the TryAugment() method adds a flow, it sets left_[l] = r for some +// nodes l and r. This induces the following changes: +// - The edges (source, l), (l, r), and (r, sink) are added to the +// flow graph. +// - The same three edges are removed from the residual flow graph. +// - The reverse edges (l, source), (r, l), and (sink, r) are added +// to the residual flow graph, which is a directional graph +// representing unused flow capacity. +// +// When the method augments a flow (moving left_[l] from some r1 to some +// other r2), this can be thought of as "undoing" the above steps with +// respect to r1 and "redoing" them with respect to r2. +// +// It bears repeating that the flow graph and residual flow graph are +// never represented explicitly, but can be derived by looking at the +// information in 'graph' and in left_. +// +// As an optimization, there is a second vector called right_ which +// does not provide any new information. Instead, it enables more +// efficient queries about edges entering or leaving the right-side nodes +// of the flow or residual flow graphs. The following invariants are +// maintained: +// +// left[l] == kUnused or right[left[l]] == l +// right[r] == kUnused or left[right[r]] == r +// +// . [ source ] . +// . ||| . +// . ||| . +// . ||\--> left[0]=1 ---\ right[0]=-1 ----\ . +// . || | | . +// . |\---> left[1]=-1 \--> right[1]=0 ---\| . +// . | || . +// . \----> left[2]=2 ------> right[2]=2 --\|| . +// . ||| . +// . elements matchers vvv . +// . [ sink ] . +// +// See Also: +// [1] Cormen, et al (2001). "Section 26.2: The Ford–Fulkerson method". +// "Introduction to Algorithms (Second ed.)", pp. 651–664. +// [2] "Ford–Fulkerson algorithm", Wikipedia, +// 'http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm' +class MaxBipartiteMatchState { + public: + explicit MaxBipartiteMatchState(const MatchMatrix& graph) + : graph_(&graph), + left_(graph_->LhsSize(), kUnused), + right_(graph_->RhsSize(), kUnused) { + } + + // Returns the edges of a maximal match, each in the form {left, right}. + ElementMatcherPairs Compute() { + // 'seen' is used for path finding { 0: unseen, 1: seen }. + ::std::vector seen; + // Searches the residual flow graph for a path from each left node to + // the sink in the residual flow graph, and if one is found, add flow + // to the graph. It's okay to search through the left nodes once. The + // edge from the implicit source node to each previously-visited left + // node will have flow if that left node has any path to the sink + // whatsoever. Subsequent augmentations can only add flow to the + // network, and cannot take away that previous flow unit from the source. + // Since the source-to-left edge can only carry one flow unit (or, + // each element can be matched to only one matcher), there is no need + // to visit the left nodes more than once looking for augmented paths. + // The flow is known to be possible or impossible by looking at the + // node once. + for (size_t ilhs = 0; ilhs < graph_->LhsSize(); ++ilhs) { + // Reset the path-marking vector and try to find a path from + // source to sink starting at the left_[ilhs] node. + GTEST_CHECK_(left_[ilhs] == kUnused) + << "ilhs: " << ilhs << ", left_[ilhs]: " << left_[ilhs]; + // 'seen' initialized to 'graph_->RhsSize()' copies of 0. + seen.assign(graph_->RhsSize(), 0); + TryAugment(ilhs, &seen); + } + ElementMatcherPairs result; + for (size_t ilhs = 0; ilhs < left_.size(); ++ilhs) { + size_t irhs = left_[ilhs]; + if (irhs == kUnused) continue; + result.push_back(ElementMatcherPair(ilhs, irhs)); + } + return result; + } + + private: + static const size_t kUnused = static_cast(-1); + + // Perform a depth-first search from left node ilhs to the sink. If a + // path is found, flow is added to the network by linking the left and + // right vector elements corresponding each segment of the path. + // Returns true if a path to sink was found, which means that a unit of + // flow was added to the network. The 'seen' vector elements correspond + // to right nodes and are marked to eliminate cycles from the search. + // + // Left nodes will only be explored at most once because they + // are accessible from at most one right node in the residual flow + // graph. + // + // Note that left_[ilhs] is the only element of left_ that TryAugment will + // potentially transition from kUnused to another value. Any other + // left_ element holding kUnused before TryAugment will be holding it + // when TryAugment returns. + // + bool TryAugment(size_t ilhs, ::std::vector* seen) { + for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) { + if ((*seen)[irhs]) + continue; + if (!graph_->HasEdge(ilhs, irhs)) + continue; + // There's an available edge from ilhs to irhs. + (*seen)[irhs] = 1; + // Next a search is performed to determine whether + // this edge is a dead end or leads to the sink. + // + // right_[irhs] == kUnused means that there is residual flow from + // right node irhs to the sink, so we can use that to finish this + // flow path and return success. + // + // Otherwise there is residual flow to some ilhs. We push flow + // along that path and call ourselves recursively to see if this + // ultimately leads to sink. + if (right_[irhs] == kUnused || TryAugment(right_[irhs], seen)) { + // Add flow from left_[ilhs] to right_[irhs]. + left_[ilhs] = irhs; + right_[irhs] = ilhs; + return true; + } + } + return false; + } + + const MatchMatrix* graph_; // not owned + // Each element of the left_ vector represents a left hand side node + // (i.e. an element) and each element of right_ is a right hand side + // node (i.e. a matcher). The values in the left_ vector indicate + // outflow from that node to a node on the the right_ side. The values + // in the right_ indicate inflow, and specify which left_ node is + // feeding that right_ node, if any. For example, left_[3] == 1 means + // there's a flow from element #3 to matcher #1. Such a flow would also + // be redundantly represented in the right_ vector as right_[1] == 3. + // Elements of left_ and right_ are either kUnused or mutually + // referent. Mutually referent means that left_[right_[i]] = i and + // right_[left_[i]] = i. + ::std::vector left_; + ::std::vector right_; + + GTEST_DISALLOW_ASSIGN_(MaxBipartiteMatchState); +}; + +const size_t MaxBipartiteMatchState::kUnused; + +GTEST_API_ ElementMatcherPairs +FindMaxBipartiteMatching(const MatchMatrix& g) { + return MaxBipartiteMatchState(g).Compute(); +} + +static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs, + ::std::ostream* stream) { + typedef ElementMatcherPairs::const_iterator Iter; + ::std::ostream& os = *stream; + os << "{"; + const char *sep = ""; + for (Iter it = pairs.begin(); it != pairs.end(); ++it) { + os << sep << "\n (" + << "element #" << it->first << ", " + << "matcher #" << it->second << ")"; + sep = ","; + } + os << "\n}"; +} + +// Tries to find a pairing, and explains the result. +GTEST_API_ bool FindPairing(const MatchMatrix& matrix, + MatchResultListener* listener) { + ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix); + + size_t max_flow = matches.size(); + bool result = (max_flow == matrix.RhsSize()); + + if (!result) { + if (listener->IsInterested()) { + *listener << "where no permutation of the elements can " + "satisfy all matchers, and the closest match is " + << max_flow << " of " << matrix.RhsSize() + << " matchers with the pairings:\n"; + LogElementMatcherPairVec(matches, listener->stream()); + } + return false; + } + + if (matches.size() > 1) { + if (listener->IsInterested()) { + const char *sep = "where:\n"; + for (size_t mi = 0; mi < matches.size(); ++mi) { + *listener << sep << " - element #" << matches[mi].first + << " is matched by matcher #" << matches[mi].second; + sep = ",\n"; + } + } + } + return true; +} + +bool MatchMatrix::NextGraph() { + for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) { + for (size_t irhs = 0; irhs < RhsSize(); ++irhs) { + char& b = matched_[SpaceIndex(ilhs, irhs)]; + if (!b) { + b = 1; + return true; + } + b = 0; + } + } + return false; +} + +void MatchMatrix::Randomize() { + for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) { + for (size_t irhs = 0; irhs < RhsSize(); ++irhs) { + char& b = matched_[SpaceIndex(ilhs, irhs)]; + b = static_cast(rand() & 1); // NOLINT + } + } +} + +string MatchMatrix::DebugString() const { + ::std::stringstream ss; + const char *sep = ""; + for (size_t i = 0; i < LhsSize(); ++i) { + ss << sep; + for (size_t j = 0; j < RhsSize(); ++j) { + ss << HasEdge(i, j); + } + sep = ";"; + } + return ss.str(); +} + +void UnorderedElementsAreMatcherImplBase::DescribeToImpl( + ::std::ostream* os) const { + if (matcher_describers_.empty()) { + *os << "is empty"; + return; + } + if (matcher_describers_.size() == 1) { + *os << "has " << Elements(1) << " and that element "; + matcher_describers_[0]->DescribeTo(os); + return; + } + *os << "has " << Elements(matcher_describers_.size()) + << " and there exists some permutation of elements such that:\n"; + const char* sep = ""; + for (size_t i = 0; i != matcher_describers_.size(); ++i) { + *os << sep << " - element #" << i << " "; + matcher_describers_[i]->DescribeTo(os); + sep = ", and\n"; + } +} + +void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl( + ::std::ostream* os) const { + if (matcher_describers_.empty()) { + *os << "isn't empty"; + return; + } + if (matcher_describers_.size() == 1) { + *os << "doesn't have " << Elements(1) + << ", or has " << Elements(1) << " that "; + matcher_describers_[0]->DescribeNegationTo(os); + return; + } + *os << "doesn't have " << Elements(matcher_describers_.size()) + << ", or there exists no permutation of elements such that:\n"; + const char* sep = ""; + for (size_t i = 0; i != matcher_describers_.size(); ++i) { + *os << sep << " - element #" << i << " "; + matcher_describers_[i]->DescribeTo(os); + sep = ", and\n"; + } +} + +// Checks that all matchers match at least one element, and that all +// elements match at least one matcher. This enables faster matching +// and better error reporting. +// Returns false, writing an explanation to 'listener', if and only +// if the success criteria are not met. +bool UnorderedElementsAreMatcherImplBase:: +VerifyAllElementsAndMatchersAreMatched( + const ::std::vector& element_printouts, + const MatchMatrix& matrix, + MatchResultListener* listener) const { + bool result = true; + ::std::vector element_matched(matrix.LhsSize(), 0); + ::std::vector matcher_matched(matrix.RhsSize(), 0); + + for (size_t ilhs = 0; ilhs < matrix.LhsSize(); ilhs++) { + for (size_t irhs = 0; irhs < matrix.RhsSize(); irhs++) { + char matched = matrix.HasEdge(ilhs, irhs); + element_matched[ilhs] |= matched; + matcher_matched[irhs] |= matched; + } + } + + { + const char* sep = + "where the following matchers don't match any elements:\n"; + for (size_t mi = 0; mi < matcher_matched.size(); ++mi) { + if (matcher_matched[mi]) + continue; + result = false; + if (listener->IsInterested()) { + *listener << sep << "matcher #" << mi << ": "; + matcher_describers_[mi]->DescribeTo(listener->stream()); + sep = ",\n"; + } + } + } + + { + const char* sep = + "where the following elements don't match any matchers:\n"; + const char* outer_sep = ""; + if (!result) { + outer_sep = "\nand "; + } + for (size_t ei = 0; ei < element_matched.size(); ++ei) { + if (element_matched[ei]) + continue; + result = false; + if (listener->IsInterested()) { + *listener << outer_sep << sep << "element #" << ei << ": " + << element_printouts[ei]; + sep = ",\n"; + outer_sep = ""; + } + } + } + return result; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements the spec builder syntax (ON_CALL and +// EXPECT_CALL). + + +#include +#include // NOLINT +#include +#include +#include + +#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC +# include // NOLINT +#endif + +namespace testing { +namespace internal { + +// Protects the mock object registry (in class Mock), all function +// mockers, and all expectations. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex); + +// Logs a message including file and line number information. +GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity, + const char* file, int line, + const string& message) { + ::std::ostringstream s; + s << file << ":" << line << ": " << message << ::std::endl; + Log(severity, s.str(), 0); +} + +// Constructs an ExpectationBase object. +ExpectationBase::ExpectationBase(const char* a_file, + int a_line, + const string& a_source_text) + : file_(a_file), + line_(a_line), + source_text_(a_source_text), + cardinality_specified_(false), + cardinality_(Exactly(1)), + call_count_(0), + retired_(false), + extra_matcher_specified_(false), + repeated_action_specified_(false), + retires_on_saturation_(false), + last_clause_(kNone), + action_count_checked_(false) {} + +// Destructs an ExpectationBase object. +ExpectationBase::~ExpectationBase() {} + +// Explicitly specifies the cardinality of this expectation. Used by +// the subclasses to implement the .Times() clause. +void ExpectationBase::SpecifyCardinality(const Cardinality& a_cardinality) { + cardinality_specified_ = true; + cardinality_ = a_cardinality; +} + +// Retires all pre-requisites of this expectation. +void ExpectationBase::RetireAllPreRequisites() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + if (is_retired()) { + // We can take this short-cut as we never retire an expectation + // until we have retired all its pre-requisites. + return; + } + + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + ExpectationBase* const prerequisite = it->expectation_base().get(); + if (!prerequisite->is_retired()) { + prerequisite->RetireAllPreRequisites(); + prerequisite->Retire(); + } + } +} + +// Returns true iff all pre-requisites of this expectation have been +// satisfied. +bool ExpectationBase::AllPrerequisitesAreSatisfied() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + if (!(it->expectation_base()->IsSatisfied()) || + !(it->expectation_base()->AllPrerequisitesAreSatisfied())) + return false; + } + return true; +} + +// Adds unsatisfied pre-requisites of this expectation to 'result'. +void ExpectationBase::FindUnsatisfiedPrerequisites(ExpectationSet* result) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + if (it->expectation_base()->IsSatisfied()) { + // If *it is satisfied and has a call count of 0, some of its + // pre-requisites may not be satisfied yet. + if (it->expectation_base()->call_count_ == 0) { + it->expectation_base()->FindUnsatisfiedPrerequisites(result); + } + } else { + // Now that we know *it is unsatisfied, we are not so interested + // in whether its pre-requisites are satisfied. Therefore we + // don't recursively call FindUnsatisfiedPrerequisites() here. + *result += *it; + } + } +} + +// Describes how many times a function call matching this +// expectation has occurred. +void ExpectationBase::DescribeCallCountTo(::std::ostream* os) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + + // Describes how many times the function is expected to be called. + *os << " Expected: to be "; + cardinality().DescribeTo(os); + *os << "\n Actual: "; + Cardinality::DescribeActualCallCountTo(call_count(), os); + + // Describes the state of the expectation (e.g. is it satisfied? + // is it active?). + *os << " - " << (IsOverSaturated() ? "over-saturated" : + IsSaturated() ? "saturated" : + IsSatisfied() ? "satisfied" : "unsatisfied") + << " and " + << (is_retired() ? "retired" : "active"); +} + +// Checks the action count (i.e. the number of WillOnce() and +// WillRepeatedly() clauses) against the cardinality if this hasn't +// been done before. Prints a warning if there are too many or too +// few actions. +void ExpectationBase::CheckActionCountIfNotDone() const + GTEST_LOCK_EXCLUDED_(mutex_) { + bool should_check = false; + { + MutexLock l(&mutex_); + if (!action_count_checked_) { + action_count_checked_ = true; + should_check = true; + } + } + + if (should_check) { + if (!cardinality_specified_) { + // The cardinality was inferred - no need to check the action + // count against it. + return; + } + + // The cardinality was explicitly specified. + const int action_count = static_cast(untyped_actions_.size()); + const int upper_bound = cardinality().ConservativeUpperBound(); + const int lower_bound = cardinality().ConservativeLowerBound(); + bool too_many; // True if there are too many actions, or false + // if there are too few. + if (action_count > upper_bound || + (action_count == upper_bound && repeated_action_specified_)) { + too_many = true; + } else if (0 < action_count && action_count < lower_bound && + !repeated_action_specified_) { + too_many = false; + } else { + return; + } + + ::std::stringstream ss; + DescribeLocationTo(&ss); + ss << "Too " << (too_many ? "many" : "few") + << " actions specified in " << source_text() << "...\n" + << "Expected to be "; + cardinality().DescribeTo(&ss); + ss << ", but has " << (too_many ? "" : "only ") + << action_count << " WillOnce()" + << (action_count == 1 ? "" : "s"); + if (repeated_action_specified_) { + ss << " and a WillRepeatedly()"; + } + ss << "."; + Log(kWarning, ss.str(), -1); // -1 means "don't print stack trace". + } +} + +// Implements the .Times() clause. +void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) { + if (last_clause_ == kTimes) { + ExpectSpecProperty(false, + ".Times() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kTimes, + ".Times() cannot appear after " + ".InSequence(), .WillOnce(), .WillRepeatedly(), " + "or .RetiresOnSaturation()."); + } + last_clause_ = kTimes; + + SpecifyCardinality(a_cardinality); +} + +// Points to the implicit sequence introduced by a living InSequence +// object (if any) in the current thread or NULL. +GTEST_API_ ThreadLocal g_gmock_implicit_sequence; + +// Reports an uninteresting call (whose description is in msg) in the +// manner specified by 'reaction'. +void ReportUninterestingCall(CallReaction reaction, const string& msg) { + switch (reaction) { + case kAllow: + Log(kInfo, msg, 3); + break; + case kWarn: + Log(kWarning, msg, 3); + break; + default: // FAIL + Expect(false, NULL, -1, msg); + } +} + +UntypedFunctionMockerBase::UntypedFunctionMockerBase() + : mock_obj_(NULL), name_("") {} + +UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {} + +// Sets the mock object this mock method belongs to, and registers +// this information in the global mock registry. Will be called +// whenever an EXPECT_CALL() or ON_CALL() is executed on this mock +// method. +void UntypedFunctionMockerBase::RegisterOwner(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + { + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + } + Mock::Register(mock_obj, this); +} + +// Sets the mock object this mock method belongs to, and sets the name +// of the mock function. Will be called upon each invocation of this +// mock function. +void UntypedFunctionMockerBase::SetOwnerAndName(const void* mock_obj, + const char* name) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + // We protect name_ under g_gmock_mutex in case this mock function + // is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + name_ = name; +} + +// Returns the name of the function being mocked. Must be called +// after RegisterOwner() or SetOwnerAndName() has been called. +const void* UntypedFunctionMockerBase::MockObject() const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + const void* mock_obj; + { + // We protect mock_obj_ under g_gmock_mutex in case this mock + // function is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + Assert(mock_obj_ != NULL, __FILE__, __LINE__, + "MockObject() must not be called before RegisterOwner() or " + "SetOwnerAndName() has been called."); + mock_obj = mock_obj_; + } + return mock_obj; +} + +// Returns the name of this mock method. Must be called after +// SetOwnerAndName() has been called. +const char* UntypedFunctionMockerBase::Name() const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + const char* name; + { + // We protect name_ under g_gmock_mutex in case this mock + // function is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + Assert(name_ != NULL, __FILE__, __LINE__, + "Name() must not be called before SetOwnerAndName() has " + "been called."); + name = name_; + } + return name; +} + +// Calculates the result of invoking this mock function with the given +// arguments, prints it, and returns it. The caller is responsible +// for deleting the result. +const UntypedActionResultHolderBase* +UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + if (untyped_expectations_.size() == 0) { + // No expectation is set on this mock method - we have an + // uninteresting call. + + // We must get Google Mock's reaction on uninteresting calls + // made on this mock object BEFORE performing the action, + // because the action may DELETE the mock object and make the + // following expression meaningless. + const CallReaction reaction = + Mock::GetReactionOnUninterestingCalls(MockObject()); + + // True iff we need to print this call's arguments and return + // value. This definition must be kept in sync with + // the behavior of ReportUninterestingCall(). + const bool need_to_report_uninteresting_call = + // If the user allows this uninteresting call, we print it + // only when he wants informational messages. + reaction == kAllow ? LogIsVisible(kInfo) : + // If the user wants this to be a warning, we print it only + // when he wants to see warnings. + reaction == kWarn ? LogIsVisible(kWarning) : + // Otherwise, the user wants this to be an error, and we + // should always print detailed information in the error. + true; + + if (!need_to_report_uninteresting_call) { + // Perform the action without printing the call information. + return this->UntypedPerformDefaultAction(untyped_args, ""); + } + + // Warns about the uninteresting call. + ::std::stringstream ss; + this->UntypedDescribeUninterestingCall(untyped_args, &ss); + + // Calculates the function result. + const UntypedActionResultHolderBase* const result = + this->UntypedPerformDefaultAction(untyped_args, ss.str()); + + // Prints the function result. + if (result != NULL) + result->PrintAsActionResult(&ss); + + ReportUninterestingCall(reaction, ss.str()); + return result; + } + + bool is_excessive = false; + ::std::stringstream ss; + ::std::stringstream why; + ::std::stringstream loc; + const void* untyped_action = NULL; + + // The UntypedFindMatchingExpectation() function acquires and + // releases g_gmock_mutex. + const ExpectationBase* const untyped_expectation = + this->UntypedFindMatchingExpectation( + untyped_args, &untyped_action, &is_excessive, + &ss, &why); + const bool found = untyped_expectation != NULL; + + // True iff we need to print the call's arguments and return value. + // This definition must be kept in sync with the uses of Expect() + // and Log() in this function. + const bool need_to_report_call = + !found || is_excessive || LogIsVisible(kInfo); + if (!need_to_report_call) { + // Perform the action without printing the call information. + return + untyped_action == NULL ? + this->UntypedPerformDefaultAction(untyped_args, "") : + this->UntypedPerformAction(untyped_action, untyped_args); + } + + ss << " Function call: " << Name(); + this->UntypedPrintArgs(untyped_args, &ss); + + // In case the action deletes a piece of the expectation, we + // generate the message beforehand. + if (found && !is_excessive) { + untyped_expectation->DescribeLocationTo(&loc); + } + + const UntypedActionResultHolderBase* const result = + untyped_action == NULL ? + this->UntypedPerformDefaultAction(untyped_args, ss.str()) : + this->UntypedPerformAction(untyped_action, untyped_args); + if (result != NULL) + result->PrintAsActionResult(&ss); + ss << "\n" << why.str(); + + if (!found) { + // No expectation matches this call - reports a failure. + Expect(false, NULL, -1, ss.str()); + } else if (is_excessive) { + // We had an upper-bound violation and the failure message is in ss. + Expect(false, untyped_expectation->file(), + untyped_expectation->line(), ss.str()); + } else { + // We had an expected call and the matching expectation is + // described in ss. + Log(kInfo, loc.str() + ss.str(), 2); + } + + return result; +} + +// Returns an Expectation object that references and co-owns exp, +// which must be an expectation on this mock function. +Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) { + for (UntypedExpectations::const_iterator it = + untyped_expectations_.begin(); + it != untyped_expectations_.end(); ++it) { + if (it->get() == exp) { + return Expectation(*it); + } + } + + Assert(false, __FILE__, __LINE__, "Cannot find expectation."); + return Expectation(); + // The above statement is just to make the code compile, and will + // never be executed. +} + +// Verifies that all expectations on this mock function have been +// satisfied. Reports one or more Google Test non-fatal failures +// and returns false if not. +bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + bool expectations_met = true; + for (UntypedExpectations::const_iterator it = + untyped_expectations_.begin(); + it != untyped_expectations_.end(); ++it) { + ExpectationBase* const untyped_expectation = it->get(); + if (untyped_expectation->IsOverSaturated()) { + // There was an upper-bound violation. Since the error was + // already reported when it occurred, there is no need to do + // anything here. + expectations_met = false; + } else if (!untyped_expectation->IsSatisfied()) { + expectations_met = false; + ::std::stringstream ss; + ss << "Actual function call count doesn't match " + << untyped_expectation->source_text() << "...\n"; + // No need to show the source file location of the expectation + // in the description, as the Expect() call that follows already + // takes care of it. + untyped_expectation->MaybeDescribeExtraMatcherTo(&ss); + untyped_expectation->DescribeCallCountTo(&ss); + Expect(false, untyped_expectation->file(), + untyped_expectation->line(), ss.str()); + } + } + + // Deleting our expectations may trigger other mock objects to be deleted, for + // example if an action contains a reference counted smart pointer to that + // mock object, and that is the last reference. So if we delete our + // expectations within the context of the global mutex we may deadlock when + // this method is called again. Instead, make a copy of the set of + // expectations to delete, clear our set within the mutex, and then clear the + // copied set outside of it. + UntypedExpectations expectations_to_delete; + untyped_expectations_.swap(expectations_to_delete); + + g_gmock_mutex.Unlock(); + expectations_to_delete.clear(); + g_gmock_mutex.Lock(); + + return expectations_met; +} + +} // namespace internal + +// Class Mock. + +namespace { + +typedef std::set FunctionMockers; + +// The current state of a mock object. Such information is needed for +// detecting leaked mock objects and explicitly verifying a mock's +// expectations. +struct MockObjectState { + MockObjectState() + : first_used_file(NULL), first_used_line(-1), leakable(false) {} + + // Where in the source file an ON_CALL or EXPECT_CALL is first + // invoked on this mock object. + const char* first_used_file; + int first_used_line; + ::std::string first_used_test_case; + ::std::string first_used_test; + bool leakable; // true iff it's OK to leak the object. + FunctionMockers function_mockers; // All registered methods of the object. +}; + +// A global registry holding the state of all mock objects that are +// alive. A mock object is added to this registry the first time +// Mock::AllowLeak(), ON_CALL(), or EXPECT_CALL() is called on it. It +// is removed from the registry in the mock object's destructor. +class MockObjectRegistry { + public: + // Maps a mock object (identified by its address) to its state. + typedef std::map StateMap; + + // This destructor will be called when a program exits, after all + // tests in it have been run. By then, there should be no mock + // object alive. Therefore we report any living object as test + // failure, unless the user explicitly asked us to ignore it. + ~MockObjectRegistry() { + // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is + // a macro. + + if (!GMOCK_FLAG(catch_leaked_mocks)) + return; + + int leaked_count = 0; + for (StateMap::const_iterator it = states_.begin(); it != states_.end(); + ++it) { + if (it->second.leakable) // The user said it's fine to leak this object. + continue; + + // TODO(wan@google.com): Print the type of the leaked object. + // This can help the user identify the leaked object. + std::cout << "\n"; + const MockObjectState& state = it->second; + std::cout << internal::FormatFileLocation(state.first_used_file, + state.first_used_line); + std::cout << " ERROR: this mock object"; + if (state.first_used_test != "") { + std::cout << " (used in test " << state.first_used_test_case << "." + << state.first_used_test << ")"; + } + std::cout << " should be deleted but never is. Its address is @" + << it->first << "."; + leaked_count++; + } + if (leaked_count > 0) { + std::cout << "\nERROR: " << leaked_count + << " leaked mock " << (leaked_count == 1 ? "object" : "objects") + << " found at program exit.\n"; + std::cout.flush(); + ::std::cerr.flush(); + // RUN_ALL_TESTS() has already returned when this destructor is + // called. Therefore we cannot use the normal Google Test + // failure reporting mechanism. + _exit(1); // We cannot call exit() as it is not reentrant and + // may already have been called. + } + } + + StateMap& states() { return states_; } + + private: + StateMap states_; +}; + +// Protected by g_gmock_mutex. +MockObjectRegistry g_mock_object_registry; + +// Maps a mock object to the reaction Google Mock should have when an +// uninteresting method is called. Protected by g_gmock_mutex. +std::map g_uninteresting_call_reaction; + +// Sets the reaction Google Mock should have when an uninteresting +// method of the given mock object is called. +void SetReactionOnUninterestingCalls(const void* mock_obj, + internal::CallReaction reaction) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_uninteresting_call_reaction[mock_obj] = reaction; +} + +} // namespace + +// Tells Google Mock to allow uninteresting calls on the given mock +// object. +void Mock::AllowUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + SetReactionOnUninterestingCalls(mock_obj, internal::kAllow); +} + +// Tells Google Mock to warn the user about uninteresting calls on the +// given mock object. +void Mock::WarnUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + SetReactionOnUninterestingCalls(mock_obj, internal::kWarn); +} + +// Tells Google Mock to fail uninteresting calls on the given mock +// object. +void Mock::FailUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + SetReactionOnUninterestingCalls(mock_obj, internal::kFail); +} + +// Tells Google Mock the given mock object is being destroyed and its +// entry in the call-reaction table should be removed. +void Mock::UnregisterCallReaction(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_uninteresting_call_reaction.erase(mock_obj); +} + +// Returns the reaction Google Mock will have on uninteresting calls +// made on the given mock object. +internal::CallReaction Mock::GetReactionOnUninterestingCalls( + const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + return (g_uninteresting_call_reaction.count(mock_obj) == 0) ? + internal::kDefault : g_uninteresting_call_reaction[mock_obj]; +} + +// Tells Google Mock to ignore mock_obj when checking for leaked mock +// objects. +void Mock::AllowLeak(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].leakable = true; +} + +// Verifies and clears all expectations on the given mock object. If +// the expectations aren't satisfied, generates one or more Google +// Test non-fatal failures and returns false. +bool Mock::VerifyAndClearExpectations(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + return VerifyAndClearExpectationsLocked(mock_obj); +} + +// Verifies all expectations on the given mock object and clears its +// default actions and expectations. Returns true iff the +// verification was successful. +bool Mock::VerifyAndClear(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + ClearDefaultActionsLocked(mock_obj); + return VerifyAndClearExpectationsLocked(mock_obj); +} + +// Verifies and clears all expectations on the given mock object. If +// the expectations aren't satisfied, generates one or more Google +// Test non-fatal failures and returns false. +bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) { + internal::g_gmock_mutex.AssertHeld(); + if (g_mock_object_registry.states().count(mock_obj) == 0) { + // No EXPECT_CALL() was set on the given mock object. + return true; + } + + // Verifies and clears the expectations on each mock method in the + // given mock object. + bool expectations_met = true; + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; + for (FunctionMockers::const_iterator it = mockers.begin(); + it != mockers.end(); ++it) { + if (!(*it)->VerifyAndClearExpectationsLocked()) { + expectations_met = false; + } + } + + // We don't clear the content of mockers, as they may still be + // needed by ClearDefaultActionsLocked(). + return expectations_met; +} + +// Registers a mock object and a mock method it owns. +void Mock::Register(const void* mock_obj, + internal::UntypedFunctionMockerBase* mocker) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker); +} + +// Tells Google Mock where in the source code mock_obj is used in an +// ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this +// information helps the user identify which object it is. +void Mock::RegisterUseByOnCallOrExpectCall(const void* mock_obj, + const char* file, int line) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + MockObjectState& state = g_mock_object_registry.states()[mock_obj]; + if (state.first_used_file == NULL) { + state.first_used_file = file; + state.first_used_line = line; + const TestInfo* const test_info = + UnitTest::GetInstance()->current_test_info(); + if (test_info != NULL) { + // TODO(wan@google.com): record the test case name when the + // ON_CALL or EXPECT_CALL is invoked from SetUpTestCase() or + // TearDownTestCase(). + state.first_used_test_case = test_info->test_case_name(); + state.first_used_test = test_info->name(); + } + } +} + +// Unregisters a mock method; removes the owning mock object from the +// registry when the last mock method associated with it has been +// unregistered. This is called only in the destructor of +// FunctionMockerBase. +void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) { + internal::g_gmock_mutex.AssertHeld(); + for (MockObjectRegistry::StateMap::iterator it = + g_mock_object_registry.states().begin(); + it != g_mock_object_registry.states().end(); ++it) { + FunctionMockers& mockers = it->second.function_mockers; + if (mockers.erase(mocker) > 0) { + // mocker was in mockers and has been just removed. + if (mockers.empty()) { + g_mock_object_registry.states().erase(it); + } + return; + } + } +} + +// Clears all ON_CALL()s set on the given mock object. +void Mock::ClearDefaultActionsLocked(void* mock_obj) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) { + internal::g_gmock_mutex.AssertHeld(); + + if (g_mock_object_registry.states().count(mock_obj) == 0) { + // No ON_CALL() was set on the given mock object. + return; + } + + // Clears the default actions for each mock method in the given mock + // object. + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; + for (FunctionMockers::const_iterator it = mockers.begin(); + it != mockers.end(); ++it) { + (*it)->ClearDefaultActionsLocked(); + } + + // We don't clear the content of mockers, as they may still be + // needed by VerifyAndClearExpectationsLocked(). +} + +Expectation::Expectation() {} + +Expectation::Expectation( + const internal::linked_ptr& an_expectation_base) + : expectation_base_(an_expectation_base) {} + +Expectation::~Expectation() {} + +// Adds an expectation to a sequence. +void Sequence::AddExpectation(const Expectation& expectation) const { + if (*last_expectation_ != expectation) { + if (last_expectation_->expectation_base() != NULL) { + expectation.expectation_base()->immediate_prerequisites_ + += *last_expectation_; + } + *last_expectation_ = expectation; + } +} + +// Creates the implicit sequence if there isn't one. +InSequence::InSequence() { + if (internal::g_gmock_implicit_sequence.get() == NULL) { + internal::g_gmock_implicit_sequence.set(new Sequence); + sequence_created_ = true; + } else { + sequence_created_ = false; + } +} + +// Deletes the implicit sequence if it was created by the constructor +// of this object. +InSequence::~InSequence() { + if (sequence_created_) { + delete internal::g_gmock_implicit_sequence.get(); + internal::g_gmock_implicit_sequence.set(NULL); + } +} + +} // namespace testing +// Copyright 2008, 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. +// +// Author: wan@google.com (Zhanyong Wan) + + +namespace testing { + +// TODO(wan@google.com): support using environment variables to +// control the flag values, like what Google Test does. + +GMOCK_DEFINE_bool_(catch_leaked_mocks, true, + "true iff Google Mock should report leaked mock objects " + "as failures."); + +GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity, + "Controls how verbose Google Mock's output is." + " Valid values:\n" + " info - prints all messages.\n" + " warning - prints warnings and errors.\n" + " error - prints errors only."); + +namespace internal { + +// Parses a string as a command line flag. The string should have the +// format "--gmock_flag=value". When def_optional is true, the +// "=value" part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +static const char* ParseGoogleMockFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--gmock_". + const std::string flag_str = std::string("--gmock_") + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a Google Mock bool flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseGoogleMockBoolFlag(const char* str, const char* flag, + bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for a Google Mock string flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseGoogleMockStringFlag(const char* str, const char* flag, + std::string* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// The internal implementation of InitGoogleMock(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleMockImpl(int* argc, CharType** argv) { + // Makes sure Google Test is initialized. InitGoogleTest() is + // idempotent, so it's fine if the user has already called it. + InitGoogleTest(argc, argv); + if (*argc <= 0) return; + + for (int i = 1; i != *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + // Do we see a Google Mock flag? + if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks", + &GMOCK_FLAG(catch_leaked_mocks)) || + ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } +} + +} // namespace internal + +// Initializes Google Mock. This must be called before running the +// tests. In particular, it parses a command line for the flags that +// Google Mock recognizes. Whenever a Google Mock flag is seen, it is +// removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Mock flag variables are +// updated. +// +// Since Google Test is needed for Google Mock to work, this function +// also initializes Google Test and parses its flags, if that hasn't +// been done. +GTEST_API_ void InitGoogleMock(int* argc, char** argv) { + internal::InitGoogleMockImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv) { + internal::InitGoogleMockImpl(argc, argv); +} + +} // namespace testing diff --git a/src/c-ares/test/gmock-1.7.0/fused-src/gmock/gmock.h b/src/c-ares/test/gmock-1.7.0/fused-src/gmock/gmock.h new file mode 100644 index 000000000..e8dd7fc3d --- /dev/null +++ b/src/c-ares/test/gmock-1.7.0/fused-src/gmock/gmock.h @@ -0,0 +1,14198 @@ +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This is the main header file a user should include. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_H_ + +// This file implements the following syntax: +// +// ON_CALL(mock_object.Method(...)) +// .With(...) ? +// .WillByDefault(...); +// +// where With() is optional and WillByDefault() must appear exactly +// once. +// +// EXPECT_CALL(mock_object.Method(...)) +// .With(...) ? +// .Times(...) ? +// .InSequence(...) * +// .WillOnce(...) * +// .WillRepeatedly(...) ? +// .RetiresOnSaturation() ? ; +// +// where all clauses are optional and WillOnce() can be repeated. + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ + +#ifndef _WIN32_WCE +# include +#endif + +#include +#include + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file defines some utilities useful for implementing Google +// Mock. They are subject to change without notice, so please DO NOT +// USE THEM IN USER CODE. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ + +#include +#include // NOLINT +#include + +// This file was GENERATED by command: +// pump.py gmock-generated-internal-utils.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file contains template meta-programming utility classes needed +// for implementing Google Mock. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ + +// Copyright 2008, 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. +// +// Author: vadimb@google.com (Vadim Berman) +// +// Low-level types and utilities for porting Google Mock to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ + +#include +#include +#include + +// Most of the types needed for porting Google Mock are also required +// for Google Test and are defined in gtest-port.h. +#include "gtest/gtest.h" + +// To avoid conditional compilation everywhere, we make it +// gmock-port.h's responsibility to #include the header implementing +// tr1/tuple. gmock-port.h does this via gtest-port.h, which is +// guaranteed to pull in the tuple header. + +// For MS Visual C++, check the compiler version. At least VS 2003 is +// required to compile Google Mock. +#if defined(_MSC_VER) && _MSC_VER < 1310 +# error "At least Visual C++ 2003 (7.1) is required to compile Google Mock." +#endif + +// Macro for referencing flags. This is public as we want the user to +// use this syntax to reference Google Mock flags. +#define GMOCK_FLAG(name) FLAGS_gmock_##name + +// Macros for declaring flags. +#define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name) +#define GMOCK_DECLARE_int32_(name) \ + extern GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name) +#define GMOCK_DECLARE_string_(name) \ + extern GTEST_API_ ::std::string GMOCK_FLAG(name) + +// Macros for defining flags. +#define GMOCK_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GMOCK_FLAG(name) = (default_val) +#define GMOCK_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name) = (default_val) +#define GMOCK_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GMOCK_FLAG(name) = (default_val) + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ + +namespace testing { + +template +class Matcher; + +namespace internal { + +// An IgnoredValue object can be implicitly constructed from ANY value. +// This is used in implementing the IgnoreResult(a) action. +class IgnoredValue { + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + template + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// MatcherTuple::type is a tuple type where each field is a Matcher +// for the corresponding field in tuple type T. +template +struct MatcherTuple; + +template <> +struct MatcherTuple< ::std::tr1::tuple<> > { + typedef ::std::tr1::tuple< > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, + Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher, Matcher, Matcher, + Matcher > type; +}; + +// Template struct Function, where F must be a function type, contains +// the following typedefs: +// +// Result: the function's return type. +// ArgumentN: the type of the N-th argument, where N starts with 1. +// ArgumentTuple: the tuple type consisting of all parameters of F. +// ArgumentMatcherTuple: the tuple type consisting of Matchers for all +// parameters of F. +// MakeResultVoid: the function type obtained by substituting void +// for the return type of F. +// MakeResultIgnoredValue: +// the function type obtained by substituting Something +// for the return type of F. +template +struct Function; + +template +struct Function { + typedef R Result; + typedef ::std::tr1::tuple<> ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(); + typedef IgnoredValue MakeResultIgnoredValue(); +}; + +template +struct Function + : Function { + typedef A1 Argument1; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1); + typedef IgnoredValue MakeResultIgnoredValue(A1); +}; + +template +struct Function + : Function { + typedef A2 Argument2; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2); +}; + +template +struct Function + : Function { + typedef A3 Argument3; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3); +}; + +template +struct Function + : Function { + typedef A4 Argument4; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4); +}; + +template +struct Function + : Function { + typedef A5 Argument5; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5); +}; + +template +struct Function + : Function { + typedef A6 Argument6; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6); +}; + +template +struct Function + : Function { + typedef A7 Argument7; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7); +}; + +template +struct Function + : Function { + typedef A8 Argument8; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8); +}; + +template +struct Function + : Function { + typedef A9 Argument9; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8, + A9); +}; + +template +struct Function + : Function { + typedef A10 Argument10; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8, + A9, A10); +}; + +} // namespace internal + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ + +namespace testing { +namespace internal { + +// Converts an identifier name to a space-separated list of lower-case +// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is +// treated as one word. For example, both "FooBar123" and +// "foo_bar_123" are converted to "foo bar 123". +GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name); + +// PointeeOf::type is the type of a value pointed to by a +// Pointer, which can be either a smart pointer or a raw pointer. The +// following default implementation is for the case where Pointer is a +// smart pointer. +template +struct PointeeOf { + // Smart pointer classes define type element_type as the type of + // their pointees. + typedef typename Pointer::element_type type; +}; +// This specialization is for the raw pointer case. +template +struct PointeeOf { typedef T type; }; // NOLINT + +// GetRawPointer(p) returns the raw pointer underlying p when p is a +// smart pointer, or returns p itself when p is already a raw pointer. +// The following default implementation is for the smart pointer case. +template +inline const typename Pointer::element_type* GetRawPointer(const Pointer& p) { + return p.get(); +} +// This overloaded version is for the raw pointer case. +template +inline Element* GetRawPointer(Element* p) { return p; } + +// This comparator allows linked_ptr to be stored in sets. +template +struct LinkedPtrLessThan { + bool operator()(const ::testing::internal::linked_ptr& lhs, + const ::testing::internal::linked_ptr& rhs) const { + return lhs.get() < rhs.get(); + } +}; + +// Symbian compilation can be done with wchar_t being either a native +// type or a typedef. Using Google Mock with OpenC without wchar_t +// should require the definition of _STLP_NO_WCHAR_T. +// +// MSVC treats wchar_t as a native type usually, but treats it as the +// same as unsigned short when the compiler option /Zc:wchar_t- is +// specified. It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t +// is a native type. +#if (GTEST_OS_SYMBIAN && defined(_STLP_NO_WCHAR_T)) || \ + (defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED)) +// wchar_t is a typedef. +#else +# define GMOCK_WCHAR_T_IS_NATIVE_ 1 +#endif + +// signed wchar_t and unsigned wchar_t are NOT in the C++ standard. +// Using them is a bad practice and not portable. So DON'T use them. +// +// Still, Google Mock is designed to work even if the user uses signed +// wchar_t or unsigned wchar_t (obviously, assuming the compiler +// supports them). +// +// To gcc, +// wchar_t == signed wchar_t != unsigned wchar_t == unsigned int +#ifdef __GNUC__ +// signed/unsigned wchar_t are valid types. +# define GMOCK_HAS_SIGNED_WCHAR_T_ 1 +#endif + +// In what follows, we use the term "kind" to indicate whether a type +// is bool, an integer type (excluding bool), a floating-point type, +// or none of them. This categorization is useful for determining +// when a matcher argument type can be safely converted to another +// type in the implementation of SafeMatcherCast. +enum TypeKind { + kBool, kInteger, kFloatingPoint, kOther +}; + +// KindOf::value is the kind of type T. +template struct KindOf { + enum { value = kOther }; // The default kind. +}; + +// This macro declares that the kind of 'type' is 'kind'. +#define GMOCK_DECLARE_KIND_(type, kind) \ + template <> struct KindOf { enum { value = kind }; } + +GMOCK_DECLARE_KIND_(bool, kBool); + +// All standard integer types. +GMOCK_DECLARE_KIND_(char, kInteger); +GMOCK_DECLARE_KIND_(signed char, kInteger); +GMOCK_DECLARE_KIND_(unsigned char, kInteger); +GMOCK_DECLARE_KIND_(short, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned short, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(int, kInteger); +GMOCK_DECLARE_KIND_(unsigned int, kInteger); +GMOCK_DECLARE_KIND_(long, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned long, kInteger); // NOLINT + +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DECLARE_KIND_(wchar_t, kInteger); +#endif + +// Non-standard integer types. +GMOCK_DECLARE_KIND_(Int64, kInteger); +GMOCK_DECLARE_KIND_(UInt64, kInteger); + +// All standard floating-point types. +GMOCK_DECLARE_KIND_(float, kFloatingPoint); +GMOCK_DECLARE_KIND_(double, kFloatingPoint); +GMOCK_DECLARE_KIND_(long double, kFloatingPoint); + +#undef GMOCK_DECLARE_KIND_ + +// Evaluates to the kind of 'type'. +#define GMOCK_KIND_OF_(type) \ + static_cast< ::testing::internal::TypeKind>( \ + ::testing::internal::KindOf::value) + +// Evaluates to true iff integer type T is signed. +#define GMOCK_IS_SIGNED_(T) (static_cast(-1) < 0) + +// LosslessArithmeticConvertibleImpl::value +// is true iff arithmetic type From can be losslessly converted to +// arithmetic type To. +// +// It's the user's responsibility to ensure that both From and To are +// raw (i.e. has no CV modifier, is not a pointer, and is not a +// reference) built-in arithmetic types, kFromKind is the kind of +// From, and kToKind is the kind of To; the value is +// implementation-defined when the above pre-condition is violated. +template +struct LosslessArithmeticConvertibleImpl : public false_type {}; + +// Converting bool to bool is lossless. +template <> +struct LosslessArithmeticConvertibleImpl + : public true_type {}; // NOLINT + +// Converting bool to any integer type is lossless. +template +struct LosslessArithmeticConvertibleImpl + : public true_type {}; // NOLINT + +// Converting bool to any floating-point type is lossless. +template +struct LosslessArithmeticConvertibleImpl + : public true_type {}; // NOLINT + +// Converting an integer to bool is lossy. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting an integer to another non-bool integer is lossless iff +// the target type's range encloses the source type's range. +template +struct LosslessArithmeticConvertibleImpl + : public bool_constant< + // When converting from a smaller size to a larger size, we are + // fine as long as we are not converting from signed to unsigned. + ((sizeof(From) < sizeof(To)) && + (!GMOCK_IS_SIGNED_(From) || GMOCK_IS_SIGNED_(To))) || + // When converting between the same size, the signedness must match. + ((sizeof(From) == sizeof(To)) && + (GMOCK_IS_SIGNED_(From) == GMOCK_IS_SIGNED_(To)))> {}; // NOLINT + +#undef GMOCK_IS_SIGNED_ + +// Converting an integer to a floating-point type may be lossy, since +// the format of a floating-point number is implementation-defined. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting a floating-point to bool is lossy. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting a floating-point to an integer is lossy. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting a floating-point to another floating-point is lossless +// iff the target type is at least as big as the source type. +template +struct LosslessArithmeticConvertibleImpl< + kFloatingPoint, From, kFloatingPoint, To> + : public bool_constant {}; // NOLINT + +// LosslessArithmeticConvertible::value is true iff arithmetic +// type From can be losslessly converted to arithmetic type To. +// +// It's the user's responsibility to ensure that both From and To are +// raw (i.e. has no CV modifier, is not a pointer, and is not a +// reference) built-in arithmetic types; the value is +// implementation-defined when the above pre-condition is violated. +template +struct LosslessArithmeticConvertible + : public LosslessArithmeticConvertibleImpl< + GMOCK_KIND_OF_(From), From, GMOCK_KIND_OF_(To), To> {}; // NOLINT + +// This interface knows how to report a Google Mock failure (either +// non-fatal or fatal). +class FailureReporterInterface { + public: + // The type of a failure (either non-fatal or fatal). + enum FailureType { + kNonfatal, kFatal + }; + + virtual ~FailureReporterInterface() {} + + // Reports a failure that occurred at the given source file location. + virtual void ReportFailure(FailureType type, const char* file, int line, + const string& message) = 0; +}; + +// Returns the failure reporter used by Google Mock. +GTEST_API_ FailureReporterInterface* GetFailureReporter(); + +// Asserts that condition is true; aborts the process with the given +// message if condition is false. We cannot use LOG(FATAL) or CHECK() +// as Google Mock might be used to mock the log sink itself. We +// inline this function to prevent it from showing up in the stack +// trace. +inline void Assert(bool condition, const char* file, int line, + const string& msg) { + if (!condition) { + GetFailureReporter()->ReportFailure(FailureReporterInterface::kFatal, + file, line, msg); + } +} +inline void Assert(bool condition, const char* file, int line) { + Assert(condition, file, line, "Assertion failed."); +} + +// Verifies that condition is true; generates a non-fatal failure if +// condition is false. +inline void Expect(bool condition, const char* file, int line, + const string& msg) { + if (!condition) { + GetFailureReporter()->ReportFailure(FailureReporterInterface::kNonfatal, + file, line, msg); + } +} +inline void Expect(bool condition, const char* file, int line) { + Expect(condition, file, line, "Expectation failed."); +} + +// Severity level of a log. +enum LogSeverity { + kInfo = 0, + kWarning = 1 +}; + +// Valid values for the --gmock_verbose flag. + +// All logs (informational and warnings) are printed. +const char kInfoVerbosity[] = "info"; +// Only warnings are printed. +const char kWarningVerbosity[] = "warning"; +// No logs are printed. +const char kErrorVerbosity[] = "error"; + +// Returns true iff a log with the given severity is visible according +// to the --gmock_verbose flag. +GTEST_API_ bool LogIsVisible(LogSeverity severity); + +// Prints the given message to stdout iff 'severity' >= the level +// specified by the --gmock_verbose flag. If stack_frames_to_skip >= +// 0, also prints the stack trace excluding the top +// stack_frames_to_skip frames. In opt mode, any positive +// stack_frames_to_skip is treated as 0, since we don't know which +// function calls will be inlined by the compiler and need to be +// conservative. +GTEST_API_ void Log(LogSeverity severity, + const string& message, + int stack_frames_to_skip); + +// TODO(wan@google.com): group all type utilities together. + +// Type traits. + +// is_reference::value is non-zero iff T is a reference type. +template struct is_reference : public false_type {}; +template struct is_reference : public true_type {}; + +// type_equals::value is non-zero iff T1 and T2 are the same type. +template struct type_equals : public false_type {}; +template struct type_equals : public true_type {}; + +// remove_reference::type removes the reference from type T, if any. +template struct remove_reference { typedef T type; }; // NOLINT +template struct remove_reference { typedef T type; }; // NOLINT + +// DecayArray::type turns an array type U[N] to const U* and preserves +// other types. Useful for saving a copy of a function argument. +template struct DecayArray { typedef T type; }; // NOLINT +template struct DecayArray { + typedef const T* type; +}; +// Sometimes people use arrays whose size is not available at the use site +// (e.g. extern const char kNamePrefix[]). This specialization covers that +// case. +template struct DecayArray { + typedef const T* type; +}; + +// Invalid() returns an invalid value of type T. This is useful +// when a value of type T is needed for compilation, but the statement +// will not really be executed (or we don't care if the statement +// crashes). +template +inline T Invalid() { + return const_cast::type&>( + *static_cast::type*>(NULL)); +} +template <> +inline void Invalid() {} + +// Given a raw type (i.e. having no top-level reference or const +// modifier) RawContainer that's either an STL-style container or a +// native array, class StlContainerView has the +// following members: +// +// - type is a type that provides an STL-style container view to +// (i.e. implements the STL container concept for) RawContainer; +// - const_reference is a type that provides a reference to a const +// RawContainer; +// - ConstReference(raw_container) returns a const reference to an STL-style +// container view to raw_container, which is a RawContainer. +// - Copy(raw_container) returns an STL-style container view of a +// copy of raw_container, which is a RawContainer. +// +// This generic version is used when RawContainer itself is already an +// STL-style container. +template +class StlContainerView { + public: + typedef RawContainer type; + typedef const type& const_reference; + + static const_reference ConstReference(const RawContainer& container) { + // Ensures that RawContainer is not a const type. + testing::StaticAssertTypeEq(); + return container; + } + static type Copy(const RawContainer& container) { return container; } +}; + +// This specialization is used when RawContainer is a native array type. +template +class StlContainerView { + public: + typedef GTEST_REMOVE_CONST_(Element) RawElement; + typedef internal::NativeArray type; + // NativeArray can represent a native array either by value or by + // reference (selected by a constructor argument), so 'const type' + // can be used to reference a const native array. We cannot + // 'typedef const type& const_reference' here, as that would mean + // ConstReference() has to return a reference to a local variable. + typedef const type const_reference; + + static const_reference ConstReference(const Element (&array)[N]) { + // Ensures that Element is not a const type. + testing::StaticAssertTypeEq(); +#if GTEST_OS_SYMBIAN + // The Nokia Symbian compiler confuses itself in template instantiation + // for this call without the cast to Element*: + // function call '[testing::internal::NativeArray].NativeArray( + // {lval} const char *[4], long, testing::internal::RelationToSource)' + // does not match + // 'testing::internal::NativeArray::NativeArray( + // char *const *, unsigned int, testing::internal::RelationToSource)' + // (instantiating: 'testing::internal::ContainsMatcherImpl + // ::Matches(const char * (&)[4]) const') + // (instantiating: 'testing::internal::StlContainerView:: + // ConstReference(const char * (&)[4])') + // (and though the N parameter type is mismatched in the above explicit + // conversion of it doesn't help - only the conversion of the array). + return type(const_cast(&array[0]), N, kReference); +#else + return type(array, N, kReference); +#endif // GTEST_OS_SYMBIAN + } + static type Copy(const Element (&array)[N]) { +#if GTEST_OS_SYMBIAN + return type(const_cast(&array[0]), N, kCopy); +#else + return type(array, N, kCopy); +#endif // GTEST_OS_SYMBIAN + } +}; + +// This specialization is used when RawContainer is a native array +// represented as a (pointer, size) tuple. +template +class StlContainerView< ::std::tr1::tuple > { + public: + typedef GTEST_REMOVE_CONST_( + typename internal::PointeeOf::type) RawElement; + typedef internal::NativeArray type; + typedef const type const_reference; + + static const_reference ConstReference( + const ::std::tr1::tuple& array) { + using ::std::tr1::get; + return type(get<0>(array), get<1>(array), kReference); + } + static type Copy(const ::std::tr1::tuple& array) { + using ::std::tr1::get; + return type(get<0>(array), get<1>(array), kCopy); + } +}; + +// The following specialization prevents the user from instantiating +// StlContainer with a reference type. +template class StlContainerView; + +// A type transform to remove constness from the first part of a pair. +// Pairs like that are used as the value_type of associative containers, +// and this transform produces a similar but assignable pair. +template +struct RemoveConstFromKey { + typedef T type; +}; + +// Partially specialized to remove constness from std::pair. +template +struct RemoveConstFromKey > { + typedef std::pair type; +}; + +// Mapping from booleans to types. Similar to boost::bool_ and +// std::integral_constant. +template +struct BooleanConstant {}; + +} // namespace internal +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ + +namespace testing { + +// To implement an action Foo, define: +// 1. a class FooAction that implements the ActionInterface interface, and +// 2. a factory function that creates an Action object from a +// const FooAction*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Action objects can now be copied like plain values. + +namespace internal { + +template +class ActionAdaptor; + +// BuiltInDefaultValue::Get() returns the "built-in" default +// value for type T, which is NULL when T is a pointer type, 0 when T +// is a numeric type, false when T is bool, or "" when T is string or +// std::string. For any other type T, this value is undefined and the +// function will abort the process. +template +class BuiltInDefaultValue { + public: + // This function returns true iff type T has a built-in default value. + static bool Exists() { return false; } + static T Get() { + Assert(false, __FILE__, __LINE__, + "Default action undefined for the function return type."); + return internal::Invalid(); + // The above statement will never be reached, but is required in + // order for this function to compile. + } +}; + +// This partial specialization says that we use the same built-in +// default value for T and const T. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return BuiltInDefaultValue::Exists(); } + static T Get() { return BuiltInDefaultValue::Get(); } +}; + +// This partial specialization defines the default values for pointer +// types. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return true; } + static T* Get() { return NULL; } +}; + +// The following specializations define the default values for +// specific types we care about. +#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ + template <> \ + class BuiltInDefaultValue { \ + public: \ + static bool Exists() { return true; } \ + static type Get() { return value; } \ + } + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT +#if GTEST_HAS_GLOBAL_STRING +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, ""); +#endif // GTEST_HAS_GLOBAL_STRING +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); + +// There's no need for a default action for signed wchar_t, as that +// type is the same as wchar_t for gcc, and invalid for MSVC. +// +// There's also no need for a default action for unsigned wchar_t, as +// that type is the same as unsigned int for gcc, and invalid for +// MSVC. +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT +#endif + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); + +#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ + +} // namespace internal + +// When an unexpected function call is encountered, Google Mock will +// let it return a default value if the user has specified one for its +// return type, or if the return type has a built-in default value; +// otherwise Google Mock won't know what value to return and will have +// to abort the process. +// +// The DefaultValue class allows a user to specify the +// default value for a type T that is both copyable and publicly +// destructible (i.e. anything that can be used as a function return +// type). The usage is: +// +// // Sets the default value for type T to be foo. +// DefaultValue::Set(foo); +template +class DefaultValue { + public: + // Sets the default value for type T; requires T to be + // copy-constructable and have a public destructor. + static void Set(T x) { + delete value_; + value_ = new T(x); + } + + // Unsets the default value for type T. + static void Clear() { + delete value_; + value_ = NULL; + } + + // Returns true iff the user has set the default value for type T. + static bool IsSet() { return value_ != NULL; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T Get() { + return value_ == NULL ? + internal::BuiltInDefaultValue::Get() : *value_; + } + + private: + static const T* value_; +}; + +// This partial specialization allows a user to set default values for +// reference types. +template +class DefaultValue { + public: + // Sets the default value for type T&. + static void Set(T& x) { // NOLINT + address_ = &x; + } + + // Unsets the default value for type T&. + static void Clear() { + address_ = NULL; + } + + // Returns true iff the user has set the default value for type T&. + static bool IsSet() { return address_ != NULL; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T& if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T& Get() { + return address_ == NULL ? + internal::BuiltInDefaultValue::Get() : *address_; + } + + private: + static T* address_; +}; + +// This specialization allows DefaultValue::Get() to +// compile. +template <> +class DefaultValue { + public: + static bool Exists() { return true; } + static void Get() {} +}; + +// Points to the user-set default value for type T. +template +const T* DefaultValue::value_ = NULL; + +// Points to the user-set default value for type T&. +template +T* DefaultValue::address_ = NULL; + +// Implement this interface to define an action for function type F. +template +class ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + ActionInterface() {} + virtual ~ActionInterface() {} + + // Performs the action. This method is not const, as in general an + // action can have side effects and be stateful. For example, a + // get-the-next-element-from-the-collection action will need to + // remember the current element. + virtual Result Perform(const ArgumentTuple& args) = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface); +}; + +// An Action is a copyable and IMMUTABLE (except by assignment) +// object that represents an action to be taken when a mock function +// of type F is called. The implementation of Action is just a +// linked_ptr to const ActionInterface, so copying is fairly cheap. +// Don't inherit from Action! +// +// You can view an object implementing ActionInterface as a +// concrete action (including its current state), and an Action +// object as a handle to it. +template +class Action { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + // Constructs a null Action. Needed for storing Action objects in + // STL containers. + Action() : impl_(NULL) {} + + // Constructs an Action from its implementation. A NULL impl is + // used to represent the "do-default" action. + explicit Action(ActionInterface* impl) : impl_(impl) {} + + // Copy constructor. + Action(const Action& action) : impl_(action.impl_) {} + + // This constructor allows us to turn an Action object into an + // Action, as long as F's arguments can be implicitly converted + // to Func's and Func's return type can be implicitly converted to + // F's. + template + explicit Action(const Action& action); + + // Returns true iff this is the DoDefault() action. + bool IsDoDefault() const { return impl_.get() == NULL; } + + // Performs the action. Note that this method is const even though + // the corresponding method in ActionInterface is not. The reason + // is that a const Action means that it cannot be re-bound to + // another concrete action, not that the concrete action it binds to + // cannot change state. (Think of the difference between a const + // pointer and a pointer to const.) + Result Perform(const ArgumentTuple& args) const { + internal::Assert( + !IsDoDefault(), __FILE__, __LINE__, + "You are using DoDefault() inside a composite action like " + "DoAll() or WithArgs(). This is not supported for technical " + "reasons. Please instead spell out the default action, or " + "assign the default action to an Action variable and use " + "the variable in various places."); + return impl_->Perform(args); + } + + private: + template + friend class internal::ActionAdaptor; + + internal::linked_ptr > impl_; +}; + +// The PolymorphicAction class template makes it easy to implement a +// polymorphic action (i.e. an action that can be used in mock +// functions of than one type, e.g. Return()). +// +// To define a polymorphic action, a user first provides a COPYABLE +// implementation class that has a Perform() method template: +// +// class FooAction { +// public: +// template +// Result Perform(const ArgumentTuple& args) const { +// // Processes the arguments and returns a result, using +// // tr1::get(args) to get the N-th (0-based) argument in the tuple. +// } +// ... +// }; +// +// Then the user creates the polymorphic action using +// MakePolymorphicAction(object) where object has type FooAction. See +// the definition of Return(void) and SetArgumentPointee(value) for +// complete examples. +template +class PolymorphicAction { + public: + explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} + + template + operator Action() const { + return Action(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + virtual Result Perform(const ArgumentTuple& args) { + return impl_.template Perform(args); + } + + private: + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); + }; + + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(PolymorphicAction); +}; + +// Creates an Action from its implementation and returns it. The +// created Action object owns the implementation. +template +Action MakeAction(ActionInterface* impl) { + return Action(impl); +} + +// Creates a polymorphic action from its implementation. This is +// easier to use than the PolymorphicAction constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicAction(foo); +// vs +// PolymorphicAction(foo); +template +inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { + return PolymorphicAction(impl); +} + +namespace internal { + +// Allows an Action object to pose as an Action, as long as F2 +// and F1 are compatible. +template +class ActionAdaptor : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit ActionAdaptor(const Action& from) : impl_(from.impl_) {} + + virtual Result Perform(const ArgumentTuple& args) { + return impl_->Perform(args); + } + + private: + const internal::linked_ptr > impl_; + + GTEST_DISALLOW_ASSIGN_(ActionAdaptor); +}; + +// Implements the polymorphic Return(x) action, which can be used in +// any function that returns the type of x, regardless of the argument +// types. +// +// Note: The value passed into Return must be converted into +// Function::Result when this action is cast to Action rather than +// when that action is performed. This is important in scenarios like +// +// MOCK_METHOD1(Method, T(U)); +// ... +// { +// Foo foo; +// X x(&foo); +// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); +// } +// +// In the example above the variable x holds reference to foo which leaves +// scope and gets destroyed. If copying X just copies a reference to foo, +// that copy will be left with a hanging reference. If conversion to T +// makes a copy of foo, the above code is safe. To support that scenario, we +// need to make sure that the type conversion happens inside the EXPECT_CALL +// statement, and conversion of the result of Return to Action is a +// good place for that. +// +template +class ReturnAction { + public: + // Constructs a ReturnAction object from the value to be returned. + // 'value' is passed by value instead of by const reference in order + // to allow Return("string literal") to compile. + explicit ReturnAction(R value) : value_(value) {} + + // This template type conversion operator allows Return(x) to be + // used in ANY function that returns x's type. + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename Function::Result Result; + GTEST_COMPILE_ASSERT_( + !internal::is_reference::value, + use_ReturnRef_instead_of_Return_to_return_a_reference); + return Action(new Impl(value_)); + } + + private: + // Implements the Return(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + // The implicit cast is necessary when Result has more than one + // single-argument constructor (e.g. Result is std::vector) and R + // has a type conversion operator template. In that case, value_(value) + // won't compile as the compiler doesn't known which constructor of + // Result to call. ImplicitCast_ forces the compiler to convert R to + // Result without considering explicit constructors, thus resolving the + // ambiguity. value_ is then initialized using its copy constructor. + explicit Impl(R value) + : value_(::testing::internal::ImplicitCast_(value)) {} + + virtual Result Perform(const ArgumentTuple&) { return value_; } + + private: + GTEST_COMPILE_ASSERT_(!internal::is_reference::value, + Result_cannot_be_a_reference_type); + Result value_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + R value_; + + GTEST_DISALLOW_ASSIGN_(ReturnAction); +}; + +// Implements the ReturnNull() action. +class ReturnNullAction { + public: + // Allows ReturnNull() to be used in any pointer-returning function. + template + static Result Perform(const ArgumentTuple&) { + GTEST_COMPILE_ASSERT_(internal::is_pointer::value, + ReturnNull_can_be_used_to_return_a_pointer_only); + return NULL; + } +}; + +// Implements the Return() action. +class ReturnVoidAction { + public: + // Allows Return() to be used in any void-returning function. + template + static void Perform(const ArgumentTuple&) { + CompileAssertTypesEqual(); + } +}; + +// Implements the polymorphic ReturnRef(x) action, which can be used +// in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefAction { + public: + // Constructs a ReturnRefAction object from the reference to be returned. + explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT + + // This template type conversion operator allows ReturnRef(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRef(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_(internal::is_reference::value, + use_Return_instead_of_ReturnRef_to_return_a_value); + return Action(new Impl(ref_)); + } + + private: + // Implements the ReturnRef(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(T& ref) : ref_(ref) {} // NOLINT + + virtual Result Perform(const ArgumentTuple&) { + return ref_; + } + + private: + T& ref_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + T& ref_; + + GTEST_DISALLOW_ASSIGN_(ReturnRefAction); +}; + +// Implements the polymorphic ReturnRefOfCopy(x) action, which can be +// used in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefOfCopyAction { + public: + // Constructs a ReturnRefOfCopyAction object from the reference to + // be returned. + explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT + + // This template type conversion operator allows ReturnRefOfCopy(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRefOfCopy(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_( + internal::is_reference::value, + use_Return_instead_of_ReturnRefOfCopy_to_return_a_value); + return Action(new Impl(value_)); + } + + private: + // Implements the ReturnRefOfCopy(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const T& value) : value_(value) {} // NOLINT + + virtual Result Perform(const ArgumentTuple&) { + return value_; + } + + private: + T value_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const T value_; + + GTEST_DISALLOW_ASSIGN_(ReturnRefOfCopyAction); +}; + +// Implements the polymorphic DoDefault() action. +class DoDefaultAction { + public: + // This template type conversion operator allows DoDefault() to be + // used in any function. + template + operator Action() const { return Action(NULL); } +}; + +// Implements the Assign action to set a given pointer referent to a +// particular value. +template +class AssignAction { + public: + AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} + + template + void Perform(const ArgumentTuple& /* args */) const { + *ptr_ = value_; + } + + private: + T1* const ptr_; + const T2 value_; + + GTEST_DISALLOW_ASSIGN_(AssignAction); +}; + +#if !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetErrnoAndReturn action to simulate return from +// various system calls and libc functions. +template +class SetErrnoAndReturnAction { + public: + SetErrnoAndReturnAction(int errno_value, T result) + : errno_(errno_value), + result_(result) {} + template + Result Perform(const ArgumentTuple& /* args */) const { + errno = errno_; + return result_; + } + + private: + const int errno_; + const T result_; + + GTEST_DISALLOW_ASSIGN_(SetErrnoAndReturnAction); +}; + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetArgumentPointee(x) action for any function +// whose N-th argument (0-based) is a pointer to x's type. The +// template parameter kIsProto is true iff type A is ProtocolMessage, +// proto2::Message, or a sub-class of those. +template +class SetArgumentPointeeAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'value'. + explicit SetArgumentPointeeAction(const A& value) : value_(value) {} + + template + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual(); + *::std::tr1::get(args) = value_; + } + + private: + const A value_; + + GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); +}; + +template +class SetArgumentPointeeAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'proto'. Both ProtocolMessage and + // proto2::Message have the CopyFrom() method, so the same + // implementation works for both. + explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) { + proto_->CopyFrom(proto); + } + + template + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual(); + ::std::tr1::get(args)->CopyFrom(*proto_); + } + + private: + const internal::linked_ptr proto_; + + GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); +}; + +// Implements the InvokeWithoutArgs(f) action. The template argument +// FunctionImpl is the implementation type of f, which can be either a +// function pointer or a functor. InvokeWithoutArgs(f) can be used as an +// Action as long as f's type is compatible with F (i.e. f can be +// assigned to a tr1::function). +template +class InvokeWithoutArgsAction { + public: + // The c'tor makes a copy of function_impl (either a function + // pointer or a functor). + explicit InvokeWithoutArgsAction(FunctionImpl function_impl) + : function_impl_(function_impl) {} + + // Allows InvokeWithoutArgs(f) to be used as any action whose type is + // compatible with f. + template + Result Perform(const ArgumentTuple&) { return function_impl_(); } + + private: + FunctionImpl function_impl_; + + GTEST_DISALLOW_ASSIGN_(InvokeWithoutArgsAction); +}; + +// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. +template +class InvokeMethodWithoutArgsAction { + public: + InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr) + : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {} + + template + Result Perform(const ArgumentTuple&) const { + return (obj_ptr_->*method_ptr_)(); + } + + private: + Class* const obj_ptr_; + const MethodPtr method_ptr_; + + GTEST_DISALLOW_ASSIGN_(InvokeMethodWithoutArgsAction); +}; + +// Implements the IgnoreResult(action) action. +template +class IgnoreResultAction { + public: + explicit IgnoreResultAction(const A& action) : action_(action) {} + + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename internal::Function::Result Result; + + // Asserts at compile time that F returns void. + CompileAssertTypesEqual(); + + return Action(new Impl(action_)); + } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const A& action) : action_(action) {} + + virtual void Perform(const ArgumentTuple& args) { + // Performs the action and ignores its result. + action_.Perform(args); + } + + private: + // Type OriginalFunction is the same as F except that its return + // type is IgnoredValue. + typedef typename internal::Function::MakeResultIgnoredValue + OriginalFunction; + + const Action action_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const A action_; + + GTEST_DISALLOW_ASSIGN_(IgnoreResultAction); +}; + +// A ReferenceWrapper object represents a reference to type T, +// which can be either const or not. It can be explicitly converted +// from, and implicitly converted to, a T&. Unlike a reference, +// ReferenceWrapper can be copied and can survive template type +// inference. This is used to support by-reference arguments in the +// InvokeArgument(...) action. The idea was from "reference +// wrappers" in tr1, which we don't have in our source tree yet. +template +class ReferenceWrapper { + public: + // Constructs a ReferenceWrapper object from a T&. + explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {} // NOLINT + + // Allows a ReferenceWrapper object to be implicitly converted to + // a T&. + operator T&() const { return *pointer_; } + private: + T* pointer_; +}; + +// Allows the expression ByRef(x) to be printed as a reference to x. +template +void PrintTo(const ReferenceWrapper& ref, ::std::ostream* os) { + T& value = ref; + UniversalPrinter::Print(value, os); +} + +// Does two actions sequentially. Used for implementing the DoAll(a1, +// a2, ...) action. +template +class DoBothAction { + public: + DoBothAction(Action1 action1, Action2 action2) + : action1_(action1), action2_(action2) {} + + // This template type conversion operator allows DoAll(a1, ..., a_n) + // to be used in ANY function of compatible type. + template + operator Action() const { + return Action(new Impl(action1_, action2_)); + } + + private: + // Implements the DoAll(...) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + typedef typename Function::MakeResultVoid VoidResult; + + Impl(const Action& action1, const Action& action2) + : action1_(action1), action2_(action2) {} + + virtual Result Perform(const ArgumentTuple& args) { + action1_.Perform(args); + return action2_.Perform(args); + } + + private: + const Action action1_; + const Action action2_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + Action1 action1_; + Action2 action2_; + + GTEST_DISALLOW_ASSIGN_(DoBothAction); +}; + +} // namespace internal + +// An Unused object can be implicitly constructed from ANY value. +// This is handy when defining actions that ignore some or all of the +// mock function arguments. For example, given +// +// MOCK_METHOD3(Foo, double(const string& label, double x, double y)); +// MOCK_METHOD3(Bar, double(int index, double x, double y)); +// +// instead of +// +// double DistanceToOriginWithLabel(const string& label, double x, double y) { +// return sqrt(x*x + y*y); +// } +// double DistanceToOriginWithIndex(int index, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXEPCT_CALL(mock, Foo("abc", _, _)) +// .WillOnce(Invoke(DistanceToOriginWithLabel)); +// EXEPCT_CALL(mock, Bar(5, _, _)) +// .WillOnce(Invoke(DistanceToOriginWithIndex)); +// +// you could write +// +// // We can declare any uninteresting argument as Unused. +// double DistanceToOrigin(Unused, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXEPCT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); +// EXEPCT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); +typedef internal::IgnoredValue Unused; + +// This constructor allows us to turn an Action object into an +// Action, as long as To's arguments can be implicitly converted +// to From's and From's return type cann be implicitly converted to +// To's. +template +template +Action::Action(const Action& from) + : impl_(new internal::ActionAdaptor(from)) {} + +// Creates an action that returns 'value'. 'value' is passed by value +// instead of const reference - otherwise Return("string literal") +// will trigger a compiler error about using array as initializer. +template +internal::ReturnAction Return(R value) { + return internal::ReturnAction(value); +} + +// Creates an action that returns NULL. +inline PolymorphicAction ReturnNull() { + return MakePolymorphicAction(internal::ReturnNullAction()); +} + +// Creates an action that returns from a void function. +inline PolymorphicAction Return() { + return MakePolymorphicAction(internal::ReturnVoidAction()); +} + +// Creates an action that returns the reference to a variable. +template +inline internal::ReturnRefAction ReturnRef(R& x) { // NOLINT + return internal::ReturnRefAction(x); +} + +// Creates an action that returns the reference to a copy of the +// argument. The copy is created when the action is constructed and +// lives as long as the action. +template +inline internal::ReturnRefOfCopyAction ReturnRefOfCopy(const R& x) { + return internal::ReturnRefOfCopyAction(x); +} + +// Creates an action that does the default action for the give mock function. +inline internal::DoDefaultAction DoDefault() { + return internal::DoDefaultAction(); +} + +// Creates an action that sets the variable pointed by the N-th +// (0-based) function argument to 'value'. +template +PolymorphicAction< + internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value> > +SetArgPointee(const T& x) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value>(x)); +} + +#if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN) +// This overload allows SetArgPointee() to accept a string literal. +// GCC prior to the version 4.0 and Symbian C++ compiler cannot distinguish +// this overload from the templated version and emit a compile error. +template +PolymorphicAction< + internal::SetArgumentPointeeAction > +SetArgPointee(const char* p) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, const char*, false>(p)); +} + +template +PolymorphicAction< + internal::SetArgumentPointeeAction > +SetArgPointee(const wchar_t* p) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, const wchar_t*, false>(p)); +} +#endif + +// The following version is DEPRECATED. +template +PolymorphicAction< + internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value> > +SetArgumentPointee(const T& x) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value>(x)); +} + +// Creates an action that sets a pointer referent to a given value. +template +PolymorphicAction > Assign(T1* ptr, T2 val) { + return MakePolymorphicAction(internal::AssignAction(ptr, val)); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +// Creates an action that sets errno and returns the appropriate error. +template +PolymorphicAction > +SetErrnoAndReturn(int errval, T result) { + return MakePolymorphicAction( + internal::SetErrnoAndReturnAction(errval, result)); +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Various overloads for InvokeWithoutArgs(). + +// Creates an action that invokes 'function_impl' with no argument. +template +PolymorphicAction > +InvokeWithoutArgs(FunctionImpl function_impl) { + return MakePolymorphicAction( + internal::InvokeWithoutArgsAction(function_impl)); +} + +// Creates an action that invokes the given method on the given object +// with no argument. +template +PolymorphicAction > +InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) { + return MakePolymorphicAction( + internal::InvokeMethodWithoutArgsAction( + obj_ptr, method_ptr)); +} + +// Creates an action that performs an_action and throws away its +// result. In other words, it changes the return type of an_action to +// void. an_action MUST NOT return void, or the code won't compile. +template +inline internal::IgnoreResultAction IgnoreResult(const A& an_action) { + return internal::IgnoreResultAction(an_action); +} + +// Creates a reference wrapper for the given L-value. If necessary, +// you can explicitly specify the type of the reference. For example, +// suppose 'derived' is an object of type Derived, ByRef(derived) +// would wrap a Derived&. If you want to wrap a const Base& instead, +// where Base is a base class of Derived, just write: +// +// ByRef(derived) +template +inline internal::ReferenceWrapper ByRef(T& l_value) { // NOLINT + return internal::ReferenceWrapper(l_value); +} + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used cardinalities. More +// cardinalities can be defined by the user implementing the +// CardinalityInterface interface if necessary. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ + +#include +#include // NOLINT + +namespace testing { + +// To implement a cardinality Foo, define: +// 1. a class FooCardinality that implements the +// CardinalityInterface interface, and +// 2. a factory function that creates a Cardinality object from a +// const FooCardinality*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Cardinality objects can now be copied like plain values. + +// The implementation of a cardinality. +class CardinalityInterface { + public: + virtual ~CardinalityInterface() {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return 0; } + virtual int ConservativeUpperBound() const { return INT_MAX; } + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; + +// A Cardinality is a copyable and IMMUTABLE (except by assignment) +// object that specifies how many times a mock function is expected to +// be called. The implementation of Cardinality is just a linked_ptr +// to const CardinalityInterface, so copying is fairly cheap. +// Don't inherit from Cardinality! +class GTEST_API_ Cardinality { + public: + // Constructs a null cardinality. Needed for storing Cardinality + // objects in STL containers. + Cardinality() {} + + // Constructs a Cardinality from its implementation. + explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } + int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } + + // Returns true iff call_count calls will satisfy this cardinality. + bool IsSatisfiedByCallCount(int call_count) const { + return impl_->IsSatisfiedByCallCount(call_count); + } + + // Returns true iff call_count calls will saturate this cardinality. + bool IsSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count); + } + + // Returns true iff call_count calls will over-saturate this + // cardinality, i.e. exceed the maximum number of allowed calls. + bool IsOverSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count) && + !impl_->IsSatisfiedByCallCount(call_count); + } + + // Describes self to an ostream + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the given actual call count to an ostream. + static void DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os); + + private: + internal::linked_ptr impl_; +}; + +// Creates a cardinality that allows at least n calls. +GTEST_API_ Cardinality AtLeast(int n); + +// Creates a cardinality that allows at most n calls. +GTEST_API_ Cardinality AtMost(int n); + +// Creates a cardinality that allows any number of calls. +GTEST_API_ Cardinality AnyNumber(); + +// Creates a cardinality that allows between min and max calls. +GTEST_API_ Cardinality Between(int min, int max); + +// Creates a cardinality that allows exactly n calls. +GTEST_API_ Cardinality Exactly(int n); + +// Creates a cardinality from its implementation. +inline Cardinality MakeCardinality(const CardinalityInterface* c) { + return Cardinality(c); +} + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + + +namespace testing { +namespace internal { + +// InvokeHelper knows how to unpack an N-tuple and invoke an N-ary +// function or method with the unpacked values, where F is a function +// type that takes N arguments. +template +class InvokeHelper; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple<>&) { + return function(); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple<>&) { + return (obj_ptr->*method_ptr)(); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), + get<8>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + get<9>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), + get<8>(args), get<9>(args)); + } +}; + +// CallableHelper has static methods for invoking "callables", +// i.e. function pointers and functors. It uses overloading to +// provide a uniform interface for invoking different kinds of +// callables. In particular, you can use: +// +// CallableHelper::Call(callable, a1, a2, ..., an) +// +// to invoke an n-ary callable, where R is its return type. If an +// argument, say a2, needs to be passed by reference, you should write +// ByRef(a2) instead of a2 in the above expression. +template +class CallableHelper { + public: + // Calls a nullary callable. + template + static R Call(Function function) { return function(); } + + // Calls a unary callable. + + // We deliberately pass a1 by value instead of const reference here + // in case it is a C-string literal. If we had declared the + // parameter as 'const A1& a1' and write Call(function, "Hi"), the + // compiler would've thought A1 is 'char[3]', which causes trouble + // when you need to copy a value of type A1. By declaring the + // parameter as 'A1 a1', the compiler will correctly infer that A1 + // is 'const char*' when it sees Call(function, "Hi"). + // + // Since this function is defined inline, the compiler can get rid + // of the copying of the arguments. Therefore the performance won't + // be hurt. + template + static R Call(Function function, A1 a1) { return function(a1); } + + // Calls a binary callable. + template + static R Call(Function function, A1 a1, A2 a2) { + return function(a1, a2); + } + + // Calls a ternary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3) { + return function(a1, a2, a3); + } + + // Calls a 4-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4) { + return function(a1, a2, a3, a4); + } + + // Calls a 5-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + return function(a1, a2, a3, a4, a5); + } + + // Calls a 6-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + return function(a1, a2, a3, a4, a5, a6); + } + + // Calls a 7-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7) { + return function(a1, a2, a3, a4, a5, a6, a7); + } + + // Calls a 8-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8) { + return function(a1, a2, a3, a4, a5, a6, a7, a8); + } + + // Calls a 9-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9) { + return function(a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + // Calls a 10-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9, A10 a10) { + return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } +}; // class CallableHelper + +// An INTERNAL macro for extracting the type of a tuple field. It's +// subject to change without notice - DO NOT USE IN USER CODE! +#define GMOCK_FIELD_(Tuple, N) \ + typename ::std::tr1::tuple_element::type + +// SelectArgs::type is the +// type of an n-ary function whose i-th (1-based) argument type is the +// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple +// type, and whose return type is Result. For example, +// SelectArgs, 0, 3>::type +// is int(bool, long). +// +// SelectArgs::Select(args) +// returns the selected fields (k1, k2, ..., k_n) of args as a tuple. +// For example, +// SelectArgs, 2, 0>::Select( +// ::std::tr1::make_tuple(true, 'a', 2.5)) +// returns ::std::tr1::tuple (2.5, true). +// +// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be +// in the range [0, 10]. Duplicates are allowed and they don't have +// to be in an ascending or descending order. + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9), + GMOCK_FIELD_(ArgumentTuple, k10)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& /* args */) { + using ::std::tr1::get; + return SelectedArgs(); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args), get(args)); + } +}; + +#undef GMOCK_FIELD_ + +// Implements the WithArgs action. +template +class WithArgsAction { + public: + explicit WithArgsAction(const InnerAction& action) : action_(action) {} + + template + operator Action() const { return MakeAction(new Impl(action_)); } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const InnerAction& action) : action_(action) {} + + virtual Result Perform(const ArgumentTuple& args) { + return action_.Perform(SelectArgs::Select(args)); + } + + private: + typedef typename SelectArgs::type InnerFunctionType; + + Action action_; + }; + + const InnerAction action_; + + GTEST_DISALLOW_ASSIGN_(WithArgsAction); +}; + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: + static Result Perform(Impl* impl, const ::std::tr1::tuple<>& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl<>(args, ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + get<9>(args)); + } +}; + +} // namespace internal + +// Various overloads for Invoke(). + +// WithArgs(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. C++ doesn't support default arguments for +// function templates, so we have to overload it. +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. +template +inline internal::DoBothAction +DoAll(Action1 a1, Action2 a2) { + return internal::DoBothAction(a1, a2); +} + +template +inline internal::DoBothAction > +DoAll(Action1 a1, Action2 a2, Action3 a3) { + return DoAll(a1, DoAll(a2, a3)); +} + +template +inline internal::DoBothAction > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) { + return DoAll(a1, DoAll(a2, a3, a4)); +} + +template +inline internal::DoBothAction > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) { + return DoAll(a1, DoAll(a2, a3, a4, a5)); +} + +template +inline internal::DoBothAction > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6)); +} + +template +inline internal::DoBothAction > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7)); +} + +template +inline internal::DoBothAction > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8)); +} + +template +inline internal::DoBothAction > > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8, Action9 a9) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9)); +} + +template +inline internal::DoBothAction > > > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8, Action9 a9, Action10 a10) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10)); +} + +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using ACTION*() inside +// a function. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_, \ + arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_, \ + arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_, \ + arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_, \ + arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_, \ + arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_, \ + arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_, \ + arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_, \ + arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_, \ + arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_, \ + arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_ + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(std::tr1::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. +#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0 +#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) kind0 name0, kind1 name1 +#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) kind0 name0, kind1 name1, kind2 name2 +#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3 +#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \ + kind2 name2, kind3 name3, kind4 name4 +#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5 +#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \ + kind5 name5, kind6 name6 +#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \ + kind4 name4, kind5 name5, kind6 name6, kind7 name7 +#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \ + kind8 name8 +#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \ + kind6 name6, kind7 name7, kind8 name8, kind9 name9 + +// Lists the template parameters. +#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0 +#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) name0, name1 +#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) name0, name1, name2 +#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) name0, name1, name2, name3 +#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \ + name4 +#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \ + name2, name3, name4, name5 +#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) name0, name1, name2, name3, name4, name5, name6 +#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7 +#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \ + name6, name7, name8 +#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \ + name3, name4, name5, name6, name7, name8, name9 + +// Declares the types of value parameters. +#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \ + typename p0##_type, typename p1##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \ + typename p0##_type, typename p1##_type, typename p2##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type, typename p8##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \ + typename p2##_type, typename p3##_type, typename p4##_type, \ + typename p5##_type, typename p6##_type, typename p7##_type, \ + typename p8##_type, typename p9##_type + +// Initializes the value parameters. +#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\ + () +#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\ + (p0##_type gmock_p0) : p0(gmock_p0) +#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\ + (p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), p1(gmock_p1) +#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\ + (p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) +#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3) +#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) +#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) +#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) +#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) +#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) +#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8), p9(gmock_p9) + +// Declares the fields for storing the value parameters. +#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0; +#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \ + p1##_type p1; +#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \ + p1##_type p1; p2##_type p2; +#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \ + p1##_type p1; p2##_type p2; p3##_type p3; +#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; +#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; +#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; +#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; p7##_type p7; +#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; +#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \ + p9##_type p9; + +// Lists the value parameters. +#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0 +#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1 +#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2 +#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3 +#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \ + p2, p3, p4 +#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \ + p1, p2, p3, p4, p5 +#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0, p1, p2, p3, p4, p5, p6 +#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0, p1, p2, p3, p4, p5, p6, p7 +#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8 +#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 + +// Lists the value parameter types. +#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \ + p1##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \ + p1##_type, p2##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + p0##_type, p1##_type, p2##_type, p3##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \ + p6##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type, p9##_type + +// Declares the value parameters. +#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0 +#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \ + p1##_type p1 +#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \ + p1##_type p1, p2##_type p2 +#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3 +#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4 +#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5 +#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6 +#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6, p7##_type p7 +#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8 +#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9 + +// The suffix of the class template implementing the action template. +#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P +#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2 +#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3 +#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4 +#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5 +#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6 +#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7 +#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) P8 +#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) P9 +#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) P10 + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION(name)\ + class name##Action {\ + public:\ + name##Action() {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl() {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl());\ + }\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##Action);\ + };\ + inline name##Action name() {\ + return name##Action();\ + }\ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##Action::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P(name, p0)\ + template \ + class name##ActionP {\ + public:\ + name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0));\ + }\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP);\ + };\ + template \ + inline name##ActionP name(p0##_type p0) {\ + return name##ActionP(p0);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P2(name, p0, p1)\ + template \ + class name##ActionP2 {\ + public:\ + name##ActionP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP2);\ + };\ + template \ + inline name##ActionP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##ActionP2(p0, p1);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP2::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P3(name, p0, p1, p2)\ + template \ + class name##ActionP3 {\ + public:\ + name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP3);\ + };\ + template \ + inline name##ActionP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##ActionP3(p0, p1, p2);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP3::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P4(name, p0, p1, p2, p3)\ + template \ + class name##ActionP4 {\ + public:\ + name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP4);\ + };\ + template \ + inline name##ActionP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##ActionP4(p0, p1, \ + p2, p3);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP4::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P5(name, p0, p1, p2, p3, p4)\ + template \ + class name##ActionP5 {\ + public:\ + name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \ + p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP5);\ + };\ + template \ + inline name##ActionP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##ActionP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP5::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\ + template \ + class name##ActionP6 {\ + public:\ + name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP6);\ + };\ + template \ + inline name##ActionP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##ActionP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP6::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\ + template \ + class name##ActionP7 {\ + public:\ + name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \ + p6(gmock_p6) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP7);\ + };\ + template \ + inline name##ActionP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##ActionP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP7::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\ + template \ + class name##ActionP8 {\ + public:\ + name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \ + p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \ + p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP8);\ + };\ + template \ + inline name##ActionP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##ActionP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP8::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\ + template \ + class name##ActionP9 {\ + public:\ + name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP9);\ + };\ + template \ + inline name##ActionP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##ActionP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP9::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\ + template \ + class name##ActionP10 {\ + public:\ + name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP10);\ + };\ + template \ + inline name##ActionP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##ActionP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP10::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +namespace testing { + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + return internal::CallableHelper::Call( + ::std::tr1::get(args)); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p0)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_2_VALUE_PARAMS(p0, p1)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + return new T(); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(p0)) { + return new T(p0); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_2_VALUE_PARAMS(p0, p1)) { + return new T(p0, p1); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + return new T(p0, p1, p2); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + return new T(p0, p1, p2, p3); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + return new T(p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + return new T(p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + return new T(p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +// This file was GENERATED by command: +// pump.py gmock-generated-function-mockers.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements the ON_CALL() and EXPECT_CALL() macros. +// +// A user can use the ON_CALL() macro to specify the default action of +// a mock method. The syntax is: +// +// ON_CALL(mock_object, Method(argument-matchers)) +// .With(multi-argument-matcher) +// .WillByDefault(action); +// +// where the .With() clause is optional. +// +// A user can use the EXPECT_CALL() macro to specify an expectation on +// a mock method. The syntax is: +// +// EXPECT_CALL(mock_object, Method(argument-matchers)) +// .With(multi-argument-matchers) +// .Times(cardinality) +// .InSequence(sequences) +// .After(expectations) +// .WillOnce(action) +// .WillRepeatedly(action) +// .RetiresOnSaturation(); +// +// where all clauses are optional, and .InSequence()/.After()/ +// .WillOnce() can appear any number of times. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ + +#include +#include +#include +#include +#include + +#if GTEST_HAS_EXCEPTIONS +# include // NOLINT +#endif + +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used argument matchers. More +// matchers can be defined by the user implementing the +// MatcherInterface interface if necessary. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + +#include +#include +#include +#include +#include // NOLINT +#include +#include +#include +#include + + +#if GTEST_LANG_CXX11 +#include // NOLINT -- must be after gtest.h +#endif + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherImpl that implements the +// MatcherInterface interface, and +// 2. a factory function that creates a Matcher object from a +// FooMatcherImpl*. +// +// The two-level delegation design makes it possible to allow a user +// to write "v" instead of "Eq(v)" where a Matcher is expected, which +// is impossible if we pass matchers by pointers. It also eases +// ownership management as Matcher objects can now be copied like +// plain values. + +// MatchResultListener is an abstract class. Its << operator can be +// used by a matcher to explain why a value matches or doesn't match. +// +// TODO(wan@google.com): add method +// bool InterestedInWhy(bool result) const; +// to indicate whether the listener is interested in why the match +// result is 'result'. +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != NULL) + *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true iff the listener is interested in an explanation of + // the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != NULL; } + + private: + ::std::ostream* const stream_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); +}; + +inline MatchResultListener::~MatchResultListener() { +} + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() {} + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true iff the matcher matches x; also explains the match + // result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Mock) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +// A match result listener that stores the explanation in a string. +class StringMatchResultListener : public MatchResultListener { + public: + StringMatchResultListener() : MatchResultListener(&ss_) {} + + // Returns the explanation accumulated so far. + internal::string str() const { return ss_.str(); } + + // Clears the explanation accumulated so far. + void Clear() { ss_.str(""); } + + private: + ::std::stringstream ss_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener); +}; + +namespace internal { + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(NULL) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase { + public: + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + bool MatchAndExplain(T x, MatchResultListener* listener) const { + return impl_->MatchAndExplain(x, listener); + } + + // Returns true iff this matcher matches x. + bool Matches(T x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const { + impl_->DescribeNegationTo(os); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(T x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + return impl_.get(); + } + + protected: + MatcherBase() {} + + // Constructs a matcher from its implementation. + explicit MatcherBase(const MatcherInterface* impl) + : impl_(impl) {} + + virtual ~MatcherBase() {} + + private: + // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar + // interfaces. The former dynamically allocates a chunk of memory + // to hold the reference count, while the latter tracks all + // references using a circular linked list without allocating + // memory. It has been observed that linked_ptr performs better in + // typical scenarios. However, shared_ptr can out-perform + // linked_ptr when there are many more uses of the copy constructor + // than the default constructor. + // + // If performance becomes a problem, we should see if using + // shared_ptr helps. + ::testing::internal::linked_ptr > impl_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a linked_ptr to const +// MatcherInterface, so copying is fairly cheap. Don't inherit +// from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + Matcher() {} + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_HAS_STRING_PIECE_ +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a StringPiece +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass StringPieces directly. + Matcher(StringPiece s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass StringPieces directly. + Matcher(StringPiece s); // NOLINT +}; +#endif // GTEST_HAS_STRING_PIECE_ + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + virtual void DescribeTo(::std::ostream* os) const { + impl_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + impl_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + + GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); + }; + + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(PolymorphicMatcher); +}; + +// Creates a matcher from its implementation. This is easier to use +// than the Matcher constructor as it doesn't require you to +// explicitly write the template argument, e.g. +// +// MakeMatcher(foo); +// vs +// Matcher(foo); +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// The MatcherCastImpl class template is a helper for implementing +// MatcherCast(). We need this helper in order to partially +// specialize the implementation of MatcherCast() (C++ allows +// class/struct templates to be partially specialized, but not +// function templates.). + +// This general version is used when MatcherCast()'s argument is a +// polymorphic matcher (i.e. something that can be converted to a +// Matcher but is not one yet; for example, Eq(value)) or a value (for +// example, "hello"). +template +class MatcherCastImpl { + public: + static Matcher Cast(M polymorphic_matcher_or_value) { + // M can be a polymorhic matcher, in which case we want to use + // its conversion operator to create Matcher. Or it can be a value + // that should be passed to the Matcher's constructor. + // + // We can't call Matcher(polymorphic_matcher_or_value) when M is a + // polymorphic matcher because it'll be ambiguous if T has an implicit + // constructor from M (this usually happens when T has an implicit + // constructor from any type). + // + // It won't work to unconditionally implict_cast + // polymorphic_matcher_or_value to Matcher because it won't trigger + // a user-defined conversion from M to T if one exists (assuming M is + // a value). + return CastImpl( + polymorphic_matcher_or_value, + BooleanConstant< + internal::ImplicitlyConvertible >::value>()); + } + + private: + static Matcher CastImpl(M value, BooleanConstant) { + // M can't be implicitly converted to Matcher, so M isn't a polymorphic + // matcher. It must be a value then. Use direct initialization to create + // a matcher. + return Matcher(ImplicitCast_(value)); + } + + static Matcher CastImpl(M polymorphic_matcher_or_value, + BooleanConstant) { + // M is implicitly convertible to Matcher, which means that either + // M is a polymorhpic matcher or Matcher has an implicit constructor + // from M. In both cases using the implicit conversion will produce a + // matcher. + // + // Even if T has an implicit constructor from M, it won't be called because + // creating Matcher would require a chain of two user-defined conversions + // (first to create T from M and then to create Matcher from T). + return polymorphic_matcher_or_value; + } +}; + +// This more specialized version is used when MatcherCast()'s argument +// is already a Matcher. This only compiles when type T can be +// statically converted to type U. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& source_matcher) { + return Matcher(new Impl(source_matcher)); + } + + private: + class Impl : public MatcherInterface { + public: + explicit Impl(const Matcher& source_matcher) + : source_matcher_(source_matcher) {} + + // We delegate the matching logic to the source matcher. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return source_matcher_.MatchAndExplain(static_cast(x), listener); + } + + virtual void DescribeTo(::std::ostream* os) const { + source_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + source_matcher_.DescribeNegationTo(os); + } + + private: + const Matcher source_matcher_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; +}; + +// This even more specialized version is used for efficiently casting +// a matcher to its own type. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& matcher) { return matcher; } +}; + +} // namespace internal + +// In order to be safe and clear, casting between different matcher +// types is done explicitly via MatcherCast(m), which takes a +// matcher m and returns a Matcher. It compiles only when T can be +// statically converted to the argument type of m. +template +inline Matcher MatcherCast(M matcher) { + return internal::MatcherCastImpl::Cast(matcher); +} + +// Implements SafeMatcherCast(). +// +// We use an intermediate class to do the actual safe casting as Nokia's +// Symbian compiler cannot decide between +// template ... (M) and +// template ... (const Matcher&) +// for function templates but can for member function templates. +template +class SafeMatcherCastImpl { + public: + // This overload handles polymorphic matchers and values only since + // monomorphic matchers are handled by the next one. + template + static inline Matcher Cast(M polymorphic_matcher_or_value) { + return internal::MatcherCastImpl::Cast(polymorphic_matcher_or_value); + } + + // This overload handles monomorphic matchers. + // + // In general, if type T can be implicitly converted to type U, we can + // safely convert a Matcher to a Matcher (i.e. Matcher is + // contravariant): just keep a copy of the original Matcher, convert the + // argument from type T to U, and then pass it to the underlying Matcher. + // The only exception is when U is a reference and T is not, as the + // underlying Matcher may be interested in the argument's address, which + // is not preserved in the conversion from T to U. + template + static inline Matcher Cast(const Matcher& matcher) { + // Enforce that T can be implicitly converted to U. + GTEST_COMPILE_ASSERT_((internal::ImplicitlyConvertible::value), + T_must_be_implicitly_convertible_to_U); + // Enforce that we are not converting a non-reference type T to a reference + // type U. + GTEST_COMPILE_ASSERT_( + internal::is_reference::value || !internal::is_reference::value, + cannot_convert_non_referentce_arg_to_reference); + // In case both T and U are arithmetic types, enforce that the + // conversion is not lossy. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT; + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU; + const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther; + const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther; + GTEST_COMPILE_ASSERT_( + kTIsOther || kUIsOther || + (internal::LosslessArithmeticConvertible::value), + conversion_of_arithmetic_types_must_be_lossless); + return MatcherCast(matcher); + } +}; + +template +inline Matcher SafeMatcherCast(const M& polymorphic_matcher) { + return SafeMatcherCastImpl::Cast(polymorphic_matcher); +} + +// A() returns a matcher that matches any value of type T. +template +Matcher A(); + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// If the explanation is not empty, prints it to the ostream. +inline void PrintIfNotEmpty(const internal::string& explanation, + ::std::ostream* os) { + if (explanation != "" && os != NULL) { + *os << ", " << explanation; + } +} + +// Returns true if the given type name is easy to read by a human. +// This is used to decide whether printing the type of a value might +// be helpful. +inline bool IsReadableTypeName(const string& type_name) { + // We consider a type name readable if it's short or doesn't contain + // a template or function type. + return (type_name.length() <= 20 || + type_name.find_first_of("<(") == string::npos); +} + +// Matches the value against the given matcher, prints the value and explains +// the match result to the listener. Returns the match result. +// 'listener' must not be NULL. +// Value cannot be passed by const reference, because some matchers take a +// non-const argument. +template +bool MatchPrintAndExplain(Value& value, const Matcher& matcher, + MatchResultListener* listener) { + if (!listener->IsInterested()) { + // If the listener is not interested, we do not need to construct the + // inner explanation. + return matcher.Matches(value); + } + + StringMatchResultListener inner_listener; + const bool match = matcher.MatchAndExplain(value, &inner_listener); + + UniversalPrint(value, listener->stream()); +#if GTEST_HAS_RTTI + const string& type_name = GetTypeName(); + if (IsReadableTypeName(type_name)) + *listener->stream() << " (of type " << type_name << ")"; +#endif + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + + return match; +} + +// An internal helper class for doing compile-time loop on a tuple's +// fields. +template +class TuplePrefix { + public: + // TuplePrefix::Matches(matcher_tuple, value_tuple) returns true + // iff the first N fields of matcher_tuple matches the first N + // fields of value_tuple, respectively. + template + static bool Matches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + using ::std::tr1::get; + return TuplePrefix::Matches(matcher_tuple, value_tuple) + && get(matcher_tuple).Matches(get(value_tuple)); + } + + // TuplePrefix::ExplainMatchFailuresTo(matchers, values, os) + // describes failures in matching the first N fields of matchers + // against the first N fields of values. If there is no failure, + // nothing will be streamed to os. + template + static void ExplainMatchFailuresTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + using ::std::tr1::tuple_element; + using ::std::tr1::get; + + // First, describes failures in the first N - 1 fields. + TuplePrefix::ExplainMatchFailuresTo(matchers, values, os); + + // Then describes the failure (if any) in the (N - 1)-th (0-based) + // field. + typename tuple_element::type matcher = + get(matchers); + typedef typename tuple_element::type Value; + Value value = get(values); + StringMatchResultListener listener; + if (!matcher.MatchAndExplain(value, &listener)) { + // TODO(wan): include in the message the name of the parameter + // as used in MOCK_METHOD*() when possible. + *os << " Expected arg #" << N - 1 << ": "; + get(matchers).DescribeTo(os); + *os << "\n Actual: "; + // We remove the reference in type Value to prevent the + // universal printer from printing the address of value, which + // isn't interesting to the user most of the time. The + // matcher's MatchAndExplain() method handles the case when + // the address is interesting. + internal::UniversalPrint(value, os); + PrintIfNotEmpty(listener.str(), os); + *os << "\n"; + } + } +}; + +// The base case. +template <> +class TuplePrefix<0> { + public: + template + static bool Matches(const MatcherTuple& /* matcher_tuple */, + const ValueTuple& /* value_tuple */) { + return true; + } + + template + static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */, + const ValueTuple& /* values */, + ::std::ostream* /* os */) {} +}; + +// TupleMatches(matcher_tuple, value_tuple) returns true iff all +// matchers in matcher_tuple match the corresponding fields in +// value_tuple. It is a compiler error if matcher_tuple and +// value_tuple have different number of fields or incompatible field +// types. +template +bool TupleMatches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + using ::std::tr1::tuple_size; + // Makes sure that matcher_tuple and value_tuple have the same + // number of fields. + GTEST_COMPILE_ASSERT_(tuple_size::value == + tuple_size::value, + matcher_and_value_have_different_numbers_of_fields); + return TuplePrefix::value>:: + Matches(matcher_tuple, value_tuple); +} + +// Describes failures in matching matchers against values. If there +// is no failure, nothing will be streamed to os. +template +void ExplainMatchFailureTupleTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + using ::std::tr1::tuple_size; + TuplePrefix::value>::ExplainMatchFailuresTo( + matchers, values, os); +} + +// TransformTupleValues and its helper. +// +// TransformTupleValuesHelper hides the internal machinery that +// TransformTupleValues uses to implement a tuple traversal. +template +class TransformTupleValuesHelper { + private: + typedef typename ::std::tr1::tuple_size TupleSize; + + public: + // For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'. + // Returns the final value of 'out' in case the caller needs it. + static OutIter Run(Func f, const Tuple& t, OutIter out) { + return IterateOverTuple()(f, t, out); + } + + private: + template + struct IterateOverTuple { + OutIter operator() (Func f, const Tup& t, OutIter out) const { + *out++ = f(::std::tr1::get(t)); + return IterateOverTuple()(f, t, out); + } + }; + template + struct IterateOverTuple { + OutIter operator() (Func /* f */, const Tup& /* t */, OutIter out) const { + return out; + } + }; +}; + +// Successively invokes 'f(element)' on each element of the tuple 't', +// appending each result to the 'out' iterator. Returns the final value +// of 'out'. +template +OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) { + return TransformTupleValuesHelper::Run(f, t, out); +} + +// Implements A(). +template +class AnyMatcherImpl : public MatcherInterface { + public: + virtual bool MatchAndExplain( + T /* x */, MatchResultListener* /* listener */) const { return true; } + virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; } + virtual void DescribeNegationTo(::std::ostream* os) const { + // This is mostly for completeness' safe, as it's not very useful + // to write Not(A()). However we cannot completely rule out + // such a possibility, and it doesn't hurt to be prepared. + *os << "never matches"; + } +}; + +// Implements _, a matcher that matches any value of any +// type. This is a polymorphic matcher, so we need a template type +// conversion operator to make it appearing as a Matcher for any +// type T. +class AnythingMatcher { + public: + template + operator Matcher() const { return A(); } +}; + +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// We define this as a macro in order to eliminate duplicated source +// code. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +#define GMOCK_IMPLEMENT_COMPARISON_MATCHER_( \ + name, op, relation, negated_relation) \ + template class name##Matcher { \ + public: \ + explicit name##Matcher(const Rhs& rhs) : rhs_(rhs) {} \ + template \ + operator Matcher() const { \ + return MakeMatcher(new Impl(rhs_)); \ + } \ + private: \ + template \ + class Impl : public MatcherInterface { \ + public: \ + explicit Impl(const Rhs& rhs) : rhs_(rhs) {} \ + virtual bool MatchAndExplain(\ + Lhs lhs, MatchResultListener* /* listener */) const { \ + return lhs op rhs_; \ + } \ + virtual void DescribeTo(::std::ostream* os) const { \ + *os << relation " "; \ + UniversalPrint(rhs_, os); \ + } \ + virtual void DescribeNegationTo(::std::ostream* os) const { \ + *os << negated_relation " "; \ + UniversalPrint(rhs_, os); \ + } \ + private: \ + Rhs rhs_; \ + GTEST_DISALLOW_ASSIGN_(Impl); \ + }; \ + Rhs rhs_; \ + GTEST_DISALLOW_ASSIGN_(name##Matcher); \ + } + +// Implements Eq(v), Ge(v), Gt(v), Le(v), Lt(v), and Ne(v) +// respectively. +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Eq, ==, "is equal to", "isn't equal to"); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ge, >=, "is >=", "isn't >="); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Gt, >, "is >", "isn't >"); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Le, <=, "is <=", "isn't <="); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Lt, <, "is <", "isn't <"); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ne, !=, "isn't equal to", "is equal to"); + +#undef GMOCK_IMPLEMENT_COMPARISON_MATCHER_ + +// Implements the polymorphic IsNull() matcher, which matches any raw or smart +// pointer that is NULL. +class IsNullMatcher { + public: + template + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return GetRawPointer(p) == NULL; + } + + void DescribeTo(::std::ostream* os) const { *os << "is NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "isn't NULL"; + } +}; + +// Implements the polymorphic NotNull() matcher, which matches any raw or smart +// pointer that is not NULL. +class NotNullMatcher { + public: + template + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return GetRawPointer(p) != NULL; + } + + void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "is NULL"; + } +}; + +// Ref(variable) matches any argument that is a reference to +// 'variable'. This matcher is polymorphic as it can match any +// super type of the type of 'variable'. +// +// The RefMatcher template class implements Ref(variable). It can +// only be instantiated with a reference type. This prevents a user +// from mistakenly using Ref(x) to match a non-reference function +// argument. For example, the following will righteously cause a +// compiler error: +// +// int n; +// Matcher m1 = Ref(n); // This won't compile. +// Matcher m2 = Ref(n); // This will compile. +template +class RefMatcher; + +template +class RefMatcher { + // Google Mock is a generic framework and thus needs to support + // mocking any function types, including those that take non-const + // reference arguments. Therefore the template parameter T (and + // Super below) can be instantiated to either a const type or a + // non-const type. + public: + // RefMatcher() takes a T& instead of const T&, as we want the + // compiler to catch using Ref(const_value) as a matcher for a + // non-const reference. + explicit RefMatcher(T& x) : object_(x) {} // NOLINT + + template + operator Matcher() const { + // By passing object_ (type T&) to Impl(), which expects a Super&, + // we make sure that Super is a super type of T. In particular, + // this catches using Ref(const_value) as a matcher for a + // non-const reference, as you cannot implicitly convert a const + // reference to a non-const reference. + return MakeMatcher(new Impl(object_)); + } + + private: + template + class Impl : public MatcherInterface { + public: + explicit Impl(Super& x) : object_(x) {} // NOLINT + + // MatchAndExplain() takes a Super& (as opposed to const Super&) + // in order to match the interface MatcherInterface. + virtual bool MatchAndExplain( + Super& x, MatchResultListener* listener) const { + *listener << "which is located @" << static_cast(&x); + return &x == &object_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "references the variable "; + UniversalPrinter::Print(object_, os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not reference the variable "; + UniversalPrinter::Print(object_, os); + } + + private: + const Super& object_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + T& object_; + + GTEST_DISALLOW_ASSIGN_(RefMatcher); +}; + +// Polymorphic helper functions for narrow and wide string matchers. +inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) { + return String::CaseInsensitiveCStringEquals(lhs, rhs); +} + +inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + return String::CaseInsensitiveWideCStringEquals(lhs, rhs); +} + +// String comparison for narrow or wide strings that can have embedded NUL +// characters. +template +bool CaseInsensitiveStringEquals(const StringType& s1, + const StringType& s2) { + // Are the heads equal? + if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) { + return false; + } + + // Skip the equal heads. + const typename StringType::value_type nul = 0; + const size_t i1 = s1.find(nul), i2 = s2.find(nul); + + // Are we at the end of either s1 or s2? + if (i1 == StringType::npos || i2 == StringType::npos) { + return i1 == i2; + } + + // Are the tails equal? + return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1)); +} + +// String matchers. + +// Implements equality-based string matchers like StrEq, StrCaseNe, and etc. +template +class StrEqualityMatcher { + public: + StrEqualityMatcher(const StringType& str, bool expect_eq, + bool case_sensitive) + : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + if (s == NULL) { + return !expect_eq_; + } + return MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + const bool eq = case_sensitive_ ? s2 == string_ : + CaseInsensitiveStringEquals(s2, string_); + return expect_eq_ == eq; + } + + void DescribeTo(::std::ostream* os) const { + DescribeToHelper(expect_eq_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + DescribeToHelper(!expect_eq_, os); + } + + private: + void DescribeToHelper(bool expect_eq, ::std::ostream* os) const { + *os << (expect_eq ? "is " : "isn't "); + *os << "equal to "; + if (!case_sensitive_) { + *os << "(ignoring case) "; + } + UniversalPrint(string_, os); + } + + const StringType string_; + const bool expect_eq_; + const bool case_sensitive_; + + GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher); +}; + +// Implements the polymorphic HasSubstr(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class HasSubstrMatcher { + public: + explicit HasSubstrMatcher(const StringType& substring) + : substring_(substring) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.find(substring_) != StringType::npos; + } + + // Describes what this matcher matches. + void DescribeTo(::std::ostream* os) const { + *os << "has substring "; + UniversalPrint(substring_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "has no substring "; + UniversalPrint(substring_, os); + } + + private: + const StringType substring_; + + GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher); +}; + +// Implements the polymorphic StartsWith(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class StartsWithMatcher { + public: + explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) { + } + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.length() >= prefix_.length() && + s2.substr(0, prefix_.length()) == prefix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "starts with "; + UniversalPrint(prefix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't start with "; + UniversalPrint(prefix_, os); + } + + private: + const StringType prefix_; + + GTEST_DISALLOW_ASSIGN_(StartsWithMatcher); +}; + +// Implements the polymorphic EndsWith(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class EndsWithMatcher { + public: + explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.length() >= suffix_.length() && + s2.substr(s2.length() - suffix_.length()) == suffix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "ends with "; + UniversalPrint(suffix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't end with "; + UniversalPrint(suffix_, os); + } + + private: + const StringType suffix_; + + GTEST_DISALLOW_ASSIGN_(EndsWithMatcher); +}; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(internal::string(s), listener); + } + + // Matches anything that can convert to internal::string. + // + // This is a template, not just a plain function with const internal::string&, + // because StringPiece has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const internal::string& s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) : + RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const internal::linked_ptr regex_; + const bool full_match_; + + GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher); +}; + +// Implements a matcher that compares the two fields of a 2-tuple +// using one of the ==, <=, <, etc, operators. The two fields being +// compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq() can be +// used to match a tuple, a tuple, +// etc). Therefore we use a template type conversion operator in the +// implementation. +// +// We define this as a macro in order to eliminate duplicated source +// code. +#define GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(name, op, relation) \ + class name##2Matcher { \ + public: \ + template \ + operator Matcher< ::std::tr1::tuple >() const { \ + return MakeMatcher(new Impl< ::std::tr1::tuple >); \ + } \ + template \ + operator Matcher&>() const { \ + return MakeMatcher(new Impl&>); \ + } \ + private: \ + template \ + class Impl : public MatcherInterface { \ + public: \ + virtual bool MatchAndExplain( \ + Tuple args, \ + MatchResultListener* /* listener */) const { \ + return ::std::tr1::get<0>(args) op ::std::tr1::get<1>(args); \ + } \ + virtual void DescribeTo(::std::ostream* os) const { \ + *os << "are " relation; \ + } \ + virtual void DescribeNegationTo(::std::ostream* os) const { \ + *os << "aren't " relation; \ + } \ + }; \ + } + +// Implements Eq(), Ge(), Gt(), Le(), Lt(), and Ne() respectively. +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Eq, ==, "an equal pair"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Ge, >=, "a pair where the first >= the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Gt, >, "a pair where the first > the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Le, <=, "a pair where the first <= the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Lt, <, "a pair where the first < the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Ne, !=, "an unequal pair"); + +#undef GMOCK_IMPLEMENT_COMPARISON2_MATCHER_ + +// Implements the Not(...) matcher for a particular argument type T. +// We do not nest it inside the NotMatcher class template, as that +// will prevent different instantiations of NotMatcher from sharing +// the same NotMatcherImpl class. +template +class NotMatcherImpl : public MatcherInterface { + public: + explicit NotMatcherImpl(const Matcher& matcher) + : matcher_(matcher) {} + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return !matcher_.MatchAndExplain(x, listener); + } + + virtual void DescribeTo(::std::ostream* os) const { + matcher_.DescribeNegationTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + matcher_.DescribeTo(os); + } + + private: + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(NotMatcherImpl); +}; + +// Implements the Not(m) matcher, which matches a value that doesn't +// match matcher m. +template +class NotMatcher { + public: + explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {} + + // This template type conversion operator allows Not(m) to be used + // to match any type m can match. + template + operator Matcher() const { + return Matcher(new NotMatcherImpl(SafeMatcherCast(matcher_))); + } + + private: + InnerMatcher matcher_; + + GTEST_DISALLOW_ASSIGN_(NotMatcher); +}; + +// Implements the AllOf(m1, m2) matcher for a particular argument type +// T. We do not nest it inside the BothOfMatcher class template, as +// that will prevent different instantiations of BothOfMatcher from +// sharing the same BothOfMatcherImpl class. +template +class BothOfMatcherImpl : public MatcherInterface { + public: + BothOfMatcherImpl(const Matcher& matcher1, const Matcher& matcher2) + : matcher1_(matcher1), matcher2_(matcher2) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeTo(os); + *os << ") and ("; + matcher2_.DescribeTo(os); + *os << ")"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeNegationTo(os); + *os << ") or ("; + matcher2_.DescribeNegationTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + // If either matcher1_ or matcher2_ doesn't match x, we only need + // to explain why one of them fails. + StringMatchResultListener listener1; + if (!matcher1_.MatchAndExplain(x, &listener1)) { + *listener << listener1.str(); + return false; + } + + StringMatchResultListener listener2; + if (!matcher2_.MatchAndExplain(x, &listener2)) { + *listener << listener2.str(); + return false; + } + + // Otherwise we need to explain why *both* of them match. + const internal::string s1 = listener1.str(); + const internal::string s2 = listener2.str(); + + if (s1 == "") { + *listener << s2; + } else { + *listener << s1; + if (s2 != "") { + *listener << ", and " << s2; + } + } + return true; + } + + private: + const Matcher matcher1_; + const Matcher matcher2_; + + GTEST_DISALLOW_ASSIGN_(BothOfMatcherImpl); +}; + +#if GTEST_LANG_CXX11 +// MatcherList provides mechanisms for storing a variable number of matchers in +// a list structure (ListType) and creating a combining matcher from such a +// list. +// The template is defined recursively using the following template paramters: +// * kSize is the length of the MatcherList. +// * Head is the type of the first matcher of the list. +// * Tail denotes the types of the remaining matchers of the list. +template +struct MatcherList { + typedef MatcherList MatcherListTail; + typedef ::std::pair ListType; + + // BuildList stores variadic type values in a nested pair structure. + // Example: + // MatcherList<3, int, string, float>::BuildList(5, "foo", 2.0) will return + // the corresponding result of type pair>. + static ListType BuildList(const Head& matcher, const Tail&... tail) { + return ListType(matcher, MatcherListTail::BuildList(tail...)); + } + + // CreateMatcher creates a Matcher from a given list of matchers (built + // by BuildList()). CombiningMatcher is used to combine the matchers of the + // list. CombiningMatcher must implement MatcherInterface and have a + // constructor taking two Matchers as input. + template class CombiningMatcher> + static Matcher CreateMatcher(const ListType& matchers) { + return Matcher(new CombiningMatcher( + SafeMatcherCast(matchers.first), + MatcherListTail::template CreateMatcher( + matchers.second))); + } +}; + +// The following defines the base case for the recursive definition of +// MatcherList. +template +struct MatcherList<2, Matcher1, Matcher2> { + typedef ::std::pair ListType; + + static ListType BuildList(const Matcher1& matcher1, + const Matcher2& matcher2) { + return ::std::pair(matcher1, matcher2); + } + + template class CombiningMatcher> + static Matcher CreateMatcher(const ListType& matchers) { + return Matcher(new CombiningMatcher( + SafeMatcherCast(matchers.first), + SafeMatcherCast(matchers.second))); + } +}; + +// VariadicMatcher is used for the variadic implementation of +// AllOf(m_1, m_2, ...) and AnyOf(m_1, m_2, ...). +// CombiningMatcher is used to recursively combine the provided matchers +// (of type Args...). +template