tint: Add hash randomization
Enable this in Kororo builds to ensure that output isn't dependent on hash values. Change-Id: Ib538385b53bebf5260186b14cea396dac15caf6c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113980 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
10182c46d9
commit
f2b86aaffb
|
@ -171,6 +171,7 @@ option_if_not_defined(TINT_BUILD_REMOTE_COMPILE "Build the remote-compile tool f
|
|||
option_if_not_defined(TINT_ENABLE_BREAK_IN_DEBUGGER "Enable tint::debugger::Break()" OFF)
|
||||
option_if_not_defined(TINT_CHECK_CHROMIUM_STYLE "Check for [chromium-style] issues during build" OFF)
|
||||
option_if_not_defined(TINT_SYMBOL_STORE_DEBUG_NAME "Enable storing of name in tint::ast::Symbol to help debugging the AST" OFF)
|
||||
option_if_not_defined(TINT_RANDOMIZE_HASHES "Randomize the hash seed value to detect non-deterministic output" OFF)
|
||||
|
||||
# Recommended setting for compability with future abseil releases.
|
||||
set(ABSL_PROPAGATE_CXX_STD ON)
|
||||
|
@ -598,6 +599,15 @@ function(tint_default_compile_options TARGET)
|
|||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TINT_RANDOMIZE_HASHES)
|
||||
if(NOT DEFINED TINT_HASH_SEED)
|
||||
string(RANDOM LENGTH 16 ALPHABET "0123456789abcdef" seed)
|
||||
set(TINT_HASH_SEED "0x${seed}" CACHE STRING "Tint hash seed value")
|
||||
message("Using TINT_HASH_SEED: ${TINT_HASH_SEED}")
|
||||
endif()
|
||||
target_compile_definitions(${TARGET} PUBLIC "-DTINT_HASH_SEED=${TINT_HASH_SEED}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
################################################################################
|
||||
|
|
|
@ -129,6 +129,7 @@ if [ "$BUILD_SYSTEM" == "cmake" ]; then
|
|||
COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_MSL_WRITER=1"
|
||||
COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_SPV_WRITER=1"
|
||||
COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_WGSL_WRITER=1"
|
||||
COMMON_CMAKE_FLAGS+=" -DTINT_RANDOMIZE_HASHES=1"
|
||||
|
||||
if [ "$BUILD_TOOLCHAIN" == "clang" ]; then
|
||||
using clang-10.0.0
|
||||
|
|
|
@ -109,7 +109,20 @@ call :status "Configuring build system"
|
|||
@echo on
|
||||
mkdir %BUILD_DIR%
|
||||
cd /d %BUILD_DIR%
|
||||
set COMMON_CMAKE_FLAGS=-DTINT_BUILD_DOCS=O -DTINT_BUILD_BENCHMARKS=1 -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DTINT_BUILD_BENCHMARKS=1 -DTINT_BUILD_SPV_READER=1 -DTINT_BUILD_WGSL_READER=1 -DTINT_BUILD_GLSL_WRITER=1 -DTINT_BUILD_HLSL_WRITER=1 -DTINT_BUILD_MSL_WRITER=1 -DTINT_BUILD_SPV_WRITER=1 -DTINT_BUILD_WGSL_WRITER=1
|
||||
set COMMON_CMAKE_FLAGS= ^
|
||||
-DTINT_BUILD_DOCS=O ^
|
||||
-DTINT_BUILD_BENCHMARKS=1 ^
|
||||
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
|
||||
-DTINT_BUILD_BENCHMARKS=1 ^
|
||||
-DTINT_BUILD_SPV_READER=1 ^
|
||||
-DTINT_BUILD_WGSL_READER=1 ^
|
||||
-DTINT_BUILD_GLSL_WRITER=1 ^
|
||||
-DTINT_BUILD_HLSL_WRITER=1 ^
|
||||
-DTINT_BUILD_MSL_WRITER=1 ^
|
||||
-DTINT_BUILD_SPV_WRITER=1 ^
|
||||
-DTINT_BUILD_WGSL_WRITER=1 ^
|
||||
-DTINT_RANDOMIZE_HASHES=1
|
||||
|
||||
@echo off
|
||||
|
||||
call :status "Building dawn"
|
||||
|
|
|
@ -34,17 +34,6 @@ TEST_F(SemStructTest, Creation) {
|
|||
EXPECT_EQ(s->SizeNoPadding(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(SemStructTest, Hash) {
|
||||
auto* a_impl = create<ast::Struct>(Sym("a"), utils::Empty, utils::Empty);
|
||||
auto* a = create<sem::Struct>(a_impl, a_impl->source, a_impl->name, utils::Empty,
|
||||
4u /* align */, 4u /* size */, 4u /* size_no_padding */);
|
||||
auto* b_impl = create<ast::Struct>(Sym("b"), utils::Empty, utils::Empty);
|
||||
auto* b = create<sem::Struct>(b_impl, b_impl->source, b_impl->name, utils::Empty,
|
||||
4u /* align */, 4u /* size */, 4u /* size_no_padding */);
|
||||
|
||||
EXPECT_NE(a->Hash(), b->Hash());
|
||||
}
|
||||
|
||||
TEST_F(SemStructTest, Equals) {
|
||||
auto* a_impl = create<ast::Struct>(Sym("a"), utils::Empty, utils::Empty);
|
||||
auto* a = create<sem::Struct>(a_impl, a_impl->source, a_impl->name, utils::Empty,
|
||||
|
|
|
@ -74,18 +74,8 @@ TEST_F(ArrayTest, CreateRuntimeArray) {
|
|||
TEST_F(ArrayTest, Hash) {
|
||||
auto* a = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 8u, 32u, 16u);
|
||||
auto* b = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 8u, 32u, 16u);
|
||||
auto* c = create<Array>(create<U32>(), create<ConstantArrayCount>(3u), 4u, 8u, 32u, 16u);
|
||||
auto* d = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 5u, 8u, 32u, 16u);
|
||||
auto* e = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 9u, 32u, 16u);
|
||||
auto* f = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 8u, 33u, 16u);
|
||||
auto* g = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 8u, 33u, 17u);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
EXPECT_NE(a->Hash(), e->Hash());
|
||||
EXPECT_NE(a->Hash(), f->Hash());
|
||||
EXPECT_NE(a->Hash(), g->Hash());
|
||||
}
|
||||
|
||||
TEST_F(ArrayTest, Equals) {
|
||||
|
|
|
@ -33,9 +33,7 @@ TEST_F(AtomicTest, Creation) {
|
|||
TEST_F(AtomicTest, Hash) {
|
||||
auto* a = create<Atomic>(create<I32>());
|
||||
auto* b = create<Atomic>(create<I32>());
|
||||
auto* c = create<Atomic>(create<U32>());
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
}
|
||||
|
||||
TEST_F(AtomicTest, Equals) {
|
||||
|
|
|
@ -36,10 +36,8 @@ TEST_F(DepthTextureTest, Creation) {
|
|||
TEST_F(DepthTextureTest, Hash) {
|
||||
auto* a = create<DepthTexture>(ast::TextureDimension::k2d);
|
||||
auto* b = create<DepthTexture>(ast::TextureDimension::k2d);
|
||||
auto* c = create<DepthTexture>(ast::TextureDimension::k2dArray);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
}
|
||||
|
||||
TEST_F(DepthTextureTest, Equals) {
|
||||
|
|
|
@ -40,14 +40,8 @@ TEST_F(MatrixTest, Creation) {
|
|||
TEST_F(MatrixTest, Hash) {
|
||||
auto* a = create<Matrix>(create<Vector>(create<I32>(), 3u), 4u);
|
||||
auto* b = create<Matrix>(create<Vector>(create<I32>(), 3u), 4u);
|
||||
auto* c = create<Matrix>(create<Vector>(create<F32>(), 3u), 4u);
|
||||
auto* d = create<Matrix>(create<Vector>(create<I32>(), 2u), 4u);
|
||||
auto* e = create<Matrix>(create<Vector>(create<I32>(), 3u), 2u);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
EXPECT_NE(a->Hash(), e->Hash());
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, Equals) {
|
||||
|
|
|
@ -38,11 +38,7 @@ TEST_F(MultisampledTextureTest, Creation) {
|
|||
TEST_F(MultisampledTextureTest, Hash) {
|
||||
auto* a = create<MultisampledTexture>(ast::TextureDimension::k2d, create<F32>());
|
||||
auto* b = create<MultisampledTexture>(ast::TextureDimension::k2d, create<F32>());
|
||||
auto* c = create<MultisampledTexture>(ast::TextureDimension::k3d, create<F32>());
|
||||
auto* d = create<MultisampledTexture>(ast::TextureDimension::k2d, create<I32>());
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
}
|
||||
|
||||
TEST_F(MultisampledTextureTest, Equals) {
|
||||
|
|
|
@ -40,14 +40,8 @@ TEST_F(PointerTest, Creation) {
|
|||
TEST_F(PointerTest, Hash) {
|
||||
auto* a = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
|
||||
auto* b = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
|
||||
auto* c = create<Pointer>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
|
||||
auto* d = create<Pointer>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
|
||||
auto* e = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
EXPECT_NE(a->Hash(), e->Hash());
|
||||
}
|
||||
|
||||
TEST_F(PointerTest, Equals) {
|
||||
|
|
|
@ -46,16 +46,8 @@ TEST_F(ReferenceTest, Hash) {
|
|||
create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
|
||||
auto* b =
|
||||
create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
|
||||
auto* c =
|
||||
create<Reference>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
|
||||
auto* d =
|
||||
create<Reference>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
|
||||
auto* e = create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
EXPECT_NE(a->Hash(), e->Hash());
|
||||
}
|
||||
|
||||
TEST_F(ReferenceTest, Equals) {
|
||||
|
|
|
@ -41,12 +41,8 @@ TEST_F(SampledTextureTest, Creation) {
|
|||
TEST_F(SampledTextureTest, Hash) {
|
||||
auto* a = create<SampledTexture>(ast::TextureDimension::kCube, create<F32>());
|
||||
auto* b = create<SampledTexture>(ast::TextureDimension::kCube, create<F32>());
|
||||
auto* c = create<SampledTexture>(ast::TextureDimension::k2d, create<F32>());
|
||||
auto* d = create<SampledTexture>(ast::TextureDimension::kCube, create<I32>());
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
}
|
||||
|
||||
TEST_F(SampledTextureTest, Equals) {
|
||||
|
|
|
@ -39,10 +39,8 @@ TEST_F(SamplerTest, Creation) {
|
|||
TEST_F(SamplerTest, Hash) {
|
||||
auto* a = create<Sampler>(ast::SamplerKind::kSampler);
|
||||
auto* b = create<Sampler>(ast::SamplerKind::kSampler);
|
||||
auto* c = create<Sampler>(ast::SamplerKind::kComparisonSampler);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
}
|
||||
|
||||
TEST_F(SamplerTest, Equals) {
|
||||
|
|
|
@ -55,17 +55,8 @@ TEST_F(StorageTextureTest, Hash) {
|
|||
ast::Access::kReadWrite);
|
||||
auto* b = Create(ast::TextureDimension::kCube, ast::TexelFormat::kRgba32Float,
|
||||
ast::Access::kReadWrite);
|
||||
auto* c =
|
||||
Create(ast::TextureDimension::k2d, ast::TexelFormat::kRgba32Float, ast::Access::kReadWrite);
|
||||
auto* d =
|
||||
Create(ast::TextureDimension::kCube, ast::TexelFormat::kR32Float, ast::Access::kReadWrite);
|
||||
auto* e =
|
||||
Create(ast::TextureDimension::kCube, ast::TexelFormat::kRgba32Float, ast::Access::kRead);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
EXPECT_NE(a->Hash(), e->Hash());
|
||||
}
|
||||
|
||||
TEST_F(StorageTextureTest, Equals) {
|
||||
|
|
|
@ -31,15 +31,6 @@ TEST_F(TypeStructTest, Creation) {
|
|||
EXPECT_EQ(s->SizeNoPadding(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Hash) {
|
||||
auto* a = create<Struct>(Source{}, Sym("a"), utils::Empty, 4u /* align */, 4u /* size */,
|
||||
4u /* size_no_padding */);
|
||||
auto* b = create<Struct>(Source{}, Sym("b"), utils::Empty, 4u /* align */, 4u /* size */,
|
||||
4u /* size_no_padding */);
|
||||
|
||||
EXPECT_NE(a->Hash(), b->Hash());
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Equals) {
|
||||
auto* a = create<Struct>(Source{}, Sym("a"), utils::Empty, 4u /* align */, 4u /* size */,
|
||||
4u /* size_no_padding */);
|
||||
|
|
|
@ -37,12 +37,8 @@ TEST_F(VectorTest, Creation) {
|
|||
TEST_F(VectorTest, Hash) {
|
||||
auto* a = create<Vector>(create<I32>(), 2u);
|
||||
auto* b = create<Vector>(create<I32>(), 2u);
|
||||
auto* c = create<Vector>(create<F32>(), 2u);
|
||||
auto* d = create<Vector>(create<F32>(), 3u);
|
||||
|
||||
EXPECT_EQ(a->Hash(), b->Hash());
|
||||
EXPECT_NE(a->Hash(), c->Hash());
|
||||
EXPECT_NE(a->Hash(), d->Hash());
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, Equals) {
|
||||
|
|
|
@ -192,7 +192,6 @@ TEST(EnumSetTest, InequalityEnum) {
|
|||
TEST(EnumSetTest, Hash) {
|
||||
auto hash = [&](EnumSet<E> s) { return std::hash<EnumSet<E>>()(s); };
|
||||
EXPECT_EQ(hash(EnumSet<E>(E::A, E::B)), hash(EnumSet<E>(E::A, E::B)));
|
||||
EXPECT_NE(hash(EnumSet<E>(E::A, E::B)), hash(EnumSet<E>(E::A, E::C)));
|
||||
}
|
||||
|
||||
TEST(EnumSetTest, Value) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/utils/crc32.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
@ -37,14 +38,26 @@ struct HashCombineOffset {};
|
|||
template <>
|
||||
struct HashCombineOffset<4> {
|
||||
/// @returns the seed bias value for HashCombine()
|
||||
static constexpr inline uint32_t value() { return 0x7f4a7c16; }
|
||||
static constexpr inline uint32_t value() {
|
||||
constexpr uint32_t base = 0x7f4a7c16;
|
||||
#ifdef TINT_HASH_SEED
|
||||
return base ^ static_cast<uint32_t>(TINT_HASH_SEED);
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialization of HashCombineOffset for size_t == 8.
|
||||
template <>
|
||||
struct HashCombineOffset<8> {
|
||||
/// @returns the seed bias value for HashCombine()
|
||||
static constexpr inline uint64_t value() { return 0x9e3779b97f4a7c16; }
|
||||
static constexpr inline uint64_t value() {
|
||||
constexpr uint64_t base = 0x9e3779b97f4a7c16;
|
||||
#ifdef TINT_HASH_SEED
|
||||
return base ^ static_cast<uint64_t>(TINT_HASH_SEED);
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
@ -76,6 +89,9 @@ struct Hasher<T*> {
|
|||
/// @returns a hash of the pointer
|
||||
size_t operator()(T* ptr) const {
|
||||
auto hash = std::hash<T*>()(ptr);
|
||||
#ifdef TINT_HASH_SEED
|
||||
hash ^= static_cast<uint32_t>(TINT_HASH_SEED);
|
||||
#endif
|
||||
return hash ^ (hash >> 4);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,28 +26,19 @@ namespace {
|
|||
|
||||
TEST(HashTests, Basic) {
|
||||
EXPECT_EQ(Hash(123), Hash(123));
|
||||
EXPECT_NE(Hash(123), Hash(321));
|
||||
EXPECT_EQ(Hash(123, 456), Hash(123, 456));
|
||||
EXPECT_NE(Hash(123, 456), Hash(456, 123));
|
||||
EXPECT_NE(Hash(123, 456), Hash(123));
|
||||
EXPECT_EQ(Hash(123, 456, false), Hash(123, 456, false));
|
||||
EXPECT_NE(Hash(123, 456, false), Hash(123, 456));
|
||||
EXPECT_EQ(Hash(std::string("hello")), Hash(std::string("hello")));
|
||||
EXPECT_NE(Hash(std::string("hello")), Hash(std::string("world")));
|
||||
}
|
||||
|
||||
TEST(HashTests, StdVector) {
|
||||
EXPECT_EQ(Hash(std::vector<int>({})), Hash(std::vector<int>({})));
|
||||
EXPECT_EQ(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 3})));
|
||||
EXPECT_NE(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 4})));
|
||||
EXPECT_NE(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 3, 4})));
|
||||
}
|
||||
|
||||
TEST(HashTests, TintVector) {
|
||||
EXPECT_EQ(Hash(Vector<int, 0>({})), Hash(Vector<int, 0>({})));
|
||||
EXPECT_EQ(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 3})));
|
||||
EXPECT_NE(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 4})));
|
||||
EXPECT_NE(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 3, 4})));
|
||||
EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 4>({1, 2, 3})));
|
||||
EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 2>({1, 2, 3})));
|
||||
}
|
||||
|
@ -55,8 +46,6 @@ TEST(HashTests, TintVector) {
|
|||
TEST(HashTests, Tuple) {
|
||||
EXPECT_EQ(Hash(std::make_tuple(1)), Hash(std::make_tuple(1)));
|
||||
EXPECT_EQ(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 3)));
|
||||
EXPECT_NE(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 4)));
|
||||
EXPECT_NE(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 3, 4)));
|
||||
}
|
||||
|
||||
TEST(HashTests, UnorderedKeyWrapper) {
|
||||
|
|
|
@ -388,11 +388,8 @@ TEST(Hashmap, HashSameSize) {
|
|||
Hashmap<int, std::string, 8> b;
|
||||
EXPECT_EQ(Hash(a), Hash(b));
|
||||
a.Add(1, "one");
|
||||
EXPECT_NE(Hash(a), Hash(b));
|
||||
b.Add(2, "two");
|
||||
EXPECT_NE(Hash(a), Hash(b));
|
||||
a.Add(2, "two");
|
||||
EXPECT_NE(Hash(a), Hash(b));
|
||||
b.Add(1, "one");
|
||||
EXPECT_EQ(Hash(a), Hash(b));
|
||||
}
|
||||
|
@ -402,11 +399,8 @@ TEST(Hashmap, HashDifferentSize) {
|
|||
Hashmap<int, std::string, 4> b;
|
||||
EXPECT_EQ(Hash(a), Hash(b));
|
||||
a.Add(1, "one");
|
||||
EXPECT_NE(Hash(a), Hash(b));
|
||||
b.Add(2, "two");
|
||||
EXPECT_NE(Hash(a), Hash(b));
|
||||
a.Add(2, "two");
|
||||
EXPECT_NE(Hash(a), Hash(b));
|
||||
b.Add(1, "one");
|
||||
EXPECT_EQ(Hash(a), Hash(b));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue