blob: 926cfc25796b11a84716aae5ed4c77004fa42400 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
scroggo028a4132015-04-02 13:19:51 -07008#include "Resources.h"
reed@google.com8d0b5772011-06-24 13:07:31 +00009#include "SkData.h"
scroggo028a4132015-04-02 13:19:51 -070010#include "SkFrontBufferedStream.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000011#include "SkOSFile.h"
12#include "SkRandom.h"
13#include "SkStream.h"
14#include "Test.h"
reed@android.com5e5adfd2009-03-07 03:39:23 +000015
reed@google.com789c6f22013-02-25 20:24:24 +000016#ifndef SK_BUILD_FOR_WIN
17#include <unistd.h>
18#include <fcntl.h>
19#endif
20
reed@android.com5e5adfd2009-03-07 03:39:23 +000021#define MAX_SIZE (256 * 1024)
22
reed@google.com789c6f22013-02-25 20:24:24 +000023static 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);
bungeman@google.com88682b72013-07-19 13:55:41 +000037 // isAtEnd might not return true until after the first failing read.
38 REPORTER_ASSERT(reporter, stream->isAtEnd());
reed@google.com789c6f22013-02-25 20:24:24 +000039}
40
41static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
tfarinaa8e2e152014-07-28 19:26:58 -070042 SkString path = SkOSPath::Join(tmpDir, "wstream_test");
reed@google.com789c6f22013-02-25 20:24:24 +000043
44 const char s[] = "abcdefghijklmnopqrstuvwxyz";
45
46 {
47 SkFILEWStream writer(path.c_str());
48 if (!writer.isValid()) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +000049 ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
reed@google.com789c6f22013-02-25 20:24:24 +000050 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);
bungeman@google.com6cab1a42013-05-29 13:43:31 +000062
scroggoa1193e42015-01-21 12:09:53 -080063 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
bungeman@google.com6cab1a42013-05-29 13:43:31 +000064 test_loop_stream(reporter, stream2.get(), s, 26, 100);
reed@google.com789c6f22013-02-25 20:24:24 +000065 }
66
reed@google.com789c6f22013-02-25 20:24:24 +000067 {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000068 FILE* file = ::fopen(path.c_str(), "rb");
69 SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
reed@google.com789c6f22013-02-25 20:24:24 +000070 REPORTER_ASSERT(reporter, stream.isValid());
71 test_loop_stream(reporter, &stream, s, 26, 100);
bungeman@google.com6cab1a42013-05-29 13:43:31 +000072
scroggoa1193e42015-01-21 12:09:53 -080073 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
bungeman@google.com6cab1a42013-05-29 13:43:31 +000074 test_loop_stream(reporter, stream2.get(), s, 26, 100);
reed@google.com789c6f22013-02-25 20:24:24 +000075 }
reed@google.com789c6f22013-02-25 20:24:24 +000076}
77
reed@android.com80e39a72009-04-02 16:59:40 +000078static 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));
reed@android.com5e5adfd2009-03-07 03:39:23 +000084 }
reed@android.com80e39a72009-04-02 16:59:40 +000085 REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
bungeman@google.com88682b72013-07-19 13:55:41 +000086
reed@android.com80e39a72009-04-02 16:59:40 +000087 char* dst = new char[100 * 26 + 1];
88 dst[100*26] = '*';
89 ds.copyTo(dst);
90 REPORTER_ASSERT(reporter, dst[100*26] == '*');
reed@android.com80e39a72009-04-02 16:59:40 +000091 for (i = 0; i < 100; i++) {
92 REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
reed@android.com5e5adfd2009-03-07 03:39:23 +000093 }
reed@google.com70442a62011-06-23 21:48:04 +000094
95 {
scroggoa1193e42015-01-21 12:09:53 -080096 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
bungeman@google.com88682b72013-07-19 13:55:41 +000097 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
scroggoa1193e42015-01-21 12:09:53 -0800101 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
bungeman@google.com88682b72013-07-19 13:55:41 +0000102 test_loop_stream(reporter, stream2.get(), s, 26, 100);
103
scroggoa1193e42015-01-21 12:09:53 -0800104 SkAutoTDelete<SkStreamAsset> stream3(stream->fork());
bungeman@google.com88682b72013-07-19 13:55:41 +0000105 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());
reed@google.com70442a62011-06-23 21:48:04 +0000120 REPORTER_ASSERT(reporter, 100 * 26 == data->size());
121 REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
bungeman@google.com88682b72013-07-19 13:55:41 +0000122 }
123
124 {
125 // Test that this works after a copyToData.
scroggoa1193e42015-01-21 12:09:53 -0800126 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
bungeman@google.com88682b72013-07-19 13:55:41 +0000127 REPORTER_ASSERT(reporter, ds.getOffset() == 0);
128 test_loop_stream(reporter, stream.get(), s, 26, 100);
129
scroggoa1193e42015-01-21 12:09:53 -0800130 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
bungeman@google.com88682b72013-07-19 13:55:41 +0000131 test_loop_stream(reporter, stream2.get(), s, 26, 100);
reed@google.com70442a62011-06-23 21:48:04 +0000132 }
reed@android.com80e39a72009-04-02 16:59:40 +0000133 delete[] dst;
reed@google.com789c6f22013-02-25 20:24:24 +0000134
halcanary87f3ba42015-01-20 09:30:20 -0800135 SkString tmpDir = skiatest::GetTmpDir();
scroggo@google.comc76218d2013-06-06 14:59:56 +0000136 if (!tmpDir.isEmpty()) {
137 test_filestreams(reporter, tmpDir.c_str());
reed@google.com789c6f22013-02-25 20:24:24 +0000138 }
reed@android.com5e5adfd2009-03-07 03:39:23 +0000139}
140
reed@google.com19f286b2011-10-18 11:49:52 +0000141static 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 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000150
151
reed@google.com19f286b2011-10-18 11:49:52 +0000152 size_t i;
153 char buffer[sizeof(sizes) * 4];
rmistry@google.comd6176b02012-08-23 18:14:13 +0000154
reed@google.com19f286b2011-10-18 11:49:52 +0000155 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();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000161
reed@google.com19f286b2011-10-18 11:49:52 +0000162 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
scroggo@google.come4904202013-01-09 22:02:58 +0000172// Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling
173// methods that access fData.
174static void TestDereferencingData(SkMemoryStream* memStream) {
175 memStream->read(NULL, 0);
176 memStream->getMemoryBase();
177 SkAutoDataUnref data(memStream->copyToData());
178}
179
180static void TestNullData() {
181 SkData* nullData = NULL;
182 SkMemoryStream memStream(nullData);
183 TestDereferencingData(&memStream);
184
185 memStream.setData(nullData);
186 TestDereferencingData(&memStream);
187
188}
189
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000190DEF_TEST(Stream, reporter) {
reed@android.com5e5adfd2009-03-07 03:39:23 +0000191 TestWStream(reporter);
reed@google.com19f286b2011-10-18 11:49:52 +0000192 TestPackedUInt(reporter);
scroggo@google.come4904202013-01-09 22:02:58 +0000193 TestNullData();
reed@android.com5e5adfd2009-03-07 03:39:23 +0000194}
scroggo028a4132015-04-02 13:19:51 -0700195
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 */
201static 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
226static 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
239static 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
stephana195f62d2015-04-09 07:57:54 -0700249// 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
scroggo028a4132015-04-02 13:19:51 -0700253DEF_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());
caryclark30ac4642015-04-14 06:08:04 -0700262 if (!fileStream.isValid()) {
263 return;
264 }
scroggo028a4132015-04-02 13:19:51 -0700265 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}
halcanarye797d0d2015-05-21 08:13:27 -0700275#endif
276
277// Asserts that asset == expected and is peekable.
278static void stream_peek_test(skiatest::Reporter* rep,
279 SkStreamAsset* asset,
280 const SkData* expected) {
281 if (asset->getLength() != expected->size()) {
282 ERRORF(rep, "Unexpected length.");
283 return;
284 }
285 SkRandom rand;
286 uint8_t buffer[4096];
287 const uint8_t* expect = expected->bytes();
288 for (size_t i = 0; i < asset->getLength(); ++i) {
289 uint32_t maxSize =
290 SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i));
291 size_t size = rand.nextRangeU(1, maxSize);
292 SkASSERT(size >= 1);
293 SkASSERT(size <= sizeof(buffer));
294 SkASSERT(size + i <= asset->getLength());
295 if (!asset->peek(buffer, size)) {
296 ERRORF(rep, "Peek Failed!");
297 return;
298 }
299 if (0 != memcmp(buffer, &expect[i], size)) {
300 ERRORF(rep, "Peek returned wrong bytes!");
301 return;
302 }
303 uint8_t value;
304 REPORTER_ASSERT(rep, 1 == asset->read(&value, 1));
305 if (value != expect[i]) {
306 ERRORF(rep, "Read Failed!");
307 return;
308 }
309 }
310}
311
312DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
313 const static int kSeed = 1234;
314 SkRandom valueSource(kSeed);
315 SkRandom rand(kSeed << 1);
316 uint8_t buffer[4096];
317 SkDynamicMemoryWStream dynamicMemoryWStream;
318 for (int i = 0; i < 32; ++i) {
319 // Randomize the length of the blocks.
320 size_t size = rand.nextRangeU(1, sizeof(buffer));
321 for (size_t j = 0; j < size; ++j) {
322 buffer[j] = valueSource.nextU() & 0xFF;
323 }
324 dynamicMemoryWStream.write(buffer, size);
325 }
326 SkAutoTDelete<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream());
327 SkAutoTUnref<SkData> expected(SkData::NewUninitialized(asset->getLength()));
328 uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data());
329 valueSource.setSeed(kSeed); // reseed.
330 // We want the exact same same "random" string of numbers to put
331 // in expected. i.e.: don't rely on SkDynamicMemoryStream to work
332 // correctly while we are testing SkDynamicMemoryStream.
333 for (size_t i = 0; i < asset->getLength(); ++i) {
334 expectedPtr[i] = valueSource.nextU() & 0xFF;
335 }
336 stream_peek_test(rep, asset, expected);
337}