// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/base/template-utils.h" #include "test/unittests/test-utils.h" namespace v8 { namespace base { namespace template_utils_unittest { //////////////////////////// // Test make_array. //////////////////////////// namespace { template void CheckArrayEquals(const std::array& arr1, const std::array& arr2) { for (size_t i = 0; i < Size; ++i) { CHECK_EQ(arr1[i], arr2[i]); } } } // namespace TEST(TemplateUtilsTest, MakeArraySimple) { auto computed_array = base::make_array<3>([](int i) { return 1 + (i * 3); }); std::array expected{{1, 4, 7}}; CheckArrayEquals(computed_array, expected); } namespace { constexpr int doubleIntValue(int i) { return i * 2; } } // namespace TEST(TemplateUtilsTest, MakeArrayConstexpr) { constexpr auto computed_array = base::make_array<3>(doubleIntValue); constexpr std::array expected{{0, 2, 4}}; CheckArrayEquals(computed_array, expected); } //////////////////////////// // Test pass_value_or_ref. //////////////////////////// // Wrap into this helper struct, such that the type is printed on errors. template struct CheckIsSame { static_assert(std::is_same::value, "test failure"); }; #define TEST_PASS_VALUE_OR_REF0(remove_extend, expected, given) \ static_assert( \ sizeof(CheckIsSame::type>) > 0, \ "check") #define TEST_PASS_VALUE_OR_REF(expected, given) \ static_assert( \ sizeof(CheckIsSame::type>) > 0, \ "check") TEST_PASS_VALUE_OR_REF(int, int&); TEST_PASS_VALUE_OR_REF(int, int&&); TEST_PASS_VALUE_OR_REF(const char*, const char[14]); TEST_PASS_VALUE_OR_REF(const char*, const char*&&); TEST_PASS_VALUE_OR_REF(const char*, const char (&)[14]); TEST_PASS_VALUE_OR_REF(const std::string&, std::string); TEST_PASS_VALUE_OR_REF(const std::string&, std::string&); TEST_PASS_VALUE_OR_REF(const std::string&, const std::string&); TEST_PASS_VALUE_OR_REF(int, const int); TEST_PASS_VALUE_OR_REF(int, const int&); TEST_PASS_VALUE_OR_REF(const int*, const int*); TEST_PASS_VALUE_OR_REF(const int*, const int* const); TEST_PASS_VALUE_OR_REF0(false, const char[14], const char[14]); TEST_PASS_VALUE_OR_REF0(false, const char[14], const char (&)[14]); TEST_PASS_VALUE_OR_REF0(false, const std::string&, std::string); TEST_PASS_VALUE_OR_REF0(false, const std::string&, std::string&); TEST_PASS_VALUE_OR_REF0(false, const std::string&, const std::string&); TEST_PASS_VALUE_OR_REF0(false, int, const int); TEST_PASS_VALUE_OR_REF0(false, int, const int&); ////////////////////////////// // Test has_output_operator. ////////////////////////////// // Intrinsic types: static_assert(has_output_operator::value, "int can be output"); static_assert(has_output_operator::value, "void* can be output"); static_assert(has_output_operator::value, "int can be output"); // Classes: class TestClass1 {}; class TestClass2 {}; extern std::ostream& operator<<(std::ostream& str, const TestClass2&); class TestClass3 {}; extern std::ostream& operator<<(std::ostream& str, TestClass3); static_assert(!has_output_operator::value, "TestClass1 can not be output"); static_assert(has_output_operator::value, "non-const TestClass2 can be output"); static_assert(has_output_operator::value, "const TestClass2 can be output"); static_assert(has_output_operator::value, "non-const TestClass3 can be output"); static_assert(has_output_operator::value, "const TestClass3 can be output"); ////////////////////////////// // Test fold. ////////////////////////////// struct FoldAllSameType { constexpr uint32_t operator()(uint32_t a, uint32_t b) const { return a | b; } }; static_assert(base::fold(FoldAllSameType{}, 3, 6) == 7, "check fold"); // Test that it works if implicit conversion is needed for one of the // parameters. static_assert(base::fold(FoldAllSameType{}, uint8_t{1}, 256) == 257, "check correct type inference"); // Test a single parameter. static_assert(base::fold(FoldAllSameType{}, 25) == 25, "check folding a single argument"); TEST(TemplateUtilsTest, FoldDifferentType) { auto fn = [](std::string str, char c) { str.push_back(c); return str; }; CHECK_EQ(base::fold(fn, std::string("foo"), 'b', 'a', 'r'), "foobar"); } TEST(TemplateUtilsTest, FoldMoveOnlyType) { auto fn = [](std::unique_ptr str, char c) { str->push_back(c); return str; }; std::unique_ptr str = base::make_unique("foo"); std::unique_ptr folded = base::fold(fn, std::move(str), 'b', 'a', 'r'); CHECK_NULL(str); CHECK_NOT_NULL(folded); CHECK_EQ(*folded, "foobar"); } struct TemplatizedFoldFunctor { template std::tuple::type> operator()( std::tuple tup, T&& val) { return std::tuple_cat(std::move(tup), std::make_tuple(std::forward(val))); } }; TEST(TemplateUtilsTest, FoldToTuple) { auto input = std::make_tuple(char{'x'}, int{4}, double{3.2}, std::unique_ptr{}, std::string{"foo"}); auto result = base::fold(TemplatizedFoldFunctor{}, std::make_tuple(), std::get<0>(input), std::get<1>(input), std::get<2>(input), std::unique_ptr{}, std::get<4>(input)); static_assert(std::is_same::value, "the resulting tuple should have the same type as the input"); DCHECK_EQ(input, result); } } // namespace template_utils_unittest } // namespace base } // namespace v8