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