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 23static 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 41static 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 78static 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 141static 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. 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 190DEF_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 */ 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 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 253DEF_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