1 /*
2  * Copyright 2016 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 "include/core/SkData.h"
9 #include "include/core/SkStream.h"
10 #include "src/codec/SkStreamBuffer.h"
11 #include "src/utils/SkOSPath.h"
12 
13 #include "tests/FakeStreams.h"
14 #include "tests/Test.h"
15 
16 static const char* gText = "Four score and seven years ago";
17 
test_get_data_at_position(skiatest::Reporter * r,SkStreamBuffer * buffer,size_t position,size_t length)18 static void test_get_data_at_position(skiatest::Reporter* r, SkStreamBuffer* buffer, size_t position,
19                                     size_t length) {
20     sk_sp<SkData> data = buffer->getDataAtPosition(position, length);
21     REPORTER_ASSERT(r, data);
22     if (data) {
23         REPORTER_ASSERT(r, !memcmp(data->data(), gText + position, length));
24     }
25 }
26 
27 // Test buffering from the beginning, by different amounts.
test_buffer_from_beginning(skiatest::Reporter * r,std::unique_ptr<SkStream> stream,size_t length)28 static void test_buffer_from_beginning(skiatest::Reporter* r, std::unique_ptr<SkStream> stream,
29                                        size_t length) {
30     if (!stream) {
31         return;
32     }
33     SkStreamBuffer buffer(std::move(stream));
34 
35     // Buffer an arbitrary amount:
36     size_t buffered = length / 2;
37     REPORTER_ASSERT(r, buffer.buffer(buffered));
38     REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, buffered));
39 
40     // Buffering less is free:
41     REPORTER_ASSERT(r, buffer.buffer(buffered / 2));
42 
43     // Buffer more should succeed:
44     REPORTER_ASSERT(r, buffer.buffer(length));
45     REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, length));
46 }
47 
48 // Test flushing the stream as we read.
test_flushing(skiatest::Reporter * r,std::unique_ptr<SkStream> stream,size_t length,bool getDataAtPosition)49 static void test_flushing(skiatest::Reporter* r, std::unique_ptr<SkStream> stream, size_t length,
50                           bool getDataAtPosition) {
51     if (!stream) {
52         return;
53     }
54     SkStreamBuffer buffer(std::move(stream));
55     const size_t step = 5;
56     for (size_t position = 0; position + step <= length; position += step) {
57         REPORTER_ASSERT(r, buffer.buffer(step));
58         REPORTER_ASSERT(r, buffer.markPosition() == position);
59 
60         if (!getDataAtPosition) {
61             REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + position, step));
62         }
63         buffer.flush();
64     }
65 
66     REPORTER_ASSERT(r, !buffer.buffer(step));
67 
68     if (getDataAtPosition) {
69         for (size_t position = 0; position + step <= length; position += step) {
70             test_get_data_at_position(r, &buffer, position, step);
71         }
72     }
73 }
74 
DEF_TEST(StreamBuffer,r)75 DEF_TEST(StreamBuffer, r) {
76     const size_t size = strlen(gText);
77     sk_sp<SkData> data(SkData::MakeWithoutCopy(gText, size));
78 
79     SkString tmpDir = skiatest::GetTmpDir();
80     const char* subdir = "streamBuffer.txt";
81     SkString path;
82 
83     if (!tmpDir.isEmpty()) {
84         path = SkOSPath::Join(tmpDir.c_str(), subdir);
85         SkFILEWStream writer(path.c_str());
86         if (!writer.isValid()) {
87             ERRORF(r, "unable to write to '%s'\n", path.c_str());
88             return;
89         }
90         writer.write(gText, size);
91     }
92 
93     struct Factory {
94         std::function<std::unique_ptr<SkStream>()>  createStream;
95         bool                                        skipIfNoTmpDir;
96     };
97 
98     Factory factories[] = {
99         { [&data]() { return std::make_unique<SkMemoryStream>(data); },       false  },
100         { [&data]() { return std::make_unique<NotAssetMemStream>(data); },    false  },
101         { [&path]() { return path.isEmpty()
102                              ? nullptr
103                              : std::make_unique<SkFILEStream>(path.c_str()); }, true },
104     };
105 
106     for (const Factory& f : factories) {
107         if (tmpDir.isEmpty() && f.skipIfNoTmpDir) {
108             continue;
109         }
110         test_buffer_from_beginning(r, f.createStream(), size);
111         test_flushing(r, f.createStream(), size, false);
112         test_flushing(r, f.createStream(), size, true);
113     }
114 
115     // Stream that will receive more data. Will be owned by the SkStreamBuffer.
116     auto halting = std::make_unique<HaltingStream>(data, 6);
117     HaltingStream* peekHalting = halting.get();
118     SkStreamBuffer buffer(std::move(halting));
119 
120     // Can only buffer less than what's available (6).
121     REPORTER_ASSERT(r, !buffer.buffer(7));
122     REPORTER_ASSERT(r, buffer.buffer(5));
123     REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 5));
124 
125     // Add some more data. We can buffer and read all of it.
126     peekHalting->addNewData(8);
127     REPORTER_ASSERT(r, buffer.buffer(14));
128     REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 14));
129 
130     // Flush the buffer, which moves the position.
131     buffer.flush();
132 
133     // Add some data, and try to read more. Can only read what is
134     // available.
135     peekHalting->addNewData(9);
136     REPORTER_ASSERT(r, !buffer.buffer(13));
137     peekHalting->addNewData(4);
138     REPORTER_ASSERT(r, buffer.buffer(13));
139 
140     // Do not call get on this data. We'll come back to this data after adding
141     // more.
142     buffer.flush();
143     const size_t remaining = size - 27;
144     REPORTER_ASSERT(r, remaining > 0);
145     peekHalting->addNewData(remaining);
146     REPORTER_ASSERT(r, buffer.buffer(remaining));
147     REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + 27, remaining));
148 
149     // Now go back to the data we skipped.
150     test_get_data_at_position(r, &buffer, 14, 13);
151 }
152