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