diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/tests/gtest/TestNonBlockingAsyncInputStream.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestNonBlockingAsyncInputStream.cpp b/xpcom/tests/gtest/TestNonBlockingAsyncInputStream.cpp new file mode 100644 index 0000000000..fa2ac810a7 --- /dev/null +++ b/xpcom/tests/gtest/TestNonBlockingAsyncInputStream.cpp @@ -0,0 +1,378 @@ +#include "gtest/gtest.h" + +#include "mozilla/NonBlockingAsyncInputStream.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "nsIAsyncInputStream.h" +#include "nsIThread.h" +#include "nsStreamUtils.h" +#include "nsString.h" +#include "nsStringStream.h" +#include "Helpers.h" + +using mozilla::NonBlockingAsyncInputStream; +using mozilla::SpinEventLoopUntil; + +TEST(TestNonBlockingAsyncInputStream, Simple) +{ + nsCString data; + data.Assign("Hello world!"); + + // It should not be async. + bool nonBlocking = false; + nsCOMPtr<nsIAsyncInputStream> async; + + { + // Let's create a test string inputStream + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + + async = do_QueryInterface(stream); + ASSERT_EQ(nullptr, async); + + // It must be non-blocking + ASSERT_EQ(NS_OK, stream->IsNonBlocking(&nonBlocking)); + ASSERT_TRUE(nonBlocking); + + // Here the non-blocking stream. + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + ASSERT_TRUE(!!async); + + // Still non-blocking + ASSERT_EQ(NS_OK, async->IsNonBlocking(&nonBlocking)); + ASSERT_TRUE(nonBlocking); + + // Testing ::Available() + uint64_t length; + ASSERT_EQ(NS_OK, async->Available(&length)); + ASSERT_EQ(data.Length(), length); + + // Read works fine. + char buffer[1024]; + uint32_t read = 0; + ASSERT_EQ(NS_OK, async->Read(buffer, sizeof(buffer), &read)); + ASSERT_EQ(data.Length(), read); + ASSERT_TRUE(data.Equals(nsCString(buffer, read))); +} + +class ReadSegmentsData { + public: + ReadSegmentsData(nsIInputStream* aStream, char* aBuffer) + : mStream(aStream), mBuffer(aBuffer) {} + + nsIInputStream* mStream; + char* mBuffer; +}; + +static nsresult ReadSegmentsFunction(nsIInputStream* aInStr, void* aClosure, + const char* aBuffer, uint32_t aOffset, + uint32_t aCount, uint32_t* aCountWritten) { + ReadSegmentsData* data = static_cast<ReadSegmentsData*>(aClosure); + if (aInStr != data->mStream) return NS_ERROR_FAILURE; + memcpy(&data->mBuffer[aOffset], aBuffer, aCount); + *aCountWritten = aCount; + return NS_OK; +} + +TEST(TestNonBlockingAsyncInputStream, ReadSegments) +{ + nsCString data; + data.Assign("Hello world!"); + + nsCOMPtr<nsIAsyncInputStream> async; + { + // Let's create a test string inputStream + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + + // Here the non-blocking stream. + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + + // Read works fine. + char buffer[1024]; + uint32_t read = 0; + ReadSegmentsData closure(async, buffer); + ASSERT_EQ(NS_OK, async->ReadSegments(ReadSegmentsFunction, &closure, + sizeof(buffer), &read)); + ASSERT_EQ(data.Length(), read); + ASSERT_TRUE(data.Equals(nsCString(buffer, read))); +} + +TEST(TestNonBlockingAsyncInputStream, AsyncWait_Simple) +{ + nsCString data; + data.Assign("Hello world!"); + + nsCOMPtr<nsIAsyncInputStream> async; + { + // Let's create a test string inputStream + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + + // Here the non-blocking stream. + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + ASSERT_TRUE(!!async); + + // Testing ::Available() + uint64_t length; + ASSERT_EQ(NS_OK, async->Available(&length)); + ASSERT_EQ(data.Length(), length); + + // Testing ::AsyncWait - without EventTarget + RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback(); + + ASSERT_EQ(NS_OK, async->AsyncWait(cb, 0, 0, nullptr)); + ASSERT_TRUE(cb->Called()); + + // Testing ::AsyncWait - with EventTarget + cb = new testing::InputStreamCallback(); + nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); + + ASSERT_EQ(NS_OK, async->AsyncWait(cb, 0, 0, thread)); + ASSERT_FALSE(cb->Called()); + MOZ_ALWAYS_TRUE(SpinEventLoopUntil( + "xpcom:TEST(TestNonBlockingAsyncInputStream, AsyncWait_Simple)"_ns, + [&]() { return cb->Called(); })); + ASSERT_TRUE(cb->Called()); + + // Read works fine. + char buffer[1024]; + uint32_t read = 0; + ASSERT_EQ(NS_OK, async->Read(buffer, sizeof(buffer), &read)); + ASSERT_EQ(data.Length(), read); + ASSERT_TRUE(data.Equals(nsCString(buffer, read))); +} + +TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withoutEventTarget) +{ + nsCString data; + data.Assign("Hello world!"); + + nsCOMPtr<nsIAsyncInputStream> async; + { + // Let's create a test string inputStream + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + + // Here the non-blocking stream. + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + ASSERT_TRUE(!!async); + + // Testing ::AsyncWait - no eventTarget + RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback(); + + ASSERT_EQ(NS_OK, async->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, + 0, nullptr)); + + ASSERT_FALSE(cb->Called()); + ASSERT_EQ(NS_OK, async->Close()); + ASSERT_TRUE(cb->Called()); +} + +TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withEventTarget) +{ + nsCString data; + data.Assign("Hello world!"); + + nsCOMPtr<nsIAsyncInputStream> async; + { + // Let's create a test string inputStream + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + + // Here the non-blocking stream. + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + ASSERT_TRUE(!!async); + + // Testing ::AsyncWait - with EventTarget + RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback(); + nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); + + ASSERT_EQ(NS_OK, async->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, + 0, thread)); + + ASSERT_FALSE(cb->Called()); + ASSERT_EQ(NS_OK, async->Close()); + ASSERT_FALSE(cb->Called()); + + MOZ_ALWAYS_TRUE(SpinEventLoopUntil( + "xpcom:TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withEventTarget)"_ns, + [&]() { return cb->Called(); })); + ASSERT_TRUE(cb->Called()); +} + +TEST(TestNonBlockingAsyncInputStream, Helper) +{ + nsCString data; + data.Assign("Hello world!"); + + nsCOMPtr<nsIAsyncInputStream> async; + { + // Let's create a test string inputStream + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + + // Here the non-blocking stream. + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + ASSERT_TRUE(!!async); + + // This should return the same object because async is already non-blocking + // and async. + nsCOMPtr<nsIAsyncInputStream> result; + nsCOMPtr<nsIAsyncInputStream> asyncTmp = async; + ASSERT_EQ(NS_OK, NS_MakeAsyncNonBlockingInputStream(asyncTmp.forget(), + getter_AddRefs(result))); + ASSERT_EQ(async, result); + + // This will use NonBlockingAsyncInputStream wrapper. + { + nsCOMPtr<nsIInputStream> stream; + ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data)); + ASSERT_EQ(NS_OK, NS_MakeAsyncNonBlockingInputStream( + stream.forget(), getter_AddRefs(result))); + } + ASSERT_TRUE(async != result); + ASSERT_TRUE(async); +} + +class QIInputStream final : public nsIInputStream, + public nsICloneableInputStream, + public nsIIPCSerializableInputStream, + public nsISeekableStream { + public: + NS_DECL_ISUPPORTS + + QIInputStream(bool aNonBlockingError, bool aCloneable, bool aIPCSerializable, + bool aSeekable) + : mNonBlockingError(aNonBlockingError), + mCloneable(aCloneable), + mIPCSerializable(aIPCSerializable), + mSeekable(aSeekable) {} + + // nsIInputStream + NS_IMETHOD Close() override { return NS_ERROR_NOT_IMPLEMENTED; } + NS_IMETHOD Available(uint64_t*) override { return NS_ERROR_NOT_IMPLEMENTED; } + NS_IMETHOD Read(char*, uint32_t, uint32_t*) override { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD ReadSegments(nsWriteSegmentFun, void*, uint32_t, + uint32_t*) override { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD IsNonBlocking(bool* aNonBlocking) override { + *aNonBlocking = true; + return mNonBlockingError ? NS_ERROR_FAILURE : NS_OK; + } + + // nsICloneableInputStream + NS_IMETHOD GetCloneable(bool*) override { return NS_ERROR_NOT_IMPLEMENTED; } + NS_IMETHOD Clone(nsIInputStream**) override { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // nsIIPCSerializableInputStream + void SerializedComplexity(uint32_t, uint32_t*, uint32_t*, + uint32_t*) override {} + void Serialize(mozilla::ipc::InputStreamParams&, uint32_t, + uint32_t*) override {} + bool Deserialize(const mozilla::ipc::InputStreamParams&) override { + return false; + } + + // nsISeekableStream + NS_IMETHOD Seek(int32_t, int64_t) override { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD SetEOF() override { return NS_ERROR_NOT_IMPLEMENTED; } + + // nsITellableStream + NS_IMETHOD Tell(int64_t*) override { return NS_ERROR_NOT_IMPLEMENTED; } + + private: + ~QIInputStream() = default; + + bool mNonBlockingError; + bool mCloneable; + bool mIPCSerializable; + bool mSeekable; +}; + +NS_IMPL_ADDREF(QIInputStream); +NS_IMPL_RELEASE(QIInputStream); + +NS_INTERFACE_MAP_BEGIN(QIInputStream) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, mCloneable) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, + mIPCSerializable) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mSeekable) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream, mSeekable) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) +NS_INTERFACE_MAP_END + +TEST(TestNonBlockingAsyncInputStream, QI) +{ + // Let's test ::Create() returning error. + + nsCOMPtr<nsIAsyncInputStream> async; + { + nsCOMPtr<nsIInputStream> stream = new QIInputStream(true, true, true, true); + + ASSERT_EQ(NS_ERROR_FAILURE, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + + // Let's test the QIs + for (int i = 0; i < 8; ++i) { + bool shouldBeCloneable = !!(i & 0x01); + bool shouldBeSerializable = !!(i & 0x02); + bool shouldBeSeekable = !!(i & 0x04); + + nsCOMPtr<nsICloneableInputStream> cloneable; + nsCOMPtr<nsIIPCSerializableInputStream> ipcSerializable; + nsCOMPtr<nsISeekableStream> seekable; + + { + nsCOMPtr<nsIInputStream> stream = new QIInputStream( + false, shouldBeCloneable, shouldBeSerializable, shouldBeSeekable); + + cloneable = do_QueryInterface(stream); + ASSERT_EQ(shouldBeCloneable, !!cloneable); + + ipcSerializable = do_QueryInterface(stream); + ASSERT_EQ(shouldBeSerializable, !!ipcSerializable); + + seekable = do_QueryInterface(stream); + ASSERT_EQ(shouldBeSeekable, !!seekable); + + ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create( + stream.forget(), getter_AddRefs(async))); + } + + // The returned async stream should be cloneable only if the underlying + // stream is. + cloneable = do_QueryInterface(async); + ASSERT_EQ(shouldBeCloneable, !!cloneable); + + // The returned async stream should be serializable only if the underlying + // stream is. + ipcSerializable = do_QueryInterface(async); + ASSERT_EQ(shouldBeSerializable, !!ipcSerializable); + + // The returned async stream should be seekable only if the underlying + // stream is. + seekable = do_QueryInterface(async); + ASSERT_EQ(shouldBeSeekable, !!seekable); + } +} |