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 "SkData.h"
9#include "SkOSFile.h"
10#include "SkRandom.h"
11#include "SkStream.h"
12#include "Test.h"
13
14#ifndef SK_BUILD_FOR_WIN
15#include <unistd.h>
16#include <fcntl.h>
17#endif
18
19#define MAX_SIZE    (256 * 1024)
20
21static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream,
22                             const void* src, size_t len, int repeat) {
23    SkAutoSMalloc<256> storage(len);
24    void* tmp = storage.get();
25
26    for (int i = 0; i < repeat; ++i) {
27        size_t bytes = stream->read(tmp, len);
28        REPORTER_ASSERT(reporter, bytes == len);
29        REPORTER_ASSERT(reporter, !memcmp(tmp, src, len));
30    }
31
32    // expect EOF
33    size_t bytes = stream->read(tmp, 1);
34    REPORTER_ASSERT(reporter, 0 == bytes);
35    // isAtEnd might not return true until after the first failing read.
36    REPORTER_ASSERT(reporter, stream->isAtEnd());
37}
38
39static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
40    SkString path = SkOSPath::Join(tmpDir, "wstream_test");
41
42    const char s[] = "abcdefghijklmnopqrstuvwxyz";
43
44    {
45        SkFILEWStream writer(path.c_str());
46        if (!writer.isValid()) {
47            ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
48            return;
49        }
50
51        for (int i = 0; i < 100; ++i) {
52            writer.write(s, 26);
53        }
54    }
55
56    {
57        SkFILEStream stream(path.c_str());
58        REPORTER_ASSERT(reporter, stream.isValid());
59        test_loop_stream(reporter, &stream, s, 26, 100);
60
61        SkAutoTUnref<SkStreamAsset> stream2(stream.duplicate());
62        test_loop_stream(reporter, stream2.get(), s, 26, 100);
63    }
64
65    {
66        FILE* file = ::fopen(path.c_str(), "rb");
67        SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
68        REPORTER_ASSERT(reporter, stream.isValid());
69        test_loop_stream(reporter, &stream, s, 26, 100);
70
71        SkAutoTUnref<SkStreamAsset> stream2(stream.duplicate());
72        test_loop_stream(reporter, stream2.get(), s, 26, 100);
73    }
74}
75
76static void TestWStream(skiatest::Reporter* reporter) {
77    SkDynamicMemoryWStream  ds;
78    const char s[] = "abcdefghijklmnopqrstuvwxyz";
79    int i;
80    for (i = 0; i < 100; i++) {
81        REPORTER_ASSERT(reporter, ds.write(s, 26));
82    }
83    REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
84
85    char* dst = new char[100 * 26 + 1];
86    dst[100*26] = '*';
87    ds.copyTo(dst);
88    REPORTER_ASSERT(reporter, dst[100*26] == '*');
89    for (i = 0; i < 100; i++) {
90        REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
91    }
92
93    {
94        SkAutoTUnref<SkStreamAsset> stream(ds.detachAsStream());
95        REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
96        REPORTER_ASSERT(reporter, ds.getOffset() == 0);
97        test_loop_stream(reporter, stream.get(), s, 26, 100);
98
99        SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate());
100        test_loop_stream(reporter, stream2.get(), s, 26, 100);
101
102        SkAutoTUnref<SkStreamAsset> stream3(stream->fork());
103        REPORTER_ASSERT(reporter, stream3->isAtEnd());
104        char tmp;
105        size_t bytes = stream->read(&tmp, 1);
106        REPORTER_ASSERT(reporter, 0 == bytes);
107        stream3->rewind();
108        test_loop_stream(reporter, stream3.get(), s, 26, 100);
109    }
110
111    for (i = 0; i < 100; i++) {
112        REPORTER_ASSERT(reporter, ds.write(s, 26));
113    }
114    REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
115
116    {
117        SkAutoTUnref<SkData> data(ds.copyToData());
118        REPORTER_ASSERT(reporter, 100 * 26 == data->size());
119        REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
120    }
121
122    {
123        // Test that this works after a copyToData.
124        SkAutoTUnref<SkStreamAsset> stream(ds.detachAsStream());
125        REPORTER_ASSERT(reporter, ds.getOffset() == 0);
126        test_loop_stream(reporter, stream.get(), s, 26, 100);
127
128        SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate());
129        test_loop_stream(reporter, stream2.get(), s, 26, 100);
130    }
131    delete[] dst;
132
133    SkString tmpDir = skiatest::Test::GetTmpDir();
134    if (!tmpDir.isEmpty()) {
135        test_filestreams(reporter, tmpDir.c_str());
136    }
137}
138
139static void TestPackedUInt(skiatest::Reporter* reporter) {
140    // we know that packeduint tries to write 1, 2 or 4 bytes for the length,
141    // so we test values around each of those transitions (and a few others)
142    const size_t sizes[] = {
143        0, 1, 2, 0xFC, 0xFD, 0xFE, 0xFF, 0x100, 0x101, 32767, 32768, 32769,
144        0xFFFD, 0xFFFE, 0xFFFF, 0x10000, 0x10001,
145        0xFFFFFD, 0xFFFFFE, 0xFFFFFF, 0x1000000, 0x1000001,
146        0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF
147    };
148
149
150    size_t i;
151    char buffer[sizeof(sizes) * 4];
152
153    SkMemoryWStream wstream(buffer, sizeof(buffer));
154    for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
155        bool success = wstream.writePackedUInt(sizes[i]);
156        REPORTER_ASSERT(reporter, success);
157    }
158    wstream.flush();
159
160    SkMemoryStream rstream(buffer, sizeof(buffer));
161    for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
162        size_t n = rstream.readPackedUInt();
163        if (sizes[i] != n) {
164            SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n);
165        }
166        REPORTER_ASSERT(reporter, sizes[i] == n);
167    }
168}
169
170// Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling
171// methods that access fData.
172static void TestDereferencingData(SkMemoryStream* memStream) {
173    memStream->read(NULL, 0);
174    memStream->getMemoryBase();
175    SkAutoDataUnref data(memStream->copyToData());
176}
177
178static void TestNullData() {
179    SkData* nullData = NULL;
180    SkMemoryStream memStream(nullData);
181    TestDereferencingData(&memStream);
182
183    memStream.setData(nullData);
184    TestDereferencingData(&memStream);
185
186}
187
188DEF_TEST(Stream, reporter) {
189    TestWStream(reporter);
190    TestPackedUInt(reporter);
191    TestNullData();
192}
193