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