Coverage Report

Created: 2018-09-25 14:53

/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
}