// Exercise concurrent serialization and deserialization. TEST_F(PushDeserializerTest, SerializationDeserialization) { auto const trajectory = BuildTrajectory(); int const byte_size = trajectory->ByteSize(); for (int i = 0; i < kRunsPerTest; ++i) { auto read_trajectory = make_not_null_unique<Trajectory>(); auto written_trajectory = BuildTrajectory(); auto storage = std::make_unique<std::uint8_t[]>(byte_size); std::uint8_t* data = &storage[0]; pull_serializer_ = std::make_unique<PullSerializer>(kSerializerChunkSize, kNumberOfChunks); push_deserializer_ = std::make_unique<PushDeserializer>( kDeserializerChunkSize, kNumberOfChunks); pull_serializer_->Start(std::move(written_trajectory)); push_deserializer_->Start( std::move(read_trajectory), PushDeserializerTest::CheckSerialization); for (;;) { Bytes const bytes = pull_serializer_->Pull(); std::memcpy(data, bytes.data, static_cast<size_t>(bytes.size)); push_deserializer_->Push(Bytes(data, bytes.size), std::bind(&PushDeserializerTest::Stomp, Bytes(data, bytes.size))); data = &data[bytes.size]; if (bytes.size == 0) { break; } } // Destroying the deserializer waits until deserialization is done. It is // important that this happens before |storage| is destroyed. pull_serializer_.reset(); push_deserializer_.reset(); } }
TEST_F(PullSerializerTest, SerializationThreading) { Trajectory read_trajectory; auto const trajectory = BuildTrajectory(); int const byte_size = trajectory->ByteSize(); auto expected_serialized_trajectory = std::make_unique<std::uint8_t[]>(byte_size); trajectory->SerializePartialToArray(&expected_serialized_trajectory[0], byte_size); // Run this test repeatedly to detect threading issues (it will flake in case // of problems). for (int i = 0; i < kRunsPerTest; ++i) { auto trajectory = BuildTrajectory(); auto actual_serialized_trajectory = std::make_unique<std::uint8_t[]>(byte_size); std::uint8_t* data = &actual_serialized_trajectory[0]; // The serialization happens concurrently with the test. pull_serializer_ = std::make_unique<PullSerializer>(kChunkSize, kNumberOfChunks); pull_serializer_->Start(std::move(trajectory)); for (;;) { Bytes const bytes = pull_serializer_->Pull(); std::memcpy(data, bytes.data, static_cast<size_t>(bytes.size)); data = &data[bytes.size]; if (bytes.size == 0) { break; } } pull_serializer_.reset(); // Check if the serialized version can be parsed and if not print the first // difference. if (!read_trajectory.ParseFromArray(&actual_serialized_trajectory[0], byte_size)) { for (int i = 0; i < byte_size; ++i) { if (expected_serialized_trajectory[i] != actual_serialized_trajectory[i]) { LOG(FATAL) << "position=" << i << ", expected=" << static_cast<int>(expected_serialized_trajectory[i]) << ", actual=" << static_cast<int>(actual_serialized_trajectory[i]); } } } } }
TEST_F(PullSerializerTest, SerializationSizes) { auto trajectory = BuildTrajectory(); pull_serializer_->Start(std::move(trajectory)); std::vector<std::int64_t> actual_sizes; std::vector<std::int64_t> expected_sizes(53, kChunkSize); expected_sizes.push_back(53); for (;;) { Bytes const bytes = pull_serializer_->Pull(); if (bytes.size == 0) { break; } actual_sizes.push_back(bytes.size); } EXPECT_THAT(actual_sizes, ElementsAreArray(expected_sizes)); }
} } // Destroying the deserializer waits until deserialization is done. It is // important that this happens before |storage| is destroyed. pull_serializer_.reset(); push_deserializer_.reset(); } } // Check that deserialization fails if we stomp on one extra bytes. TEST_F(PushDeserializerDeathTest, Stomp) { EXPECT_DEATH({ const int kStompChunk = 77; auto read_trajectory = make_not_null_unique<Trajectory>(); auto const trajectory = BuildTrajectory(); int const byte_size = trajectory->ByteSize(); auto serialized_trajectory = std::make_unique<std::uint8_t[]>(byte_size); trajectory->SerializePartialToArray(&serialized_trajectory[0], byte_size); push_deserializer_->Start( std::move(read_trajectory), &PushDeserializerTest::CheckSerialization); int left = byte_size; for (int i = 0; i < byte_size; i += kStompChunk) { Bytes bytes(&serialized_trajectory[i], std::min(left, kStompChunk)); push_deserializer_->Push(bytes, std::bind(&PushDeserializerTest::Stomp, Bytes(bytes.data, bytes.size + 1))); left -= kStompChunk; } push_deserializer_->Push(Bytes(), nullptr);