blob: 2b3f8447d3510891a4e677a2776b4ef413841964 [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 "ObjectTraversal.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "ReferenceOps.hpp"
#include "Types.h"
#include "Utils.hpp"
#include "ObjectOps.hpp"
using namespace kotlin;
using ::testing::_;
namespace {
struct EmptyPayload {
static constexpr test_support::NoRefFields<EmptyPayload> kFields{};
};
struct Payload {
mm::RefField field1;
mm::RefField field2;
mm::RefField field3;
static constexpr std::array kFields{
&Payload::field1,
&Payload::field2,
&Payload::field3,
};
};
using ProcessFunMock = testing::StrictMock<testing::MockFunction<void(mm::RefFieldAccessor)>>;
MATCHER_P(SameAccessor, accessor, "") {
return arg.direct().location() == accessor.direct().location();
}
} // namespace
TEST(ObjectTraversalTest, TraverseFieldsExceptions) {
struct CallableWithExceptions {
void operator()(mm::RefFieldAccessor) noexcept(false) {}
};
struct CallableWithoutExceptions {
void operator()(mm::RefFieldAccessor) noexcept {}
};
static_assert(
noexcept(traverseObjectFields(std::declval<ObjHeader*>(), std::declval<CallableWithoutExceptions>())),
"Callable is noexcept, so traverse is noexcept");
static_assert(
!noexcept(traverseObjectFields(std::declval<ObjHeader*>(), std::declval<CallableWithExceptions>())),
"Callable is noexcept(false), so traverse is noexcept(false)");
}
TEST(ObjectTraversalTest, TraverseEmptyObjectFields) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
ProcessFunMock process;
EXPECT_CALL(process, Call(_)).Times(0);
traverseObjectFields(object.header(), process.AsStdFunction());
}
TEST(ObjectTraversalTest, TraverseObjectFields) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1{};
ObjHeader field3{};
test_support::Object<Payload> object(type.typeInfo());
object->field1.direct() = &field1;
object->field3.direct() = &field3;
ProcessFunMock process;
EXPECT_CALL(process, Call(SameAccessor(object->field1.accessor())));
EXPECT_CALL(process, Call(SameAccessor(object->field2.accessor())));
EXPECT_CALL(process, Call(SameAccessor(object->field3.accessor())));
traverseObjectFields(object.header(), process.AsStdFunction());
}
TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) {
constexpr int kException = 1;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1{};
ObjHeader field2{};
ObjHeader field3{};
test_support::Object<Payload> object(type.typeInfo());
object->field1.direct() = &field1;
object->field2.direct() = &field2;
object->field3.direct() = &field3;
ProcessFunMock process;
EXPECT_CALL(process, Call(SameAccessor(object->field1.accessor())));
EXPECT_CALL(process, Call(SameAccessor(object->field2.accessor()))).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(SameAccessor(object->field3.accessor()))).Times(0);
try {
traverseObjectFields(object.header(), process.AsStdFunction());
} catch (int exception) {
EXPECT_THAT(exception, kException);
} catch (...) {
EXPECT_TRUE(false);
}
}
TEST(ObjectTraversalTest, TraverseEmptyArrayFields) {
test_support::ObjectArray<0> array;
ProcessFunMock process;
EXPECT_CALL(process, Call(_)).Times(0);
traverseObjectFields(array.header(), process.AsStdFunction());
}
TEST(ObjectTraversalTest, TraverseArrayFields) {
ObjHeader element1{};
ObjHeader element3{};
test_support::ObjectArray<3> array;
array.elements()[0].direct() = &element1;
array.elements()[2].direct() = &element3;
ProcessFunMock process;
EXPECT_CALL(process, Call(SameAccessor(array.elements()[0].accessor())));
EXPECT_CALL(process, Call(SameAccessor(array.elements()[1].accessor())));
EXPECT_CALL(process, Call(SameAccessor(array.elements()[2].accessor())));
traverseObjectFields(array.header(), process.AsStdFunction());
}
TEST(ObjectTraversalTest, TraverseArrayFieldsWithException) {
constexpr int kException = 1;
ObjHeader element1{};
ObjHeader element2{};
ObjHeader element3{};
test_support::ObjectArray<3> array;
array.elements()[0].direct() = &element1;
array.elements()[1].direct() = &element2;
array.elements()[2].direct() = &element3;
ProcessFunMock process;
EXPECT_CALL(process, Call(SameAccessor(array.elements()[0].accessor())));
EXPECT_CALL(process, Call(SameAccessor(array.elements()[1].accessor()))).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(SameAccessor(array.elements()[2].accessor()))).Times(0);
try {
traverseObjectFields(array.header(), process.AsStdFunction());
} catch (int exception) {
EXPECT_THAT(exception, kException);
} catch (...) {
EXPECT_TRUE(false);
}
}
TEST(ObjectTraversalTest, TraverseRefsExceptions) {
struct CallableWithExceptions {
void operator()(ObjHeader*) noexcept(false) {}
};
struct CallableWithoutExceptions {
void operator()(ObjHeader*) noexcept {}
};
static_assert(
noexcept(traverseReferredObjects(std::declval<ObjHeader*>(), std::declval<CallableWithoutExceptions>())),
"Callable is noexcept, so traverse is noexcept");
static_assert(
!noexcept(traverseReferredObjects(std::declval<ObjHeader*>(), std::declval<CallableWithExceptions>())),
"Callable is noexcept(false), so traverse is noexcept(false)");
}
TEST(ObjectTraversalTest, TraverseEmptyObjectRefs) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(_)).Times(0);
traverseReferredObjects(object.header(), [&process](ObjHeader* field) { process.Call(field); });
}
TEST(ObjectTraversalTest, TraverseObjectRefs) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1{};
ObjHeader field3{};
test_support::Object<Payload> object(type.typeInfo());
object->field1.direct() = &field1;
object->field3.direct() = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&field1));
EXPECT_CALL(process, Call(&field3));
traverseReferredObjects(object.header(), [&process](ObjHeader* field) { process.Call(field); });
}
TEST(ObjectTraversalTest, TraverseObjectRefsWithException) {
constexpr int kException = 1;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1{};
ObjHeader field2{};
ObjHeader field3{};
test_support::Object<Payload> object(type.typeInfo());
object->field1.direct() = &field1;
object->field2.direct() = &field2;
object->field3.direct() = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&field1));
EXPECT_CALL(process, Call(&field2)).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&field3)).Times(0);
try {
traverseReferredObjects(object.header(), [&process](ObjHeader* field) { process.Call(field); });
} catch (int exception) {
EXPECT_THAT(exception, kException);
} catch (...) {
EXPECT_TRUE(false);
}
}
TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) {
test_support::ObjectArray<0> array;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(_)).Times(0);
traverseReferredObjects(array.header(), [&process](ObjHeader* field) { process.Call(field); });
}
TEST(ObjectTraversalTest, TraverseArrayRefs) {
ObjHeader element1{};
ObjHeader element3{};
test_support::ObjectArray<3> array;
array.elements()[0].direct() = &element1;
array.elements()[2].direct() = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&element1));
EXPECT_CALL(process, Call(&element3));
traverseReferredObjects(array.header(), [&process](ObjHeader* field) { process.Call(field); });
}
TEST(ObjectTraversalTest, TraverseArrayRefsWithException) {
constexpr int kException = 1;
ObjHeader element1{};
ObjHeader element2{};
ObjHeader element3{};
test_support::ObjectArray<3> array;
array.elements()[0].direct() = &element1;
array.elements()[1].direct() = &element2;
array.elements()[2].direct() = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&element1));
EXPECT_CALL(process, Call(&element2)).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&element3)).Times(0);
try {
traverseReferredObjects(array.header(), [&process](ObjHeader* field) { process.Call(field); });
} catch (int exception) {
EXPECT_THAT(exception, kException);
} catch (...) {
EXPECT_TRUE(false);
}
}