diff options
Diffstat (limited to 'ipc/gtest/TestDataPipe.cpp')
-rw-r--r-- | ipc/gtest/TestDataPipe.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/ipc/gtest/TestDataPipe.cpp b/ipc/gtest/TestDataPipe.cpp new file mode 100644 index 0000000000..340da7c218 --- /dev/null +++ b/ipc/gtest/TestDataPipe.cpp @@ -0,0 +1,374 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "chrome/common/ipc_message.h" +#include "gtest/gtest.h" + +#include "mozilla/gtest/MozAssertions.h" +#include "mozilla/ipc/DataPipe.h" +#include "nsIAsyncInputStream.h" +#include "nsIAsyncOutputStream.h" +#include "nsNetUtil.h" +#include "nsStreamUtils.h" + +namespace mozilla::ipc { + +namespace { + +struct InputStreamCallback : public nsIInputStreamCallback { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + explicit InputStreamCallback( + std::function<nsresult(nsIAsyncInputStream*)> aFunc = nullptr) + : mFunc(std::move(aFunc)) {} + + NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream* aStream) override { + MOZ_ALWAYS_FALSE(mCalled.exchange(true)); + return mFunc ? mFunc(aStream) : NS_OK; + } + + bool Called() const { return mCalled; } + + private: + virtual ~InputStreamCallback() = default; + + std::atomic<bool> mCalled = false; + std::function<nsresult(nsIAsyncInputStream*)> mFunc; +}; + +NS_IMPL_ISUPPORTS(InputStreamCallback, nsIInputStreamCallback) + +struct OutputStreamCallback : public nsIOutputStreamCallback { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + explicit OutputStreamCallback( + std::function<nsresult(nsIAsyncOutputStream*)> aFunc = nullptr) + : mFunc(std::move(aFunc)) {} + + NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream* aStream) override { + MOZ_ALWAYS_FALSE(mCalled.exchange(true)); + return mFunc ? mFunc(aStream) : NS_OK; + } + + bool Called() const { return mCalled; } + + private: + virtual ~OutputStreamCallback() = default; + + std::atomic<bool> mCalled = false; + std::function<nsresult(nsIAsyncOutputStream*)> mFunc; +}; + +NS_IMPL_ISUPPORTS(OutputStreamCallback, nsIOutputStreamCallback) + +// Populate an array with the given number of bytes. Data is lorem ipsum +// random text, but deterministic across multiple calls. +void CreateData(uint32_t aNumBytes, nsCString& aDataOut) { + static const char data[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec egestas " + "purus eu condimentum iaculis. In accumsan leo eget odio porttitor, non " + "rhoncus nulla vestibulum. Etiam lacinia consectetur nisl nec " + "sollicitudin. Sed fringilla accumsan diam, pulvinar varius massa. Duis " + "mollis dignissim felis, eget tempus nisi tristique ut. Fusce euismod, " + "lectus non lacinia tempor, tellus diam suscipit quam, eget hendrerit " + "lacus nunc fringilla ante. Sed ultrices massa vitae risus molestie, ut " + "finibus quam laoreet nullam."; + static const uint32_t dataLength = sizeof(data) - 1; + + aDataOut.SetCapacity(aNumBytes); + + while (aNumBytes > 0) { + uint32_t amount = std::min(dataLength, aNumBytes); + aDataOut.Append(data, amount); + aNumBytes -= amount; + } +} + +// Synchronously consume the given input stream and validate the resulting data +// against the given string of expected values. +void ConsumeAndValidateStream(nsIInputStream* aStream, + const nsACString& aExpectedData) { + nsAutoCString outputData; + nsresult rv = NS_ConsumeStream(aStream, UINT32_MAX, outputData); + ASSERT_NS_SUCCEEDED(rv); + ASSERT_EQ(aExpectedData.Length(), outputData.Length()); + ASSERT_TRUE(aExpectedData.Equals(outputData)); +} + +} // namespace + +TEST(DataPipe, SegmentedReadWrite) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + + nsresult rv = + NewDataPipe(1024, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + nsCString inputData1; + CreateData(512, inputData1); + + uint32_t numWritten = 0; + rv = writer->Write(inputData1.BeginReading(), inputData1.Length(), + &numWritten); + ASSERT_NS_SUCCEEDED(rv); + EXPECT_EQ(numWritten, 512u); + + uint64_t available = 0; + rv = reader->Available(&available); + EXPECT_EQ(available, 512u); + ConsumeAndValidateStream(reader, inputData1); + + nsCString inputData2; + CreateData(1024, inputData2); + + rv = writer->Write(inputData2.BeginReading(), inputData2.Length(), + &numWritten); + ASSERT_NS_SUCCEEDED(rv); + EXPECT_EQ(numWritten, 1024u); + + rv = reader->Available(&available); + EXPECT_EQ(available, 1024u); + ConsumeAndValidateStream(reader, inputData2); +} + +TEST(DataPipe, SegmentedPartialRead) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + + nsresult rv = + NewDataPipe(1024, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + nsCString inputData1; + CreateData(512, inputData1); + + uint32_t numWritten = 0; + rv = writer->Write(inputData1.BeginReading(), inputData1.Length(), + &numWritten); + ASSERT_NS_SUCCEEDED(rv); + EXPECT_EQ(numWritten, 512u); + + uint64_t available = 0; + rv = reader->Available(&available); + EXPECT_EQ(available, 512u); + ConsumeAndValidateStream(reader, inputData1); + + nsCString inputData2; + CreateData(1024, inputData2); + + rv = writer->Write(inputData2.BeginReading(), inputData2.Length(), + &numWritten); + ASSERT_NS_SUCCEEDED(rv); + EXPECT_EQ(numWritten, 1024u); + + rv = reader->Available(&available); + EXPECT_EQ(available, 1024u); + + nsAutoCString outputData; + rv = NS_ReadInputStreamToString(reader, outputData, 768); + ASSERT_NS_SUCCEEDED(rv); + ASSERT_EQ(768u, outputData.Length()); + ASSERT_TRUE(Substring(inputData2, 0, 768).Equals(outputData)); + + rv = reader->Available(&available); + EXPECT_EQ(available, 256u); + + nsAutoCString outputData2; + rv = NS_ReadInputStreamToString(reader, outputData2, 256); + ASSERT_NS_SUCCEEDED(rv); + ASSERT_EQ(256u, outputData2.Length()); + ASSERT_TRUE(Substring(inputData2, 768).Equals(outputData2)); +} + +TEST(DataPipe, Write_AsyncWait) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + + const uint32_t segmentSize = 1024; + + nsresult rv = + NewDataPipe(segmentSize, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + nsCString inputData; + CreateData(segmentSize, inputData); + + uint32_t numWritten = 0; + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_NS_SUCCEEDED(rv); + EXPECT_EQ(numWritten, segmentSize); + + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv); + + RefPtr<OutputStreamCallback> cb = new OutputStreamCallback(); + + rv = writer->AsyncWait(cb, 0, 0, GetCurrentSerialEventTarget()); + ASSERT_NS_SUCCEEDED(rv); + + NS_ProcessPendingEvents(nullptr); + + ASSERT_FALSE(cb->Called()); + + ConsumeAndValidateStream(reader, inputData); + + ASSERT_FALSE(cb->Called()); + + NS_ProcessPendingEvents(nullptr); + + ASSERT_TRUE(cb->Called()); +} + +TEST(DataPipe, Read_AsyncWait) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + + const uint32_t segmentSize = 1024; + + nsresult rv = + NewDataPipe(segmentSize, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + nsCString inputData; + CreateData(segmentSize, inputData); + + RefPtr<InputStreamCallback> cb = new InputStreamCallback(); + + rv = reader->AsyncWait(cb, 0, 0, GetCurrentSerialEventTarget()); + ASSERT_NS_SUCCEEDED(rv); + + NS_ProcessPendingEvents(nullptr); + + ASSERT_FALSE(cb->Called()); + + uint32_t numWritten = 0; + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_NS_SUCCEEDED(rv); + + ASSERT_FALSE(cb->Called()); + + NS_ProcessPendingEvents(nullptr); + + ASSERT_TRUE(cb->Called()); + + ConsumeAndValidateStream(reader, inputData); +} + +TEST(DataPipe, Write_AsyncWait_Cancel) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + + const uint32_t segmentSize = 1024; + + nsresult rv = + NewDataPipe(segmentSize, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + nsCString inputData; + CreateData(segmentSize, inputData); + + uint32_t numWritten = 0; + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_NS_SUCCEEDED(rv); + EXPECT_EQ(numWritten, segmentSize); + + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv); + + RefPtr<OutputStreamCallback> cb = new OutputStreamCallback(); + + // Register a callback and immediately cancel it. + rv = writer->AsyncWait(cb, 0, 0, GetCurrentSerialEventTarget()); + ASSERT_NS_SUCCEEDED(rv); + rv = writer->AsyncWait(nullptr, 0, 0, nullptr); + ASSERT_NS_SUCCEEDED(rv); + + // Even after consuming the stream and processing pending events, the callback + // shouldn't be called as it was cancelled. + ConsumeAndValidateStream(reader, inputData); + NS_ProcessPendingEvents(nullptr); + ASSERT_FALSE(cb->Called()); +} + +TEST(DataPipe, Read_AsyncWait_Cancel) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + + const uint32_t segmentSize = 1024; + + nsresult rv = + NewDataPipe(segmentSize, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + nsCString inputData; + CreateData(segmentSize, inputData); + + RefPtr<InputStreamCallback> cb = new InputStreamCallback(); + + // Register a callback and immediately cancel it. + rv = reader->AsyncWait(cb, 0, 0, GetCurrentSerialEventTarget()); + ASSERT_NS_SUCCEEDED(rv); + + rv = reader->AsyncWait(nullptr, 0, 0, nullptr); + ASSERT_NS_SUCCEEDED(rv); + + // Write data into the pipe to make the callback become ready. + uint32_t numWritten = 0; + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_NS_SUCCEEDED(rv); + + // Even after processing pending events, the callback shouldn't be called as + // it was cancelled. + NS_ProcessPendingEvents(nullptr); + ASSERT_FALSE(cb->Called()); + + ConsumeAndValidateStream(reader, inputData); +} + +TEST(DataPipe, SerializeReader) +{ + RefPtr<DataPipeReceiver> reader; + RefPtr<DataPipeSender> writer; + nsresult rv = + NewDataPipe(1024, getter_AddRefs(writer), getter_AddRefs(reader)); + ASSERT_NS_SUCCEEDED(rv); + + IPC::Message msg(MSG_ROUTING_NONE, 0); + IPC::MessageWriter msgWriter(msg); + IPC::WriteParam(&msgWriter, reader); + + uint64_t available = 0; + rv = reader->Available(&available); + ASSERT_NS_FAILED(rv); + + nsCString inputData; + CreateData(512, inputData); + + uint32_t numWritten = 0; + rv = writer->Write(inputData.BeginReading(), inputData.Length(), &numWritten); + ASSERT_NS_SUCCEEDED(rv); + + RefPtr<DataPipeReceiver> reader2; + IPC::MessageReader msgReader(msg); + ASSERT_TRUE(IPC::ReadParam(&msgReader, &reader2)); + ASSERT_TRUE(reader2); + + rv = reader2->Available(&available); + ASSERT_NS_SUCCEEDED(rv); + ASSERT_EQ(available, 512u); + ConsumeAndValidateStream(reader2, inputData); +} + +} // namespace mozilla::ipc |