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 "SkStreamPriv.h"
15 #include "Test.h"
16
17 #ifndef SK_BUILD_FOR_WIN
18 #include <unistd.h>
19 #include <fcntl.h>
20 #endif
21
22 #define MAX_SIZE (256 * 1024)
23
test_loop_stream(skiatest::Reporter * reporter,SkStream * stream,const void * src,size_t len,int repeat)24 static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream,
25 const void* src, size_t len, int repeat) {
26 SkAutoSMalloc<256> storage(len);
27 void* tmp = storage.get();
28
29 for (int i = 0; i < repeat; ++i) {
30 size_t bytes = stream->read(tmp, len);
31 REPORTER_ASSERT(reporter, bytes == len);
32 REPORTER_ASSERT(reporter, !memcmp(tmp, src, len));
33 }
34
35 // expect EOF
36 size_t bytes = stream->read(tmp, 1);
37 REPORTER_ASSERT(reporter, 0 == bytes);
38 // isAtEnd might not return true until after the first failing read.
39 REPORTER_ASSERT(reporter, stream->isAtEnd());
40 }
41
test_filestreams(skiatest::Reporter * reporter,const char * tmpDir)42 static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
43 SkString path = SkOSPath::Join(tmpDir, "wstream_test");
44
45 const char s[] = "abcdefghijklmnopqrstuvwxyz";
46
47 {
48 SkFILEWStream writer(path.c_str());
49 if (!writer.isValid()) {
50 ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
51 return;
52 }
53
54 for (int i = 0; i < 100; ++i) {
55 writer.write(s, 26);
56 }
57 }
58
59 {
60 SkFILEStream stream(path.c_str());
61 REPORTER_ASSERT(reporter, stream.isValid());
62 test_loop_stream(reporter, &stream, s, 26, 100);
63
64 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
65 test_loop_stream(reporter, stream2.get(), s, 26, 100);
66 }
67
68 {
69 FILE* file = ::fopen(path.c_str(), "rb");
70 SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
71 REPORTER_ASSERT(reporter, stream.isValid());
72 test_loop_stream(reporter, &stream, s, 26, 100);
73
74 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
75 test_loop_stream(reporter, stream2.get(), s, 26, 100);
76 }
77 }
78
TestWStream(skiatest::Reporter * reporter)79 static void TestWStream(skiatest::Reporter* reporter) {
80 SkDynamicMemoryWStream ds;
81 const char s[] = "abcdefghijklmnopqrstuvwxyz";
82 int i;
83 for (i = 0; i < 100; i++) {
84 REPORTER_ASSERT(reporter, ds.write(s, 26));
85 }
86 REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
87
88 char* dst = new char[100 * 26 + 1];
89 dst[100*26] = '*';
90 ds.copyTo(dst);
91 REPORTER_ASSERT(reporter, dst[100*26] == '*');
92 for (i = 0; i < 100; i++) {
93 REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
94 }
95
96 {
97 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
98 REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
99 REPORTER_ASSERT(reporter, ds.getOffset() == 0);
100 test_loop_stream(reporter, stream.get(), s, 26, 100);
101
102 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
103 test_loop_stream(reporter, stream2.get(), s, 26, 100);
104
105 SkAutoTDelete<SkStreamAsset> stream3(stream->fork());
106 REPORTER_ASSERT(reporter, stream3->isAtEnd());
107 char tmp;
108 size_t bytes = stream->read(&tmp, 1);
109 REPORTER_ASSERT(reporter, 0 == bytes);
110 stream3->rewind();
111 test_loop_stream(reporter, stream3.get(), s, 26, 100);
112 }
113
114 for (i = 0; i < 100; i++) {
115 REPORTER_ASSERT(reporter, ds.write(s, 26));
116 }
117 REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
118
119 {
120 SkAutoTUnref<SkData> data(ds.copyToData());
121 REPORTER_ASSERT(reporter, 100 * 26 == data->size());
122 REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
123 }
124
125 {
126 // Test that this works after a copyToData.
127 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
128 REPORTER_ASSERT(reporter, ds.getOffset() == 0);
129 test_loop_stream(reporter, stream.get(), s, 26, 100);
130
131 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
132 test_loop_stream(reporter, stream2.get(), s, 26, 100);
133 }
134 delete[] dst;
135
136 SkString tmpDir = skiatest::GetTmpDir();
137 if (!tmpDir.isEmpty()) {
138 test_filestreams(reporter, tmpDir.c_str());
139 }
140 }
141
TestPackedUInt(skiatest::Reporter * reporter)142 static void TestPackedUInt(skiatest::Reporter* reporter) {
143 // we know that packeduint tries to write 1, 2 or 4 bytes for the length,
144 // so we test values around each of those transitions (and a few others)
145 const size_t sizes[] = {
146 0, 1, 2, 0xFC, 0xFD, 0xFE, 0xFF, 0x100, 0x101, 32767, 32768, 32769,
147 0xFFFD, 0xFFFE, 0xFFFF, 0x10000, 0x10001,
148 0xFFFFFD, 0xFFFFFE, 0xFFFFFF, 0x1000000, 0x1000001,
149 0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF
150 };
151
152
153 size_t i;
154 char buffer[sizeof(sizes) * 4];
155
156 SkMemoryWStream wstream(buffer, sizeof(buffer));
157 for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
158 bool success = wstream.writePackedUInt(sizes[i]);
159 REPORTER_ASSERT(reporter, success);
160 }
161 wstream.flush();
162
163 SkMemoryStream rstream(buffer, sizeof(buffer));
164 for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
165 size_t n = rstream.readPackedUInt();
166 if (sizes[i] != n) {
167 ERRORF(reporter, "sizes:%x != n:%x\n", i, sizes[i], n);
168 }
169 }
170 }
171
172 // Test that setting an SkMemoryStream to a nullptr 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(nullptr, 0);
176 memStream->getMemoryBase();
177 SkAutoDataUnref data(memStream->copyToData());
178 }
179
TestNullData()180 static void TestNullData() {
181 SkData* nullData = nullptr;
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 the amount successfully read minus the amount successfully peeked.
200 */
compare_peek_to_read(skiatest::Reporter * reporter,SkStream * stream,size_t bytesToPeek)201 static size_t 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 const size_t bytesPeeked = stream->peek(peekPtr, bytesToPeek);
211 const size_t bytesRead = stream->read(readPtr, bytesToPeek);
212
213 // bytesRead should only be less than attempted if the stream is at the
214 // end.
215 REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
216
217 // peek and read should behave the same, except peek returned to the
218 // original position, so they read the same data.
219 REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesPeeked));
220
221 // A stream should never be able to peek more than it can read.
222 REPORTER_ASSERT(reporter, bytesRead >= bytesPeeked);
223
224 return bytesRead - bytesPeeked;
225 }
226
test_fully_peekable_stream(skiatest::Reporter * r,SkStream * stream,size_t limit)227 static void test_fully_peekable_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
228 for (size_t i = 1; !stream->isAtEnd(); i++) {
229 REPORTER_ASSERT(r, compare_peek_to_read(r, stream, i) == 0);
230 }
231 }
232
test_peeking_front_buffered_stream(skiatest::Reporter * r,const SkStream & original,size_t bufferSize)233 static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
234 const SkStream& original,
235 size_t bufferSize) {
236 SkStream* dupe = original.duplicate();
237 REPORTER_ASSERT(r, dupe != nullptr);
238 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
239 REPORTER_ASSERT(r, bufferedStream != nullptr);
240
241 size_t peeked = 0;
242 for (size_t i = 1; !bufferedStream->isAtEnd(); i++) {
243 const size_t unpeekableBytes = compare_peek_to_read(r, bufferedStream, i);
244 if (unpeekableBytes > 0) {
245 // This could not have returned a number greater than i.
246 REPORTER_ASSERT(r, unpeekableBytes <= i);
247
248 // We have reached the end of the buffer. Verify that it was at least
249 // bufferSize.
250 REPORTER_ASSERT(r, peeked + i - unpeekableBytes >= bufferSize);
251 // No more peeking is supported.
252 break;
253 }
254 peeked += i;
255 }
256
257 // Test that attempting to peek beyond the length of the buffer does not prevent rewinding.
258 bufferedStream.reset(SkFrontBufferedStream::Create(original.duplicate(), bufferSize));
259 REPORTER_ASSERT(r, bufferedStream != nullptr);
260
261 const size_t bytesToPeek = bufferSize + 1;
262 SkAutoMalloc peekStorage(bytesToPeek);
263 SkAutoMalloc readStorage(bytesToPeek);
264
265 for (size_t start = 0; start <= bufferSize; start++) {
266 // Skip to the starting point
267 REPORTER_ASSERT(r, bufferedStream->skip(start) == start);
268
269 const size_t bytesPeeked = bufferedStream->peek(peekStorage.get(), bytesToPeek);
270 if (0 == bytesPeeked) {
271 // Peeking should only fail completely if we have read/skipped beyond the buffer.
272 REPORTER_ASSERT(r, start >= bufferSize);
273 break;
274 }
275
276 // Only read the amount that was successfully peeked.
277 const size_t bytesRead = bufferedStream->read(readStorage.get(), bytesPeeked);
278 REPORTER_ASSERT(r, bytesRead == bytesPeeked);
279 REPORTER_ASSERT(r, !memcmp(peekStorage.get(), readStorage.get(), bytesPeeked));
280
281 // This should be safe to rewind.
282 REPORTER_ASSERT(r, bufferedStream->rewind());
283 }
284 }
285
286 // This test uses file system operations that don't work out of the
287 // box on iOS. It's likely that we don't need them on iOS. Ignoring for now.
288 // TODO(stephana): Re-evaluate if we need this in the future.
289 #ifndef SK_BUILD_FOR_IOS
DEF_TEST(StreamPeek,reporter)290 DEF_TEST(StreamPeek, reporter) {
291 // Test a memory stream.
292 const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
293 SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
294 test_fully_peekable_stream(reporter, &memStream, memStream.getLength());
295
296 // Test an arbitrary file stream. file streams do not support peeking.
297 SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
298 REPORTER_ASSERT(reporter, fileStream.isValid());
299 if (!fileStream.isValid()) {
300 return;
301 }
302 SkAutoMalloc storage(fileStream.getLength());
303 for (size_t i = 1; i < fileStream.getLength(); i++) {
304 REPORTER_ASSERT(reporter, fileStream.peek(storage.get(), i) == 0);
305 }
306
307 // Now test some FrontBufferedStreams
308 for (size_t i = 1; i < memStream.getLength(); i++) {
309 test_peeking_front_buffered_stream(reporter, memStream, i);
310 }
311 }
312 #endif
313
314 // Asserts that asset == expected and is peekable.
stream_peek_test(skiatest::Reporter * rep,SkStreamAsset * asset,const SkData * expected)315 static void stream_peek_test(skiatest::Reporter* rep,
316 SkStreamAsset* asset,
317 const SkData* expected) {
318 if (asset->getLength() != expected->size()) {
319 ERRORF(rep, "Unexpected length.");
320 return;
321 }
322 SkRandom rand;
323 uint8_t buffer[4096];
324 const uint8_t* expect = expected->bytes();
325 for (size_t i = 0; i < asset->getLength(); ++i) {
326 uint32_t maxSize =
327 SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i));
328 size_t size = rand.nextRangeU(1, maxSize);
329 SkASSERT(size >= 1);
330 SkASSERT(size <= sizeof(buffer));
331 SkASSERT(size + i <= asset->getLength());
332 if (asset->peek(buffer, size) < size) {
333 ERRORF(rep, "Peek Failed!");
334 return;
335 }
336 if (0 != memcmp(buffer, &expect[i], size)) {
337 ERRORF(rep, "Peek returned wrong bytes!");
338 return;
339 }
340 uint8_t value;
341 REPORTER_ASSERT(rep, 1 == asset->read(&value, 1));
342 if (value != expect[i]) {
343 ERRORF(rep, "Read Failed!");
344 return;
345 }
346 }
347 }
348
DEF_TEST(StreamPeek_BlockMemoryStream,rep)349 DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
350 const static int kSeed = 1234;
351 SkRandom valueSource(kSeed);
352 SkRandom rand(kSeed << 1);
353 uint8_t buffer[4096];
354 SkDynamicMemoryWStream dynamicMemoryWStream;
355 for (int i = 0; i < 32; ++i) {
356 // Randomize the length of the blocks.
357 size_t size = rand.nextRangeU(1, sizeof(buffer));
358 for (size_t j = 0; j < size; ++j) {
359 buffer[j] = valueSource.nextU() & 0xFF;
360 }
361 dynamicMemoryWStream.write(buffer, size);
362 }
363 SkAutoTDelete<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream());
364 SkAutoTUnref<SkData> expected(SkData::NewUninitialized(asset->getLength()));
365 uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data());
366 valueSource.setSeed(kSeed); // reseed.
367 // We want the exact same same "random" string of numbers to put
368 // in expected. i.e.: don't rely on SkDynamicMemoryStream to work
369 // correctly while we are testing SkDynamicMemoryStream.
370 for (size_t i = 0; i < asset->getLength(); ++i) {
371 expectedPtr[i] = valueSource.nextU() & 0xFF;
372 }
373 stream_peek_test(rep, asset, expected);
374 }
375
376 namespace {
377 class DumbStream : public SkStream {
378 public:
DumbStream(const uint8_t * data,size_t n)379 DumbStream(const uint8_t* data, size_t n)
380 : fData(data), fCount(n), fIdx(0) {}
read(void * buffer,size_t size)381 size_t read(void* buffer, size_t size) override {
382 size_t copyCount = SkTMin(fCount - fIdx, size);
383 if (copyCount) {
384 memcpy(buffer, &fData[fIdx], copyCount);
385 fIdx += copyCount;
386 }
387 return copyCount;
388 }
isAtEnd() const389 bool isAtEnd() const override {
390 return fCount == fIdx;
391 }
392 private:
393 const uint8_t* fData;
394 size_t fCount, fIdx;
395 };
396 } // namespace
397
stream_copy_test(skiatest::Reporter * reporter,const void * srcData,size_t N,SkStream * stream)398 static void stream_copy_test(skiatest::Reporter* reporter,
399 const void* srcData,
400 size_t N,
401 SkStream* stream) {
402 SkDynamicMemoryWStream tgt;
403 if (!SkStreamCopy(&tgt, stream)) {
404 ERRORF(reporter, "SkStreamCopy failed");
405 return;
406 }
407 SkAutoTUnref<SkData> data(tgt.copyToData());
408 tgt.reset();
409 if (data->size() != N) {
410 ERRORF(reporter, "SkStreamCopy incorrect size");
411 return;
412 }
413 if (0 != memcmp(data->data(), srcData, N)) {
414 ERRORF(reporter, "SkStreamCopy bad copy");
415 }
416 }
417
DEF_TEST(StreamCopy,reporter)418 DEF_TEST(StreamCopy, reporter) {
419 SkRandom random(123456);
420 static const int N = 10000;
421 SkAutoTMalloc<uint8_t> src((size_t)N);
422 for (int j = 0; j < N; ++j) {
423 src[j] = random.nextU() & 0xff;
424 }
425 // SkStreamCopy had two code paths; this test both.
426 DumbStream dumbStream(src.get(), (size_t)N);
427 stream_copy_test(reporter, src, N, &dumbStream);
428 SkMemoryStream smartStream(src.get(), (size_t)N);
429 stream_copy_test(reporter, src, N, &smartStream);
430 }
431
DEF_TEST(StreamEmptyStreamMemoryBase,r)432 DEF_TEST(StreamEmptyStreamMemoryBase, r) {
433 SkDynamicMemoryWStream tmp;
434 SkAutoTDelete<SkStreamAsset> asset(tmp.detachAsStream());
435 REPORTER_ASSERT(r, nullptr == asset->getMemoryBase());
436 }
437
438