summaryrefslogtreecommitdiffstats
path: root/netwerk/test/gtest/TestPartiallySeekableInputStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/test/gtest/TestPartiallySeekableInputStream.cpp494
1 files changed, 494 insertions, 0 deletions
diff --git a/netwerk/test/gtest/TestPartiallySeekableInputStream.cpp b/netwerk/test/gtest/TestPartiallySeekableInputStream.cpp
new file mode 100644
index 0000000000..1dffd6fcaf
--- /dev/null
+++ b/netwerk/test/gtest/TestPartiallySeekableInputStream.cpp
@@ -0,0 +1,494 @@
+#include "gtest/gtest.h"
+
+#include "Helpers.h"
+#include "nsCOMPtr.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/net/PartiallySeekableInputStream.h"
+
+using mozilla::GetCurrentSerialEventTarget;
+using mozilla::SpinEventLoopUntil;
+using mozilla::net::PartiallySeekableInputStream;
+
+class NonSeekableStream final : public nsIInputStream {
+ nsCOMPtr<nsIInputStream> mStream;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit NonSeekableStream(const nsACString& aBuffer) {
+ NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
+ }
+
+ NS_IMETHOD
+ Available(uint64_t* aLength) override { return mStream->Available(aLength); }
+
+ 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 {
+ return mStream->IsNonBlocking(aNonBlocking);
+ }
+
+ private:
+ ~NonSeekableStream() = default;
+};
+
+NS_IMPL_ISUPPORTS(NonSeekableStream, nsIInputStream)
+
+// Helper function for creating a non-seekable nsIInputStream + a
+// PartiallySeekableInputStream.
+PartiallySeekableInputStream* CreateStream(uint32_t aSize, uint64_t aStreamSize,
+ nsCString& aBuffer) {
+ aBuffer.SetLength(aSize);
+ for (uint32_t i = 0; i < aSize; ++i) {
+ aBuffer.BeginWriting()[i] = i % 10;
+ }
+
+ RefPtr<NonSeekableStream> stream = new NonSeekableStream(aBuffer);
+ return new PartiallySeekableInputStream(stream.forget(), aStreamSize);
+}
+
+// Simple reading.
+TEST(TestPartiallySeekableInputStream, SimpleRead)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 5, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ char buf2[kBufSize];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
+
+ // At this point, after reading more than the buffer size, seek is not
+ // allowed.
+ ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED,
+ psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
+
+ ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED,
+ psi->Seek(nsISeekableStream::NS_SEEK_END, 0));
+
+ ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED,
+ psi->Seek(nsISeekableStream::NS_SEEK_CUR, 0));
+
+ // Position is at the end of the stream.
+ int64_t pos;
+ ASSERT_EQ(NS_OK, psi->Tell(&pos));
+ ASSERT_EQ((int64_t)kBufSize, pos);
+}
+
+// Simple seek
+TEST(TestPartiallySeekableInputStream, SimpleSeek)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 5, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ uint32_t count;
+
+ {
+ char buf2[3];
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, sizeof(buf2));
+ ASSERT_TRUE(nsCString(buf.get(), sizeof(buf2))
+ .Equals(nsCString(buf2, sizeof(buf2))));
+
+ int64_t pos;
+ ASSERT_EQ(NS_OK, psi->Tell(&pos));
+ ASSERT_EQ((int64_t)sizeof(buf2), pos);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2), length);
+ }
+
+ // Let's seek back to the beginning using NS_SEEK_SET
+ ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
+
+ {
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ char buf2[3];
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, sizeof(buf2));
+ ASSERT_TRUE(nsCString(buf.get(), sizeof(buf2))
+ .Equals(nsCString(buf2, sizeof(buf2))));
+
+ int64_t pos;
+ ASSERT_EQ(NS_OK, psi->Tell(&pos));
+ ASSERT_EQ((int64_t)sizeof(buf2), pos);
+
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2), length);
+ }
+
+ // Let's seek back of 2 bytes using NS_SEEK_CUR
+ ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_CUR, -2));
+
+ {
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize - 1, length);
+
+ char buf2[3];
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, sizeof(buf2));
+ ASSERT_TRUE(nsCString(buf.get() + 1, sizeof(buf2))
+ .Equals(nsCString(buf2, sizeof(buf2))));
+
+ int64_t pos;
+ ASSERT_EQ(NS_OK, psi->Tell(&pos));
+ ASSERT_EQ((int64_t)sizeof(buf2) + 1, pos);
+
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2) - 1, length);
+ }
+
+ // Let's seek back to the beginning using NS_SEEK_SET
+ ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
+
+ {
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ char buf2[kBufSize];
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
+ }
+}
+
+// Full in cache
+TEST(TestPartiallySeekableInputStream, FullCachedSeek)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 4096, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ char buf2[kBufSize];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
+
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)0, length);
+
+ ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0));
+
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
+
+ ASSERT_EQ(NS_OK, psi->Available(&length));
+ ASSERT_EQ((uint64_t)0, length);
+}
+
+TEST(TestPartiallySeekableInputStream, QIInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ for (int i = 0; i < 4; i++) {
+ nsCOMPtr<nsIInputStream> psis;
+ {
+ RefPtr<testing::LengthInputStream> stream =
+ new testing::LengthInputStream(buf, i % 2, i > 1);
+ psis = new PartiallySeekableInputStream(stream.forget());
+ }
+
+ {
+ nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_EQ(!!(i % 2), !!qi);
+ }
+
+ {
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_EQ(i > 1, !!qi);
+ }
+ }
+}
+
+TEST(TestPartiallySeekableInputStream, InputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> psis;
+ {
+ RefPtr<testing::LengthInputStream> stream =
+ new testing::LengthInputStream(buf, true, false);
+ psis = new PartiallySeekableInputStream(stream.forget());
+ }
+
+ nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(!!qi);
+
+ int64_t size;
+ nsresult rv = qi->Length(&size);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(buf.Length(), size);
+}
+
+TEST(TestPartiallySeekableInputStream, NegativeInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> psis;
+ {
+ RefPtr<testing::LengthInputStream> stream =
+ new testing::LengthInputStream(buf, true, false, NS_OK, true);
+ psis = new PartiallySeekableInputStream(stream.forget());
+ }
+
+ nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(!!qi);
+
+ int64_t size;
+ nsresult rv = qi->Length(&size);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(-1, size);
+}
+
+TEST(TestPartiallySeekableInputStream, AsyncInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> psis;
+ {
+ RefPtr<testing::LengthInputStream> stream =
+ new testing::LengthInputStream(buf, false, true);
+ psis = new PartiallySeekableInputStream(stream.forget());
+ }
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(!!qi);
+
+ RefPtr<testing::LengthCallback> callback = new testing::LengthCallback();
+
+ nsresult rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+ ASSERT_EQ(buf.Length(), callback->Size());
+}
+
+TEST(TestPartiallySeekableInputStream, NegativeAsyncInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> psis;
+ {
+ RefPtr<testing::LengthInputStream> stream =
+ new testing::LengthInputStream(buf, false, true, NS_OK, true);
+ psis = new PartiallySeekableInputStream(stream.forget());
+ }
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(!!qi);
+
+ RefPtr<testing::LengthCallback> callback = new testing::LengthCallback();
+
+ nsresult rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+ ASSERT_EQ(-1, callback->Size());
+}
+
+TEST(TestPartiallySeekableInputStream, AbortLengthCallback)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> psis;
+ {
+ RefPtr<testing::LengthInputStream> stream =
+ new testing::LengthInputStream(buf, false, true, NS_OK, true);
+ psis = new PartiallySeekableInputStream(stream.forget());
+ }
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(!!qi);
+
+ RefPtr<testing::LengthCallback> callback1 = new testing::LengthCallback();
+ nsresult rv = qi->AsyncLengthWait(callback1, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ RefPtr<testing::LengthCallback> callback2 = new testing::LengthCallback();
+ rv = qi->AsyncLengthWait(callback2, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback2->Called(); }));
+ ASSERT_TRUE(!callback1->Called());
+ ASSERT_EQ(-1, callback2->Size());
+}
+
+TEST(TestPartiallySeekableInputStream, AsyncWaitAfterConsumed)
+{
+ nsCString buf{"The Quick Brown Fox Jumps over the Lazy Dog"};
+ const size_t bufsize = 44;
+
+ auto stream = MakeRefPtr<testing::AsyncStringStream>(buf);
+ nsCOMPtr<nsIAsyncInputStream> psis =
+ new PartiallySeekableInputStream(stream.forget(), bufsize);
+ ASSERT_TRUE(psis);
+
+ auto callback = MakeRefPtr<testing::InputStreamCallback>();
+
+ nsresult rv = psis->AsyncWait(callback, 0, 0, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+
+ char rdbuf[bufsize] = {'\0'};
+ uint32_t count;
+ ASSERT_EQ(NS_OK, psis->Read(rdbuf, sizeof(rdbuf), &count));
+ ASSERT_STREQ(rdbuf, buf.Data());
+
+ callback = MakeRefPtr<testing::InputStreamCallback>();
+
+ rv = psis->AsyncWait(callback, 0, 0, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+
+ memset(rdbuf, 0x0, bufsize);
+ ASSERT_EQ(NS_OK, psis->Read(rdbuf, sizeof(rdbuf), &count));
+ ASSERT_EQ(0U, count);
+}
+
+TEST(TestPartiallySeekableInputStream, AsyncWaitAfterClosed)
+{
+ nsCString buf{"The Quick Brown Fox Jumps over the Lazy Dog"};
+ const size_t bufsize = 44;
+
+ auto stream = MakeRefPtr<testing::AsyncStringStream>(buf);
+ nsCOMPtr<nsIAsyncInputStream> psis =
+ new PartiallySeekableInputStream(stream.forget(), bufsize);
+ ASSERT_TRUE(psis);
+
+ auto callback = MakeRefPtr<testing::InputStreamCallback>();
+
+ nsresult rv = psis->AsyncWait(callback, 0, 0, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+
+ ASSERT_EQ(NS_OK, psis->Close());
+
+ callback = MakeRefPtr<testing::InputStreamCallback>();
+
+ rv = psis->AsyncWait(callback, 0, 0, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+}
+
+TEST(TestPartiallySeekableInputStream, AsyncLengthWaitAfterClosed)
+{
+ nsCString buf{"The Quick Brown Fox Jumps over the Lazy Dog"};
+
+ auto stream = MakeRefPtr<testing::LengthInputStream>(buf, false, true);
+ nsCOMPtr<nsIInputStream> psis =
+ new PartiallySeekableInputStream(stream.forget());
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(qi);
+
+ auto callback = MakeRefPtr<testing::LengthCallback>();
+
+ nsresult rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+ ASSERT_EQ(buf.Length(), callback->Size());
+
+ ASSERT_EQ(NS_OK, psis->Close());
+
+ callback = MakeRefPtr<testing::LengthCallback>();
+
+ rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+ ASSERT_EQ(-1, callback->Size());
+}
+
+TEST(TestPartiallySeekableInputStream, AsyncLengthWaitAfterConsumed)
+{
+ nsCString buf{"The Quick Brown Fox Jumps over the Lazy Dog"};
+ const size_t bufsize = 44;
+
+ auto stream = MakeRefPtr<testing::LengthInputStream>(buf, false, true);
+ nsCOMPtr<nsIInputStream> psis =
+ new PartiallySeekableInputStream(stream.forget());
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis);
+ ASSERT_TRUE(qi);
+
+ auto callback = MakeRefPtr<testing::LengthCallback>();
+
+ nsresult rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+ ASSERT_EQ(buf.Length(), callback->Size());
+
+ char rdbuf[bufsize] = {'\0'};
+ uint32_t count;
+ ASSERT_EQ(NS_OK, psis->Read(rdbuf, sizeof(rdbuf), &count));
+ ASSERT_STREQ(rdbuf, buf.Data());
+
+ callback = MakeRefPtr<testing::LengthCallback>();
+
+ rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
+ ASSERT_EQ(0U, callback->Size());
+
+ memset(rdbuf, 0x0, bufsize);
+ ASSERT_EQ(NS_OK, psis->Read(rdbuf, sizeof(rdbuf), &count));
+ ASSERT_EQ(0U, count);
+}