Skip to content

Commit 82e888a

Browse files
authored
Ensure consistency between readers for construction from streams, and move roundtrip tests to a seperate files (#478)
* Ensure consistency between readers for construction from streams, and move roundtrip tests to a seperate files * Missing template keyword * Missing template keyword * Explicit 64-bit size type
1 parent 97999ff commit 82e888a

12 files changed

Lines changed: 175 additions & 71 deletions

File tree

libraries/core/lib/morpheus/core/serialisation/adapters/boost/circular_buffer.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ template <concepts::WriteSerialiser Serialiser, typename T, typename Allocator>
2424
void serialise(Serialiser& serialiser, boost::circular_buffer<T, Allocator> const& buffer)
2525
{
2626
serialiser.writer().beginComposite();
27-
serialiser.serialise("capacity", buffer.capacity());
27+
serialiser.serialise("capacity", static_cast<std::uint64_t>(buffer.capacity()));
2828
serialiser.serialise("values", conf::ranges::subrange(buffer.begin(), buffer.end()));
2929
serialiser.writer().endComposite();
3030
}
@@ -34,8 +34,10 @@ boost::circular_buffer<T, Allocator> deserialise(Serialiser& serialiser, std::ty
3434
{
3535
boost::circular_buffer<T, Allocator> buffer;
3636
serialiser.reader().beginComposite();
37-
buffer.set_capacity(serialiser.template deserialise<std::size_t>("capacity"));
37+
buffer.set_capacity(serialiser.template deserialise<std::uint64_t>("capacity"));
38+
serialiser.reader().beginValue("values");
3839
conf::ranges::copy(serialiser.template sequence<T>(), std::back_inserter(buffer));
40+
serialiser.reader().endValue();
3941
serialiser.reader().endComposite();
4042
return buffer;
4143
}

libraries/core/lib/morpheus/core/serialisation/binary_writer.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace morpheus::serialisation
2727
class BinaryWriter
2828
{
2929
public:
30-
BinaryWriter(std::ostream& outStream) noexcept
30+
explicit BinaryWriter(std::ostream& outStream) noexcept
3131
: mOutStream(outStream)
3232
{}
3333

libraries/core/lib/morpheus/core/serialisation/json_reader.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ bool JsonReader::isAtEndSequence()
179179
return event == Event::EndSequence;
180180
}
181181

182-
JsonReader::JsonReader(OwnedStream stream, bool validate)
183-
: mSourceStream(std::move(stream))
184-
, mStream(std::make_unique<rapidjson::IStreamWrapper>(*mSourceStream))
182+
JsonReader::JsonReader(std::istream& stream, bool validate)
183+
: mSourceStream(stream)
184+
, mStream(std::make_unique<rapidjson::IStreamWrapper>(mSourceStream.get()))
185185
, mJsonReader(std::make_unique<rapidjson::Reader>())
186186
, mExtractor(std::make_unique<JsonExtracter>())
187187
, mValidate(validate)

libraries/core/lib/morpheus/core/serialisation/json_reader.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <concepts>
1818
#include <cstddef>
1919
#include <cstdint>
20+
#include <functional>
2021
#include <istream>
2122
#include <limits>
2223
#include <memory>
@@ -47,8 +48,6 @@ class MORPHEUSCORE_EXPORT JsonReader
4748
};
4849

4950
public:
50-
using OwnedStream = std::unique_ptr<std::istream>;
51-
5251
static constexpr bool canBeTextual() { return true; }
5352

5453
/// \copydoc morpheus::serialisation::concepts::ReaderArchetype::isTextual()
@@ -57,7 +56,7 @@ class MORPHEUSCORE_EXPORT JsonReader
5756
/// Json reader take in a stream of json to extract data members from.
5857
/// \param[in] stream Stream used to read in the json source. This must outlive the reader as its held by reference.
5958
/// \param[in] validate If true, the json will be validated against the schema. If false, no validation is performed.
60-
explicit JsonReader(OwnedStream stream, bool validate = true);
59+
explicit JsonReader(std::istream& stream, bool validate = true);
6160

6261
explicit JsonReader(JsonReader const& rhs) = delete;
6362
JsonReader& operator=(JsonReader const& rhs) = delete;
@@ -207,7 +206,7 @@ class MORPHEUSCORE_EXPORT JsonReader
207206
[[nodiscard]] bool isAtEndSequence();
208207
[[nodiscard]] std::vector<std::byte> readBytes();
209208

210-
OwnedStream mSourceStream; /// Owned input stream containing the Json source.
209+
std::reference_wrapper<std::istream> mSourceStream;
211210
std::unique_ptr<rapidjson::IStreamWrapper> mStream;
212211
std::unique_ptr<rapidjson::Reader> mJsonReader;
213212
std::unique_ptr<struct JsonExtracter> mExtractor;

libraries/core/testing/morpheus/serialisation/helpers.hpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@ constexpr std::array<char, sizeof...(Ts)> makeCharArray(Ts&&... args) noexcept
2929
/// Serialiser a value to a binary buffer.
3030
/// \param[in] value The value to be serialised.
3131
/// \return The binary blob containing the serialised form of the input value.
32+
template <concepts::WriteSerialiser Serialiser>
3233
std::vector<char> serialise(auto const& value)
3334
{
3435
using namespace boost::iostreams;
3536
std::vector<char> storage;
3637
back_insert_device sink{storage};
3738
stream os{sink};
38-
BinaryWriteSerialiser serialiser{os};
39+
Serialiser serialiser{std::in_place, os};
3940
serialiser.serialise(value);
4041
return storage;
4142
}
@@ -53,7 +54,7 @@ std::array<char, Size> serialiseWithLimitedSpace(auto const& value)
5354
std::array<char, Size> storage = {};
5455
array_sink sink{storage.data(), storage.size()};
5556
stream os{sink};
56-
BinaryWriteSerialiser serialiser{os};
57+
BinaryWriteSerialiser serialiser{std::in_place, os};
5758
serialiser.serialise(value);
5859
return storage;
5960
}
@@ -70,8 +71,8 @@ T deserialiseWithIoStream(auto const& storage)
7071
using namespace boost::iostreams;
7172
array_source source{storage.data(), storage.size()};
7273
stream is{source};
73-
BinaryReadSerialiser serialiser{is};
74-
return serialiser.deserialise<T>();
74+
BinaryReadSerialiser serialiser{std::in_place, is};
75+
return serialiser.template deserialise<T>();
7576
}
7677

7778
/// Serialise a value to a binary buffer with limited space. This is useful for testing or error cases such as testing
@@ -87,7 +88,7 @@ std::array<char, Size> serialiseWithSpanStream(auto const& value)
8788
std::array<char, Size> storage;
8889
std::span view{storage};
8990
std::ospanstream stream{view};
90-
BinaryWriteSerialiser serialiser{stream};
91+
BinaryWriteSerialiser serialiser{std::in_place, stream};
9192
serialiser.serialise(value);
9293
return storage;
9394
}
@@ -103,8 +104,8 @@ T deserialiseWithSpanStream(auto const& storage)
103104
{
104105
std::span view{storage};
105106
std::ispanstream stream{view};
106-
BinaryReadSerialiser serialiser{stream};
107-
return serialiser.deserialise<T>();
107+
BinaryReadSerialiser serialiser{std::in_place, stream};
108+
return serialiser.template deserialise<T>();
108109
}
109110
#endif
110111

@@ -113,25 +114,30 @@ T deserialiseWithSpanStream(auto const& storage)
113114
/// \tparam T The type of value to be deserialised.
114115
/// \param[in] value The binary blob representing the serialised form of the type.
115116
/// \returns The deserialised value.
116-
template <typename T>
117+
template <typename T, concepts::ReadSerialiser Serialiser>
117118
T deserialise(std::vector<char> const& value)
118119
{
119120
using namespace boost::iostreams;
120121
array_source source{value.data(), value.size()};
121122
stream is{source};
122-
BinaryReadSerialiser serialiser{is};
123-
return serialiser.deserialise<T>();
123+
Serialiser serialiser(std::in_place, is);
124+
return serialiser.template deserialise<T>();
124125
}
125126

126127
/// Serialises an object to a binary blob and then deserialise back into the original form.
127128
///
128129
/// \tparam T The type of value to be serialised/deserialised.
129130
/// \param[in] value The value to be serialised then deserialised.
130131
/// \returns The resulting deserialised value.
131-
template <typename T>
132+
template <concepts::WriteSerialiser WriteSerialiser, concepts::ReadSerialiser ReadSerialiser, typename T>
132133
T roundtrip(T const& value)
133134
{
134-
return deserialise<T>(serialise(value));
135+
return deserialise<T, ReadSerialiser>(serialise<WriteSerialiser>(value));
136+
}
137+
138+
std::vector<char> binarySerialise(auto const& value)
139+
{
140+
return serialise<BinaryWriteSerialiser>(value);
135141
}
136142

137143
} // namespace morpheus::serialisation::testing

libraries/core/tests/serialisation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ target_sources(MorpheusCoreTests
66
concept_checks.tests.cpp
77
exceptions.tests.cpp
88
json_reader.tests.cpp
9+
json_roundtrip.tests.cpp
910
json_writer.tests.cpp
1011
read_serialiser.tests.cpp
1112
write_serialiser.tests.cpp

libraries/core/tests/serialisation/binary_reader.tests.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ using namespace Catch;
2929
namespace morpheus::serialisation
3030
{
3131

32+
using namespace testing;
33+
3234
TEST_CASE("Binary reader handles error cases gracefully", "[morpheus.serialisation.binary_reader.error_handling]")
3335
{
3436
constexpr auto bytes = testing::makeBytes(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
@@ -38,8 +40,8 @@ TEST_CASE("Binary reader handles error cases gracefully", "[morpheus.serialisati
3840
SECTION("Serialise via spanstream to test failure condition when the the underling stream runs out of memory while writing but does not throw an exception")
3941
{
4042
REQUIRE(testing::deserialiseWithSpanStream<std::int64_t>(testing::makeCharArray(0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)) == std::int64_t{100});
41-
REQUIRE(testing::deserialiseWithSpanStream<std::string>(testing::serialise(string)) == string);
42-
REQUIRE(conf::ranges::equal(testing::deserialiseWithSpanStream<std::vector<std::byte>>(testing::serialise(std::span{bytes})), std::span{bytes}));
43+
REQUIRE(testing::deserialiseWithSpanStream<std::string>(binarySerialise(string)) == string);
44+
REQUIRE(conf::ranges::equal(testing::deserialiseWithSpanStream<std::vector<std::byte>>(binarySerialise(std::span{bytes})), std::span{bytes}));
4345

4446
REQUIRE_THROWS_AS(testing::deserialiseWithSpanStream<std::int64_t>(testing::makeCharArray(0x64, 0x00, 0x00, 0x00)), BinaryException);
4547
REQUIRE_THROWS_AS(testing::deserialiseWithSpanStream<std::string>(testing::makeCharArray(0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)),
@@ -51,8 +53,8 @@ TEST_CASE("Binary reader handles error cases gracefully", "[morpheus.serialisati
5153
SECTION("Serialise via boost::iostream to test failure condition when the the underling stream runs out of memory while writing resulting in an exception")
5254
{
5355
REQUIRE(testing::deserialiseWithIoStream<std::int64_t>(testing::makeCharArray(0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)) == std::int64_t{100});
54-
REQUIRE(testing::deserialiseWithIoStream<std::string>(testing::serialise(string)) == string);
55-
REQUIRE(conf::ranges::equal(testing::deserialiseWithIoStream<std::vector<std::byte>>(testing::serialise(std::span{bytes})), std::span{bytes}));
56+
REQUIRE(testing::deserialiseWithIoStream<std::string>(testing::binarySerialise(string)) == string);
57+
REQUIRE(conf::ranges::equal(testing::deserialiseWithIoStream<std::vector<std::byte>>(binarySerialise(std::span{bytes})), std::span{bytes}));
5658

5759
REQUIRE_THROWS_AS(testing::deserialiseWithIoStream<std::int64_t>(testing::makeCharArray(0x64, 0x00, 0x00, 0x00)), BinaryException);
5860
REQUIRE_THROWS_AS(testing::deserialiseWithIoStream<std::string>(testing::makeCharArray(0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)),

libraries/core/tests/serialisation/binary_roundtrip.tests.cpp

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,37 @@ using namespace Catch;
3232

3333
namespace morpheus::serialisation
3434
{
35-
using namespace testing;
35+
36+
template <typename T>
37+
T binaryRoundtrip(T const& value)
38+
{
39+
return testing::roundtrip<BinaryWriteSerialiser, BinaryReadSerialiser>(value);
40+
}
3641

3742
TEST_CASE("Binary serialisation can roundtrip standard library types to binary and back", "[morpheus.serialisation.binary.roundtrip.adapters.std]")
3843
{
3944
SECTION("Chrono types")
4045
{
41-
REQUIRE(roundtrip(std::chrono::nanoseconds{123}) == std::chrono::nanoseconds{123});
42-
REQUIRE(roundtrip(std::chrono::microseconds{456}) == std::chrono::microseconds{456});
43-
REQUIRE(roundtrip(std::chrono::milliseconds{789}) == std::chrono::milliseconds{789});
44-
REQUIRE(roundtrip(std::chrono::seconds{123}) == std::chrono::seconds{123});
45-
REQUIRE(roundtrip(std::chrono::minutes{60}) == std::chrono::minutes{60});
46-
REQUIRE(roundtrip(std::chrono::hours{24}) == std::chrono::hours{24});
47-
REQUIRE(roundtrip(std::chrono::days{7}) == std::chrono::days{7});
48-
REQUIRE(roundtrip(std::chrono::weeks{52}) == std::chrono::weeks{52});
49-
REQUIRE(roundtrip(std::chrono::years{100}) == std::chrono::years{100});
50-
REQUIRE(roundtrip(std::chrono::months{12}) == std::chrono::months{12});
46+
REQUIRE(binaryRoundtrip(std::chrono::nanoseconds{123}) == std::chrono::nanoseconds{123});
47+
REQUIRE(binaryRoundtrip(std::chrono::microseconds{456}) == std::chrono::microseconds{456});
48+
REQUIRE(binaryRoundtrip(std::chrono::milliseconds{789}) == std::chrono::milliseconds{789});
49+
REQUIRE(binaryRoundtrip(std::chrono::seconds{123}) == std::chrono::seconds{123});
50+
REQUIRE(binaryRoundtrip(std::chrono::minutes{60}) == std::chrono::minutes{60});
51+
REQUIRE(binaryRoundtrip(std::chrono::hours{24}) == std::chrono::hours{24});
52+
REQUIRE(binaryRoundtrip(std::chrono::days{7}) == std::chrono::days{7});
53+
REQUIRE(binaryRoundtrip(std::chrono::weeks{52}) == std::chrono::weeks{52});
54+
REQUIRE(binaryRoundtrip(std::chrono::years{100}) == std::chrono::years{100});
55+
REQUIRE(binaryRoundtrip(std::chrono::months{12}) == std::chrono::months{12});
5156
}
52-
REQUIRE(roundtrip(std::bitset<4>{"1101"}) == std::bitset<4>{"1101"});
53-
REQUIRE(roundtrip(conf::vt::indirect<int>{42}) == conf::vt::indirect<int>{42});
54-
REQUIRE(roundtrip(std::monostate{}) == std::monostate{});
55-
REQUIRE(roundtrip(std::optional<int>{100}) == std::optional<int>{100});
56-
REQUIRE(roundtrip(std::optional<int>{}) == std::optional<int>{});
57-
REQUIRE(roundtrip(std::pair<int, bool>{50, true}) == std::pair<int, bool>{50, true});
58-
REQUIRE(roundtrip(std::string("Hello")) == std::string("Hello"));
59-
REQUIRE(*roundtrip(std::make_unique<int>(123)) == 123);
60-
REQUIRE(roundtrip(std::vector<int>{1, 2, 3, 4, 5}) == std::vector<int>{1, 2, 3, 4, 5});
57+
REQUIRE(binaryRoundtrip(std::bitset<4>{"1101"}) == std::bitset<4>{"1101"});
58+
REQUIRE(binaryRoundtrip(conf::vt::indirect<int>{42}) == conf::vt::indirect<int>{42});
59+
REQUIRE(binaryRoundtrip(std::monostate{}) == std::monostate{});
60+
REQUIRE(binaryRoundtrip(std::optional<int>{100}) == std::optional<int>{100});
61+
REQUIRE(binaryRoundtrip(std::optional<int>{}) == std::optional<int>{});
62+
REQUIRE(binaryRoundtrip(std::pair<int, bool>{50, true}) == std::pair<int, bool>{50, true});
63+
REQUIRE(binaryRoundtrip(std::string("Hello")) == std::string("Hello"));
64+
REQUIRE(*binaryRoundtrip(std::make_unique<int>(123)) == 123);
65+
REQUIRE(binaryRoundtrip(std::vector<int>{1, 2, 3, 4, 5}) == std::vector<int>{1, 2, 3, 4, 5});
6166

6267
/* REQUIRE(test::serialise(std::variant<int, bool, std::string>{true}) == R"({"type":"bool","value":true})");
6368
*/
@@ -66,8 +71,8 @@ TEST_CASE("Binary serialisation can roundtrip standard library types to binary a
6671
TEST_CASE("Binary serialisation can roundtrip standard library types to binary and back", "[morpheus.serialisation.binary.roundtrip.adapters.boost]")
6772
{
6873
auto const values = std::vector{1, 2, 3, 4, 5};
69-
REQUIRE(roundtrip(boost::circular_buffer<int>{values.begin(), values.end()}) == boost::circular_buffer<int>{values.begin(), values.end()});
70-
REQUIRE(roundtrip(boost::dynamic_bitset<>{"1101"}) == boost::dynamic_bitset<>{"1101"});
74+
REQUIRE(binaryRoundtrip(boost::circular_buffer<int>{values.begin(), values.end()}) == boost::circular_buffer<int>{values.begin(), values.end()});
75+
REQUIRE(binaryRoundtrip(boost::dynamic_bitset<>{"1101"}) == boost::dynamic_bitset<>{"1101"});
7176
}
7277

7378
} // namespace morpheus::serialisation

libraries/core/tests/serialisation/binary_writer.tests.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@ using namespace Catch;
2525

2626
namespace morpheus::serialisation
2727
{
28+
using namespace testing;
2829

2930
TEST_CASE("Binary writer handles error cases gracefully", "[morpheus.serialisation.binary_writer.error_handling]")
3031
{
31-
constexpr auto bytes = testing::makeBytes(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
32+
constexpr auto bytes = makeBytes(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
3233
constexpr auto string = std::string_view{"String longer than 4-bytes"};
3334

3435
#if (__cpp_lib_spanstream >= 202106L)
3536
SECTION("Serialise via spanstream to test failure condition when the the underling stream runs out of memory while writing but does not throw an exception")
3637
{
3738
REQUIRE(conf::ranges::equal(testing::serialiseWithSpanStream<sizeof(std::int64_t)>(std::int64_t{100}),
3839
testing::makeCharArray(0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)));
39-
REQUIRE(conf::ranges::equal(testing::serialiseWithSpanStream<sizeof(std::size_t) + string.size()>(string), testing::serialise(string)));
40-
REQUIRE(
41-
conf::ranges::equal(testing::serialiseWithSpanStream<sizeof(std::size_t) + bytes.size()>(std::span{bytes}), testing::serialise(std::span{bytes})));
40+
REQUIRE(conf::ranges::equal(testing::serialiseWithSpanStream<sizeof(std::size_t) + string.size()>(string), binarySerialise(string)));
41+
REQUIRE(conf::ranges::equal(testing::serialiseWithSpanStream<sizeof(std::size_t) + bytes.size()>(std::span{bytes}), binarySerialise(std::span{bytes})));
4242

4343
REQUIRE_THROWS_AS(testing::serialiseWithSpanStream<sizeof(std::int32_t)>(std::int64_t{100}), BinaryException);
4444
REQUIRE_THROWS_AS(testing::serialiseWithSpanStream<sizeof(std::int64_t)>(string), BinaryException);
@@ -49,9 +49,9 @@ TEST_CASE("Binary writer handles error cases gracefully", "[morpheus.serialisati
4949
{
5050
REQUIRE(conf::ranges::equal(testing::serialiseWithLimitedSpace<sizeof(std::int64_t)>(std::int64_t{100}),
5151
testing::makeCharArray(0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)));
52-
REQUIRE(conf::ranges::equal(testing::serialiseWithLimitedSpace<sizeof(std::size_t) + string.size()>(string), testing::serialise(string)));
53-
REQUIRE(conf::ranges::equal(testing::serialiseWithLimitedSpace<sizeof(std::size_t) + bytes.size()>(std::span{bytes}),
54-
testing::serialise(std::span{bytes})));
52+
REQUIRE(conf::ranges::equal(testing::serialiseWithLimitedSpace<sizeof(std::size_t) + string.size()>(string), binarySerialise(string)));
53+
REQUIRE(
54+
conf::ranges::equal(testing::serialiseWithLimitedSpace<sizeof(std::size_t) + bytes.size()>(std::span{bytes}), binarySerialise(std::span{bytes})));
5555

5656
REQUIRE_THROWS_AS(testing::serialiseWithLimitedSpace<sizeof(std::int32_t)>(std::int64_t{100}), std::ios_base::failure);
5757
REQUIRE_THROWS_AS(testing::serialiseWithLimitedSpace<sizeof(std::int32_t)>(string), std::ios_base::failure);

0 commit comments

Comments
 (0)