blob: 9bb776222bc25adaaa63db4fde3a5020759a0377 [file]
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Freezing.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "FreezeHooksTestSupport.hpp"
#include "Memory.h"
#include "ObjectTestSupport.hpp"
#include "Utils.hpp"
#include "TestSupport.hpp"
using namespace kotlin;
using ::testing::_;
namespace {
struct WithFreezeHook {
static constexpr bool hasFreezeHook = true;
};
struct NoFreezeHook {
static constexpr bool hasFreezeHook = false;
};
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
struct Payload {
ObjHeader* field1;
ObjHeader* field2;
ObjHeader* field3;
static constexpr std::array kFields{
&Payload::field1,
&Payload::field2,
&Payload::field3,
};
};
template <typename Payload, typename Traits>
class ObjectHolder : private Pinned {
public:
static test_support::TypeInfoHolder::ObjectBuilder<Payload> TypeBuilder() {
auto builder = test_support::TypeInfoHolder::ObjectBuilder<Payload>();
if (Traits::hasFreezeHook) {
builder.addFlag(TF_HAS_FREEZE_HOOK);
}
return builder;
}
ObjectHolder() : type_(TypeBuilder()), object_(type_.typeInfo()) {}
~ObjectHolder() {
if (header()->has_meta_object()) {
ObjHeader::destroyMetaObject(header());
}
}
ObjHeader* header() { return object_.header(); }
ObjHeader*& operator[](size_t field) { return object_.fields()[field]; }
void MakePermanent() { header()->typeInfoOrMeta_ = setPointerBits(header()->typeInfoOrMeta_, OBJECT_TAG_PERMANENT_CONTAINER); }
private:
test_support::TypeInfoHolder type_;
test_support::Object<Payload> object_;
};
// Arrays types are predetermined, and none of them have freeze hooks.
template <size_t Elements>
class ArrayHolder : private Pinned {
public:
ArrayHolder() {}
~ArrayHolder() {
if (header()->has_meta_object()) {
ObjHeader::destroyMetaObject(header());
}
}
ObjHeader* header() { return array_.header(); }
ObjHeader*& operator[](size_t index) { return array_.elements()[index]; }
void MakePermanent() { header()->typeInfoOrMeta_ = setPointerBits(header()->typeInfoOrMeta_, OBJECT_TAG_PERMANENT_CONTAINER); }
private:
test_support::ObjectArray<Elements> array_;
};
class FreezingTest : public testing::Test {
public:
testing::MockFunction<void(ObjHeader*)>& freezeHook() { return freezeHooks_.freezeHook(); }
private:
FreezeHooksTestSupport freezeHooks_;
};
class TypesNames {
public:
template <typename T>
static std::string GetName(int i) {
switch (i) {
case 0:
return "object";
case 1:
return "array";
default:
return "unknown";
}
}
};
template <typename T>
class FreezingEmptyNoHookTest : public FreezingTest {};
using EmptyNoHookTypes = testing::Types<ObjectHolder<EmptyPayload, NoFreezeHook>, ArrayHolder<0>>;
TYPED_TEST_SUITE(FreezingEmptyNoHookTest, EmptyNoHookTypes, TypesNames);
template <typename T>
class FreezingEmptyWithHookTest : public FreezingTest {};
using EmptyWithHookTypes = testing::Types<ObjectHolder<EmptyPayload, WithFreezeHook>>;
TYPED_TEST_SUITE(FreezingEmptyWithHookTest, EmptyWithHookTypes, TypesNames);
template <typename T>
class FreezingNoHookTest : public FreezingTest {};
using NoHookTypes = testing::Types<ObjectHolder<Payload, NoFreezeHook>, ArrayHolder<3>>;
TYPED_TEST_SUITE(FreezingNoHookTest, NoHookTypes, TypesNames);
template <typename T>
class FreezingWithHookTest : public FreezingTest {};
using WithHookTypes = testing::Types<ObjectHolder<Payload, WithFreezeHook>>;
TYPED_TEST_SUITE(FreezingWithHookTest, WithHookTypes, TypesNames);
} // namespace
TYPED_TEST(FreezingEmptyNoHookTest, UnfrozenByDefault) {
TypeParam object;
EXPECT_FALSE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, PermanentIsFrozen) {
TypeParam object;
object.MakePermanent();
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, FailToEnsureNeverFrozen) {
ScopedMemoryInit init;
TypeParam object;
ASSERT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
ASSERT_TRUE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::EnsureNeverFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, FailToEnsureNeverFrozenPermanent) {
TypeParam object;
object.MakePermanent();
ASSERT_TRUE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::EnsureNeverFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, Freeze) {
ScopedMemoryInit init;
TypeParam object;
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, FreezePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, FreezeTwice) {
ScopedMemoryInit init;
TypeParam object;
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyNoHookTest, FreezeForbidden) {
ScopedMemoryInit init;
TypeParam object;
ASSERT_TRUE(mm::EnsureNeverFrozen(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), object.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyWithHookTest, Freeze) {
ScopedMemoryInit init;
TypeParam object;
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyWithHookTest, FreezePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
EXPECT_CALL(this->freezeHook(), Call(_)).Times(0);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyWithHookTest, FreezeTwice) {
ScopedMemoryInit init;
TypeParam object;
// Only called for the first freeze.
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
testing::Mock::VerifyAndClearExpectations(&this->freezeHook());
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingEmptyWithHookTest, FreezeForbidden) {
ScopedMemoryInit init;
TypeParam object;
ASSERT_TRUE(mm::EnsureNeverFrozen(object.header()));
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), object.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingNoHookTest, Freeze) {
ScopedMemoryInit init;
TypeParam object;
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTwice) {
ScopedMemoryInit init;
TypeParam object;
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeForbidden) {
ScopedMemoryInit init;
TypeParam object;
ASSERT_TRUE(mm::EnsureNeverFrozen(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), object.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTree) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
TypeParam field1;
field1.MakePermanent();
TypeParam field2;
field2.MakePermanent();
TypeParam field3;
field3.MakePermanent();
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreePermanentLeaf) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
field1.MakePermanent();
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreeTwice) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreeForbidden) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
ASSERT_TRUE(mm::EnsureNeverFrozen(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), object.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::IsFrozen(field1.header()));
EXPECT_FALSE(mm::IsFrozen(field2.header()));
EXPECT_FALSE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreeForbiddenByField) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
ASSERT_TRUE(mm::EnsureNeverFrozen(field2.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), field2.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::IsFrozen(field1.header()));
EXPECT_FALSE(mm::IsFrozen(field2.header()));
EXPECT_FALSE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreeRecursive) {
ScopedMemoryInit init;
TypeParam object;
TypeParam inner1;
TypeParam inner2;
object[0] = inner1.header();
inner1[0] = inner2.header();
inner2[0] = object.header();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(inner1.header()));
EXPECT_TRUE(mm::IsFrozen(inner2.header()));
}
TYPED_TEST(FreezingNoHookTest, FreezeTreeRecursivePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
TypeParam inner1;
inner1.MakePermanent();
TypeParam inner2;
inner2.MakePermanent();
object[0] = inner1.header();
inner1[0] = inner2.header();
inner2[0] = object.header();
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(inner1.header()));
EXPECT_TRUE(mm::IsFrozen(inner2.header()));
}
TYPED_TEST(FreezingWithHookTest, Freeze) {
ScopedMemoryInit init;
TypeParam object;
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
EXPECT_CALL(this->freezeHook(), Call(_)).Times(0);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTwice) {
ScopedMemoryInit init;
TypeParam object;
// Only called for the first freeze.
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
testing::Mock::VerifyAndClearExpectations(&this->freezeHook());
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeForbidden) {
ScopedMemoryInit init;
TypeParam object;
ASSERT_TRUE(mm::EnsureNeverFrozen(object.header()));
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), object.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTree) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header()));
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
TypeParam field1;
field1.MakePermanent();
TypeParam field2;
field2.MakePermanent();
TypeParam field3;
field3.MakePermanent();
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_CALL(this->freezeHook(), Call(_)).Times(0);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreePermanentLeaf) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
field1.MakePermanent();
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header())).Times(0);
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeTwice) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
// Only called for the first freeze.
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header()));
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
testing::Mock::VerifyAndClearExpectations(&this->freezeHook());
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeForbidden) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
ASSERT_TRUE(mm::EnsureNeverFrozen(object.header()));
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header()));
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), object.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::IsFrozen(field1.header()));
EXPECT_FALSE(mm::IsFrozen(field2.header()));
EXPECT_FALSE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeForbiddenByField) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
ASSERT_TRUE(mm::EnsureNeverFrozen(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header()));
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), field2.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::IsFrozen(field1.header()));
EXPECT_FALSE(mm::IsFrozen(field2.header()));
EXPECT_FALSE(mm::IsFrozen(field3.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeRecursive) {
ScopedMemoryInit init;
TypeParam object;
TypeParam inner1;
TypeParam inner2;
object[0] = inner1.header();
inner1[0] = inner2.header();
inner2[0] = object.header();
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(inner1.header()));
EXPECT_CALL(this->freezeHook(), Call(inner2.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(inner1.header()));
EXPECT_TRUE(mm::IsFrozen(inner2.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeRecursivePermanent) {
ScopedMemoryInit init;
TypeParam object;
object.MakePermanent();
TypeParam inner1;
inner1.MakePermanent();
TypeParam inner2;
inner2.MakePermanent();
object[0] = inner1.header();
inner1[0] = inner2.header();
inner2[0] = object.header();
EXPECT_CALL(this->freezeHook(), Call(_)).Times(0);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(inner1.header()));
EXPECT_TRUE(mm::IsFrozen(inner2.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeWithHookRewrite) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
TypeParam oldInner;
TypeParam newInner;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
field2[0] = oldInner.header();
ON_CALL(this->freezeHook(), Call(field2.header())).WillByDefault([&field2, &newInner](ObjHeader* obj) {
field2[0] = newInner.header();
});
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header()));
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_CALL(this->freezeHook(), Call(newInner.header()));
EXPECT_CALL(this->freezeHook(), Call(oldInner.header())).Times(0);
EXPECT_THAT(mm::FreezeSubgraph(object.header()), nullptr);
EXPECT_TRUE(mm::IsFrozen(object.header()));
EXPECT_TRUE(mm::IsFrozen(field1.header()));
EXPECT_TRUE(mm::IsFrozen(field2.header()));
EXPECT_TRUE(mm::IsFrozen(field3.header()));
EXPECT_TRUE(mm::IsFrozen(newInner.header()));
EXPECT_FALSE(mm::IsFrozen(oldInner.header()));
}
TYPED_TEST(FreezingWithHookTest, FreezeTreeForbiddenByHook) {
ScopedMemoryInit init;
TypeParam object;
TypeParam field1;
TypeParam field2;
TypeParam field3;
object[0] = field1.header();
object[1] = field2.header();
object[2] = field3.header();
ON_CALL(this->freezeHook(), Call(field2.header())).WillByDefault([](ObjHeader* obj) { EXPECT_TRUE(mm::EnsureNeverFrozen(obj)); });
EXPECT_CALL(this->freezeHook(), Call(object.header()));
EXPECT_CALL(this->freezeHook(), Call(field1.header()));
EXPECT_CALL(this->freezeHook(), Call(field2.header()));
EXPECT_CALL(this->freezeHook(), Call(field3.header()));
EXPECT_THAT(mm::FreezeSubgraph(object.header()), field2.header());
EXPECT_FALSE(mm::IsFrozen(object.header()));
EXPECT_FALSE(mm::IsFrozen(field1.header()));
EXPECT_FALSE(mm::IsFrozen(field2.header()));
}