| /* |
| * Copyright (C) 2022 Igalia S.L. |
| * Copyright (C) 2023 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "ArgumentCoders.h" |
| #include "Decoder.h" |
| #include "Encoder.h" |
| #include "StreamConnectionEncoder.h" |
| #include "Test.h" |
| #include <wtf/StdLibExtras.h> |
| |
| namespace TestWebKitAPI { |
| |
| // ArgumentCoderEncoderDecoderTest internally constructs the encoder object specified |
| // as the template parameter, into which each test can encode the desired objects. |
| // It then uses that Encoder's internal buffer to create a Decoder object, intended for |
| // the test to decode those objects. |
| |
| struct EncoderDecoderTest { |
| static constexpr IPC::MessageName name() { return IPC::MessageName::IPCTester_EmptyMessage; } |
| }; |
| |
| struct EncoderTypeNames { |
| template<typename T> |
| static std::string GetName(int) |
| { |
| if (std::is_same_v<T, IPC::Encoder>) |
| return "Encoder"; |
| if (std::is_same_v<T, IPC::StreamConnectionEncoder>) |
| return "StreamConnectionEncoder"; |
| return "<unknown>"; |
| } |
| }; |
| |
| using EncoderTypes = ::testing::Types<IPC::Encoder, IPC::StreamConnectionEncoder>; |
| |
| template<typename T> class ArgumentCoderEncoderDecoderTest; |
| |
| template<> |
| class ArgumentCoderEncoderDecoderTest<IPC::Encoder> : public ::testing::Test { |
| public: |
| void SetUp() override |
| { |
| m_encoder = makeUnique<IPC::Encoder>(EncoderDecoderTest::name(), 0); |
| ASSERT_EQ(m_encoder->span().size(), headerSize()); |
| } |
| |
| IPC::Encoder& encoder() const { return *m_encoder; } |
| size_t headerSize() const { return 16; } |
| size_t encoderSize() const { return m_encoder->span().size(); } |
| |
| std::unique_ptr<IPC::Decoder> createDecoder() const |
| { |
| return IPC::Decoder::create(m_encoder->span(), { }); |
| } |
| |
| private: |
| std::unique_ptr<IPC::Encoder> m_encoder; |
| }; |
| |
| template<> |
| class ArgumentCoderEncoderDecoderTest<IPC::StreamConnectionEncoder> : public ::testing::Test { |
| public: |
| void SetUp() override |
| { |
| m_impl = makeUnique<Impl>(); |
| ASSERT_EQ(m_impl->encoder.size(), headerSize()); |
| } |
| |
| IPC::StreamConnectionEncoder& encoder() const { return m_impl->encoder; } |
| size_t headerSize() const { return 2; } |
| size_t encoderSize() const { return m_impl->encoder.size(); } |
| |
| std::unique_ptr<IPC::Decoder> createDecoder() const |
| { |
| auto decoder = makeUnique<IPC::Decoder>(m_impl->buffer.span(), 0); |
| return decoder; |
| } |
| |
| private: |
| struct Impl { |
| WTF_DEPRECATED_MAKE_STRUCT_FAST_ALLOCATED(Impl); |
| |
| Impl() |
| : buffer(1024, static_cast<uint8_t>(0)) |
| , encoder(EncoderDecoderTest::name(), buffer.mutableSpan()) |
| { } |
| |
| Vector<uint8_t> buffer; |
| IPC::StreamConnectionEncoder encoder; |
| }; |
| std::unique_ptr<Impl> m_impl; |
| }; |
| |
| |
| struct EncodingCounter { |
| WTF_DEPRECATED_MAKE_STRUCT_FAST_ALLOCATED(EncodingCounter); |
| |
| struct CounterValues { |
| CounterValues() = default; |
| CounterValues(unsigned encodingLValue, unsigned encodingRValue) |
| : encodingLValue(encodingLValue) |
| , encodingRValue(encodingRValue) |
| { } |
| |
| unsigned encodingLValue { 0 }; |
| unsigned encodingRValue { 0 }; |
| |
| bool operator==(const CounterValues& other) const |
| { |
| return encodingLValue == other.encodingLValue && encodingRValue == other.encodingRValue; |
| } |
| }; |
| |
| EncodingCounter(CounterValues& counterValues) |
| : m_counterValues(counterValues) |
| { } |
| |
| void encode(IPC::Encoder& encoder) const & { |
| encoder << static_cast<uint32_t>(0); |
| ++m_counterValues.encodingLValue; |
| } |
| |
| void encode(IPC::Encoder& encoder) && { |
| encoder << static_cast<uint32_t>(0); |
| ++m_counterValues.encodingRValue; |
| } |
| |
| static std::optional<EncodingCounter> decode(IPC::Decoder&) { return std::nullopt; } |
| |
| CounterValues& m_counterValues; |
| }; |
| |
| } // namespace TestWebKitAPI |
| |
| namespace IPC { |
| |
| template<> struct ArgumentCoder<TestWebKitAPI::EncodingCounter> { |
| static void encode(Encoder& encoder, const TestWebKitAPI::EncodingCounter& counter) |
| { |
| counter.encode(encoder); |
| } |
| static void encode(Encoder& encoder, TestWebKitAPI::EncodingCounter&& counter) |
| { |
| WTF::move(counter).encode(encoder); |
| } |
| }; |
| |
| } // namespace IPC |
| |
| namespace TestWebKitAPI { |
| |
| enum class EncodingCounterTestType { |
| LValue, |
| RValue, |
| MovedRValue, |
| }; |
| |
| void PrintTo(EncodingCounterTestType value, ::std::ostream* o) |
| { |
| switch (value) { |
| case EncodingCounterTestType::LValue: |
| *o << "LValue"; |
| return; |
| case EncodingCounterTestType::RValue: |
| *o << "RValue"; |
| return; |
| case EncodingCounterTestType::MovedRValue: |
| *o << "MovedRValue"; |
| return; |
| default: |
| break; |
| } |
| |
| *o << "Unknown"; |
| } |
| |
| class ArgumentCoderEncodingCounterTest : public ::testing::TestWithParam<std::tuple<EncodingCounterTestType>> { |
| public: |
| ArgumentCoderEncodingCounterTest() |
| : m_encoder(static_cast<IPC::MessageName>(0), 42) |
| { } |
| |
| template<typename F> |
| void testEncoding(unsigned expectedEncodingCount, const F& createFunctor) |
| { |
| EncodingCounter::CounterValues counterValues; |
| |
| switch (std::get<0>(GetParam())) { |
| case EncodingCounterTestType::LValue: { |
| auto object = createFunctor(counterValues); |
| m_encoder << object; |
| |
| ASSERT_EQ(counterValues, EncodingCounter::CounterValues(expectedEncodingCount, 0)); |
| break; |
| } |
| case EncodingCounterTestType::RValue: { |
| m_encoder << createFunctor(counterValues); |
| |
| ASSERT_EQ(counterValues, EncodingCounter::CounterValues(0, expectedEncodingCount)); |
| break; |
| } |
| case EncodingCounterTestType::MovedRValue: { |
| auto object = createFunctor(counterValues); |
| m_encoder << WTF::move(object); |
| |
| ASSERT_EQ(counterValues, EncodingCounter::CounterValues(0, expectedEncodingCount)); |
| break; |
| } |
| } |
| } |
| |
| private: |
| IPC::Encoder m_encoder; |
| }; |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeRawObject) |
| { |
| testEncoding(1, |
| [](auto& counterValues) |
| { |
| return EncodingCounter { counterValues }; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeOptional) |
| { |
| testEncoding(1, |
| [](auto& counterValues) |
| { |
| return std::optional<EncodingCounter> { EncodingCounter { counterValues } }; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodePair) |
| { |
| testEncoding(1, |
| [](auto& counterValues) |
| { |
| return std::pair<unsigned, EncodingCounter> { 0, EncodingCounter { counterValues } }; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeTuple) |
| { |
| testEncoding(2, |
| [](auto& counterValues) |
| { |
| return std::tuple<EncodingCounter, unsigned, EncodingCounter> { |
| EncodingCounter { counterValues }, 0, |
| EncodingCounter { counterValues }, |
| }; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeArray) |
| { |
| testEncoding(4, |
| [](auto& counterValues) |
| { |
| return std::array<EncodingCounter, 4> { |
| EncodingCounter { counterValues }, |
| EncodingCounter { counterValues }, |
| EncodingCounter { counterValues }, |
| EncodingCounter { counterValues }, |
| }; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeVector) |
| { |
| testEncoding(16, |
| [](auto& counterValues) |
| { |
| Vector<EncodingCounter> counters; |
| for (unsigned i = 0; i < 16; ++i) |
| counters.append(EncodingCounter { counterValues }); |
| return counters; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeVariant) |
| { |
| testEncoding(1, |
| [](auto& counterValues) |
| { |
| return Variant<EncodingCounter, unsigned> { EncodingCounter { counterValues } }; |
| }); |
| } |
| |
| TEST_P(ArgumentCoderEncodingCounterTest, EncodeUniquePtr) |
| { |
| testEncoding(1, |
| [](auto& counterValues) |
| { |
| return makeUnique<EncodingCounter>(counterValues); |
| }); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(ArgumentCoderTest, |
| ArgumentCoderEncodingCounterTest, |
| testing::Values(EncodingCounterTestType::LValue, EncodingCounterTestType::RValue, EncodingCounterTestType::MovedRValue), |
| TestParametersToStringFormatter()); |
| |
| |
| template<typename T> class ArgumentCoderDecodingMoveCounterTest : public ArgumentCoderEncoderDecoderTest<T> { }; |
| TYPED_TEST_SUITE_P(ArgumentCoderDecodingMoveCounterTest); |
| |
| // DecodingMoveCounter is a move-only class whose move constructor and |
| // move assignment operator increase the moved-in object's move counter. |
| |
| struct DecodingMoveCounter { |
| WTF_DEPRECATED_MAKE_STRUCT_FAST_ALLOCATED(DecodingMoveCounter); |
| |
| DecodingMoveCounter() = default; |
| |
| DecodingMoveCounter(const DecodingMoveCounter&) = delete; |
| DecodingMoveCounter& operator=(const DecodingMoveCounter&) = delete; |
| |
| DecodingMoveCounter(DecodingMoveCounter&& o) |
| { |
| moveCounter = o.moveCounter + 1; |
| } |
| |
| DecodingMoveCounter& operator=(DecodingMoveCounter&& o) |
| { |
| moveCounter = o.moveCounter + 1; |
| return *this; |
| } |
| |
| template<typename Encoder> |
| void encode(Encoder& encoder) |
| { |
| encoder << static_cast<uint64_t>(42); |
| } |
| |
| template<typename Decoder> |
| static std::optional<DecodingMoveCounter> decode(Decoder& decoder) |
| { |
| auto value = decoder.template decode<uint64_t>(); |
| if (!value || *value != 42) |
| return std::nullopt; |
| return std::make_optional<DecodingMoveCounter>(); |
| } |
| |
| unsigned moveCounter { 0 }; |
| }; |
| |
| } // namespace TestWebKitAPI |
| |
| namespace IPC { |
| |
| template<> struct ArgumentCoder<TestWebKitAPI::DecodingMoveCounter> { |
| template<typename Encoder> |
| static void encode(Encoder& encoder, TestWebKitAPI::DecodingMoveCounter&& counter) |
| { |
| WTF::move(counter).encode(encoder); |
| } |
| static std::optional<TestWebKitAPI::DecodingMoveCounter> decode(Decoder& decoder) |
| { |
| return TestWebKitAPI::DecodingMoveCounter::decode(decoder); |
| } |
| }; |
| |
| } // namespace IPC |
| |
| namespace TestWebKitAPI { |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeDirectly) |
| { |
| TestFixture::encoder() << DecodingMoveCounter { }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| auto counter = DecodingMoveCounter::decode(*decoder); |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 0u); |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeValue) |
| { |
| TestFixture::encoder() << DecodingMoveCounter { } << DecodingMoveCounter { }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<DecodingMoveCounter> counter; |
| *decoder >> counter; |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 1u); |
| } |
| { |
| auto counter = decoder->template decode<DecodingMoveCounter>(); |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 0u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeOptional) |
| { |
| TestFixture::encoder() << std::make_optional<DecodingMoveCounter>(); |
| TestFixture::encoder() << std::make_optional<DecodingMoveCounter>(); |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<std::optional<DecodingMoveCounter>> optional; |
| *decoder >> optional; |
| ASSERT_TRUE(!!optional); |
| |
| auto& counter = *optional; |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 2u); |
| } |
| { |
| auto optional = decoder->template decode<std::optional<DecodingMoveCounter>>(); |
| ASSERT_TRUE(!!optional); |
| |
| auto& counter = *optional; |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 1u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodePair) |
| { |
| TestFixture::encoder() << std::pair { static_cast<uint64_t>(0), DecodingMoveCounter { } }; |
| TestFixture::encoder() << std::pair { static_cast<uint64_t>(0), DecodingMoveCounter { } }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<std::pair<uint64_t, DecodingMoveCounter>> pair; |
| *decoder >> pair; |
| ASSERT_TRUE(!!pair); |
| ASSERT_EQ(pair->second.moveCounter, 2u); |
| } |
| { |
| auto pair = decoder->template decode<std::pair<uint64_t, DecodingMoveCounter>>(); |
| ASSERT_TRUE(!!pair); |
| ASSERT_EQ(pair->second.moveCounter, 1u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeTuple) |
| { |
| using TupleType = std::tuple<DecodingMoveCounter, uint64_t, DecodingMoveCounter>; |
| TestFixture::encoder() << TupleType { DecodingMoveCounter { }, 42, DecodingMoveCounter { } }; |
| TestFixture::encoder() << TupleType { DecodingMoveCounter { }, 42, DecodingMoveCounter { } }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<TupleType> tuple; |
| *decoder >> tuple; |
| ASSERT_TRUE(!!tuple); |
| ASSERT_EQ(std::get<0>(*tuple).moveCounter, 2u); |
| ASSERT_EQ(std::get<2>(*tuple).moveCounter, 2u); |
| } |
| { |
| auto tuple = decoder->template decode<TupleType>(); |
| ASSERT_TRUE(!!tuple); |
| ASSERT_EQ(std::get<0>(*tuple).moveCounter, 1u); |
| ASSERT_EQ(std::get<2>(*tuple).moveCounter, 1u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeArray) |
| { |
| TestFixture::encoder() << std::array<DecodingMoveCounter, 2> { DecodingMoveCounter { }, DecodingMoveCounter { } }; |
| TestFixture::encoder() << std::array<DecodingMoveCounter, 2> { DecodingMoveCounter { }, DecodingMoveCounter { } }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<std::array<DecodingMoveCounter, 2>> array; |
| *decoder >> array; |
| ASSERT_TRUE(!!array); |
| for (auto&& entry : *array) |
| ASSERT_EQ(entry.moveCounter, 3u); |
| } |
| { |
| auto array = decoder->template decode<std::array<DecodingMoveCounter, 2>>(); |
| ASSERT_TRUE(!!array); |
| for (auto&& entry : *array) |
| ASSERT_EQ(entry.moveCounter, 2u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeVector) |
| { |
| TestFixture::encoder() << Vector<DecodingMoveCounter>::from(DecodingMoveCounter { }, DecodingMoveCounter { }); |
| TestFixture::encoder() << Vector<DecodingMoveCounter>::from(DecodingMoveCounter { }, DecodingMoveCounter { }); |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<Vector<DecodingMoveCounter>> vector; |
| *decoder >> vector; |
| ASSERT_TRUE(!!vector); |
| ASSERT_EQ(vector->size(), 2u); |
| for (auto&& entry : *vector) |
| ASSERT_EQ(entry.moveCounter, 1u); |
| } |
| { |
| auto vector = decoder->template decode<Vector<DecodingMoveCounter>>(); |
| ASSERT_TRUE(!!vector); |
| ASSERT_EQ(vector->size(), 2u); |
| for (auto&& entry : *vector) |
| ASSERT_EQ(entry.moveCounter, 1u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeVariant) |
| { |
| using VariantType = Variant<DecodingMoveCounter, uint64_t>; |
| TestFixture::encoder() << VariantType { DecodingMoveCounter { } }; |
| TestFixture::encoder() << VariantType { DecodingMoveCounter { } }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<VariantType> variant; |
| *decoder >> variant; |
| ASSERT_TRUE(!!variant); |
| ASSERT_EQ(variant->index(), 0u); |
| ASSERT_EQ(std::get<0>(*variant).moveCounter, 2u); |
| } |
| { |
| auto variant = decoder->template decode<VariantType>(); |
| ASSERT_TRUE(!!variant); |
| ASSERT_EQ(variant->index(), 0u); |
| ASSERT_EQ(std::get<0>(*variant).moveCounter, 1u); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderDecodingMoveCounterTest, DecodeUniquePtr) |
| { |
| TestFixture::encoder() << makeUnique<DecodingMoveCounter>() << makeUnique<DecodingMoveCounter>(); |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| std::optional<std::unique_ptr<DecodingMoveCounter>> pointer; |
| *decoder >> pointer; |
| ASSERT_TRUE(!!pointer); |
| |
| auto& counter = *pointer; |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 1u); |
| } |
| { |
| auto pointer = decoder->template decode<std::unique_ptr<DecodingMoveCounter>>(); |
| ASSERT_TRUE(!!pointer); |
| |
| auto& counter = *pointer; |
| ASSERT_TRUE(!!counter); |
| ASSERT_EQ(counter->moveCounter, 1u); |
| } |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(ArgumentCoderDecodingMoveCounterTest, |
| DecodeDirectly, DecodeValue, DecodeOptional, DecodePair, DecodeTuple, |
| DecodeArray, DecodeVector, DecodeVariant, DecodeUniquePtr); |
| INSTANTIATE_TYPED_TEST_SUITE_P(ArgumentCoderTest, ArgumentCoderDecodingMoveCounterTest, EncoderTypes, EncoderTypeNames); |
| |
| |
| template<typename T> class ArgumentCoderSpanTest : public ArgumentCoderEncoderDecoderTest<T> { }; |
| TYPED_TEST_SUITE_P(ArgumentCoderSpanTest); |
| |
| TYPED_TEST_P(ArgumentCoderSpanTest, SimpleSpan) |
| { |
| std::array<uint8_t, 16> data8 { 0, 0, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 }; |
| std::array<uint32_t, 16> data32 { 0, 0, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 }; |
| TestFixture::encoder() << std::span<const uint8_t> { data8 } << std::span<const uint32_t> { data32 } << std::span<const uint64_t> { }; |
| |
| auto decoder = TestFixture::createDecoder(); |
| { |
| auto span = decoder->template decode<std::span<const uint8_t>>(); |
| ASSERT_TRUE(!!span); |
| ASSERT_EQ(data8.size(), span->size()); |
| ASSERT_EQ(data8.size() * sizeof(uint8_t), span->size_bytes()); |
| ASSERT_TRUE(equalSpans(std::span { data8 }, *span)); |
| } |
| { |
| auto span = decoder->template decode<std::span<const uint32_t>>(); |
| ASSERT_TRUE(!!span); |
| ASSERT_EQ(data32.size(), span->size()); |
| ASSERT_EQ(data32.size() * sizeof(uint32_t), span->size_bytes()); |
| ASSERT_TRUE(equalSpans(std::span { data32 }, *span)); |
| } |
| { |
| auto span = decoder->template decode<std::span<const uint64_t>>(); |
| ASSERT_TRUE(!!span); |
| ASSERT_EQ(span->data(), nullptr); |
| ASSERT_EQ(span->size(), 0u); |
| } |
| } |
| |
| template<typename T, size_t S> |
| struct EncodedValue { |
| using Type = T; |
| static constexpr size_t Size = S; |
| }; |
| |
| template<typename EncodedValueType, typename... EncodedValueTypes> |
| static size_t calculateEncodedSize(size_t value, EncodedValueType, EncodedValueTypes...) |
| { |
| value = roundUpToMultipleOf<alignof(typename EncodedValueType::Type)>(value) + EncodedValueType::Size * sizeof(typename EncodedValueType::Type); |
| if constexpr (sizeof...(EncodedValueTypes) > 0) |
| return calculateEncodedSize(value, EncodedValueTypes { }...); |
| else |
| return value; |
| } |
| |
| struct alignas(16) AlignedStructure { }; |
| |
| TYPED_TEST_P(ArgumentCoderSpanTest, AlignedSpan) |
| { |
| ASSERT_EQ(alignof(AlignedStructure), 16u); |
| |
| auto& encoder = TestFixture::encoder(); |
| { |
| // This one byte will misalign the encoded data, making proper aligning of AlignedStructure properly testable. |
| encoder << static_cast<uint8_t>(42); |
| ASSERT_EQ(TestFixture::encoderSize(), calculateEncodedSize(TestFixture::headerSize(), EncodedValue<uint8_t, 1> { })); |
| ASSERT_TRUE(!!(TestFixture::encoderSize() % alignof(AlignedStructure))); |
| } |
| { |
| // Span over the array data is encoded. Encoded data now includes the header, the previous byte, the span size, and array data. |
| std::array<AlignedStructure, 2> alignedData { AlignedStructure { }, AlignedStructure { } }; |
| encoder << std::span<const AlignedStructure> { alignedData }; |
| ASSERT_EQ(TestFixture::encoderSize(), calculateEncodedSize(TestFixture::headerSize(), |
| EncodedValue<uint8_t, 1> { }, EncodedValue<uint64_t, 1> { }, EncodedValue<AlignedStructure, 2> { })); |
| } |
| |
| auto decoder = TestFixture::createDecoder(); |
| ASSERT_EQ(decoder->currentBufferOffset(), TestFixture::headerSize()); |
| { |
| auto byte = decoder->template decode<uint8_t>(); |
| ASSERT_TRUE(!!byte); |
| ASSERT_EQ(*byte, 42); |
| ASSERT_EQ(decoder->currentBufferOffset(), calculateEncodedSize(TestFixture::headerSize(), EncodedValue<uint8_t, 1> { })); |
| } |
| { |
| auto alignedData = decoder->template decode<std::span<const AlignedStructure>>(); |
| ASSERT_TRUE(!!alignedData); |
| ASSERT_NE(alignedData->data(), nullptr); |
| ASSERT_EQ(alignedData->size(), 2u); |
| ASSERT_EQ(decoder->currentBufferOffset(), calculateEncodedSize(TestFixture::headerSize(), |
| EncodedValue<uint8_t, 1> { }, EncodedValue<uint64_t, 1> { }, EncodedValue<AlignedStructure, 2> { })); |
| } |
| } |
| |
| TYPED_TEST_P(ArgumentCoderSpanTest, AlignedEmptySpan) |
| { |
| ASSERT_EQ(alignof(AlignedStructure), 16u); |
| |
| auto& encoder = TestFixture::encoder(); |
| { |
| // Only data about the empty span that's encoded is the 64-bit size value, and nothing more. |
| encoder << std::span<const AlignedStructure> { }; |
| ASSERT_EQ(TestFixture::encoderSize(), calculateEncodedSize(TestFixture::headerSize(), EncodedValue<uint64_t, 1> { })); |
| } |
| { |
| // Another byte is encoded tightly after the span size. |
| encoder << static_cast<uint8_t>(42); |
| ASSERT_EQ(TestFixture::encoderSize(), calculateEncodedSize(TestFixture::headerSize(), EncodedValue<uint64_t, 1> { }, EncodedValue<uint8_t, 1> { })); |
| } |
| |
| auto decoder = TestFixture::createDecoder(); |
| ASSERT_EQ(decoder->currentBufferOffset(), TestFixture::headerSize()); |
| { |
| // A valid but empty span should be decoded, meaning a null data pointer and 0 size. |
| auto alignedData = decoder->template decode<std::span<const AlignedStructure>>(); |
| ASSERT_TRUE(!!alignedData); |
| ASSERT_EQ(alignedData->data(), nullptr); |
| ASSERT_EQ(alignedData->size(), 0u); |
| ASSERT_EQ(decoder->currentBufferOffset(), calculateEncodedSize(TestFixture::headerSize(), EncodedValue<uint64_t, 1> { })); |
| } |
| { |
| auto byte = decoder->template decode<uint8_t>(); |
| ASSERT_TRUE(!!byte); |
| ASSERT_EQ(*byte, 42); |
| ASSERT_EQ(decoder->currentBufferOffset(), calculateEncodedSize(TestFixture::headerSize(), EncodedValue<uint64_t, 1> { }, EncodedValue<uint8_t, 1> { })); |
| } |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(ArgumentCoderSpanTest, |
| SimpleSpan, AlignedSpan, AlignedEmptySpan); |
| INSTANTIATE_TYPED_TEST_SUITE_P(ArgumentCoderTest, ArgumentCoderSpanTest, EncoderTypes, EncoderTypeNames); |
| |
| template<typename T> class ArgumentCoderVectorTest : public ArgumentCoderEncoderDecoderTest<T> { }; |
| TYPED_TEST_SUITE_P(ArgumentCoderVectorTest); |
| |
| TYPED_TEST_P(ArgumentCoderVectorTest, VectorTooBig) |
| { |
| std::array<uint8_t, 9> bytes { 255, 255, 255, 255, 255, 255, 255, 255, 255 }; |
| TestFixture::encoder() << std::span<uint8_t, 9>(bytes); |
| auto optionalVector = TestFixture::createDecoder()->template decode<Vector<String>>(); |
| ASSERT_FALSE(optionalVector); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(ArgumentCoderVectorTest, |
| VectorTooBig); |
| INSTANTIATE_TYPED_TEST_SUITE_P(ArgumentCoderTest, ArgumentCoderVectorTest, EncoderTypes, EncoderTypeNames); |
| |
| } // namespace TestWebKitAPI |