blob: 7cc029651acdfb9e1c3750c30c4dcc5dc0ed5028 [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_containers/functional.h"
#include <string>
#include <variant>
#include "pw_unit_test/framework.h"
namespace {
TEST(Hash, BasicTypes) {
pw::Hash hasher;
EXPECT_NE(hasher(1), hasher(2));
EXPECT_NE(hasher(1.0), hasher(2.0));
EXPECT_NE(hasher('a'), hasher('b'));
EXPECT_NE(hasher(true), hasher(false));
EXPECT_EQ(hasher(1), hasher(1));
EXPECT_EQ(hasher(1.0), hasher(1.0));
EXPECT_EQ(hasher('a'), hasher('a'));
EXPECT_EQ(hasher(true), hasher(true));
}
TEST(Hash, String) {
pw::Hash hasher;
const char* str1 = "hello";
const char* str2 = "hello";
const char* str3 = "world";
EXPECT_EQ(hasher(std::string(str1)), hasher(std::string(str2)));
EXPECT_NE(hasher(std::string(str1)), hasher(std::string(str3)));
}
TEST(Hash, Pointers) {
pw::Hash hasher;
int x = 5;
int y = 5;
int* p1 = &x;
int* p2 = &x;
int* p3 = &y;
int* null_ptr = nullptr;
EXPECT_EQ(hasher(p1), hasher(p2));
EXPECT_NE(hasher(p1), hasher(p3));
EXPECT_EQ(hasher(null_ptr), hasher(nullptr));
EXPECT_NE(hasher(p1), hasher(null_ptr));
}
TEST(Hash, DifferentSeeds) {
pw::Hash hasher1(17);
pw::Hash hasher2(42);
EXPECT_NE(hasher1(123), hasher2(123));
EXPECT_NE(hasher1(std::string("hello")), hasher2(std::string("hello")));
}
struct CustomTypeA {
int x;
double y;
template <typename H>
friend H PwHashValue(H h, const CustomTypeA& t) {
return H::combine(std::move(h), t.x, t.y);
}
};
TEST(Hash, CustomTypePwHashValue) {
pw::Hash hasher;
CustomTypeA p1{1, 2.0};
CustomTypeA p2{1, 2.0};
CustomTypeA p3{2, 1.0};
EXPECT_EQ(hasher(p1), hasher(p2));
EXPECT_NE(hasher(p1), hasher(p3));
}
struct CustomTypeB {
int x;
double y;
};
} // namespace
namespace std {
template <>
struct hash<CustomTypeB> {
size_t operator()(const CustomTypeB& t) const {
pw::Hash hasher;
return hasher(t.x) ^ hasher(t.y);
}
};
} // namespace std
namespace {
TEST(Hash, CustomTypeStdHash) {
pw::Hash hasher;
CustomTypeB p1{1, 2};
CustomTypeB p2{1, 2};
CustomTypeB p3{2, 1};
EXPECT_EQ(hasher(p1), hasher(p2));
EXPECT_NE(hasher(p1), hasher(p3));
}
TEST(Hash, Combine) {
pw::HashState h1 = pw::HashState();
h1 = pw::HashState::combine(std::move(h1), 1);
h1 = pw::HashState::combine(std::move(h1), 2);
h1 = pw::HashState::combine(std::move(h1), 3);
size_t hash1 = h1.finalize();
pw::HashState h2 = pw::HashState();
h2 = pw::HashState::combine(std::move(h2), 1);
h2 = pw::HashState::combine(std::move(h2), 2);
h2 = pw::HashState::combine(std::move(h2), 3);
size_t hash2 = h2.finalize();
pw::HashState h3 = pw::HashState();
h3 = pw::HashState::combine(std::move(h3), 3);
h3 = pw::HashState::combine(std::move(h3), 2);
h3 = pw::HashState::combine(std::move(h3), 1);
size_t hash3 = h3.finalize();
EXPECT_EQ(hash1, hash2);
EXPECT_NE(hash1, hash3);
std::string s1 = "hello";
std::string s2 = "world";
pw::HashState h4 = pw::HashState();
h4 = pw::HashState::combine(std::move(h4), s1);
h4 = pw::HashState::combine(std::move(h4), 5);
size_t hash4 = h4.finalize();
pw::HashState h5 = pw::HashState();
h5 = pw::HashState::combine(std::move(h5), s1);
h5 = pw::HashState::combine(std::move(h5), 5);
size_t hash5 = h5.finalize();
pw::HashState h6 = pw::HashState();
h6 = pw::HashState::combine(std::move(h6), s2);
h6 = pw::HashState::combine(std::move(h6), 5);
size_t hash6 = h6.finalize();
pw::HashState h7 = pw::HashState();
h7 = pw::HashState::combine(std::move(h7), s1);
h7 = pw::HashState::combine(std::move(h7), 6);
size_t hash7 = h7.finalize();
EXPECT_EQ(hash4, hash5);
EXPECT_NE(hash4, hash6);
EXPECT_NE(hash4, hash7);
}
TEST(Hash, CombineContiguous) {
int data1[] = {1, 2, 3};
int data2[] = {1, 2, 3};
int data3[] = {3, 2, 1};
pw::HashState h1;
h1 = pw::HashState::combine_contiguous(std::move(h1), data1, 3);
size_t hash1 = h1.finalize();
pw::HashState h2;
h2 = pw::HashState::combine_contiguous(std::move(h2), data2, 3);
size_t hash2 = h2.finalize();
pw::HashState h3;
h3 = pw::HashState::combine_contiguous(std::move(h3), data3, 3);
size_t hash3 = h3.finalize();
EXPECT_EQ(hash1, hash2);
EXPECT_NE(hash1, hash3);
const char* str1 = "hello";
const char* str2 = "hello";
const char* str3 = "world";
pw::HashState h4;
h4 = pw::HashState::combine_contiguous(std::move(h4), str1, 5);
size_t hash4 = h4.finalize();
pw::HashState h5;
h5 = pw::HashState::combine_contiguous(std::move(h5), str2, 5);
size_t hash5 = h5.finalize();
pw::HashState h6;
h6 = pw::HashState::combine_contiguous(std::move(h6), str3, 5);
size_t hash6 = h6.finalize();
EXPECT_EQ(hash4, hash5);
EXPECT_NE(hash4, hash6);
}
TEST(Hash, CombineUnordered) {
int data1[] = {1, 2, 3};
int data2[] = {3, 2, 1};
int data3[] = {1, 2, 4};
pw::HashState h1;
h1 = pw::HashState::combine_unordered(
std::move(h1), std::begin(data1), std::end(data1));
size_t hash1 = h1.finalize();
pw::HashState h2;
h2 = pw::HashState::combine_unordered(
std::move(h2), std::begin(data2), std::end(data2));
size_t hash2 = h2.finalize();
pw::HashState h3;
h3 = pw::HashState::combine_unordered(
std::move(h3), std::begin(data3), std::end(data3));
size_t hash3 = h3.finalize();
EXPECT_EQ(hash1, hash2);
EXPECT_NE(hash1, hash3);
}
enum class MyEnum { kValue1, kValue2 };
TEST(Hash, Enum) {
pw::Hash hasher;
EXPECT_EQ(hasher(MyEnum::kValue1), hasher(MyEnum::kValue1));
EXPECT_NE(hasher(MyEnum::kValue1), hasher(MyEnum::kValue2));
}
TEST(Hash, Variant) {
pw::Hash hasher;
std::variant<int, std::string> v1 = 42;
std::variant<int, std::string> v2 = 42;
std::variant<int, std::string> v3 = "42";
std::variant<int, std::string> v4 = "hello";
EXPECT_EQ(hasher(v1), hasher(v2));
EXPECT_NE(hasher(v1), hasher(v3));
EXPECT_NE(hasher(v3), hasher(v4));
std::variant<int, CustomTypeA> v5 = CustomTypeA{1, 2.0};
std::variant<int, CustomTypeA> v6 = CustomTypeA{1, 2.0};
std::variant<int, CustomTypeA> v7 = CustomTypeA{2, 1.0};
std::variant<int, CustomTypeA> v8 = 1;
EXPECT_EQ(hasher(v5), hasher(v6));
EXPECT_NE(hasher(v5), hasher(v7));
EXPECT_NE(hasher(v5), hasher(v8));
std::variant<int, int> v9(std::in_place_index<0>, 42);
std::variant<int, int> v10(std::in_place_index<1>, 42);
EXPECT_NE(hasher(v9), hasher(v10));
EXPECT_EQ(std::get<0>(v9), std::get<1>(v10));
}
TEST(EqualTo, BasicTypes) {
pw::EqualTo equal_to;
EXPECT_TRUE(equal_to(1, 1));
EXPECT_FALSE(equal_to(1, 2));
EXPECT_TRUE(equal_to(1.0, 1.0));
EXPECT_FALSE(equal_to(1.0, 2.0));
EXPECT_TRUE(equal_to('a', 'a'));
EXPECT_FALSE(equal_to('a', 'b'));
EXPECT_TRUE(equal_to(true, true));
EXPECT_FALSE(equal_to(true, false));
const char* str1 = "hello";
const char* str2 = "hello";
const char* str3 = "world";
EXPECT_TRUE(equal_to(std::string(str1), std::string(str2)));
EXPECT_FALSE(equal_to(std::string(str1), std::string(str3)));
}
TEST(EqualTo, MixedTypes) {
pw::EqualTo equal_to;
EXPECT_TRUE(equal_to(1, 1L));
EXPECT_TRUE(equal_to(1, 1.0));
EXPECT_FALSE(equal_to(1, 2.0));
EXPECT_TRUE(equal_to(0, false));
EXPECT_TRUE(equal_to(1, true));
EXPECT_FALSE(equal_to(2, true));
}
TEST(EqualTo, Pointers) {
pw::EqualTo equal_to;
int x = 5;
int y = 5;
int* p1 = &x;
int* p2 = &x;
int* p3 = &y;
int* null_ptr = nullptr;
EXPECT_TRUE(equal_to(p1, p2));
EXPECT_FALSE(equal_to(p1, p3));
EXPECT_TRUE(equal_to(null_ptr, nullptr));
EXPECT_FALSE(equal_to(p1, null_ptr));
}
template <typename T>
struct Comparable {
T value;
bool operator==(const Comparable<T>& other) const {
return value == other.value;
}
};
template <typename T>
bool operator==(const Comparable<T>& lhs, const T& rhs) {
return lhs.value == rhs;
}
template <typename T>
bool operator==(const T& lhs, const Comparable<T>& rhs) {
return lhs == rhs.value;
}
TEST(EqualTo, UserDefinedTypes) {
pw::EqualTo equal_to;
Comparable<int> c1{1};
Comparable<int> c2{1};
Comparable<int> c3{2};
EXPECT_TRUE(equal_to(c1, c2));
EXPECT_FALSE(equal_to(c1, c3));
EXPECT_TRUE(equal_to(c1, 1));
EXPECT_FALSE(equal_to(c1, 2));
EXPECT_TRUE(equal_to(1, c1));
EXPECT_FALSE(equal_to(2, c1));
Comparable<double> cd1{1.0};
Comparable<double> cd2{1.0};
Comparable<double> cd3{2.0};
EXPECT_TRUE(equal_to(cd1, cd2));
EXPECT_FALSE(equal_to(cd1, cd3));
EXPECT_TRUE(equal_to(cd1, 1.0));
EXPECT_FALSE(equal_to(cd1, 2.0));
EXPECT_TRUE(equal_to(1.0, cd1));
EXPECT_FALSE(equal_to(2.0, cd1));
Comparable<std::string> cs1{"hello"};
Comparable<std::string> cs2{"hello"};
Comparable<std::string> cs3{"world"};
EXPECT_TRUE(equal_to(cs1, cs2));
EXPECT_FALSE(equal_to(cs1, cs3));
EXPECT_TRUE(equal_to(cs1, std::string("hello")));
EXPECT_FALSE(equal_to(cs1, std::string("world")));
EXPECT_TRUE(equal_to(std::string("hello"), cs1));
EXPECT_FALSE(equal_to(std::string("world"), cs1));
}
} // namespace