/src/mozilla-central/xpcom/tests/gtest/TestMultiplexInputStream.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "gtest/gtest.h" |
8 | | #include "nsIAsyncInputStream.h" |
9 | | #include "nsComponentManagerUtils.h" |
10 | | #include "nsIInputStream.h" |
11 | | #include "nsIMultiplexInputStream.h" |
12 | | #include "nsISeekableStream.h" |
13 | | #include "nsStreamUtils.h" |
14 | | #include "nsThreadUtils.h" |
15 | | #include "Helpers.h" |
16 | | |
17 | | TEST(MultiplexInputStream, Seek_SET) |
18 | 0 | { |
19 | 0 | nsCString buf1; |
20 | 0 | nsCString buf2; |
21 | 0 | nsCString buf3; |
22 | 0 | buf1.AssignLiteral("Hello world"); |
23 | 0 | buf2.AssignLiteral("The quick brown fox jumped over the lazy dog"); |
24 | 0 | buf3.AssignLiteral("Foo bar"); |
25 | 0 |
|
26 | 0 | nsCOMPtr<nsIInputStream> inputStream1; |
27 | 0 | nsCOMPtr<nsIInputStream> inputStream2; |
28 | 0 | nsCOMPtr<nsIInputStream> inputStream3; |
29 | 0 | nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream1), buf1); |
30 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
31 | 0 | rv = NS_NewCStringInputStream(getter_AddRefs(inputStream2), buf2); |
32 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
33 | 0 | rv = NS_NewCStringInputStream(getter_AddRefs(inputStream3), buf3); |
34 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
35 | 0 |
|
36 | 0 | nsCOMPtr<nsIMultiplexInputStream> multiplexStream = |
37 | 0 | do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); |
38 | 0 | ASSERT_TRUE(multiplexStream); |
39 | 0 | nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream)); |
40 | 0 | ASSERT_TRUE(stream); |
41 | 0 |
|
42 | 0 | rv = multiplexStream->AppendStream(inputStream1); |
43 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
44 | 0 | rv = multiplexStream->AppendStream(inputStream2); |
45 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
46 | 0 | rv = multiplexStream->AppendStream(inputStream3); |
47 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
48 | 0 |
|
49 | 0 | uint64_t length; |
50 | 0 | uint32_t count; |
51 | 0 | char readBuf[4096]; |
52 | 0 | nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(multiplexStream); |
53 | 0 | ASSERT_TRUE(seekStream); |
54 | 0 |
|
55 | 0 | // Seek forward in first input stream |
56 | 0 | rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 1); |
57 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
58 | 0 | rv = stream->Available(&length); |
59 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
60 | 0 | ASSERT_EQ((uint64_t)buf1.Length() + buf2.Length() + buf3.Length() - 1, |
61 | 0 | length); |
62 | 0 |
|
63 | 0 | // Check read is correct |
64 | 0 | rv = stream->Read(readBuf, 3, &count); |
65 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
66 | 0 | ASSERT_EQ((uint64_t)3, count); |
67 | 0 | rv = stream->Available(&length); |
68 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
69 | 0 | ASSERT_EQ((uint64_t)buf1.Length() + buf2.Length() + buf3.Length() - 4, |
70 | 0 | length); |
71 | 0 | ASSERT_EQ(0, strncmp(readBuf, "ell", count)); |
72 | 0 |
|
73 | 0 | // Seek to start of third input stream |
74 | 0 | rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, |
75 | 0 | buf1.Length() + buf2.Length()); |
76 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
77 | 0 | rv = stream->Available(&length); |
78 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
79 | 0 | ASSERT_EQ((uint64_t)buf3.Length(), length); |
80 | 0 |
|
81 | 0 | // Check read is correct |
82 | 0 | rv = stream->Read(readBuf, 5, &count); |
83 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
84 | 0 | ASSERT_EQ((uint64_t)5, count); |
85 | 0 | rv = stream->Available(&length); |
86 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
87 | 0 | ASSERT_EQ((uint64_t)buf3.Length() - 5, length); |
88 | 0 | ASSERT_EQ(0, strncmp(readBuf, "Foo b", count)); |
89 | 0 |
|
90 | 0 | // Seek back to start of second stream (covers bug 1272371) |
91 | 0 | rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, buf1.Length()); |
92 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
93 | 0 | rv = stream->Available(&length); |
94 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
95 | 0 | ASSERT_EQ((uint64_t)buf2.Length() + buf3.Length(), length); |
96 | 0 |
|
97 | 0 | // Check read is correct |
98 | 0 | rv = stream->Read(readBuf, 6, &count); |
99 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
100 | 0 | ASSERT_EQ((uint64_t)6, count); |
101 | 0 | rv = stream->Available(&length); |
102 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
103 | 0 | ASSERT_EQ((uint64_t)buf2.Length() - 6 + buf3.Length(), length); |
104 | 0 | ASSERT_EQ(0, strncmp(readBuf, "The qu", count)); |
105 | 0 | } |
106 | | |
107 | | already_AddRefed<nsIInputStream> |
108 | | CreateStreamHelper() |
109 | 0 | { |
110 | 0 | nsCOMPtr<nsIMultiplexInputStream> multiplexStream = |
111 | 0 | do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); |
112 | 0 |
|
113 | 0 | nsCString buf1; |
114 | 0 | buf1.AssignLiteral("Hello"); |
115 | 0 |
|
116 | 0 | nsCOMPtr<nsIInputStream> inputStream1 = new testing::AsyncStringStream(buf1); |
117 | 0 | multiplexStream->AppendStream(inputStream1); |
118 | 0 |
|
119 | 0 | nsCString buf2; |
120 | 0 | buf2.AssignLiteral(" "); |
121 | 0 |
|
122 | 0 | nsCOMPtr<nsIInputStream> inputStream2 = new testing::AsyncStringStream(buf2); |
123 | 0 | multiplexStream->AppendStream(inputStream2); |
124 | 0 |
|
125 | 0 | nsCString buf3; |
126 | 0 | buf3.AssignLiteral("World!"); |
127 | 0 |
|
128 | 0 | nsCOMPtr<nsIInputStream> inputStream3 = new testing::AsyncStringStream(buf3); |
129 | 0 | multiplexStream->AppendStream(inputStream3); |
130 | 0 |
|
131 | 0 | nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream)); |
132 | 0 | return stream.forget(); |
133 | 0 | } |
134 | | |
135 | | // AsyncWait - without EventTarget |
136 | 0 | TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget) { |
137 | 0 | nsCOMPtr<nsIInputStream> is = CreateStreamHelper(); |
138 | 0 |
|
139 | 0 | nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is); |
140 | 0 | ASSERT_TRUE(!!ais); |
141 | 0 |
|
142 | 0 | RefPtr<testing::InputStreamCallback> cb = |
143 | 0 | new testing::InputStreamCallback(); |
144 | 0 |
|
145 | 0 | ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, nullptr)); |
146 | 0 | ASSERT_TRUE(cb->Called()); |
147 | 0 | } |
148 | | |
149 | | // AsyncWait - with EventTarget |
150 | 0 | TEST(TestMultiplexInputStream, AsyncWait_withEventTarget) { |
151 | 0 | nsCOMPtr<nsIInputStream> is = CreateStreamHelper(); |
152 | 0 |
|
153 | 0 | nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is); |
154 | 0 | ASSERT_TRUE(!!ais); |
155 | 0 |
|
156 | 0 | RefPtr<testing::InputStreamCallback> cb = |
157 | 0 | new testing::InputStreamCallback(); |
158 | 0 | nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); |
159 | 0 |
|
160 | 0 | ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, thread)); |
161 | 0 |
|
162 | 0 | ASSERT_FALSE(cb->Called()); |
163 | 0 |
|
164 | 0 | // Eventually it is called. |
165 | 0 | MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); })); |
166 | 0 | ASSERT_TRUE(cb->Called()); |
167 | 0 | } |
168 | | |
169 | | // AsyncWait - without EventTarget - closureOnly |
170 | 0 | TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget_closureOnly) { |
171 | 0 | nsCOMPtr<nsIInputStream> is = CreateStreamHelper(); |
172 | 0 |
|
173 | 0 | nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is); |
174 | 0 | ASSERT_TRUE(!!ais); |
175 | 0 |
|
176 | 0 | RefPtr<testing::InputStreamCallback> cb = |
177 | 0 | new testing::InputStreamCallback(); |
178 | 0 |
|
179 | 0 | ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, |
180 | 0 | 0, nullptr)); |
181 | 0 | ASSERT_FALSE(cb->Called()); |
182 | 0 |
|
183 | 0 | ais->CloseWithStatus(NS_ERROR_FAILURE); |
184 | 0 | ASSERT_TRUE(cb->Called()); |
185 | 0 | } |
186 | | |
187 | | // AsyncWait - withEventTarget - closureOnly |
188 | 0 | TEST(TestMultiplexInputStream, AsyncWait_withEventTarget_closureOnly) { |
189 | 0 | nsCOMPtr<nsIInputStream> is = CreateStreamHelper(); |
190 | 0 |
|
191 | 0 | nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is); |
192 | 0 | ASSERT_TRUE(!!ais); |
193 | 0 |
|
194 | 0 | RefPtr<testing::InputStreamCallback> cb = |
195 | 0 | new testing::InputStreamCallback(); |
196 | 0 | nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); |
197 | 0 |
|
198 | 0 | ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, |
199 | 0 | 0, thread)); |
200 | 0 |
|
201 | 0 | ASSERT_FALSE(cb->Called()); |
202 | 0 | ais->CloseWithStatus(NS_ERROR_FAILURE); |
203 | 0 | ASSERT_FALSE(cb->Called()); |
204 | 0 |
|
205 | 0 | // Eventually it is called. |
206 | 0 | MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); })); |
207 | 0 | ASSERT_TRUE(cb->Called()); |
208 | 0 | } |
209 | | |
210 | | class ClosedStream final : public nsIInputStream |
211 | | { |
212 | | public: |
213 | | NS_DECL_THREADSAFE_ISUPPORTS |
214 | | |
215 | 0 | ClosedStream() {} |
216 | | |
217 | | NS_IMETHOD |
218 | | Available(uint64_t* aLength) override |
219 | 0 | { |
220 | 0 | return NS_BASE_STREAM_CLOSED; |
221 | 0 | } |
222 | | |
223 | | NS_IMETHOD |
224 | | Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override |
225 | 0 | { |
226 | 0 | MOZ_CRASH("This should not be called!"); |
227 | 0 | return NS_OK; |
228 | 0 | } |
229 | | |
230 | | NS_IMETHOD |
231 | | ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, |
232 | | uint32_t aCount, uint32_t *aResult) override |
233 | 0 | { |
234 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
235 | 0 | } |
236 | | |
237 | | NS_IMETHOD |
238 | 0 | Close() override { return NS_OK; } |
239 | | |
240 | | NS_IMETHOD |
241 | | IsNonBlocking(bool* aNonBlocking) override |
242 | 0 | { |
243 | 0 | *aNonBlocking = true; |
244 | 0 | return NS_OK; |
245 | 0 | } |
246 | | |
247 | | private: |
248 | | ~ClosedStream() = default; |
249 | | }; |
250 | | |
251 | | NS_IMPL_ISUPPORTS(ClosedStream, nsIInputStream) |
252 | | |
253 | | class AsyncStream final : public nsIAsyncInputStream |
254 | | { |
255 | | public: |
256 | | NS_DECL_THREADSAFE_ISUPPORTS |
257 | | |
258 | 0 | explicit AsyncStream(int64_t aSize) : mState(eBlocked), mSize(aSize) {} |
259 | | |
260 | | void |
261 | | Unblock() |
262 | 0 | { |
263 | 0 | mState = eUnblocked; |
264 | 0 | } |
265 | | |
266 | | NS_IMETHOD |
267 | | Available(uint64_t* aLength) override |
268 | 0 | { |
269 | 0 | *aLength = mState == eBlocked ? 0 : mSize; |
270 | 0 | return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_OK; |
271 | 0 | } |
272 | | |
273 | | NS_IMETHOD |
274 | | Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override |
275 | 0 | { |
276 | 0 | MOZ_CRASH("This should not be called!"); |
277 | 0 | return NS_OK; |
278 | 0 | } |
279 | | |
280 | | NS_IMETHOD |
281 | | ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, |
282 | | uint32_t aCount, uint32_t *aResult) override |
283 | 0 | { |
284 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
285 | 0 | } |
286 | | |
287 | | NS_IMETHOD |
288 | | Close() override |
289 | 0 | { |
290 | 0 | mState = eClosed; |
291 | 0 | return NS_OK; |
292 | 0 | } |
293 | | |
294 | | NS_IMETHOD |
295 | | IsNonBlocking(bool* aNonBlocking) override |
296 | 0 | { |
297 | 0 | *aNonBlocking = true; |
298 | 0 | return NS_OK; |
299 | 0 | } |
300 | | |
301 | | NS_IMETHOD |
302 | | AsyncWait(nsIInputStreamCallback* aCallback, |
303 | | uint32_t aFlags, uint32_t aRequestedCount, |
304 | | nsIEventTarget* aEventTarget) override |
305 | 0 | { |
306 | 0 | MOZ_CRASH("This should not be called!"); |
307 | 0 | return NS_OK; |
308 | 0 | } |
309 | | |
310 | | NS_IMETHOD |
311 | | CloseWithStatus(nsresult aStatus) override |
312 | 0 | { |
313 | 0 | return NS_OK; |
314 | 0 | } |
315 | | |
316 | | private: |
317 | | ~AsyncStream() = default; |
318 | | |
319 | | enum { |
320 | | eBlocked, |
321 | | eUnblocked, |
322 | | eClosed |
323 | | } mState; |
324 | | |
325 | | uint64_t mSize; |
326 | | }; |
327 | | |
328 | | NS_IMPL_ISUPPORTS(AsyncStream, nsIInputStream, nsIAsyncInputStream) |
329 | | |
330 | 0 | TEST(TestMultiplexInputStream, Available) { |
331 | 0 | nsCOMPtr<nsIMultiplexInputStream> multiplexStream = |
332 | 0 | do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); |
333 | 0 |
|
334 | 0 | nsCOMPtr<nsIInputStream> s = do_QueryInterface(multiplexStream); |
335 | 0 | ASSERT_TRUE(!!s); |
336 | 0 |
|
337 | 0 | nsCOMPtr<nsIAsyncInputStream> as = do_QueryInterface(multiplexStream); |
338 | 0 | ASSERT_TRUE(!as); |
339 | 0 |
|
340 | 0 | uint64_t length; |
341 | 0 |
|
342 | 0 | // The stream returns NS_BASE_STREAM_CLOSED if there are no substreams. |
343 | 0 | nsresult rv = s->Available(&length); |
344 | 0 | ASSERT_EQ(NS_BASE_STREAM_CLOSED, rv); |
345 | 0 |
|
346 | 0 | rv = multiplexStream->AppendStream(new ClosedStream()); |
347 | 0 | ASSERT_EQ(NS_OK, rv); |
348 | 0 |
|
349 | 0 | uint64_t asyncSize = 2; |
350 | 0 | RefPtr<AsyncStream> asyncStream = new AsyncStream(2); |
351 | 0 | rv = multiplexStream->AppendStream(asyncStream); |
352 | 0 | ASSERT_EQ(NS_OK, rv); |
353 | 0 |
|
354 | 0 | nsCString buffer; |
355 | 0 | buffer.Assign("World!!!"); |
356 | 0 |
|
357 | 0 | nsCOMPtr<nsIInputStream> stringStream; |
358 | 0 | rv = NS_NewCStringInputStream(getter_AddRefs(stringStream), buffer); |
359 | 0 | ASSERT_EQ(NS_OK, rv); |
360 | 0 |
|
361 | 0 | rv = multiplexStream->AppendStream(stringStream); |
362 | 0 | ASSERT_EQ(NS_OK, rv); |
363 | 0 |
|
364 | 0 | // Now we are async. |
365 | 0 | as = do_QueryInterface(multiplexStream); |
366 | 0 | ASSERT_TRUE(!!as); |
367 | 0 |
|
368 | 0 | // Available should skip the closed stream and return 0 because the |
369 | 0 | // asyncStream returns 0 and it's async. |
370 | 0 | rv = s->Available(&length); |
371 | 0 | ASSERT_EQ(NS_OK, rv); |
372 | 0 | ASSERT_EQ((uint64_t)0, length); |
373 | 0 |
|
374 | 0 | asyncStream->Unblock(); |
375 | 0 |
|
376 | 0 | // Now we should return only the size of the async stream because we don't |
377 | 0 | // know when this is completed. |
378 | 0 | rv = s->Available(&length); |
379 | 0 | ASSERT_EQ(NS_OK, rv); |
380 | 0 | ASSERT_EQ(asyncSize, length); |
381 | 0 |
|
382 | 0 | asyncStream->Close(); |
383 | 0 |
|
384 | 0 | rv = s->Available(&length); |
385 | 0 | ASSERT_EQ(NS_OK, rv); |
386 | 0 | ASSERT_EQ(buffer.Length(), length); |
387 | 0 | } |
388 | | |
389 | | class NonBufferableStringStream final : public nsIInputStream |
390 | | { |
391 | | nsCOMPtr<nsIInputStream> mStream; |
392 | | |
393 | | public: |
394 | | NS_DECL_THREADSAFE_ISUPPORTS |
395 | | |
396 | | explicit NonBufferableStringStream(const nsACString& aBuffer) |
397 | 0 | { |
398 | 0 | NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer); |
399 | 0 | } |
400 | | |
401 | | NS_IMETHOD |
402 | | Available(uint64_t* aLength) override |
403 | 0 | { |
404 | 0 | return mStream->Available(aLength); |
405 | 0 | } |
406 | | |
407 | | NS_IMETHOD |
408 | | Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override |
409 | 0 | { |
410 | 0 | return mStream->Read(aBuffer, aCount, aReadCount); |
411 | 0 | } |
412 | | |
413 | | NS_IMETHOD |
414 | | ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, |
415 | | uint32_t aCount, uint32_t *aResult) override |
416 | 0 | { |
417 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
418 | 0 | } |
419 | | |
420 | | NS_IMETHOD |
421 | | Close() override |
422 | 0 | { |
423 | 0 | return mStream->Close(); |
424 | 0 | } |
425 | | |
426 | | NS_IMETHOD |
427 | | IsNonBlocking(bool* aNonBlocking) override |
428 | 0 | { |
429 | 0 | return mStream->IsNonBlocking(aNonBlocking); |
430 | 0 | } |
431 | | |
432 | | private: |
433 | 0 | ~NonBufferableStringStream() = default; |
434 | | }; |
435 | | |
436 | | NS_IMPL_ISUPPORTS(NonBufferableStringStream, nsIInputStream) |
437 | | |
438 | 0 | TEST(TestMultiplexInputStream, Bufferable) { |
439 | 0 | nsCOMPtr<nsIMultiplexInputStream> multiplexStream = |
440 | 0 | do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); |
441 | 0 |
|
442 | 0 | nsCOMPtr<nsIInputStream> s = do_QueryInterface(multiplexStream); |
443 | 0 | ASSERT_TRUE(!!s); |
444 | 0 |
|
445 | 0 | nsCString buf1; |
446 | 0 | buf1.AssignLiteral("Hello "); |
447 | 0 | nsCOMPtr<nsIInputStream> inputStream1; |
448 | 0 | nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream1), buf1); |
449 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
450 | 0 |
|
451 | 0 | nsCString buf2; |
452 | 0 | buf2.AssignLiteral("world"); |
453 | 0 | nsCOMPtr<nsIInputStream> inputStream2 = new NonBufferableStringStream(buf2); |
454 | 0 |
|
455 | 0 | rv = multiplexStream->AppendStream(inputStream1); |
456 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
457 | 0 |
|
458 | 0 | rv = multiplexStream->AppendStream(inputStream2); |
459 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
460 | 0 |
|
461 | 0 | nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream)); |
462 | 0 | ASSERT_TRUE(!!stream); |
463 | 0 |
|
464 | 0 | char buf3[1024]; |
465 | 0 | uint32_t size = 0; |
466 | 0 | rv = stream->ReadSegments(NS_CopySegmentToBuffer, buf3, sizeof(buf3), &size); |
467 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
468 | 0 |
|
469 | 0 | ASSERT_EQ(size, buf1.Length() + buf2.Length()); |
470 | 0 | ASSERT_TRUE(!strncmp(buf3, "Hello world", size)); |
471 | 0 | } |
472 | | |
473 | 0 | TEST(TestMultiplexInputStream, QILengthInputStream) { |
474 | 0 | nsCString buf; |
475 | 0 | buf.AssignLiteral("Hello world"); |
476 | 0 |
|
477 | 0 | nsCOMPtr<nsIMultiplexInputStream> multiplexStream = |
478 | 0 | do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); |
479 | 0 |
|
480 | 0 | // nsMultiplexInputStream doesn't expose nsIInputStreamLength if there are |
481 | 0 | // no nsIInputStreamLength sub streams. |
482 | 0 | { |
483 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
484 | 0 | nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), buf); |
485 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
486 | 0 |
|
487 | 0 | rv = multiplexStream->AppendStream(inputStream); |
488 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
489 | 0 |
|
490 | 0 | nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream); |
491 | 0 | ASSERT_TRUE(!fsis); |
492 | 0 |
|
493 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream); |
494 | 0 | ASSERT_TRUE(!afsis); |
495 | 0 | } |
496 | 0 |
|
497 | 0 | // nsMultiplexInputStream exposes nsIInputStreamLength if there is one or |
498 | 0 | // more nsIInputStreamLength sub streams. |
499 | 0 | { |
500 | 0 | RefPtr<testing::LengthInputStream> inputStream = |
501 | 0 | new testing::LengthInputStream(buf, true, false); |
502 | 0 |
|
503 | 0 | nsresult rv = multiplexStream->AppendStream(inputStream); |
504 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
505 | 0 |
|
506 | 0 | nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream); |
507 | 0 | ASSERT_TRUE(!!fsis); |
508 | 0 |
|
509 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream); |
510 | 0 | ASSERT_TRUE(!afsis); |
511 | 0 | } |
512 | 0 |
|
513 | 0 | // nsMultiplexInputStream exposes nsIAsyncInputStreamLength if there is one |
514 | 0 | // or more nsIAsyncInputStreamLength sub streams. |
515 | 0 | { |
516 | 0 | RefPtr<testing::LengthInputStream> inputStream = |
517 | 0 | new testing::LengthInputStream(buf, true, true); |
518 | 0 |
|
519 | 0 | nsresult rv = multiplexStream->AppendStream(inputStream); |
520 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
521 | 0 |
|
522 | 0 | nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream); |
523 | 0 | ASSERT_TRUE(!!fsis); |
524 | 0 |
|
525 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream); |
526 | 0 | ASSERT_TRUE(!!afsis); |
527 | 0 | } |
528 | 0 | } |
529 | | |
530 | 0 | TEST(TestMultiplexInputStream, LengthInputStream) { |
531 | 0 | nsCOMPtr<nsIMultiplexInputStream> multiplexStream = |
532 | 0 | do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); |
533 | 0 |
|
534 | 0 | // First stream is a a simple one. |
535 | 0 | nsCString buf; |
536 | 0 | buf.AssignLiteral("Hello world"); |
537 | 0 |
|
538 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
539 | 0 | nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), buf); |
540 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
541 | 0 |
|
542 | 0 | rv = multiplexStream->AppendStream(inputStream); |
543 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
544 | 0 |
|
545 | 0 | // A LengthInputStream, non-async. |
546 | 0 | RefPtr<testing::LengthInputStream> lengthStream = |
547 | 0 | new testing::LengthInputStream(buf, true, false); |
548 | 0 |
|
549 | 0 | rv = multiplexStream->AppendStream(lengthStream); |
550 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
551 | 0 |
|
552 | 0 | nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream); |
553 | 0 | ASSERT_TRUE(!!fsis); |
554 | 0 |
|
555 | 0 | // Size is the sum of the 2 streams. |
556 | 0 | int64_t length; |
557 | 0 | rv = fsis->Length(&length); |
558 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
559 | 0 | ASSERT_EQ(buf.Length() * 2, length); |
560 | 0 |
|
561 | 0 | // An async LengthInputStream. |
562 | 0 | RefPtr<testing::LengthInputStream> asyncLengthStream = |
563 | 0 | new testing::LengthInputStream(buf, true, true, NS_BASE_STREAM_WOULD_BLOCK); |
564 | 0 |
|
565 | 0 | rv = multiplexStream->AppendStream(asyncLengthStream); |
566 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
567 | 0 |
|
568 | 0 | nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream); |
569 | 0 | ASSERT_TRUE(!!afsis); |
570 | 0 |
|
571 | 0 | // Now it would block. |
572 | 0 | rv = fsis->Length(&length); |
573 | 0 | ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv); |
574 | 0 |
|
575 | 0 | // Let's read the size async. |
576 | 0 | RefPtr<testing::LengthCallback> callback = new testing::LengthCallback(); |
577 | 0 | rv = afsis->AsyncLengthWait(callback, GetCurrentThreadSerialEventTarget()); |
578 | 0 | ASSERT_EQ(NS_OK, rv); |
579 | 0 |
|
580 | 0 | MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); })); |
581 | 0 | ASSERT_EQ(buf.Length() * 3, callback->Size()); |
582 | 0 |
|
583 | 0 | // Now a negative stream |
584 | 0 | lengthStream = |
585 | 0 | new testing::LengthInputStream(buf, true, false, NS_OK, true); |
586 | 0 |
|
587 | 0 | rv = multiplexStream->AppendStream(lengthStream); |
588 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
589 | 0 |
|
590 | 0 | rv = fsis->Length(&length); |
591 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
592 | 0 | ASSERT_EQ(-1, length); |
593 | 0 |
|
594 | 0 | // Another async LengthInputStream. |
595 | 0 | asyncLengthStream = |
596 | 0 | new testing::LengthInputStream(buf, true, true, NS_BASE_STREAM_WOULD_BLOCK); |
597 | 0 |
|
598 | 0 | rv = multiplexStream->AppendStream(asyncLengthStream); |
599 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
600 | 0 |
|
601 | 0 | afsis = do_QueryInterface(multiplexStream); |
602 | 0 | ASSERT_TRUE(!!afsis); |
603 | 0 |
|
604 | 0 | // Let's read the size async. |
605 | 0 | RefPtr<testing::LengthCallback> callback1 = new testing::LengthCallback(); |
606 | 0 | rv = afsis->AsyncLengthWait(callback1, GetCurrentThreadSerialEventTarget()); |
607 | 0 | ASSERT_EQ(NS_OK, rv); |
608 | 0 |
|
609 | 0 | RefPtr<testing::LengthCallback> callback2 = new testing::LengthCallback(); |
610 | 0 | rv = afsis->AsyncLengthWait(callback2, GetCurrentThreadSerialEventTarget()); |
611 | 0 | ASSERT_EQ(NS_OK, rv); |
612 | 0 |
|
613 | 0 | MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback2->Called(); })); |
614 | 0 | ASSERT_FALSE(callback1->Called()); |
615 | 0 | ASSERT_TRUE(callback2->Called()); |
616 | 0 | } |