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