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 "Helpers.h"
9 #include "nsICloneableInputStream.h"
10 #include "nsStringStream.h"
11 #include "nsTArray.h"
12 #include "nsIInputStream.h"
13 #include "nsCOMPtr.h"
14 #include "nsStreamUtils.h"
15 #include "mozilla/Span.h"
16 #include "nsISeekableStream.h"
17 
18 namespace {
19 
TestStringStream(uint32_t aNumBytes)20 static void TestStringStream(uint32_t aNumBytes) {
21   nsTArray<char> inputData;
22   testing::CreateData(aNumBytes, inputData);
23   nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
24 
25   nsCOMPtr<nsIInputStream> stream;
26   nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), inputString);
27   ASSERT_TRUE(NS_SUCCEEDED(rv));
28 
29   testing::ConsumeAndValidateStream(stream, inputString);
30 }
31 
TestStringStreamClone(uint32_t aNumBytes)32 static void TestStringStreamClone(uint32_t aNumBytes) {
33   nsTArray<char> inputData;
34   testing::CreateData(aNumBytes, inputData);
35   nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
36 
37   nsCOMPtr<nsIInputStream> stream;
38   nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), inputString);
39   ASSERT_TRUE(NS_SUCCEEDED(rv));
40 
41   nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(stream);
42   ASSERT_TRUE(cloneable != nullptr);
43   ASSERT_TRUE(cloneable->GetCloneable());
44 
45   nsCOMPtr<nsIInputStream> clone;
46   rv = cloneable->Clone(getter_AddRefs(clone));
47 
48   testing::ConsumeAndValidateStream(stream, inputString);
49 
50   // Release the stream to verify that the clone's string survives correctly.
51   stream = nullptr;
52 
53   testing::ConsumeAndValidateStream(clone, inputString);
54 }
55 
56 }  // namespace
57 
TEST(StringStream,Simple_4k)58 TEST(StringStream, Simple_4k)
59 { TestStringStream(1024 * 4); }
60 
TEST(StringStream,Clone_4k)61 TEST(StringStream, Clone_4k)
62 { TestStringStreamClone(1024 * 4); }
63 
CloseStreamThenRead(nsIInputStream * aInStr,void * aClosure,const char * aBuffer,uint32_t aOffset,uint32_t aCount,uint32_t * aCountWritten)64 static nsresult CloseStreamThenRead(nsIInputStream* aInStr, void* aClosure,
65                                     const char* aBuffer, uint32_t aOffset,
66                                     uint32_t aCount, uint32_t* aCountWritten) {
67   // Closing the stream will free the data
68   nsresult rv = aInStr->Close();
69   if (NS_FAILED(rv)) {
70     return rv;
71   }
72   // This will likely be allocated in the same slot as what we have in aBuffer
73   char* newAlloc = moz_xstrdup("abcd");
74 
75   char* toBuf = static_cast<char*>(aClosure);
76   memcpy(&toBuf[aOffset], aBuffer, aCount);
77   *aCountWritten = aCount;
78   free(newAlloc);
79   return NS_OK;
80 }
81 
TEST(StringStream,CancelInReadSegments)82 TEST(StringStream, CancelInReadSegments)
83 {
84   char* buffer = moz_xstrdup("test");
85   nsCOMPtr<nsIInputStream> stream;
86   nsresult rv = NS_NewByteInputStream(
87       getter_AddRefs(stream), mozilla::Span(buffer, 5), NS_ASSIGNMENT_ADOPT);
88   ASSERT_TRUE(NS_SUCCEEDED(rv));
89 
90   char buf[100];
91   uint32_t count = 0;
92   uint64_t available = 0;
93   rv = stream->Available(&available);
94   ASSERT_TRUE(NS_SUCCEEDED(rv));
95   rv = stream->ReadSegments(CloseStreamThenRead, buf, available, &count);
96   ASSERT_TRUE(NS_SUCCEEDED(rv));
97   ASSERT_TRUE(count == 5);
98   ASSERT_TRUE(!strcmp(buf, "test"));
99 }
100 
101 // In bug 1595886 we added a diagnostic assert that the buffer of the
102 // string stream is not replaced by the closure passed to ReadSegments
103 // but if it is we restore it afterwards.
104 // The following test runs only if !MOZ_DIAGNOSTIC_ASSERT_ENABLED
105 // We check that the string is still what it was originally.
106 #ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
107 
ChangeStreamThenRead(nsIInputStream * aInStr,void * aClosure,const char * aBuffer,uint32_t aOffset,uint32_t aCount,uint32_t * aCountWritten)108 static nsresult ChangeStreamThenRead(nsIInputStream* aInStr, void* aClosure,
109                                      const char* aBuffer, uint32_t aOffset,
110                                      uint32_t aCount, uint32_t* aCountWritten) {
111   nsCOMPtr<nsIStringInputStream> stream = do_QueryInterface(aInStr);
112 
113   // This does free the old data, which may be safely stowed in a temp variable
114   // We now do something that "no sane code in our codebase would do" ©
115   // We set a new buffer. The pointer we got in aBuffer should still be valid
116   // upon exiting ReadSegments and we are intentionally restoring it after
117   // the call is over.
118   char* newAlloc = moz_xstrdup("abcd");
119   nsresult rv = stream->AdoptData(newAlloc, 5);
120   if (NS_FAILED(rv)) {
121     return rv;
122   }
123 
124   char* toBuf = static_cast<char*>(aClosure);
125   memcpy(&toBuf[aOffset], aBuffer, aCount);
126   *aCountWritten = aCount;
127   return NS_OK;
128 }
129 
TEST(StringStream,ReplacedInReadSegments)130 TEST(StringStream, ReplacedInReadSegments)
131 {
132   char* buffer = moz_xstrdup("test");
133   nsCOMPtr<nsIInputStream> stream;
134   nsresult rv = NS_NewByteInputStream(
135       getter_AddRefs(stream), mozilla::Span(buffer, 5), NS_ASSIGNMENT_ADOPT);
136   ASSERT_TRUE(NS_SUCCEEDED(rv));
137 
138   char buf[100];
139   uint32_t count = 0;
140   uint64_t available = 0;
141   rv = stream->Available(&available);
142   ASSERT_TRUE(NS_SUCCEEDED(rv));
143   rv = stream->ReadSegments(ChangeStreamThenRead, buf, available, &count);
144   ASSERT_TRUE(NS_SUCCEEDED(rv));
145   ASSERT_TRUE(count == 5);
146   ASSERT_TRUE(!strcmp(buf, "test"));
147   buf[0] = '\0';
148   count = 0;
149   available = 0;
150   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
151   rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
152   ASSERT_TRUE(NS_SUCCEEDED(rv));
153   rv = stream->Available(&available);
154   ASSERT_TRUE(NS_SUCCEEDED(rv));
155   rv = stream->ReadSegments(NS_CopySegmentToBuffer, buf, available, &count);
156   ASSERT_TRUE(NS_SUCCEEDED(rv));
157   ASSERT_TRUE(count == 5);
158 
159   // Check that the StringStream's mData was restored after ChangeStreamThenRead
160   // was executed.
161   ASSERT_TRUE(!strcmp(buf, "test"));
162 }
163 
164 #endif  // ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
165