mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-09 00:33:55 +00:00
431 lines
15 KiB
C++
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;
|
|
} |