diff --git a/appveyor.yml b/appveyor.yml index ec155a76ad..5c419c32f9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -60,7 +60,7 @@ install: # install Bazel if ($env:build_system -eq "bazel") { - appveyor DownloadFile https://github.com/bazelbuild/bazel/releases/download/0.28.1/bazel-0.28.1-windows-x86_64.exe -FileName bazel.exe + appveyor DownloadFile https://github.com/bazelbuild/bazel/releases/download/3.6.0/bazel-3.6.0-windows-x86_64.exe -FileName bazel.exe } if ($env:build_system -eq "cmake") { diff --git a/googlemock/docs/cheat_sheet.md b/googlemock/docs/cheat_sheet.md index cc7e699b85..78871bf87e 100644 --- a/googlemock/docs/cheat_sheet.md +++ b/googlemock/docs/cheat_sheet.md @@ -498,7 +498,7 @@ which must be a permanent callback. | :----------------------------------- | :------------------------------------ | | `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | | `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a matcher `IsDivisibleBy(n)` to match a number divisible by `n`. | -| `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | +| `MATCHER_P2(IsBetween, a, b, absl::StrCat(negation ? "isn't" : "is", " between ", PrintToString(a), " and ", PrintToString(b))) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | **Notes:** diff --git a/googlemock/docs/cook_book.md b/googlemock/docs/cook_book.md index f8e9a50fb9..817d5cabec 100644 --- a/googlemock/docs/cook_book.md +++ b/googlemock/docs/cook_book.md @@ -859,6 +859,22 @@ using ::testing::Not; NULL)); ``` +Matchers are function objects, and parametrized matchers can be composed just +like any other function. However because their types can be long and rarely +provide meaningful information, it can be easier to express them with C++14 +generic lambdas to avoid specifying types. For example, + +```cpp +using ::testing::Contains; +using ::testing::Property; + +inline constexpr auto HasFoo = [](const auto& f) { + return Property(&MyClass::foo, Contains(f)); +}; +... + EXPECT_THAT(x, HasFoo("blah")); +``` + ### Casting Matchers {#SafeMatcherCast} gMock matchers are statically typed, meaning that the compiler can catch your diff --git a/googlemock/docs/pump_manual.md b/googlemock/docs/pump_manual.md index 19f1a48eb5..17fb370dee 100644 --- a/googlemock/docs/pump_manual.md +++ b/googlemock/docs/pump_manual.md @@ -119,7 +119,7 @@ Func(a1 + a2 + a3); // If n is 3. We support the following meta programming constructs: | `$var id = exp` | Defines a named constant value. `$id` is | -: : valid util the end of the current meta : +: : valid until the end of the current meta : : : lexical block. : | :------------------------------- | :--------------------------------------- | | `$range id exp..exp` | Sets the range of an iteration variable, | diff --git a/googlemock/scripts/fuse_gmock_files.py b/googlemock/scripts/fuse_gmock_files.py index c33c7253fe..0c5bb9f48c 100755 --- a/googlemock/scripts/fuse_gmock_files.py +++ b/googlemock/scripts/fuse_gmock_files.py @@ -28,8 +28,8 @@ # 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. +"""fuse_gmock_files.py v0.1.0. -"""fuse_gmock_files.py v0.1.0 Fuses Google Mock and Google Test source code into two .h files and a .cc file. SYNOPSIS @@ -55,27 +55,31 @@ This tool is experimental. In particular, it assumes that there is no conditional inclusion of Google Mock or Google Test headers. Please report any problems to googlemock@googlegroups.com. You can read -https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md for more +https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md +for more information. """ -__author__ = 'wan@google.com (Zhanyong Wan)' +from __future__ import print_function import os import re -import sets import sys +import fuse_gtest_files + +__author__ = 'wan@google.com (Zhanyong Wan)' + # We assume that this file is in the scripts/ directory in the Google # Mock root directory. DEFAULT_GMOCK_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..') # We need to call into googletest/scripts/fuse_gtest_files.py. sys.path.append(os.path.join(DEFAULT_GMOCK_ROOT_DIR, '../googletest/scripts')) -import fuse_gtest_files gtest = fuse_gtest_files -# Regex for matching '#include "gmock/..."'. +# Regex for matching +# '#include "gmock/..."'. INCLUDE_GMOCK_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(gmock/.+)"') # Where to find the source seed files. @@ -98,6 +102,9 @@ def ValidateGMockRootDir(gmock_root): """Makes sure gmock_root points to a valid gmock root directory. The function aborts the program on failure. + + Args: + gmock_root: A string with the mock root directory. """ gtest.ValidateGTestRootDir(GetGTestRootDir(gmock_root)) @@ -109,6 +116,9 @@ def ValidateOutputDir(output_dir): """Makes sure output_dir points to a valid output directory. The function aborts the program on failure. + + Args: + output_dir: A string representing the output directory. """ gtest.VerifyOutputFile(output_dir, gtest.GTEST_H_OUTPUT) @@ -119,8 +129,8 @@ def ValidateOutputDir(output_dir): def FuseGMockH(gmock_root, output_dir): """Scans folder gmock_root to generate gmock/gmock.h in output_dir.""" - output_file = file(os.path.join(output_dir, GMOCK_H_OUTPUT), 'w') - processed_files = sets.Set() # Holds all gmock headers we've processed. + output_file = open(os.path.join(output_dir, GMOCK_H_OUTPUT), 'w') + processed_files = frozenset() # Holds all gmock headers we've processed. def ProcessFile(gmock_header_path): """Processes the given gmock header file.""" @@ -132,25 +142,29 @@ def ProcessFile(gmock_header_path): processed_files.add(gmock_header_path) # Reads each line in the given gmock header. - for line in file(os.path.join(gmock_root, gmock_header_path), 'r'): - m = INCLUDE_GMOCK_FILE_REGEX.match(line) - if m: - # It's '#include "gmock/..."' - let's process it recursively. - ProcessFile('include/' + m.group(1)) - else: - m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line) + + with open(os.path.join(gmock_root, gmock_header_path), 'r') as fh: + for line in fh: + m = INCLUDE_GMOCK_FILE_REGEX.match(line) if m: - # It's '#include "gtest/foo.h"'. We translate it to - # "gtest/gtest.h", regardless of what foo is, since all - # gtest headers are fused into gtest/gtest.h. - - # There is no need to #include gtest.h twice. - if not gtest.GTEST_H_SEED in processed_files: - processed_files.add(gtest.GTEST_H_SEED) - output_file.write('#include "%s"\n' % (gtest.GTEST_H_OUTPUT,)) + # '#include "gmock/..."' + # - let's process it recursively. + ProcessFile('include/' + m.group(1)) else: - # Otherwise we copy the line unchanged to the output file. - output_file.write(line) + m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line) + if m: + # '#include "third_party/googletest/googletest/ + # include/gtest/foo.h"'. + # We translate it to "gtest/gtest.h", regardless of what foo is, + # since all gtest headers are fused into gtest/gtest.h. + + # There is no need to #include gtest.h twice. + if gtest.GTEST_H_SEED not in processed_files: + processed_files.add(gtest.GTEST_H_SEED) + output_file.write('#include "%s"\n' % (gtest.GTEST_H_OUTPUT,)) + else: + # Otherwise we copy the line unchanged to the output file. + output_file.write(line) ProcessFile(GMOCK_H_SEED) output_file.close() @@ -159,7 +173,7 @@ def ProcessFile(gmock_header_path): def FuseGMockAllCcToFile(gmock_root, output_file): """Scans folder gmock_root to fuse gmock-all.cc into output_file.""" - processed_files = sets.Set() + processed_files = frozenset() def ProcessFile(gmock_source_file): """Processes the given gmock source file.""" @@ -171,32 +185,37 @@ def ProcessFile(gmock_source_file): processed_files.add(gmock_source_file) # Reads each line in the given gmock source file. - for line in file(os.path.join(gmock_root, gmock_source_file), 'r'): - m = INCLUDE_GMOCK_FILE_REGEX.match(line) - if m: - # It's '#include "gmock/foo.h"'. We treat it as '#include - # "gmock/gmock.h"', as all other gmock headers are being fused - # into gmock.h and cannot be #included directly. - - # There is no need to #include "gmock/gmock.h" more than once. - if not GMOCK_H_SEED in processed_files: - processed_files.add(GMOCK_H_SEED) - output_file.write('#include "%s"\n' % (GMOCK_H_OUTPUT,)) - else: - m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line) + + with open(os.path.join(gmock_root, gmock_source_file), 'r') as fh: + for line in fh: + m = INCLUDE_GMOCK_FILE_REGEX.match(line) if m: - # It's '#include "gtest/..."'. - # There is no need to #include gtest.h as it has been - # #included by gtest-all.cc. - pass + # '#include "gmock/foo.h"'. + # We treat it as '#include "gmock/gmock.h"', as all other gmock + # headers are being fused into gmock.h and cannot be + # included directly. No need to #include + # "third_party/googletest/googlemock/include/gmock/gmock.h" + # more than once. + + if GMOCK_H_SEED not in processed_files: + processed_files.add(GMOCK_H_SEED) + output_file.write('#include "%s"\n' % (GMOCK_H_OUTPUT,)) else: - m = gtest.INCLUDE_SRC_FILE_REGEX.match(line) + m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line) if m: - # It's '#include "src/foo"' - let's process it recursively. - ProcessFile(m.group(1)) + # '#include "gtest/..."'. + # There is no need to #include gtest.h as it has been + # #included by gtest-all.cc. + + pass else: - # Otherwise we copy the line unchanged to the output file. - output_file.write(line) + m = gtest.INCLUDE_SRC_FILE_REGEX.match(line) + if m: + # It's '#include "src/foo"' - let's process it recursively. + ProcessFile(m.group(1)) + else: + # Otherwise we copy the line unchanged to the output file. + output_file.write(line) ProcessFile(GMOCK_ALL_CC_SEED) @@ -204,12 +223,12 @@ def ProcessFile(gmock_source_file): def FuseGMockGTestAllCc(gmock_root, output_dir): """Scans folder gmock_root to generate gmock-gtest-all.cc in output_dir.""" - output_file = file(os.path.join(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT), 'w') - # First, fuse gtest-all.cc into gmock-gtest-all.cc. - gtest.FuseGTestAllCcToFile(GetGTestRootDir(gmock_root), output_file) - # Next, append fused gmock-all.cc to gmock-gtest-all.cc. - FuseGMockAllCcToFile(gmock_root, output_file) - output_file.close() + with open(os.path.join(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT), + 'w') as output_file: + # First, fuse gtest-all.cc into gmock-gtest-all.cc. + gtest.FuseGTestAllCcToFile(GetGTestRootDir(gmock_root), output_file) + # Next, append fused gmock-all.cc to gmock-gtest-all.cc. + FuseGMockAllCcToFile(gmock_root, output_file) def FuseGMock(gmock_root, output_dir): @@ -232,7 +251,7 @@ def main(): # fuse_gmock_files.py GMOCK_ROOT_DIR OUTPUT_DIR FuseGMock(sys.argv[1], sys.argv[2]) else: - print __doc__ + print(__doc__) sys.exit(1) diff --git a/googletest/include/gtest/gtest-printers.h b/googletest/include/gtest/gtest-printers.h index f24512a9f2..f697e2f0d2 100644 --- a/googletest/include/gtest/gtest-printers.h +++ b/googletest/include/gtest/gtest-printers.h @@ -192,51 +192,43 @@ struct PointerPrinter { } }; -namespace internal_stream { +namespace internal_stream_operator_without_lexical_name_lookup { -struct Sentinel; -template -Sentinel* operator<<(::std::basic_ostream& os, const T& x); - -// Check if the user has a user-defined operator<< for their type. -// -// We put this in its own namespace to inject a custom operator<< that allows us -// to probe the type's operator. -// -// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If -// we define it to take an std::ostream instead, we'll get an -// "ambiguous overloads" compiler error when trying to print a type -// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether -// operator<<(std::ostream&, const T&) or -// operator<<(std::basic_stream, const Foo&) is more -// specific. -template -constexpr bool UseStreamOperator() { - return !std::is_same() - << std::declval()), - Sentinel*>::value; -} - -} // namespace internal_stream +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); struct StreamPrinter { - template ()>::type> + template ::value>::type, + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + typename = decltype(std::declval() + << std::declval())> static void PrintValue(const T& value, ::std::ostream* os) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. *os << value; } }; +} // namespace internal_stream_operator_without_lexical_name_lookup + struct ProtobufPrinter { // We print a protobuf using its ShortDebugString() when the string // doesn't exceed this many characters; otherwise we print it using // DebugString() for better readability. static const size_t kProtobufOneLinerMaxLength = 50; - template ::value>::type> + template ::value>::type> static void PrintValue(const T& value, ::std::ostream* os) { std::string pretty_str = value.ShortDebugString(); if (pretty_str.length() > kProtobufOneLinerMaxLength) { @@ -307,7 +299,8 @@ template void PrintWithFallback(const T& value, ::std::ostream* os) { using Printer = typename FindFirstPrinter< T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, - StreamPrinter, ProtobufPrinter, ConvertibleToIntegerPrinter, + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ProtobufPrinter, ConvertibleToIntegerPrinter, ConvertibleToStringViewPrinter, FallbackPrinter>::type; Printer::PrintValue(value, os); } diff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h index 233724ccdd..738e6c3ac1 100644 --- a/googletest/include/gtest/internal/gtest-internal.h +++ b/googletest/include/gtest/internal/gtest-internal.h @@ -892,11 +892,34 @@ class GTEST_API_ Random { #define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ typename std::remove_const::type>::type -// IsAProtocolMessage::value is a compile-time bool constant that's -// true if and only if T is type proto2::MessageLite or a subclass of it. +// HasDebugStringAndShortDebugString::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. template -struct IsAProtocolMessage - : public std::is_convertible {}; +class HasDebugStringAndShortDebugString { + private: + template + static constexpr auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().DebugString())>::type; + template + static constexpr std::false_type CheckDebugString(...); + + template + static constexpr auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().ShortDebugString())>::type; + template + static constexpr std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +template +constexpr bool HasDebugStringAndShortDebugString::value; // When the compiler sees expression IsContainerTest(0), if C is an // STL-style container class, the first overload of IsContainerTest @@ -1186,6 +1209,8 @@ struct ElemFromList { static_cast(nullptr)...)); }; +struct FlatTupleConstructTag {}; + template class FlatTuple; @@ -1196,7 +1221,9 @@ template struct FlatTupleElemBase, I> { using value_type = typename ElemFromList::type; FlatTupleElemBase() = default; - explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {} + template + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward(t)) {} value_type value; }; @@ -1208,8 +1235,30 @@ struct FlatTupleBase, IndexSequence> : FlatTupleElemBase, Idx>... { using Indices = IndexSequence; FlatTupleBase() = default; - explicit FlatTupleBase(T... t) - : FlatTupleElemBase, Idx>(std::move(t))... {} + template + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase, Idx>(FlatTupleConstructTag{}, + std::forward(args))... {} + + template + const typename ElemFromList::type& Get() const { + return FlatTupleElemBase, I>::value; + } + + template + typename ElemFromList::type& Get() { + return FlatTupleElemBase, I>::value; + } + + template + auto Apply(F&& f) -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } + + template + auto Apply(F&& f) const -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } }; // Analog to std::tuple but with different tradeoffs. @@ -1230,17 +1279,17 @@ class FlatTuple public: FlatTuple() = default; - explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {} - - template - const typename ElemFromList::type& Get() const { - return static_cast*>(this)->value; - } - - template - typename ElemFromList::type& Get() { - return static_cast*>(this)->value; - } + template ::type...)>::value && + (sizeof...(T) >= 1)>::type> + explicit FlatTuple(Args&&... args) + : FlatTuple::FlatTupleBase(FlatTupleConstructTag{}, + std::forward(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; }; // Utility functions to be called with static_assert to induce deprecation @@ -1273,6 +1322,14 @@ constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } } // namespace internal } // namespace testing +namespace std { + +template +class tuple_size> + : public std::integral_constant {}; + +} // namespace std + #define GTEST_MESSAGE_AT_(file, line, message, result_type) \ ::testing::internal::AssertHelper(result_type, file, line, message) \ = ::testing::Message() diff --git a/googletest/include/gtest/internal/gtest-param-util.h b/googletest/include/gtest/internal/gtest-param-util.h index f26d7d1186..138d372e06 100644 --- a/googletest/include/gtest/internal/gtest-param-util.h +++ b/googletest/include/gtest/internal/gtest-param-util.h @@ -783,10 +783,15 @@ internal::ParamGenerator ValuesIn( namespace internal { // Used in the Values() function to provide polymorphic capabilities. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + template class ValueArray { public: - ValueArray(Ts... v) : v_{std::move(v)...} {} + explicit ValueArray(Ts... v) : v_(std::move(v)...) {} template operator ParamGenerator() const { // NOLINT @@ -802,6 +807,10 @@ class ValueArray { FlatTuple v_; }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + template class CartesianProductGenerator : public ParamGeneratorInterface<::std::tuple> { diff --git a/googletest/test/googletest-printers-test.cc b/googletest/test/googletest-printers-test.cc index ba2befb8fc..c81af37198 100644 --- a/googletest/test/googletest-printers-test.cc +++ b/googletest/test/googletest-printers-test.cc @@ -90,6 +90,18 @@ class BiggestIntConvertible { operator ::testing::internal::BiggestInt() const { return 42; } }; +// A parent class with two child classes. The parent and one of the kids have +// stream operators. +class ParentClass {}; +class ChildClassWithStreamOperator : public ParentClass {}; +class ChildClassWithoutStreamOperator : public ParentClass {}; +static void operator<<(std::ostream& os, const ParentClass&) { + os << "ParentClass"; +} +static void operator<<(std::ostream& os, const ChildClassWithStreamOperator&) { + os << "ChildClassWithStreamOperator"; +} + // A user-defined unprintable class template in the global namespace. template class UnprintableTemplateInGlobal { @@ -177,6 +189,17 @@ inline ::std::ostream& operator<<(::std::ostream& os, return os << "StreamableTemplateInFoo: " << x.value(); } +// A user-defined streamable type in a user namespace whose operator<< is +// templated on the type of the output stream. +struct TemplatedStreamableInFoo {}; + +template +OutputStream& operator<<(OutputStream& os, + const TemplatedStreamableInFoo& /*ts*/) { + os << "TemplatedStreamableInFoo"; + return os; +} + // A user-defined streamable but recursivly-defined container type in // a user namespace, it mimics therefore std::filesystem::path or // boost::filesystem::path. @@ -1201,6 +1224,20 @@ TEST(PrintStreamableTypeTest, TemplateTypeInUserNamespace) { Print(::foo::StreamableTemplateInFoo())); } +TEST(PrintStreamableTypeTest, TypeInUserNamespaceWithTemplatedStreamOperator) { + EXPECT_EQ("TemplatedStreamableInFoo", + Print(::foo::TemplatedStreamableInFoo())); +} + +TEST(PrintStreamableTypeTest, SubclassUsesSuperclassStreamOperator) { + ParentClass parent; + ChildClassWithStreamOperator child_stream; + ChildClassWithoutStreamOperator child_no_stream; + EXPECT_EQ("ParentClass", Print(parent)); + EXPECT_EQ("ChildClassWithStreamOperator", Print(child_stream)); + EXPECT_EQ("ParentClass", Print(child_no_stream)); +} + // Tests printing a user-defined recursive container type that has a << // operator. TEST(PrintStreamableTypeTest, PathLikeInUserNamespace) { diff --git a/googletest/test/gtest_unittest.cc b/googletest/test/gtest_unittest.cc index 7aa884a0fc..aa4c21a69e 100644 --- a/googletest/test/gtest_unittest.cc +++ b/googletest/test/gtest_unittest.cc @@ -64,6 +64,7 @@ TEST(CommandLineFlagsTest, CanBeAccessedInCodeOnceGTestHIsIncluded) { #include #include #include +#include #include #include #include @@ -254,8 +255,8 @@ using testing::internal::GetTimeInMillis; using testing::internal::GetTypeId; using testing::internal::GetUnitTestImpl; using testing::internal::GTestFlagSaver; +using testing::internal::HasDebugStringAndShortDebugString; using testing::internal::Int32FromEnvOrDie; -using testing::internal::IsAProtocolMessage; using testing::internal::IsContainer; using testing::internal::IsContainerTest; using testing::internal::IsNotContainer; @@ -7185,24 +7186,71 @@ GTEST_TEST(AlternativeNameTest, Works) { // GTEST_TEST is the same as TEST. class ConversionHelperBase {}; class ConversionHelperDerived : public ConversionHelperBase {}; -// Tests that IsAProtocolMessage::value is a compile-time constant. -TEST(IsAProtocolMessageTest, ValueIsCompileTimeConstant) { - GTEST_COMPILE_ASSERT_(IsAProtocolMessage<::proto2::MessageLite>::value, - const_true); - GTEST_COMPILE_ASSERT_(!IsAProtocolMessage::value, const_false); -} +struct HasDebugStringMethods { + std::string DebugString() const { return ""; } + std::string ShortDebugString() const { return ""; } +}; + +struct InheritsDebugStringMethods : public HasDebugStringMethods {}; + +struct WrongTypeDebugStringMethod { + std::string DebugString() const { return ""; } + int ShortDebugString() const { return 1; } +}; -// Tests that IsAProtocolMessage::value is true when T is -// proto2::Message or a sub-class of it. -TEST(IsAProtocolMessageTest, ValueIsTrueWhenTypeIsAProtocolMessage) { - EXPECT_TRUE(IsAProtocolMessage<::proto2::MessageLite>::value); +struct NotConstDebugStringMethod { + std::string DebugString() { return ""; } + std::string ShortDebugString() const { return ""; } +}; + +struct MissingDebugStringMethod { + std::string DebugString() { return ""; } +}; + +struct IncompleteType; + +// Tests that HasDebugStringAndShortDebugString::value is a compile-time +// constant. +TEST(HasDebugStringAndShortDebugStringTest, ValueIsCompileTimeConstant) { + GTEST_COMPILE_ASSERT_( + HasDebugStringAndShortDebugString::value, + const_true); + GTEST_COMPILE_ASSERT_( + HasDebugStringAndShortDebugString::value, + const_true); + GTEST_COMPILE_ASSERT_(HasDebugStringAndShortDebugString< + const InheritsDebugStringMethods>::value, + const_true); + GTEST_COMPILE_ASSERT_( + !HasDebugStringAndShortDebugString::value, + const_false); + GTEST_COMPILE_ASSERT_( + !HasDebugStringAndShortDebugString::value, + const_false); + GTEST_COMPILE_ASSERT_( + !HasDebugStringAndShortDebugString::value, + const_false); + GTEST_COMPILE_ASSERT_( + !HasDebugStringAndShortDebugString::value, const_false); + GTEST_COMPILE_ASSERT_(!HasDebugStringAndShortDebugString::value, + const_false); +} + +// Tests that HasDebugStringAndShortDebugString::value is true when T has +// needed methods. +TEST(HasDebugStringAndShortDebugStringTest, + ValueIsTrueWhenTypeHasDebugStringAndShortDebugString) { + EXPECT_TRUE( + HasDebugStringAndShortDebugString::value); } -// Tests that IsAProtocolMessage::value is false when T is neither -// ::proto2::Message nor a sub-class of it. -TEST(IsAProtocolMessageTest, ValueIsFalseWhenTypeIsNotAProtocolMessage) { - EXPECT_FALSE(IsAProtocolMessage::value); - EXPECT_FALSE(IsAProtocolMessage::value); +// Tests that HasDebugStringAndShortDebugString::value is false when T +// doesn't have needed methods. +TEST(HasDebugStringAndShortDebugStringTest, + ValueIsFalseWhenTypeIsNotAProtocolMessage) { + EXPECT_FALSE(HasDebugStringAndShortDebugString::value); + EXPECT_FALSE( + HasDebugStringAndShortDebugString::value); } // Tests GTEST_REMOVE_REFERENCE_AND_CONST_. @@ -7486,6 +7534,142 @@ TEST(FlatTuple, Basic) { EXPECT_EQ(5.1, tuple.Get<1>()); } +namespace { +std::string AddIntToString(int i, const std::string& s) { + return s + std::to_string(i); +} +} // namespace + +TEST(FlatTuple, Apply) { + using testing::internal::FlatTuple; + + FlatTuple tuple{5, "Hello"}; + + // Lambda. + EXPECT_TRUE(tuple.Apply([](int i, const std::string& s) -> bool { + return i == static_cast(s.size()); + })); + + // Function. + EXPECT_EQ(tuple.Apply(AddIntToString), "Hello5"); + + // Mutating operations. + tuple.Apply([](int& i, std::string& s) { + ++i; + s += s; + }); + EXPECT_EQ(tuple.Get<0>(), 6); + EXPECT_EQ(tuple.Get<1>(), "HelloHello"); +} + +struct ConstructionCounting { + ConstructionCounting() { ++default_ctor_calls; } + ~ConstructionCounting() { ++dtor_calls; } + ConstructionCounting(const ConstructionCounting&) { ++copy_ctor_calls; } + ConstructionCounting(ConstructionCounting&&) noexcept { ++move_ctor_calls; } + ConstructionCounting& operator=(const ConstructionCounting&) { + ++copy_assignment_calls; + return *this; + } + ConstructionCounting& operator=(ConstructionCounting&&) noexcept { + ++move_assignment_calls; + return *this; + } + + static void Reset() { + default_ctor_calls = 0; + dtor_calls = 0; + copy_ctor_calls = 0; + move_ctor_calls = 0; + copy_assignment_calls = 0; + move_assignment_calls = 0; + } + + static int default_ctor_calls; + static int dtor_calls; + static int copy_ctor_calls; + static int move_ctor_calls; + static int copy_assignment_calls; + static int move_assignment_calls; +}; + +int ConstructionCounting::default_ctor_calls = 0; +int ConstructionCounting::dtor_calls = 0; +int ConstructionCounting::copy_ctor_calls = 0; +int ConstructionCounting::move_ctor_calls = 0; +int ConstructionCounting::copy_assignment_calls = 0; +int ConstructionCounting::move_assignment_calls = 0; + +TEST(FlatTuple, ConstructorCalls) { + using testing::internal::FlatTuple; + + // Default construction. + ConstructionCounting::Reset(); + { FlatTuple tuple; } + EXPECT_EQ(ConstructionCounting::default_ctor_calls, 1); + EXPECT_EQ(ConstructionCounting::dtor_calls, 1); + EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0); + EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0); + + // Copy construction. + ConstructionCounting::Reset(); + { + ConstructionCounting elem; + FlatTuple tuple{elem}; + } + EXPECT_EQ(ConstructionCounting::default_ctor_calls, 1); + EXPECT_EQ(ConstructionCounting::dtor_calls, 2); + EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 1); + EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0); + EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0); + + // Move construction. + ConstructionCounting::Reset(); + { FlatTuple tuple{ConstructionCounting{}}; } + EXPECT_EQ(ConstructionCounting::default_ctor_calls, 1); + EXPECT_EQ(ConstructionCounting::dtor_calls, 2); + EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::move_ctor_calls, 1); + EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0); + EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0); + + // Copy assignment. + // TODO(ofats): it should be testing assignment operator of FlatTuple, not its + // elements + ConstructionCounting::Reset(); + { + FlatTuple tuple; + ConstructionCounting elem; + tuple.Get<0>() = elem; + } + EXPECT_EQ(ConstructionCounting::default_ctor_calls, 2); + EXPECT_EQ(ConstructionCounting::dtor_calls, 2); + EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 1); + EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0); + + // Move assignment. + // TODO(ofats): it should be testing assignment operator of FlatTuple, not its + // elements + ConstructionCounting::Reset(); + { + FlatTuple tuple; + tuple.Get<0>() = ConstructionCounting{}; + } + EXPECT_EQ(ConstructionCounting::default_ctor_calls, 2); + EXPECT_EQ(ConstructionCounting::dtor_calls, 2); + EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0); + EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0); + EXPECT_EQ(ConstructionCounting::move_assignment_calls, 1); + + ConstructionCounting::Reset(); +} + TEST(FlatTuple, ManyTypes) { using testing::internal::FlatTuple;