gecko-dev/netwerk/test/gtest/TestPartiallySeekableInputS...

366 строки
10 KiB
C++

#include "gtest/gtest.h"
#include "Helpers.h"
#include "nsCOMPtr.h"
#include "nsIPipe.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "mozilla/net/PartiallySeekableInputStream.h"
using mozilla::GetCurrentThreadSerialEventTarget;
using mozilla::net::PartiallySeekableInputStream;
using mozilla::SpinEventLoopUntil;
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() {}
};
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, GetCurrentThreadSerialEventTarget());
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, GetCurrentThreadSerialEventTarget());
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, GetCurrentThreadSerialEventTarget());
ASSERT_EQ(NS_OK, rv);
RefPtr<testing::LengthCallback> callback2 = new testing::LengthCallback();
rv = qi->AsyncLengthWait(callback2, GetCurrentThreadSerialEventTarget());
ASSERT_EQ(NS_OK, rv);
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback2->Called(); }));
ASSERT_TRUE(!callback1->Called());
ASSERT_EQ(-1, callback2->Size());
}