| // Copyright 2014 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/functional.h" |
| |
| #include <limits> |
| #include <set> |
| |
| #include "test/unittests/test-utils.h" |
| |
| namespace v8 { |
| namespace base { |
| |
| TEST(FunctionalTest, HashBool) { |
| hash<bool> h, h1, h2; |
| EXPECT_EQ(h1(true), h2(true)); |
| EXPECT_EQ(h1(false), h2(false)); |
| EXPECT_NE(h(true), h(false)); |
| } |
| |
| |
| TEST(FunctionalTest, HashFloatZero) { |
| hash<float> h; |
| EXPECT_EQ(h(0.0f), h(-0.0f)); |
| } |
| |
| |
| TEST(FunctionalTest, HashDoubleZero) { |
| hash<double> h; |
| EXPECT_EQ(h(0.0), h(-0.0)); |
| } |
| |
| namespace { |
| |
| inline int64_t GetRandomSeedFromFlag(int random_seed) { |
| return random_seed ? random_seed : TimeTicks::Now().ToInternalValue(); |
| } |
| |
| } // namespace |
| |
| template <typename T> |
| class FunctionalTest : public ::testing::Test { |
| public: |
| FunctionalTest() |
| : rng_(GetRandomSeedFromFlag(::v8::internal::FLAG_random_seed)) {} |
| ~FunctionalTest() override = default; |
| FunctionalTest(const FunctionalTest&) = delete; |
| FunctionalTest& operator=(const FunctionalTest&) = delete; |
| |
| RandomNumberGenerator* rng() { return &rng_; } |
| |
| private: |
| RandomNumberGenerator rng_; |
| }; |
| |
| using FunctionalTypes = |
| ::testing::Types<signed char, unsigned char, |
| short, // NOLINT(runtime/int) |
| unsigned short, // NOLINT(runtime/int) |
| int, unsigned int, long, // NOLINT(runtime/int) |
| unsigned long, // NOLINT(runtime/int) |
| long long, // NOLINT(runtime/int) |
| unsigned long long, // NOLINT(runtime/int) |
| int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, |
| int64_t, uint64_t, float, double>; |
| |
| TYPED_TEST_SUITE(FunctionalTest, FunctionalTypes); |
| |
| TYPED_TEST(FunctionalTest, EqualToImpliesSameHashCode) { |
| hash<TypeParam> h; |
| std::equal_to<TypeParam> e; |
| TypeParam values[32]; |
| this->rng()->NextBytes(values, sizeof(values)); |
| TRACED_FOREACH(TypeParam, v1, values) { |
| TRACED_FOREACH(TypeParam, v2, values) { |
| if (e(v1, v2)) { |
| EXPECT_EQ(h(v1), h(v2)); |
| } |
| } |
| } |
| } |
| |
| |
| TYPED_TEST(FunctionalTest, HashEqualsHashValue) { |
| for (int i = 0; i < 128; ++i) { |
| TypeParam v; |
| this->rng()->NextBytes(&v, sizeof(v)); |
| hash<TypeParam> h; |
| EXPECT_EQ(h(v), hash_value(v)); |
| } |
| } |
| |
| |
| TYPED_TEST(FunctionalTest, HashIsStateless) { |
| hash<TypeParam> h1, h2; |
| for (int i = 0; i < 128; ++i) { |
| TypeParam v; |
| this->rng()->NextBytes(&v, sizeof(v)); |
| EXPECT_EQ(h1(v), h2(v)); |
| } |
| } |
| |
| |
| TYPED_TEST(FunctionalTest, HashIsOkish) { |
| std::set<TypeParam> vs; |
| for (size_t i = 0; i < 128; ++i) { |
| TypeParam v; |
| this->rng()->NextBytes(&v, sizeof(v)); |
| vs.insert(v); |
| } |
| std::set<size_t> hs; |
| for (const auto& v : vs) { |
| hash<TypeParam> h; |
| hs.insert(h(v)); |
| } |
| EXPECT_LE(vs.size() / 4u, hs.size()); |
| } |
| |
| |
| TYPED_TEST(FunctionalTest, HashValueArrayUsesHashRange) { |
| TypeParam values[128]; |
| this->rng()->NextBytes(&values, sizeof(values)); |
| EXPECT_EQ(hash_range(values, values + arraysize(values)), hash_value(values)); |
| } |
| |
| |
| TYPED_TEST(FunctionalTest, BitEqualTo) { |
| bit_equal_to<TypeParam> pred; |
| for (size_t i = 0; i < 128; ++i) { |
| TypeParam v1, v2; |
| this->rng()->NextBytes(&v1, sizeof(v1)); |
| this->rng()->NextBytes(&v2, sizeof(v2)); |
| EXPECT_PRED2(pred, v1, v1); |
| EXPECT_PRED2(pred, v2, v2); |
| EXPECT_EQ(memcmp(&v1, &v2, sizeof(TypeParam)) == 0, pred(v1, v2)); |
| } |
| } |
| |
| |
| TYPED_TEST(FunctionalTest, BitEqualToImpliesSameBitHash) { |
| bit_hash<TypeParam> h; |
| bit_equal_to<TypeParam> e; |
| TypeParam values[32]; |
| this->rng()->NextBytes(&values, sizeof(values)); |
| TRACED_FOREACH(TypeParam, v1, values) { |
| TRACED_FOREACH(TypeParam, v2, values) { |
| if (e(v1, v2)) { |
| EXPECT_EQ(h(v1), h(v2)); |
| } |
| } |
| } |
| } |
| |
| |
| namespace { |
| |
| struct Foo { |
| int x; |
| double y; |
| }; |
| |
| |
| size_t hash_value(Foo const& v) { return hash_combine(v.x, v.y); } |
| |
| } // namespace |
| |
| |
| TEST(FunctionalTest, HashUsesArgumentDependentLookup) { |
| const int kIntValues[] = {std::numeric_limits<int>::min(), -1, 0, 1, 42, |
| std::numeric_limits<int>::max()}; |
| const double kDoubleValues[] = { |
| std::numeric_limits<double>::min(), -1, -0, 0, 1, |
| std::numeric_limits<double>::max()}; |
| TRACED_FOREACH(int, x, kIntValues) { |
| TRACED_FOREACH(double, y, kDoubleValues) { |
| hash<Foo> h; |
| Foo foo = {x, y}; |
| EXPECT_EQ(hash_combine(x, y), h(foo)); |
| } |
| } |
| } |
| |
| |
| TEST(FunctionalTest, BitEqualToFloat) { |
| bit_equal_to<float> pred; |
| EXPECT_FALSE(pred(0.0f, -0.0f)); |
| EXPECT_FALSE(pred(-0.0f, 0.0f)); |
| float const qNaN = std::numeric_limits<float>::quiet_NaN(); |
| float const sNaN = std::numeric_limits<float>::signaling_NaN(); |
| EXPECT_PRED2(pred, qNaN, qNaN); |
| EXPECT_PRED2(pred, sNaN, sNaN); |
| } |
| |
| |
| TEST(FunctionalTest, BitHashFloatDifferentForZeroAndMinusZero) { |
| bit_hash<float> h; |
| EXPECT_NE(h(0.0f), h(-0.0f)); |
| } |
| |
| |
| TEST(FunctionalTest, BitEqualToDouble) { |
| bit_equal_to<double> pred; |
| EXPECT_FALSE(pred(0.0, -0.0)); |
| EXPECT_FALSE(pred(-0.0, 0.0)); |
| double const qNaN = std::numeric_limits<double>::quiet_NaN(); |
| double const sNaN = std::numeric_limits<double>::signaling_NaN(); |
| EXPECT_PRED2(pred, qNaN, qNaN); |
| EXPECT_PRED2(pred, sNaN, sNaN); |
| } |
| |
| |
| TEST(FunctionalTest, BitHashDoubleDifferentForZeroAndMinusZero) { |
| bit_hash<double> h; |
| EXPECT_NE(h(0.0), h(-0.0)); |
| } |
| |
| } // namespace base |
| } // namespace v8 |