Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestNonBlockingAsyncInputStream.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "gtest/gtest.h"
2
3
#include "mozilla/NonBlockingAsyncInputStream.h"
4
#include "nsIAsyncInputStream.h"
5
#include "nsStreamUtils.h"
6
#include "nsString.h"
7
#include "nsStringStream.h"
8
#include "Helpers.h"
9
10
0
TEST(TestNonBlockingAsyncInputStream, Simple) {
11
0
  nsCString data;
12
0
  data.Assign("Hello world!");
13
0
14
0
  // It should not be async.
15
0
  bool nonBlocking = false;
16
0
  nsCOMPtr<nsIAsyncInputStream> async;
17
0
18
0
  {
19
0
    // Let's create a test string inputStream
20
0
    nsCOMPtr<nsIInputStream> stream;
21
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
22
0
23
0
    async = do_QueryInterface(stream);
24
0
    ASSERT_EQ(nullptr, async);
25
0
26
0
    // It must be non-blocking
27
0
    ASSERT_EQ(NS_OK, stream->IsNonBlocking(&nonBlocking));
28
0
    ASSERT_TRUE(nonBlocking);
29
0
30
0
    // Here the non-blocking stream.
31
0
    ASSERT_EQ(NS_OK,
32
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
33
0
                                                  getter_AddRefs(async)));
34
0
  }
35
0
  ASSERT_TRUE(!!async);
36
0
37
0
  // Still non-blocking
38
0
  ASSERT_EQ(NS_OK, async->IsNonBlocking(&nonBlocking));
39
0
  ASSERT_TRUE(nonBlocking);
40
0
41
0
  // Testing ::Available()
42
0
  uint64_t length;
43
0
  ASSERT_EQ(NS_OK, async->Available(&length));
44
0
  ASSERT_EQ(data.Length(), length);
45
0
46
0
  // Read works fine.
47
0
  char buffer[1024];
48
0
  uint32_t read = 0;
49
0
  ASSERT_EQ(NS_OK, async->Read(buffer, sizeof(buffer), &read));
50
0
  ASSERT_EQ(data.Length(), read);
51
0
  ASSERT_TRUE(data.Equals(nsCString(buffer, read)));
52
0
}
53
54
class ReadSegmentsData
55
{
56
public:
57
  ReadSegmentsData(nsIInputStream* aStream, char* aBuffer)
58
    : mStream(aStream)
59
    , mBuffer(aBuffer)
60
0
  {}
61
62
  nsIInputStream* mStream;
63
  char* mBuffer;
64
};
65
66
nsresult
67
ReadSegmentsFunction(nsIInputStream* aInStr,
68
                     void* aClosure,
69
                     const char* aBuffer,
70
                     uint32_t aOffset,
71
                     uint32_t aCount,
72
                     uint32_t* aCountWritten)
73
0
{
74
0
  ReadSegmentsData* data = static_cast<ReadSegmentsData*>(aClosure);
75
0
  if (aInStr != data->mStream) return NS_ERROR_FAILURE;
76
0
  memcpy(&data->mBuffer[aOffset], aBuffer, aCount);
77
0
  *aCountWritten = aCount;
78
0
  return NS_OK;
79
0
}
80
81
0
TEST(TestNonBlockingAsyncInputStream, ReadSegments) {
82
0
  nsCString data;
83
0
  data.Assign("Hello world!");
84
0
85
0
  nsCOMPtr<nsIAsyncInputStream> async;
86
0
  {
87
0
    // Let's create a test string inputStream
88
0
    nsCOMPtr<nsIInputStream> stream;
89
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
90
0
91
0
    // Here the non-blocking stream.
92
0
    ASSERT_EQ(NS_OK,
93
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
94
0
                                                  getter_AddRefs(async)));
95
0
  }
96
0
97
0
  // Read works fine.
98
0
  char buffer[1024];
99
0
  uint32_t read = 0;
100
0
  ReadSegmentsData closure(async, buffer);
101
0
  ASSERT_EQ(NS_OK, async->ReadSegments(ReadSegmentsFunction, &closure,
102
0
                                       sizeof(buffer), &read));
103
0
  ASSERT_EQ(data.Length(), read);
104
0
  ASSERT_TRUE(data.Equals(nsCString(buffer, read)));
105
0
}
106
107
0
TEST(TestNonBlockingAsyncInputStream, AsyncWait_Simple) {
108
0
  nsCString data;
109
0
  data.Assign("Hello world!");
110
0
111
0
  nsCOMPtr<nsIAsyncInputStream> async;
112
0
  {
113
0
    // Let's create a test string inputStream
114
0
    nsCOMPtr<nsIInputStream> stream;
115
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
116
0
117
0
    // Here the non-blocking stream.
118
0
    ASSERT_EQ(NS_OK,
119
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
120
0
                                                  getter_AddRefs(async)));
121
0
  }
122
0
  ASSERT_TRUE(!!async);
123
0
124
0
  // Testing ::Available()
125
0
  uint64_t length;
126
0
  ASSERT_EQ(NS_OK, async->Available(&length));
127
0
  ASSERT_EQ(data.Length(), length);
128
0
129
0
  // Testing ::AsyncWait - without EventTarget
130
0
  RefPtr<testing::InputStreamCallback> cb =
131
0
    new testing::InputStreamCallback();
132
0
133
0
  ASSERT_EQ(NS_OK, async->AsyncWait(cb, 0, 0, nullptr));
134
0
  ASSERT_TRUE(cb->Called());
135
0
136
0
  // Testing ::AsyncWait - with EventTarget
137
0
  cb = new testing::InputStreamCallback();
138
0
  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
139
0
140
0
  ASSERT_EQ(NS_OK, async->AsyncWait(cb, 0, 0, thread));
141
0
  ASSERT_FALSE(cb->Called());
142
0
  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cb->Called(); }));
143
0
  ASSERT_TRUE(cb->Called());
144
0
145
0
  // Read works fine.
146
0
  char buffer[1024];
147
0
  uint32_t read = 0;
148
0
  ASSERT_EQ(NS_OK, async->Read(buffer, sizeof(buffer), &read));
149
0
  ASSERT_EQ(data.Length(), read);
150
0
  ASSERT_TRUE(data.Equals(nsCString(buffer, read)));
151
0
}
152
153
0
TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withoutEventTarget) {
154
0
  nsCString data;
155
0
  data.Assign("Hello world!");
156
0
157
0
  nsCOMPtr<nsIAsyncInputStream> async;
158
0
  {
159
0
    // Let's create a test string inputStream
160
0
    nsCOMPtr<nsIInputStream> stream;
161
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
162
0
163
0
    // Here the non-blocking stream.
164
0
    ASSERT_EQ(NS_OK,
165
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
166
0
                                                  getter_AddRefs(async)));
167
0
  }
168
0
  ASSERT_TRUE(!!async);
169
0
170
0
  // Testing ::AsyncWait - no eventTarget
171
0
  RefPtr<testing::InputStreamCallback> cb =
172
0
    new testing::InputStreamCallback();
173
0
174
0
  ASSERT_EQ(NS_OK, async->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, nullptr));
175
0
176
0
  ASSERT_FALSE(cb->Called());
177
0
  ASSERT_EQ(NS_OK, async->Close());
178
0
  ASSERT_TRUE(cb->Called());
179
0
}
180
181
0
TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withEventTarget) {
182
0
  nsCString data;
183
0
  data.Assign("Hello world!");
184
0
185
0
  nsCOMPtr<nsIAsyncInputStream> async;
186
0
  {
187
0
    // Let's create a test string inputStream
188
0
    nsCOMPtr<nsIInputStream> stream;
189
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
190
0
191
0
    // Here the non-blocking stream.
192
0
    ASSERT_EQ(NS_OK,
193
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
194
0
                                                  getter_AddRefs(async)));
195
0
  }
196
0
  ASSERT_TRUE(!!async);
197
0
198
0
  // Testing ::AsyncWait - with EventTarget
199
0
  RefPtr<testing::InputStreamCallback> cb =
200
0
    new testing::InputStreamCallback();
201
0
  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
202
0
203
0
  ASSERT_EQ(NS_OK, async->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, thread));
204
0
205
0
  ASSERT_FALSE(cb->Called());
206
0
  ASSERT_EQ(NS_OK, async->Close());
207
0
  ASSERT_FALSE(cb->Called());
208
0
209
0
  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cb->Called(); }));
210
0
  ASSERT_TRUE(cb->Called());
211
0
}
212
213
0
TEST(TestNonBlockingAsyncInputStream, Helper) {
214
0
  nsCString data;
215
0
  data.Assign("Hello world!");
216
0
217
0
  nsCOMPtr<nsIAsyncInputStream> async;
218
0
  {
219
0
    // Let's create a test string inputStream
220
0
    nsCOMPtr<nsIInputStream> stream;
221
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
222
0
223
0
    // Here the non-blocking stream.
224
0
    ASSERT_EQ(NS_OK,
225
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
226
0
                                                  getter_AddRefs(async)));
227
0
  }
228
0
  ASSERT_TRUE(!!async);
229
0
230
0
  // This should return the same object because async is already non-blocking
231
0
  // and async.
232
0
  nsCOMPtr<nsIAsyncInputStream> result;
233
0
  nsCOMPtr<nsIAsyncInputStream> asyncTmp = async;
234
0
  ASSERT_EQ(NS_OK,
235
0
            NS_MakeAsyncNonBlockingInputStream(asyncTmp.forget(),
236
0
                                               getter_AddRefs(result)));
237
0
  ASSERT_EQ(async, result);
238
0
239
0
  // This will use NonBlockingAsyncInputStream wrapper.
240
0
  {
241
0
    nsCOMPtr<nsIInputStream> stream;
242
0
    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
243
0
    ASSERT_EQ(NS_OK,
244
0
              NS_MakeAsyncNonBlockingInputStream(stream.forget(),
245
0
                                                 getter_AddRefs(result)));
246
0
  }
247
0
  ASSERT_TRUE(async != result);
248
0
  ASSERT_TRUE(async);
249
0
}
250
251
class QIInputStream final : public nsIInputStream
252
                          , public nsICloneableInputStream
253
                          , public nsIIPCSerializableInputStream
254
                          , public nsISeekableStream
255
{
256
public:
257
  NS_DECL_ISUPPORTS
258
259
  QIInputStream(bool aNonBlockingError, bool aCloneable,
260
                bool aIPCSerializable, bool aSeekable)
261
    : mNonBlockingError(aNonBlockingError)
262
    , mCloneable(aCloneable)
263
    , mIPCSerializable(aIPCSerializable)
264
    , mSeekable(aSeekable)
265
0
  {}
266
267
  // nsIInputStream
268
0
  NS_IMETHOD Close() override { return NS_ERROR_NOT_IMPLEMENTED; }
269
0
  NS_IMETHOD Available(uint64_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
270
0
  NS_IMETHOD Read(char*, uint32_t, uint32_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
271
0
  NS_IMETHOD ReadSegments(nsWriteSegmentFun, void*, uint32_t, uint32_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
272
  NS_IMETHOD IsNonBlocking(bool* aNonBlocking) override
273
0
  {
274
0
    *aNonBlocking = true;
275
0
    return mNonBlockingError ? NS_ERROR_FAILURE : NS_OK;
276
0
  }
277
278
  // nsICloneableInputStream
279
0
  NS_IMETHOD GetCloneable(bool*) override { return NS_ERROR_NOT_IMPLEMENTED; }
280
0
  NS_IMETHOD Clone(nsIInputStream**) override { return NS_ERROR_NOT_IMPLEMENTED; }
281
282
  // nsIIPCSerializableInputStream
283
0
  void Serialize(mozilla::ipc::InputStreamParams&, FileDescriptorArray&) override {}
284
  bool Deserialize(const mozilla::ipc::InputStreamParams&,
285
0
                   const FileDescriptorArray&) override { return false; }
286
0
  mozilla::Maybe<uint64_t> ExpectedSerializedLength() override { return mozilla::Nothing(); }
287
288
  // nsISeekableStream
289
0
  NS_IMETHOD Seek(int32_t, int64_t) override { return NS_ERROR_NOT_IMPLEMENTED; }
290
0
  NS_IMETHOD Tell(int64_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
291
0
  NS_IMETHOD SetEOF() override { return NS_ERROR_NOT_IMPLEMENTED; }
292
293
private:
294
  ~QIInputStream() = default;
295
296
  bool mNonBlockingError;
297
  bool mCloneable;
298
  bool mIPCSerializable;
299
  bool mSeekable;
300
};
301
302
NS_IMPL_ADDREF(QIInputStream);
303
NS_IMPL_RELEASE(QIInputStream);
304
305
0
NS_INTERFACE_MAP_BEGIN(QIInputStream)
306
0
  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
307
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, mCloneable)
308
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, mIPCSerializable)
309
0
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mSeekable)
310
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
311
0
NS_INTERFACE_MAP_END
312
313
0
TEST(TestNonBlockingAsyncInputStream, QI) {
314
0
  // Let's test ::Create() returning error.
315
0
316
0
  nsCOMPtr<nsIAsyncInputStream> async;
317
0
  {
318
0
    nsCOMPtr<nsIInputStream> stream = new QIInputStream(true, true, true, true);
319
0
320
0
    ASSERT_EQ(NS_ERROR_FAILURE,
321
0
              NonBlockingAsyncInputStream::Create(stream.forget(),
322
0
                                                  getter_AddRefs(async)));
323
0
  }
324
0
325
0
  // Let's test the QIs
326
0
  for (int i = 0; i < 8; ++i) {
327
0
    bool shouldBeCloneable = !!(i & 0x01);
328
0
    bool shouldBeSerializable = !!(i & 0x02);
329
0
    bool shouldBeSeekable = !!(i & 0x04);
330
0
331
0
    nsCOMPtr<nsICloneableInputStream> cloneable;
332
0
    nsCOMPtr<nsIIPCSerializableInputStream> ipcSerializable;
333
0
    nsCOMPtr<nsISeekableStream> seekable;
334
0
335
0
    {
336
0
      nsCOMPtr<nsIInputStream> stream =
337
0
        new QIInputStream(false, shouldBeCloneable, shouldBeSerializable, shouldBeSeekable);
338
0
339
0
      cloneable = do_QueryInterface(stream);
340
0
      ASSERT_EQ(shouldBeCloneable, !!cloneable);
341
0
342
0
      ipcSerializable = do_QueryInterface(stream);
343
0
      ASSERT_EQ(shouldBeSerializable, !!ipcSerializable);
344
0
345
0
      seekable = do_QueryInterface(stream);
346
0
      ASSERT_EQ(shouldBeSeekable, !!seekable);
347
0
348
0
      ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create(stream.forget(),
349
0
                                                           getter_AddRefs(async)));
350
0
    }
351
0
352
0
    // The returned async stream should be cloneable only if the underlying
353
0
    // stream is.
354
0
    cloneable = do_QueryInterface(async);
355
0
    ASSERT_EQ(shouldBeCloneable, !!cloneable);
356
0
357
0
    // The returned async stream should be serializable only if the underlying
358
0
    // stream is.
359
0
    ipcSerializable = do_QueryInterface(async);
360
0
    ASSERT_EQ(shouldBeSerializable, !!ipcSerializable);
361
0
362
0
    // The returned async stream should be seekable only if the underlying
363
0
    // stream is.
364
0
    seekable = do_QueryInterface(async);
365
0
    ASSERT_EQ(shouldBeSeekable, !!seekable);
366
0
  }
367
0
}