blob: 838a8a4d0f1ed285a81be7aa0d12a9a99e373834 [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"
halcanary48305e82015-08-18 13:30:25 -070014#include "SkStreamPriv.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000015#include "Test.h"
reed@android.com5e5adfd2009-03-07 03:39:23 +000016
reed@google.com789c6f22013-02-25 20:24:24 +000017#ifndef SK_BUILD_FOR_WIN
18#include <unistd.h>
19#include <fcntl.h>
20#endif
21
reed@android.com5e5adfd2009-03-07 03:39:23 +000022#define MAX_SIZE (256 * 1024)
23
reed@google.com789c6f22013-02-25 20:24:24 +000024static 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);
bungeman@google.com88682b72013-07-19 13:55:41 +000038 // isAtEnd might not return true until after the first failing read.
39 REPORTER_ASSERT(reporter, stream->isAtEnd());
reed@google.com789c6f22013-02-25 20:24:24 +000040}
41
42static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
tfarinaa8e2e152014-07-28 19:26:58 -070043 SkString path = SkOSPath::Join(tmpDir, "wstream_test");
reed@google.com789c6f22013-02-25 20:24:24 +000044
45 const char s[] = "abcdefghijklmnopqrstuvwxyz";
46
47 {
48 SkFILEWStream writer(path.c_str());
49 if (!writer.isValid()) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +000050 ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
reed@google.com789c6f22013-02-25 20:24:24 +000051 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);
bungeman@google.com6cab1a42013-05-29 13:43:31 +000063
scroggoa1193e42015-01-21 12:09:53 -080064 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
bungeman@google.com6cab1a42013-05-29 13:43:31 +000065 test_loop_stream(reporter, stream2.get(), s, 26, 100);
reed@google.com789c6f22013-02-25 20:24:24 +000066 }
67
reed@google.com789c6f22013-02-25 20:24:24 +000068 {
bungeman@google.com6cab1a42013-05-29 13:43:31 +000069 FILE* file = ::fopen(path.c_str(), "rb");
70 SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
reed@google.com789c6f22013-02-25 20:24:24 +000071 REPORTER_ASSERT(reporter, stream.isValid());
72 test_loop_stream(reporter, &stream, s, 26, 100);
bungeman@google.com6cab1a42013-05-29 13:43:31 +000073
scroggoa1193e42015-01-21 12:09:53 -080074 SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
bungeman@google.com6cab1a42013-05-29 13:43:31 +000075 test_loop_stream(reporter, stream2.get(), s, 26, 100);
reed@google.com789c6f22013-02-25 20:24:24 +000076 }
reed@google.com789c6f22013-02-25 20:24:24 +000077}
78
reed@android.com80e39a72009-04-02 16:59:40 +000079static 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));
reed@android.com5e5adfd2009-03-07 03:39:23 +000085 }
reed@android.com80e39a72009-04-02 16:59:40 +000086 REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
bungeman@google.com88682b72013-07-19 13:55:41 +000087
reed@android.com80e39a72009-04-02 16:59:40 +000088 char* dst = new char[100 * 26 + 1];
89 dst[100*26] = '*';
90 ds.copyTo(dst);
91 REPORTER_ASSERT(reporter, dst[100*26] == '*');
reed@android.com80e39a72009-04-02 16:59:40 +000092 for (i = 0; i < 100; i++) {
93 REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
reed@android.com5e5adfd2009-03-07 03:39:23 +000094 }
reed@google.com70442a62011-06-23 21:48:04 +000095
96 {
scroggoa1193e42015-01-21 12:09:53 -080097 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
bungeman@google.com88682b72013-07-19 13:55:41 +000098 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
scroggoa1193e42015-01-21 12:09:53 -0800102 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
bungeman@google.com88682b72013-07-19 13:55:41 +0000103 test_loop_stream(reporter, stream2.get(), s, 26, 100);
104
scroggoa1193e42015-01-21 12:09:53 -0800105 SkAutoTDelete<SkStreamAsset> stream3(stream->fork());
bungeman@google.com88682b72013-07-19 13:55:41 +0000106 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());
reed@google.com70442a62011-06-23 21:48:04 +0000121 REPORTER_ASSERT(reporter, 100 * 26 == data->size());
122 REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
bungeman@google.com88682b72013-07-19 13:55:41 +0000123 }
124
125 {
126 // Test that this works after a copyToData.
scroggoa1193e42015-01-21 12:09:53 -0800127 SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
bungeman@google.com88682b72013-07-19 13:55:41 +0000128 REPORTER_ASSERT(reporter, ds.getOffset() == 0);
129 test_loop_stream(reporter, stream.get(), s, 26, 100);
130
scroggoa1193e42015-01-21 12:09:53 -0800131 SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
bungeman@google.com88682b72013-07-19 13:55:41 +0000132 test_loop_stream(reporter, stream2.get(), s, 26, 100);
reed@google.com70442a62011-06-23 21:48:04 +0000133 }
reed@android.com80e39a72009-04-02 16:59:40 +0000134 delete[] dst;
reed@google.com789c6f22013-02-25 20:24:24 +0000135
halcanary87f3ba42015-01-20 09:30:20 -0800136 SkString tmpDir = skiatest::GetTmpDir();
scroggo@google.comc76218d2013-06-06 14:59:56 +0000137 if (!tmpDir.isEmpty()) {
138 test_filestreams(reporter, tmpDir.c_str());
reed@google.com789c6f22013-02-25 20:24:24 +0000139 }
reed@android.com5e5adfd2009-03-07 03:39:23 +0000140}
141
reed@google.com19f286b2011-10-18 11:49:52 +0000142static 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 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000151
152
reed@google.com19f286b2011-10-18 11:49:52 +0000153 size_t i;
154 char buffer[sizeof(sizes) * 4];
rmistry@google.comd6176b02012-08-23 18:14:13 +0000155
reed@google.com19f286b2011-10-18 11:49:52 +0000156 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();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000162
reed@google.com19f286b2011-10-18 11:49:52 +0000163 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 SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n);
168 }
169 REPORTER_ASSERT(reporter, sizes[i] == n);
170 }
171}
172
halcanary96fcdcc2015-08-27 07:41:13 -0700173// Test that setting an SkMemoryStream to a nullptr data does not result in a crash when calling
scroggo@google.come4904202013-01-09 22:02:58 +0000174// methods that access fData.
175static void TestDereferencingData(SkMemoryStream* memStream) {
halcanary96fcdcc2015-08-27 07:41:13 -0700176 memStream->read(nullptr, 0);
scroggo@google.come4904202013-01-09 22:02:58 +0000177 memStream->getMemoryBase();
178 SkAutoDataUnref data(memStream->copyToData());
179}
180
181static void TestNullData() {
halcanary96fcdcc2015-08-27 07:41:13 -0700182 SkData* nullData = nullptr;
scroggo@google.come4904202013-01-09 22:02:58 +0000183 SkMemoryStream memStream(nullData);
184 TestDereferencingData(&memStream);
185
186 memStream.setData(nullData);
187 TestDereferencingData(&memStream);
188
189}
190
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000191DEF_TEST(Stream, reporter) {
reed@android.com5e5adfd2009-03-07 03:39:23 +0000192 TestWStream(reporter);
reed@google.com19f286b2011-10-18 11:49:52 +0000193 TestPackedUInt(reporter);
scroggo@google.come4904202013-01-09 22:02:58 +0000194 TestNullData();
reed@android.com5e5adfd2009-03-07 03:39:23 +0000195}
scroggo028a4132015-04-02 13:19:51 -0700196
197/**
198 * Tests peeking and then reading the same amount. The two should provide the
199 * same results.
200 * Returns whether the stream could peek.
201 */
202static bool compare_peek_to_read(skiatest::Reporter* reporter,
203 SkStream* stream, size_t bytesToPeek) {
204 // The rest of our tests won't be very interesting if bytesToPeek is zero.
205 REPORTER_ASSERT(reporter, bytesToPeek > 0);
206 SkAutoMalloc peekStorage(bytesToPeek);
207 SkAutoMalloc readStorage(bytesToPeek);
208 void* peekPtr = peekStorage.get();
209 void* readPtr = peekStorage.get();
210
211 if (!stream->peek(peekPtr, bytesToPeek)) {
212 return false;
213 }
214 const size_t bytesRead = stream->read(readPtr, bytesToPeek);
215
216 // bytesRead should only be less than attempted if the stream is at the
217 // end.
218 REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
219
220 // peek and read should behave the same, except peek returned to the
221 // original position, so they read the same data.
222 REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesRead));
223
224 return true;
225}
226
227static void test_peeking_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
228 size_t peeked = 0;
229 for (size_t i = 1; !stream->isAtEnd(); i++) {
230 const bool couldPeek = compare_peek_to_read(r, stream, i);
231 if (!couldPeek) {
232 REPORTER_ASSERT(r, peeked + i > limit);
233 // No more peeking is supported.
234 break;
235 }
236 peeked += i;
237 }
238}
239
240static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
241 const SkStream& original,
242 size_t bufferSize) {
243 SkStream* dupe = original.duplicate();
halcanary96fcdcc2015-08-27 07:41:13 -0700244 REPORTER_ASSERT(r, dupe != nullptr);
scroggo028a4132015-04-02 13:19:51 -0700245 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
halcanary96fcdcc2015-08-27 07:41:13 -0700246 REPORTER_ASSERT(r, bufferedStream != nullptr);
scroggo028a4132015-04-02 13:19:51 -0700247 test_peeking_stream(r, bufferedStream, bufferSize);
248}
249
stephana195f62d2015-04-09 07:57:54 -0700250// This test uses file system operations that don't work out of the
251// box on iOS. It's likely that we don't need them on iOS. Ignoring for now.
252// TODO(stephana): Re-evaluate if we need this in the future.
253#ifndef SK_BUILD_FOR_IOS
scroggo028a4132015-04-02 13:19:51 -0700254DEF_TEST(StreamPeek, reporter) {
255 // Test a memory stream.
256 const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
257 SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
258 test_peeking_stream(reporter, &memStream, memStream.getLength());
259
260 // Test an arbitrary file stream. file streams do not support peeking.
261 SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
262 REPORTER_ASSERT(reporter, fileStream.isValid());
caryclark30ac4642015-04-14 06:08:04 -0700263 if (!fileStream.isValid()) {
264 return;
265 }
scroggo028a4132015-04-02 13:19:51 -0700266 SkAutoMalloc storage(fileStream.getLength());
267 for (size_t i = 1; i < fileStream.getLength(); i++) {
268 REPORTER_ASSERT(reporter, !fileStream.peek(storage.get(), i));
269 }
270
271 // Now test some FrontBufferedStreams
272 for (size_t i = 1; i < memStream.getLength(); i++) {
273 test_peeking_front_buffered_stream(reporter, memStream, i);
274 }
275}
halcanarye797d0d2015-05-21 08:13:27 -0700276#endif
277
278// Asserts that asset == expected and is peekable.
279static void stream_peek_test(skiatest::Reporter* rep,
280 SkStreamAsset* asset,
281 const SkData* expected) {
282 if (asset->getLength() != expected->size()) {
283 ERRORF(rep, "Unexpected length.");
284 return;
285 }
286 SkRandom rand;
287 uint8_t buffer[4096];
288 const uint8_t* expect = expected->bytes();
289 for (size_t i = 0; i < asset->getLength(); ++i) {
290 uint32_t maxSize =
291 SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i));
292 size_t size = rand.nextRangeU(1, maxSize);
293 SkASSERT(size >= 1);
294 SkASSERT(size <= sizeof(buffer));
295 SkASSERT(size + i <= asset->getLength());
296 if (!asset->peek(buffer, size)) {
297 ERRORF(rep, "Peek Failed!");
298 return;
299 }
300 if (0 != memcmp(buffer, &expect[i], size)) {
301 ERRORF(rep, "Peek returned wrong bytes!");
302 return;
303 }
304 uint8_t value;
305 REPORTER_ASSERT(rep, 1 == asset->read(&value, 1));
306 if (value != expect[i]) {
307 ERRORF(rep, "Read Failed!");
308 return;
309 }
310 }
311}
312
313DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
314 const static int kSeed = 1234;
315 SkRandom valueSource(kSeed);
316 SkRandom rand(kSeed << 1);
317 uint8_t buffer[4096];
318 SkDynamicMemoryWStream dynamicMemoryWStream;
319 for (int i = 0; i < 32; ++i) {
320 // Randomize the length of the blocks.
321 size_t size = rand.nextRangeU(1, sizeof(buffer));
322 for (size_t j = 0; j < size; ++j) {
323 buffer[j] = valueSource.nextU() & 0xFF;
324 }
325 dynamicMemoryWStream.write(buffer, size);
326 }
327 SkAutoTDelete<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream());
328 SkAutoTUnref<SkData> expected(SkData::NewUninitialized(asset->getLength()));
329 uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data());
330 valueSource.setSeed(kSeed); // reseed.
331 // We want the exact same same "random" string of numbers to put
332 // in expected. i.e.: don't rely on SkDynamicMemoryStream to work
333 // correctly while we are testing SkDynamicMemoryStream.
334 for (size_t i = 0; i < asset->getLength(); ++i) {
335 expectedPtr[i] = valueSource.nextU() & 0xFF;
336 }
337 stream_peek_test(rep, asset, expected);
338}
halcanary48305e82015-08-18 13:30:25 -0700339
340namespace {
341class DumbStream : public SkStream {
342public:
343 DumbStream(const uint8_t* data, size_t n)
344 : fData(data), fCount(n), fIdx(0) {}
345 size_t read(void* buffer, size_t size) override {
halcanaryd75e21d2015-08-19 07:22:04 -0700346 size_t copyCount = SkTMin(fCount - fIdx, size);
347 if (copyCount) {
348 memcpy(buffer, &fData[fIdx], copyCount);
349 fIdx += copyCount;
halcanary48305e82015-08-18 13:30:25 -0700350 }
halcanaryd75e21d2015-08-19 07:22:04 -0700351 return copyCount;
halcanary48305e82015-08-18 13:30:25 -0700352 }
353 bool isAtEnd() const override {
halcanaryd75e21d2015-08-19 07:22:04 -0700354 return fCount == fIdx;
halcanary48305e82015-08-18 13:30:25 -0700355 }
356 private:
357 const uint8_t* fData;
358 size_t fCount, fIdx;
359};
360} // namespace
361
362static void stream_copy_test(skiatest::Reporter* reporter,
363 const void* srcData,
364 size_t N,
365 SkStream* stream) {
366 SkDynamicMemoryWStream tgt;
367 if (!SkStreamCopy(&tgt, stream)) {
368 ERRORF(reporter, "SkStreamCopy failed");
369 return;
370 }
371 SkAutoTUnref<SkData> data(tgt.copyToData());
372 tgt.reset();
373 if (data->size() != N) {
374 ERRORF(reporter, "SkStreamCopy incorrect size");
375 return;
376 }
377 if (0 != memcmp(data->data(), srcData, N)) {
378 ERRORF(reporter, "SkStreamCopy bad copy");
379 }
380}
381
382DEF_TEST(StreamCopy, reporter) {
383 SkRandom random(123456);
halcanarycb9241b2015-08-19 06:12:40 -0700384 static const int N = 10000;
385 SkAutoTMalloc<uint8_t> src((size_t)N);
386 for (int j = 0; j < N; ++j) {
halcanary48305e82015-08-18 13:30:25 -0700387 src[j] = random.nextU() & 0xff;
388 }
389 // SkStreamCopy had two code paths; this test both.
halcanarycb9241b2015-08-19 06:12:40 -0700390 DumbStream dumbStream(src.get(), (size_t)N);
halcanary48305e82015-08-18 13:30:25 -0700391 stream_copy_test(reporter, src, N, &dumbStream);
halcanarycb9241b2015-08-19 06:12:40 -0700392 SkMemoryStream smartStream(src.get(), (size_t)N);
halcanary48305e82015-08-18 13:30:25 -0700393 stream_copy_test(reporter, src, N, &smartStream);
halcanary48305e82015-08-18 13:30:25 -0700394}
halcanary209c1622015-09-28 07:29:39 -0700395
396DEF_TEST(StreamEmptyStreamMemoryBase, r) {
397 SkDynamicMemoryWStream tmp;
398 SkAutoTDelete<SkStreamAsset> asset(tmp.detachAsStream());
399 REPORTER_ASSERT(r, nullptr == asset->getMemoryBase());
400}
401