1 /*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "Resources.h"
9 #include "SkData.h"
10 #include "SkFrontBufferedStream.h"
11 #include "SkOSFile.h"
12 #include "SkRandom.h"
13 #include "SkStream.h"
14 #include "Test.h"
15
16 #ifndef SK_BUILD_FOR_WIN
17 #include <unistd.h>
18 #include <fcntl.h>
19 #endif
20
21 #define MAX_SIZE (256 * 1024)
22
test_loop_stream(skiatest::Reporter * reporter,SkStream * stream,const void * src,size_t len,int repeat)23 static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream,
24 const void* src, size_t len, int repeat) {
25 SkAutoSMalloc<256> storage(len);
26 void* tmp = storage.get();
27
28 for (int i = 0; i < repeat; ++i) {
29 size_t bytes = stream->read(tmp, len);
30 REPORTER_ASSERT(reporter, bytes == len);
31 REPORTER_ASSERT(reporter, !memcmp(tmp, src, len));
32 }
33
34 // expect EOF
35 size_t bytes = stream->read(tmp, 1);
36 REPORTER_ASSERT(reporter, 0 == bytes);
37 // isAtEnd might not return true until after the first failing read.
38 REPORTER_ASSERT(reporter, stream->isAtEnd());
39 }
40
test_filestreams(skiatest::Reporter * reporter,const char * tmpDir)41 static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
42 SkString path = SkOSPath::Join(tmpDir, "wstream_test");
43
44 const char s[] = "abcdefghijklmnopqrstuvwxyz";
45
46 {
47 SkFILEWStream writer(path.c_str());
48 if (!writer.isValid()) {
49 ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
50 return;
51 }
52
53 for (int i = 0; i < 100; ++i) {
54 writer.write(s, 26);
55 }
56 }
57
58 {
59 SkFILEStream stream(path.c_str());
60 REPORTER_ASSERT(reporter, stream.isValid());
61 test_loop_stream(reporter, &stream, s, 26, 100);
62
63 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
64 test_loop_stream(reporter, stream2.get(), s, 26, 100);
65 }
66
67 {
68 FILE* file = ::fopen(path.c_str(), "rb");
69 SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
70 REPORTER_ASSERT(reporter, stream.isValid());
71 test_loop_stream(reporter, &stream, s, 26, 100);
72
73 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
74 test_loop_stream(reporter, stream2.get(), s, 26, 100);
75 }
76 }
77
TestWStream(skiatest::Reporter * reporter)78 static void TestWStream(skiatest::Reporter* reporter) {
79 SkDynamicMemoryWStream ds;
80 const char s[] = "abcdefghijklmnopqrstuvwxyz";
81 int i;
82 for (i = 0; i < 100; i++) {
83 REPORTER_ASSERT(reporter, ds.write(s, 26));
84 }
85 REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
86
87 char* dst = new char[100 * 26 + 1];
88 dst[100*26] = '*';
89 ds.copyTo(dst);
90 REPORTER_ASSERT(reporter, dst[100*26] == '*');
91 for (i = 0; i < 100; i++) {
92 REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
93 }
94
95 {
96 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
97 REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
98 REPORTER_ASSERT(reporter, ds.getOffset() == 0);
99 test_loop_stream(reporter, stream.get(), s, 26, 100);
100
101 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
102 test_loop_stream(reporter, stream2.get(), s, 26, 100);
103
104 SkAutoTDelete<SkStreamAsset> stream3(stream->fork());
105 REPORTER_ASSERT(reporter, stream3->isAtEnd());
106 char tmp;
107 size_t bytes = stream->read(&tmp, 1);
108 REPORTER_ASSERT(reporter, 0 == bytes);
109 stream3->rewind();
110 test_loop_stream(reporter, stream3.get(), s, 26, 100);
111 }
112
113 for (i = 0; i < 100; i++) {
114 REPORTER_ASSERT(reporter, ds.write(s, 26));
115 }
116 REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
117
118 {
119 SkAutoTUnref<SkData> data(ds.copyToData());
120 REPORTER_ASSERT(reporter, 100 * 26 == data->size());
121 REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
122 }
123
124 {
125 // Test that this works after a copyToData.
126 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
127 REPORTER_ASSERT(reporter, ds.getOffset() == 0);
128 test_loop_stream(reporter, stream.get(), s, 26, 100);
129
130 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
131 test_loop_stream(reporter, stream2.get(), s, 26, 100);
132 }
133 delete[] dst;
134
135 SkString tmpDir = skiatest::GetTmpDir();
136 if (!tmpDir.isEmpty()) {
137 test_filestreams(reporter, tmpDir.c_str());
138 }
139 }
140
TestPackedUInt(skiatest::Reporter * reporter)141 static void TestPackedUInt(skiatest::Reporter* reporter) {
142 // we know that packeduint tries to write 1, 2 or 4 bytes for the length,
143 // so we test values around each of those transitions (and a few others)
144 const size_t sizes[] = {
145 0, 1, 2, 0xFC, 0xFD, 0xFE, 0xFF, 0x100, 0x101, 32767, 32768, 32769,
146 0xFFFD, 0xFFFE, 0xFFFF, 0x10000, 0x10001,
147 0xFFFFFD, 0xFFFFFE, 0xFFFFFF, 0x1000000, 0x1000001,
148 0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF
149 };
150
151
152 size_t i;
153 char buffer[sizeof(sizes) * 4];
154
155 SkMemoryWStream wstream(buffer, sizeof(buffer));
156 for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
157 bool success = wstream.writePackedUInt(sizes[i]);
158 REPORTER_ASSERT(reporter, success);
159 }
160 wstream.flush();
161
162 SkMemoryStream rstream(buffer, sizeof(buffer));
163 for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
164 size_t n = rstream.readPackedUInt();
165 if (sizes[i] != n) {
166 SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n);
167 }
168 REPORTER_ASSERT(reporter, sizes[i] == n);
169 }
170 }
171
172 // Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling
173 // methods that access fData.
TestDereferencingData(SkMemoryStream * memStream)174 static void TestDereferencingData(SkMemoryStream* memStream) {
175 memStream->read(NULL, 0);
176 memStream->getMemoryBase();
177 SkAutoDataUnref data(memStream->copyToData());
178 }
179
TestNullData()180 static void TestNullData() {
181 SkData* nullData = NULL;
182 SkMemoryStream memStream(nullData);
183 TestDereferencingData(&memStream);
184
185 memStream.setData(nullData);
186 TestDereferencingData(&memStream);
187
188 }
189
DEF_TEST(Stream,reporter)190 DEF_TEST(Stream, reporter) {
191 TestWStream(reporter);
192 TestPackedUInt(reporter);
193 TestNullData();
194 }
195
196 /**
197 * Tests peeking and then reading the same amount. The two should provide the
198 * same results.
199 * Returns whether the stream could peek.
200 */
compare_peek_to_read(skiatest::Reporter * reporter,SkStream * stream,size_t bytesToPeek)201 static bool compare_peek_to_read(skiatest::Reporter* reporter,
202 SkStream* stream, size_t bytesToPeek) {
203 // The rest of our tests won't be very interesting if bytesToPeek is zero.
204 REPORTER_ASSERT(reporter, bytesToPeek > 0);
205 SkAutoMalloc peekStorage(bytesToPeek);
206 SkAutoMalloc readStorage(bytesToPeek);
207 void* peekPtr = peekStorage.get();
208 void* readPtr = peekStorage.get();
209
210 if (!stream->peek(peekPtr, bytesToPeek)) {
211 return false;
212 }
213 const size_t bytesRead = stream->read(readPtr, bytesToPeek);
214
215 // bytesRead should only be less than attempted if the stream is at the
216 // end.
217 REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
218
219 // peek and read should behave the same, except peek returned to the
220 // original position, so they read the same data.
221 REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesRead));
222
223 return true;
224 }
225
test_peeking_stream(skiatest::Reporter * r,SkStream * stream,size_t limit)226 static void test_peeking_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
227 size_t peeked = 0;
228 for (size_t i = 1; !stream->isAtEnd(); i++) {
229 const bool couldPeek = compare_peek_to_read(r, stream, i);
230 if (!couldPeek) {
231 REPORTER_ASSERT(r, peeked + i > limit);
232 // No more peeking is supported.
233 break;
234 }
235 peeked += i;
236 }
237 }
238
test_peeking_front_buffered_stream(skiatest::Reporter * r,const SkStream & original,size_t bufferSize)239 static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
240 const SkStream& original,
241 size_t bufferSize) {
242 SkStream* dupe = original.duplicate();
243 REPORTER_ASSERT(r, dupe != NULL);
244 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
245 REPORTER_ASSERT(r, bufferedStream != NULL);
246 test_peeking_stream(r, bufferedStream, bufferSize);
247 }
248
249 // This test uses file system operations that don't work out of the
250 // box on iOS. It's likely that we don't need them on iOS. Ignoring for now.
251 // TODO(stephana): Re-evaluate if we need this in the future.
252 #ifndef SK_BUILD_FOR_IOS
DEF_TEST(StreamPeek,reporter)253 DEF_TEST(StreamPeek, reporter) {
254 // Test a memory stream.
255 const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
256 SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
257 test_peeking_stream(reporter, &memStream, memStream.getLength());
258
259 // Test an arbitrary file stream. file streams do not support peeking.
260 SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
261 REPORTER_ASSERT(reporter, fileStream.isValid());
262 if (!fileStream.isValid()) {
263 return;
264 }
265 SkAutoMalloc storage(fileStream.getLength());
266 for (size_t i = 1; i < fileStream.getLength(); i++) {
267 REPORTER_ASSERT(reporter, !fileStream.peek(storage.get(), i));
268 }
269
270 // Now test some FrontBufferedStreams
271 for (size_t i = 1; i < memStream.getLength(); i++) {
272 test_peeking_front_buffered_stream(reporter, memStream, i);
273 }
274 }
275 #endif
276