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 /* Helper routines for xpcom gtests. */
8 
9 #include "Helpers.h"
10 
11 #include <algorithm>
12 #include "gtest/gtest.h"
13 #include "nsIOutputStream.h"
14 #include "nsStreamUtils.h"
15 #include "nsTArray.h"
16 #include "nsThreadUtils.h"
17 
18 namespace testing {
19 
20 // Populate an array with the given number of bytes.  Data is lorem ipsum
21 // random text, but deterministic across multiple calls.
CreateData(uint32_t aNumBytes,nsTArray<char> & aDataOut)22 void CreateData(uint32_t aNumBytes, nsTArray<char>& aDataOut) {
23   static const char data[] =
24       "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec egestas "
25       "purus eu condimentum iaculis. In accumsan leo eget odio porttitor, non "
26       "rhoncus nulla vestibulum. Etiam lacinia consectetur nisl nec "
27       "sollicitudin. Sed fringilla accumsan diam, pulvinar varius massa. Duis "
28       "mollis dignissim felis, eget tempus nisi tristique ut. Fusce euismod, "
29       "lectus non lacinia tempor, tellus diam suscipit quam, eget hendrerit "
30       "lacus nunc fringilla ante. Sed ultrices massa vitae risus molestie, ut "
31       "finibus quam laoreet nullam.";
32   static const uint32_t dataLength = sizeof(data) - 1;
33 
34   aDataOut.SetCapacity(aNumBytes);
35 
36   while (aNumBytes > 0) {
37     uint32_t amount = std::min(dataLength, aNumBytes);
38     aDataOut.AppendElements(data, amount);
39     aNumBytes -= amount;
40   }
41 }
42 
43 // Write the given number of bytes out to the stream.  Loop until expected
44 // bytes count is reached or an error occurs.
Write(nsIOutputStream * aStream,const nsTArray<char> & aData,uint32_t aOffset,uint32_t aNumBytes)45 void Write(nsIOutputStream* aStream, const nsTArray<char>& aData,
46            uint32_t aOffset, uint32_t aNumBytes) {
47   uint32_t remaining =
48       std::min(aNumBytes, static_cast<uint32_t>(aData.Length() - aOffset));
49 
50   while (remaining > 0) {
51     uint32_t numWritten;
52     nsresult rv =
53         aStream->Write(aData.Elements() + aOffset, remaining, &numWritten);
54     ASSERT_TRUE(NS_SUCCEEDED(rv));
55     if (numWritten < 1) {
56       break;
57     }
58     aOffset += numWritten;
59     remaining -= numWritten;
60   }
61 }
62 
63 // Write the given number of bytes and then close the stream.
WriteAllAndClose(nsIOutputStream * aStream,const nsTArray<char> & aData)64 void WriteAllAndClose(nsIOutputStream* aStream, const nsTArray<char>& aData) {
65   Write(aStream, aData, 0, aData.Length());
66   aStream->Close();
67 }
68 
69 // Synchronously consume the given input stream and validate the resulting data
70 // against the given array of expected values.
ConsumeAndValidateStream(nsIInputStream * aStream,const nsTArray<char> & aExpectedData)71 void ConsumeAndValidateStream(nsIInputStream* aStream,
72                               const nsTArray<char>& aExpectedData) {
73   nsDependentCSubstring data(aExpectedData.Elements(), aExpectedData.Length());
74   ConsumeAndValidateStream(aStream, data);
75 }
76 
77 // Synchronously consume the given input stream and validate the resulting data
78 // against the given string of expected values.
ConsumeAndValidateStream(nsIInputStream * aStream,const nsACString & aExpectedData)79 void ConsumeAndValidateStream(nsIInputStream* aStream,
80                               const nsACString& aExpectedData) {
81   nsAutoCString outputData;
82   nsresult rv = NS_ConsumeStream(aStream, UINT32_MAX, outputData);
83   ASSERT_TRUE(NS_SUCCEEDED(rv));
84   ASSERT_EQ(aExpectedData.Length(), outputData.Length());
85   ASSERT_TRUE(aExpectedData.Equals(outputData));
86 }
87 
88 NS_IMPL_ISUPPORTS(OutputStreamCallback, nsIOutputStreamCallback);
89 
OutputStreamCallback()90 OutputStreamCallback::OutputStreamCallback() : mCalled(false) {}
91 
92 OutputStreamCallback::~OutputStreamCallback() = default;
93 
94 NS_IMETHODIMP
OnOutputStreamReady(nsIAsyncOutputStream * aStream)95 OutputStreamCallback::OnOutputStreamReady(nsIAsyncOutputStream* aStream) {
96   mCalled = true;
97   return NS_OK;
98 }
99 
100 NS_IMPL_ISUPPORTS(InputStreamCallback, nsIInputStreamCallback);
101 
InputStreamCallback()102 InputStreamCallback::InputStreamCallback() : mCalled(false) {}
103 
104 InputStreamCallback::~InputStreamCallback() = default;
105 
106 NS_IMETHODIMP
OnInputStreamReady(nsIAsyncInputStream * aStream)107 InputStreamCallback::OnInputStreamReady(nsIAsyncInputStream* aStream) {
108   mCalled = true;
109   return NS_OK;
110 }
111 
AsyncStringStream(const nsACString & aBuffer)112 AsyncStringStream::AsyncStringStream(const nsACString& aBuffer) {
113   NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
114 }
115 
116 NS_IMETHODIMP
Available(uint64_t * aLength)117 AsyncStringStream::Available(uint64_t* aLength) {
118   return mStream->Available(aLength);
119 }
120 
121 NS_IMETHODIMP
Read(char * aBuffer,uint32_t aCount,uint32_t * aReadCount)122 AsyncStringStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) {
123   return mStream->Read(aBuffer, aCount, aReadCount);
124 }
125 
126 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * aResult)127 AsyncStringStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
128                                 uint32_t aCount, uint32_t* aResult) {
129   return NS_ERROR_NOT_IMPLEMENTED;
130 }
131 
132 NS_IMETHODIMP
Close()133 AsyncStringStream::Close() {
134   nsresult rv = mStream->Close();
135   if (NS_SUCCEEDED(rv)) {
136     MaybeExecCallback(mCallback, mCallbackEventTarget);
137   }
138   return rv;
139 }
140 
141 NS_IMETHODIMP
IsNonBlocking(bool * aNonBlocking)142 AsyncStringStream::IsNonBlocking(bool* aNonBlocking) {
143   return mStream->IsNonBlocking(aNonBlocking);
144 }
145 
146 NS_IMETHODIMP
CloseWithStatus(nsresult aStatus)147 AsyncStringStream::CloseWithStatus(nsresult aStatus) { return Close(); }
148 
149 NS_IMETHODIMP
AsyncWait(nsIInputStreamCallback * aCallback,uint32_t aFlags,uint32_t aRequestedCount,nsIEventTarget * aEventTarget)150 AsyncStringStream::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
151                              uint32_t aRequestedCount,
152                              nsIEventTarget* aEventTarget) {
153   if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
154     mCallback = aCallback;
155     mCallbackEventTarget = aEventTarget;
156     return NS_OK;
157   }
158 
159   MaybeExecCallback(aCallback, aEventTarget);
160   return NS_OK;
161 }
162 
MaybeExecCallback(nsIInputStreamCallback * aCallback,nsIEventTarget * aEventTarget)163 void AsyncStringStream::MaybeExecCallback(nsIInputStreamCallback* aCallback,
164                                           nsIEventTarget* aEventTarget) {
165   if (!aCallback) {
166     return;
167   }
168 
169   nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
170   nsCOMPtr<nsIAsyncInputStream> self = this;
171 
172   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
173       "AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
174 
175   if (aEventTarget) {
176     aEventTarget->Dispatch(r.forget());
177   } else {
178     r->Run();
179   }
180 }
181 
182 NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
183 
184 NS_IMPL_ADDREF(LengthInputStream);
185 NS_IMPL_RELEASE(LengthInputStream);
186 
187 NS_INTERFACE_MAP_BEGIN(LengthInputStream)
188   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
189   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength, mIsInputStreamLength)
190   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
191                                      mIsAsyncInputStreamLength)
192   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
193 NS_INTERFACE_MAP_END
194 
195 NS_IMPL_ISUPPORTS(LengthCallback, nsIInputStreamLengthCallback)
196 
197 }  // namespace testing
198