Files
bitsery/tests/serialization_ext_pointer.cpp
2017-10-27 08:14:01 +03:00

431 lines
15 KiB
C++

//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/pointer.h>
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerLinkingContext;
using testing::Eq;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, PointerLinkingContext>;
class SerializeExtensionPointerSerialization: public testing::Test {
public:
//data used for serialization
int16_t d1{1597};
int16_t* pd1 = &d1;
MyEnumClass d2{MyEnumClass::E2};
MyEnumClass* pd2 = &d2;
MyStruct1 d3{184, 897};
MyStruct1* pd3 = &d3;
//data used for deserialization
int16_t r1{-84};
int16_t* pr1 = &r1;
MyEnumClass r2{MyEnumClass::E4};
MyEnumClass* pr2 = &r2;
MyStruct1 r3{-4984, -14597};
MyStruct1* pr3 = &r3;
//null pointers
int16_t* p1null = nullptr;
MyEnumClass* p2null = nullptr;
MyStruct1* p3null = nullptr;
PointerLinkingContext plctx1{};
SerContext sctx1{};
typename SerContext::TSerializer& createSerializer() {
return sctx1.createSerializer(&plctx1);
}
bool isPointerContextValid() {
return plctx1.isValid();
}
};
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
MyStruct1* data = nullptr;
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
sctx1.createSerializer(&plctx1).ext(data, PointerOwner{});
sctx1.createDeserializer(&plctx1).ext(data, PointerOwner{});
//linking context in tuple
using ContextInTuple = std::tuple<int, PointerLinkingContext, float, char>;
ContextInTuple plctx2(0, PointerLinkingContext{}, 0.0f, 'a');
BasicSerializationContext<SessionsEnabledConfig, ContextInTuple> sctx2;
sctx2.createSerializer(&plctx2).ext(data, PointerObserver{});
sctx2.createDeserializer(&plctx2).ext(data, PointerObserver{});
}
TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId) {
MyStruct1 data{};
//pretend that this is shared ptr
MyStruct1* sharedPtr = &data;
//linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.createId(sharedPtr, bitsery::ext::details_pointer::PointerOwnershipType::Shared), Eq(1));
EXPECT_THAT(plctx1.createId(sharedPtr, bitsery::ext::details_pointer::PointerOwnershipType::Shared), Eq(1));
EXPECT_THAT(plctx1.createId(sharedPtr, bitsery::ext::details_pointer::PointerOwnershipType::Shared), Eq(1));
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext2b(p1null, PointerObserver{});
ser.ext(p3null, PointerOwner{});
ser.ext(p3null, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
#ifndef NDEBUG
TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullThenAssert) {
MyStruct1* data = nullptr;
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
EXPECT_DEATH(sctx1.createSerializer(nullptr).ext(data, PointerOwner{}), "");
EXPECT_DEATH(sctx1.createDeserializer(nullptr).ext(data, PointerObserver{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
ser.ext2b(p1null, PointerOwner{});
//dublicating pointer
EXPECT_DEATH(ser.ext2b(pd1, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1) {
auto& ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(d3, ReferencedByPointer{});
EXPECT_DEATH(ser1.ext(pd3, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext4b(d2, ReferencedByPointer{});
EXPECT_DEATH(ser1.ext2b(d1, ReferencedByPointer{}), "");
}
#endif
TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerThenIsValid) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
EXPECT_THAT(plctx1.isValid(), Eq(false));
ser1.ext4b(pd2, PointerOwner{});//now d2 is owning pointer
ser1.ext4b(pd2, PointerObserver{});//points to d2, but this time d2 has owner
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedByPointerObservers) {
auto& ser1 = createSerializer();
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
EXPECT_THAT(plctx1.isValid(), Eq(false));
ser1.ext4b(d2, ReferencedByPointer{});//now d2 is marked by marked as owning pointer
ser1.ext4b(pd2, PointerObserver{});//points to d2, but this time d2 has owner
ser1.ext(p3null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
auto& ser1 = createSerializer();
ser1.ext(p3null, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2));
size_t res;
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(0));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(0));
}
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext2b(p1null, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
size_t res;
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(1));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(2));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(2));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(0));
}
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3));
}
TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAndObject) {
auto& ser1 = createSerializer();
ser1.ext2b(d1, ReferencedByPointer{});
ser1.ext4b(d2, ReferencedByPointer{});
ser1.ext4b(pd2, PointerObserver{});
auto& des = sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3+6));
size_t id{};
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(1));
des.value2b(r1);
EXPECT_THAT(r1, Eq(d1));
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(2));
des.value4b(r2);
EXPECT_THAT(r2, Eq(d2));
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(2));
}
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
auto& ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(pd3, PointerOwner{});
auto& des1 = sctx1.createDeserializer();
//2x ids + int32_t + MyStruct1
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE ));
size_t id;
bitsery::details::readSize(*sctx1.br, id, 10000u);
des1.value4b(r2);
EXPECT_THAT(r2, Eq(*pd2));
bitsery::details::readSize(*sctx1.br, id, 10000u);
des1.object(r3);
EXPECT_THAT(r3, Eq(*pd3));
}
class SerializeExtensionPointerDeserialization: public SerializeExtensionPointerSerialization {
public:
typename SerContext::TSerializer& createSerializer() {
return sctx1.createSerializer(&plctx1);
}
typename SerContext::TDeserializer& createDeserializer() {
return sctx1.createDeserializer(&plctx1);
}
};
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
auto& ser = createSerializer();
ser.ext2b(d1, ReferencedByPointer{});
ser.ext4b(d2, ReferencedByPointer{});
ser.ext(d3, ReferencedByPointer{});
auto& des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
des.ext4b(r2, ReferencedByPointer{});
des.ext(r3, ReferencedByPointer{});
EXPECT_THAT(r1, Eq(d1));
EXPECT_THAT(r2, Eq(d2));
EXPECT_THAT(r3, Eq(d3));
}
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsZeroPointerIdThenInvalidPointerError) {
auto& ser = createSerializer();
bitsery::details::writeSize(*sctx1.bw, 0u);
ser.ext2b(d1, ReferencedByPointer{});
auto& des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
auto& ser = createSerializer();
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
ser.ext(pd3, PointerOwner{});
auto& des = createDeserializer();
des.ext2b(p1null, PointerOwner{});
des.ext4b(p2null, PointerOwner{});
des.ext(p3null, PointerOwner{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
EXPECT_THAT(p1null, ::testing::NotNull());
EXPECT_THAT(p2null, ::testing::NotNull());
EXPECT_THAT(p3null, ::testing::NotNull());
EXPECT_THAT(*p1null, Eq(*pd1));
EXPECT_THAT(*p2null, Eq(*pd2));
EXPECT_THAT(*p3null, Eq(*pd3));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext4b(p2null, PointerOwner{});
ser.ext(p3null, PointerOwner{});
auto& des = createDeserializer();
//pr cannot link to local variables, need to allocate them separately
pr1 = new int16_t{};
pr2 = new MyEnumClass{};
pr3 = new MyStruct1{3,4};
des.ext2b(pr1, PointerOwner{});
des.ext4b(pr2, PointerOwner{});
des.ext(pr3, PointerOwner{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
EXPECT_THAT(pr1, ::testing::IsNull());
EXPECT_THAT(pr2, ::testing::IsNull());
EXPECT_THAT(pr3, ::testing::IsNull());
}
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
auto& ser = createSerializer();
//first owner, than observer
ser.ext4b(d2, ReferencedByPointer{});
ser.ext2b(p1null, PointerObserver{});
ser.ext4b(pd2, PointerObserver{});
//first observer, than owner
ser.ext(pd3, PointerObserver{});
ser.ext(pd3, PointerOwner{});
auto& des = createDeserializer();
des.ext4b(r2, ReferencedByPointer{});
des.ext2b(pr1, PointerObserver{});
des.ext4b(p2null, PointerObserver{});
des.ext(pr3, PointerObserver{});
des.ext(pr3, PointerOwner{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
//serialize null, override non-null
EXPECT_THAT(pr1, Eq(p1null));
//serialize non-null, override null
EXPECT_THAT(*p2null, Eq(*pd2));
EXPECT_THAT(p2null, Eq(&r2));
//serialize non-null override non-null
EXPECT_THAT(*pr3, Eq(*pd3));
EXPECT_THAT(pr3, Eq(&r3));
}
struct Test1Data {
std::vector<MyStruct1> vdata;
std::vector<MyStruct1*> vptr;
MyStruct1 o1;
MyStruct1* po1;
int32_t i1;
int32_t* pi1;
template <typename S>
void serialize(S& s) {
//set container elements to be candidates for non-owning pointers
s.container(vdata, 100, [&s](MyStruct1& d){
s.ext(d, ReferencedByPointer{});
});
//contains non owning pointers
//
//IMPORTANT !!!
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
//
s.container(vptr, 100, [&s](MyStruct1* (&d)){
s.ext(d, PointerObserver{});
});
//just a regular fields
s.object(o1);
s.value4b(i1);
//observer
s.ext(po1, PointerObserver{});
//owner
s.ext4b(pi1, PointerOwner{});
}
};
TEST(SerializeExtensionPointer, IntegrationTest) {
Test1Data data{};
data.vdata.push_back({165,-45});
data.vdata.push_back({7895,-1576});
data.vdata.push_back({5987,-798});
//container of non owning pointers (observers)
data.vptr.push_back(nullptr);
data.vptr.push_back(std::addressof(data.vdata[0]));
data.vptr.push_back(std::addressof(data.vdata[2]));
//regular fields
data.o1 = MyStruct1{145,948};
data.i1 = 945415;
//observer
data.po1 = std::addressof(data.vdata[1]);
//owning pointer
data.pi1 = new int32_t{};
Test1Data res{};
PointerLinkingContext plctx1{};
SerContext sctx1;
sctx1.createSerializer(&plctx1).object(data);
sctx1.createDeserializer(&plctx1).object(res);
EXPECT_THAT(plctx1.isValid(), Eq(true));
//check regular fields
EXPECT_THAT(res.i1, Eq(data.i1));
EXPECT_THAT(res.o1, Eq(data.o1));
//check data container
EXPECT_THAT(res.vdata, ::testing::ContainerEq(data.vdata));
//check owning pointers
EXPECT_THAT(*res.pi1, Eq(*data.pi1));
EXPECT_THAT(res.pi1, ::testing::Ne(data.pi1));
//check if observers points to correct data
EXPECT_THAT(res.po1, Eq(std::addressof(res.vdata[1])));
EXPECT_THAT(res.vptr[0], ::testing::IsNull());
EXPECT_THAT(res.vptr[1], Eq(std::addressof(res.vdata[0])));
EXPECT_THAT(res.vptr[2], Eq(std::addressof(res.vdata[2])));
//free owning raw pointers
delete data.pi1;
delete res.pi1;
}