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