/src/mozilla-central/netwerk/test/gtest/TestPartiallySeekableInputStream.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #include "gtest/gtest.h" |
2 | | |
3 | | #include "Helpers.h" |
4 | | #include "nsCOMPtr.h" |
5 | | #include "nsIPipe.h" |
6 | | #include "nsStreamUtils.h" |
7 | | #include "nsString.h" |
8 | | #include "nsStringStream.h" |
9 | | #include "mozilla/net/PartiallySeekableInputStream.h" |
10 | | |
11 | | using mozilla::GetCurrentThreadSerialEventTarget; |
12 | | using mozilla::net::PartiallySeekableInputStream; |
13 | | using mozilla::SpinEventLoopUntil; |
14 | | |
15 | | class NonSeekableStream final : public nsIInputStream |
16 | | { |
17 | | nsCOMPtr<nsIInputStream> mStream; |
18 | | |
19 | | public: |
20 | | NS_DECL_THREADSAFE_ISUPPORTS |
21 | | |
22 | | explicit NonSeekableStream(const nsACString& aBuffer) |
23 | 0 | { |
24 | 0 | NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer); |
25 | 0 | } |
26 | | |
27 | | NS_IMETHOD |
28 | | Available(uint64_t* aLength) override |
29 | 0 | { |
30 | 0 | return mStream->Available(aLength); |
31 | 0 | } |
32 | | |
33 | | NS_IMETHOD |
34 | | Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override |
35 | 0 | { |
36 | 0 | return mStream->Read(aBuffer, aCount, aReadCount); |
37 | 0 | } |
38 | | |
39 | | NS_IMETHOD |
40 | | ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, |
41 | | uint32_t aCount, uint32_t *aResult) override |
42 | 0 | { |
43 | 0 | return mStream->ReadSegments(aWriter, aClosure, aCount, aResult); |
44 | 0 | } |
45 | | |
46 | | NS_IMETHOD |
47 | | Close() override |
48 | 0 | { |
49 | 0 | return mStream->Close(); |
50 | 0 | } |
51 | | |
52 | | NS_IMETHOD |
53 | | IsNonBlocking(bool* aNonBlocking) override |
54 | 0 | { |
55 | 0 | return mStream->IsNonBlocking(aNonBlocking); |
56 | 0 | } |
57 | | |
58 | | private: |
59 | 0 | ~NonSeekableStream() {} |
60 | | }; |
61 | | |
62 | | NS_IMPL_ISUPPORTS(NonSeekableStream, nsIInputStream) |
63 | | |
64 | | // Helper function for creating a non-seekable nsIInputStream + a |
65 | | // PartiallySeekableInputStream. |
66 | | PartiallySeekableInputStream* |
67 | | CreateStream(uint32_t aSize, uint64_t aStreamSize, nsCString& aBuffer) |
68 | 0 | { |
69 | 0 | aBuffer.SetLength(aSize); |
70 | 0 | for (uint32_t i = 0; i < aSize; ++i) { |
71 | 0 | aBuffer.BeginWriting()[i] = i % 10; |
72 | 0 | } |
73 | 0 |
|
74 | 0 | RefPtr<NonSeekableStream> stream = new NonSeekableStream(aBuffer); |
75 | 0 | return new PartiallySeekableInputStream(stream.forget(), aStreamSize); |
76 | 0 | } |
77 | | |
78 | | // Simple reading. |
79 | 0 | TEST(TestPartiallySeekableInputStream, SimpleRead) { |
80 | 0 | const size_t kBufSize = 10; |
81 | 0 |
|
82 | 0 | nsCString buf; |
83 | 0 | RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 5, buf); |
84 | 0 |
|
85 | 0 | uint64_t length; |
86 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
87 | 0 | ASSERT_EQ((uint64_t)kBufSize, length); |
88 | 0 |
|
89 | 0 | char buf2[kBufSize]; |
90 | 0 | uint32_t count; |
91 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
92 | 0 | ASSERT_EQ(count, buf.Length()); |
93 | 0 | ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count))); |
94 | 0 |
|
95 | 0 | // At this point, after reading more than the buffer size, seek is not |
96 | 0 | // allowed. |
97 | 0 | ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED, |
98 | 0 | psi->Seek(nsISeekableStream::NS_SEEK_SET, 0)); |
99 | 0 |
|
100 | 0 | ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED, |
101 | 0 | psi->Seek(nsISeekableStream::NS_SEEK_END, 0)); |
102 | 0 |
|
103 | 0 | ASSERT_EQ(NS_ERROR_NOT_IMPLEMENTED, |
104 | 0 | psi->Seek(nsISeekableStream::NS_SEEK_CUR, 0)); |
105 | 0 |
|
106 | 0 | // Position is at the end of the stream. |
107 | 0 | int64_t pos; |
108 | 0 | ASSERT_EQ(NS_OK, psi->Tell(&pos)); |
109 | 0 | ASSERT_EQ((int64_t)kBufSize, pos); |
110 | 0 | } |
111 | | |
112 | | // Simple seek |
113 | 0 | TEST(TestPartiallySeekableInputStream, SimpleSeek) { |
114 | 0 | const size_t kBufSize = 10; |
115 | 0 |
|
116 | 0 | nsCString buf; |
117 | 0 | RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 5, buf); |
118 | 0 |
|
119 | 0 | uint64_t length; |
120 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
121 | 0 | ASSERT_EQ((uint64_t)kBufSize, length); |
122 | 0 |
|
123 | 0 | uint32_t count; |
124 | 0 |
|
125 | 0 | { |
126 | 0 | char buf2[3]; |
127 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
128 | 0 | ASSERT_EQ(count, sizeof(buf2)); |
129 | 0 | ASSERT_TRUE(nsCString(buf.get(), sizeof(buf2)).Equals(nsCString(buf2, sizeof(buf2)))); |
130 | 0 |
|
131 | 0 | int64_t pos; |
132 | 0 | ASSERT_EQ(NS_OK, psi->Tell(&pos)); |
133 | 0 | ASSERT_EQ((int64_t)sizeof(buf2), pos); |
134 | 0 |
|
135 | 0 | uint64_t length; |
136 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
137 | 0 | ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2), length); |
138 | 0 | } |
139 | 0 |
|
140 | 0 | // Let's seek back to the beginning using NS_SEEK_SET |
141 | 0 | ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0)); |
142 | 0 |
|
143 | 0 | { |
144 | 0 | uint64_t length; |
145 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
146 | 0 | ASSERT_EQ((uint64_t)kBufSize, length); |
147 | 0 |
|
148 | 0 | char buf2[3]; |
149 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
150 | 0 | ASSERT_EQ(count, sizeof(buf2)); |
151 | 0 | ASSERT_TRUE(nsCString(buf.get(), sizeof(buf2)).Equals(nsCString(buf2, sizeof(buf2)))); |
152 | 0 |
|
153 | 0 | int64_t pos; |
154 | 0 | ASSERT_EQ(NS_OK, psi->Tell(&pos)); |
155 | 0 | ASSERT_EQ((int64_t)sizeof(buf2), pos); |
156 | 0 |
|
157 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
158 | 0 | ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2), length); |
159 | 0 | } |
160 | 0 |
|
161 | 0 | // Let's seek back of 2 bytes using NS_SEEK_CUR |
162 | 0 | ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_CUR, -2)); |
163 | 0 |
|
164 | 0 | { |
165 | 0 | uint64_t length; |
166 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
167 | 0 | ASSERT_EQ((uint64_t)kBufSize - 1, length); |
168 | 0 |
|
169 | 0 | char buf2[3]; |
170 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
171 | 0 | ASSERT_EQ(count, sizeof(buf2)); |
172 | 0 | ASSERT_TRUE(nsCString(buf.get() + 1, sizeof(buf2)).Equals(nsCString(buf2, sizeof(buf2)))); |
173 | 0 |
|
174 | 0 | int64_t pos; |
175 | 0 | ASSERT_EQ(NS_OK, psi->Tell(&pos)); |
176 | 0 | ASSERT_EQ((int64_t)sizeof(buf2) + 1, pos); |
177 | 0 |
|
178 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
179 | 0 | ASSERT_EQ((uint64_t)kBufSize - sizeof(buf2) - 1, length); |
180 | 0 | } |
181 | 0 |
|
182 | 0 | // Let's seek back to the beginning using NS_SEEK_SET |
183 | 0 | ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0)); |
184 | 0 |
|
185 | 0 | { |
186 | 0 | uint64_t length; |
187 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
188 | 0 | ASSERT_EQ((uint64_t)kBufSize, length); |
189 | 0 |
|
190 | 0 | char buf2[kBufSize]; |
191 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
192 | 0 | ASSERT_EQ(count, buf.Length()); |
193 | 0 | ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count))); |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | // Full in cache |
198 | 0 | TEST(TestPartiallySeekableInputStream, FullCachedSeek) { |
199 | 0 | const size_t kBufSize = 10; |
200 | 0 |
|
201 | 0 | nsCString buf; |
202 | 0 | RefPtr<PartiallySeekableInputStream> psi = CreateStream(kBufSize, 4096, buf); |
203 | 0 |
|
204 | 0 | uint64_t length; |
205 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
206 | 0 | ASSERT_EQ((uint64_t)kBufSize, length); |
207 | 0 |
|
208 | 0 | char buf2[kBufSize]; |
209 | 0 | uint32_t count; |
210 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
211 | 0 | ASSERT_EQ(count, buf.Length()); |
212 | 0 | ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count))); |
213 | 0 |
|
214 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
215 | 0 | ASSERT_EQ((uint64_t)0, length); |
216 | 0 |
|
217 | 0 | ASSERT_EQ(NS_OK, psi->Seek(nsISeekableStream::NS_SEEK_SET, 0)); |
218 | 0 |
|
219 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
220 | 0 | ASSERT_EQ((uint64_t)kBufSize, length); |
221 | 0 |
|
222 | 0 | ASSERT_EQ(NS_OK, psi->Read(buf2, sizeof(buf2), &count)); |
223 | 0 | ASSERT_EQ(count, buf.Length()); |
224 | 0 | ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count))); |
225 | 0 |
|
226 | 0 | ASSERT_EQ(NS_OK, psi->Available(&length)); |
227 | 0 | ASSERT_EQ((uint64_t)0, length); |
228 | 0 | } |
229 | | |
230 | 0 | TEST(TestPartiallySeekableInputStream, QIInputStreamLength) { |
231 | 0 | nsCString buf; |
232 | 0 | buf.AssignLiteral("Hello world"); |
233 | 0 |
|
234 | 0 | for (int i = 0; i < 4; i++) { |
235 | 0 | nsCOMPtr<nsIInputStream> psis; |
236 | 0 | { |
237 | 0 | RefPtr<testing::LengthInputStream> stream = |
238 | 0 | new testing::LengthInputStream(buf, i % 2, i > 1); |
239 | 0 | psis = new PartiallySeekableInputStream(stream.forget()); |
240 | 0 | } |
241 | 0 |
|
242 | 0 | { |
243 | 0 | nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(psis); |
244 | 0 | ASSERT_EQ(!!(i % 2), !!qi); |
245 | 0 | } |
246 | 0 |
|
247 | 0 | { |
248 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis); |
249 | 0 | ASSERT_EQ(i > 1, !!qi); |
250 | 0 | } |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | 0 | TEST(TestPartiallySeekableInputStream, InputStreamLength) { |
255 | 0 | nsCString buf; |
256 | 0 | buf.AssignLiteral("Hello world"); |
257 | 0 |
|
258 | 0 | nsCOMPtr<nsIInputStream> psis; |
259 | 0 | { |
260 | 0 | RefPtr<testing::LengthInputStream> stream = |
261 | 0 | new testing::LengthInputStream(buf, true, false); |
262 | 0 | psis = new PartiallySeekableInputStream(stream.forget()); |
263 | 0 | } |
264 | 0 |
|
265 | 0 | nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(psis); |
266 | 0 | ASSERT_TRUE(!!qi); |
267 | 0 |
|
268 | 0 | int64_t size; |
269 | 0 | nsresult rv = qi->Length(&size); |
270 | 0 | ASSERT_EQ(NS_OK, rv); |
271 | 0 | ASSERT_EQ(buf.Length(), size); |
272 | 0 | } |
273 | | |
274 | 0 | TEST(TestPartiallySeekableInputStream, NegativeInputStreamLength) { |
275 | 0 | nsCString buf; |
276 | 0 | buf.AssignLiteral("Hello world"); |
277 | 0 |
|
278 | 0 | nsCOMPtr<nsIInputStream> psis; |
279 | 0 | { |
280 | 0 | RefPtr<testing::LengthInputStream> stream = |
281 | 0 | new testing::LengthInputStream(buf, true, false, NS_OK, true); |
282 | 0 | psis = new PartiallySeekableInputStream(stream.forget()); |
283 | 0 | } |
284 | 0 |
|
285 | 0 | nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(psis); |
286 | 0 | ASSERT_TRUE(!!qi); |
287 | 0 |
|
288 | 0 | int64_t size; |
289 | 0 | nsresult rv = qi->Length(&size); |
290 | 0 | ASSERT_EQ(NS_OK, rv); |
291 | 0 | ASSERT_EQ(-1, size); |
292 | 0 | } |
293 | | |
294 | 0 | TEST(TestPartiallySeekableInputStream, AsyncInputStreamLength) { |
295 | 0 | nsCString buf; |
296 | 0 | buf.AssignLiteral("Hello world"); |
297 | 0 |
|
298 | 0 | nsCOMPtr<nsIInputStream> psis; |
299 | 0 | { |
300 | 0 | RefPtr<testing::LengthInputStream> stream = |
301 | 0 | new testing::LengthInputStream(buf, false, true); |
302 | 0 | psis = new PartiallySeekableInputStream(stream.forget()); |
303 | 0 | } |
304 | 0 |
|
305 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis); |
306 | 0 | ASSERT_TRUE(!!qi); |
307 | 0 |
|
308 | 0 | RefPtr<testing::LengthCallback> callback = new testing::LengthCallback(); |
309 | 0 |
|
310 | 0 | nsresult rv = qi->AsyncLengthWait(callback, GetCurrentThreadSerialEventTarget()); |
311 | 0 | ASSERT_EQ(NS_OK, rv); |
312 | 0 |
|
313 | 0 | MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); })); |
314 | 0 | ASSERT_EQ(buf.Length(), callback->Size()); |
315 | 0 | } |
316 | | |
317 | 0 | TEST(TestPartiallySeekableInputStream, NegativeAsyncInputStreamLength) { |
318 | 0 | nsCString buf; |
319 | 0 | buf.AssignLiteral("Hello world"); |
320 | 0 |
|
321 | 0 | nsCOMPtr<nsIInputStream> psis; |
322 | 0 | { |
323 | 0 | RefPtr<testing::LengthInputStream> stream = |
324 | 0 | new testing::LengthInputStream(buf, false, true, NS_OK, true); |
325 | 0 | psis = new PartiallySeekableInputStream(stream.forget()); |
326 | 0 | } |
327 | 0 |
|
328 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis); |
329 | 0 | ASSERT_TRUE(!!qi); |
330 | 0 |
|
331 | 0 | RefPtr<testing::LengthCallback> callback = new testing::LengthCallback(); |
332 | 0 |
|
333 | 0 | nsresult rv = qi->AsyncLengthWait(callback, GetCurrentThreadSerialEventTarget()); |
334 | 0 | ASSERT_EQ(NS_OK, rv); |
335 | 0 |
|
336 | 0 | MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); })); |
337 | 0 | ASSERT_EQ(-1, callback->Size()); |
338 | 0 | } |
339 | | |
340 | 0 | TEST(TestPartiallySeekableInputStream, AbortLengthCallback) { |
341 | 0 | nsCString buf; |
342 | 0 | buf.AssignLiteral("Hello world"); |
343 | 0 |
|
344 | 0 | nsCOMPtr<nsIInputStream> psis; |
345 | 0 | { |
346 | 0 | RefPtr<testing::LengthInputStream> stream = |
347 | 0 | new testing::LengthInputStream(buf, false, true, NS_OK, true); |
348 | 0 | psis = new PartiallySeekableInputStream(stream.forget()); |
349 | 0 | } |
350 | 0 |
|
351 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(psis); |
352 | 0 | ASSERT_TRUE(!!qi); |
353 | 0 |
|
354 | 0 | RefPtr<testing::LengthCallback> callback1 = new testing::LengthCallback(); |
355 | 0 | nsresult rv = qi->AsyncLengthWait(callback1, GetCurrentThreadSerialEventTarget()); |
356 | 0 | ASSERT_EQ(NS_OK, rv); |
357 | 0 |
|
358 | 0 | RefPtr<testing::LengthCallback> callback2 = new testing::LengthCallback(); |
359 | 0 | rv = qi->AsyncLengthWait(callback2, GetCurrentThreadSerialEventTarget()); |
360 | 0 | ASSERT_EQ(NS_OK, rv); |
361 | 0 |
|
362 | 0 | MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback2->Called(); })); |
363 | 0 | ASSERT_TRUE(!callback1->Called()); |
364 | 0 | ASSERT_EQ(-1, callback2->Size()); |
365 | 0 | } |