• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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