#include "gtest/gtest.h" #include "nsIStreamTransportService.h" #include "nsStreamUtils.h" #include "nsThreadUtils.h" #include "Helpers.h" #include "nsNetCID.h" #include "nsServiceManagerUtils.h" #include "nsITransport.h" #include "nsNetUtil.h" static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); void CreateStream(already_AddRefed aSource, nsIAsyncInputStream** aStream) { nsCOMPtr source = std::move(aSource); nsresult rv; nsCOMPtr sts = do_GetService(kStreamTransportServiceCID, &rv); ASSERT_EQ(NS_OK, rv); nsCOMPtr transport; rv = sts->CreateInputTransport(source, true, getter_AddRefs(transport)); ASSERT_EQ(NS_OK, rv); nsCOMPtr wrapper; rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper)); ASSERT_EQ(NS_OK, rv); nsCOMPtr asyncStream = do_QueryInterface(wrapper); MOZ_RELEASE_ASSERT(asyncStream); asyncStream.forget(aStream); } class BlockingSyncStream final : public nsIInputStream { nsCOMPtr mStream; public: NS_DECL_THREADSAFE_ISUPPORTS explicit BlockingSyncStream(const nsACString& aBuffer) { NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer); } NS_IMETHOD Available(uint64_t* aLength) override { return mStream->Available(aLength); } NS_IMETHOD StreamStatus() override { return mStream->StreamStatus(); } NS_IMETHOD Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override { return mStream->Read(aBuffer, aCount, aReadCount); } NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, uint32_t* aResult) override { return mStream->ReadSegments(aWriter, aClosure, aCount, aResult); } NS_IMETHOD Close() override { return mStream->Close(); } NS_IMETHOD IsNonBlocking(bool* aNonBlocking) override { *aNonBlocking = false; return NS_OK; } private: ~BlockingSyncStream() = default; }; NS_IMPL_ISUPPORTS(BlockingSyncStream, nsIInputStream) // Testing a simple blocking stream. TEST(TestInputStreamTransport, BlockingNotAsync) { RefPtr stream = new BlockingSyncStream("Hello world"_ns); nsCOMPtr ais; CreateStream(stream.forget(), getter_AddRefs(ais)); ASSERT_TRUE(!!ais); nsAutoCString data; nsresult rv = NS_ReadInputStreamToString(ais, data, -1); ASSERT_EQ(NS_OK, rv); ASSERT_TRUE(data.EqualsLiteral("Hello world")); } class BlockingAsyncStream final : public nsIAsyncInputStream { nsCOMPtr mStream; bool mPending; public: NS_DECL_THREADSAFE_ISUPPORTS explicit BlockingAsyncStream(const nsACString& aBuffer) : mPending(false) { NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer); } NS_IMETHOD Available(uint64_t* aLength) override { mStream->Available(aLength); // 1 char at the time, just to test the asyncWait+Read loop a bit more. if (*aLength > 0) { *aLength = 1; } return NS_OK; } NS_IMETHOD StreamStatus() override { return mStream->StreamStatus(); } NS_IMETHOD Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override { mPending = !mPending; if (mPending) { return NS_BASE_STREAM_WOULD_BLOCK; } // 1 char at the time, just to test the asyncWait+Read loop a bit more. aCount = 1; return mStream->Read(aBuffer, aCount, aReadCount); } NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, uint32_t* aResult) override { mPending = !mPending; if (mPending) { return NS_BASE_STREAM_WOULD_BLOCK; } // 1 char at the time, just to test the asyncWait+Read loop a bit more. aCount = 1; return mStream->ReadSegments(aWriter, aClosure, aCount, aResult); } NS_IMETHOD Close() override { return mStream->Close(); } NS_IMETHOD IsNonBlocking(bool* aNonBlocking) override { *aNonBlocking = false; return NS_OK; } NS_IMETHOD CloseWithStatus(nsresult aStatus) override { return Close(); } NS_IMETHOD AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget* aEventTarget) override { if (!aCallback) { return NS_OK; } RefPtr self = this; nsCOMPtr callback = aCallback; nsCOMPtr r = NS_NewRunnableFunction( "gtest-asyncwait", [self, callback]() { callback->OnInputStreamReady(self); }); if (aEventTarget) { aEventTarget->Dispatch(r.forget()); } else { r->Run(); } return NS_OK; } private: ~BlockingAsyncStream() = default; }; NS_IMPL_ISUPPORTS(BlockingAsyncStream, nsIInputStream, nsIAsyncInputStream) // Testing an async blocking stream. TEST(TestInputStreamTransport, BlockingAsync) { RefPtr stream = new BlockingAsyncStream("Hello world"_ns); nsCOMPtr ais; CreateStream(stream.forget(), getter_AddRefs(ais)); ASSERT_TRUE(!!ais); nsAutoCString data; nsresult rv = NS_ReadInputStreamToString(ais, data, -1); ASSERT_EQ(NS_OK, rv); ASSERT_TRUE(data.EqualsLiteral("Hello world")); }