1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6 #include <limits>
7 #include <memory>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/macros.h"
16 #include "base/run_loop.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/test/scoped_task_environment.h"
19 #include "mojo/public/cpp/system/data_pipe.h"
20 #include "mojo/public/cpp/system/file_data_pipe_producer.h"
21 #include "mojo/public/cpp/system/simple_watcher.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 namespace mojo {
25 namespace {
26
27 // Test helper. Reads a consumer handle, accumulating data into a string. Reads
28 // until encountering an error (e.g. peer closure), at which point it invokes an
29 // async callback.
30 class DataPipeReader {
31 public:
DataPipeReader(ScopedDataPipeConsumerHandle consumer_handle,size_t read_size,base::OnceClosure on_read_done)32 explicit DataPipeReader(ScopedDataPipeConsumerHandle consumer_handle,
33 size_t read_size,
34 base::OnceClosure on_read_done)
35 : consumer_handle_(std::move(consumer_handle)),
36 read_size_(read_size),
37 on_read_done_(std::move(on_read_done)),
38 watcher_(FROM_HERE,
39 SimpleWatcher::ArmingPolicy::AUTOMATIC,
40 base::SequencedTaskRunnerHandle::Get()) {
41 watcher_.Watch(
42 consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
43 MOJO_WATCH_CONDITION_SATISFIED,
44 base::Bind(&DataPipeReader::OnDataAvailable, base::Unretained(this)));
45 }
46 ~DataPipeReader() = default;
47
data() const48 const std::string& data() const { return data_; }
49
50 private:
OnDataAvailable(MojoResult result,const HandleSignalsState & state)51 void OnDataAvailable(MojoResult result, const HandleSignalsState& state) {
52 if (result == MOJO_RESULT_OK) {
53 uint32_t size = static_cast<uint32_t>(read_size_);
54 std::vector<char> buffer(size, 0);
55 MojoResult read_result;
56 do {
57 read_result = consumer_handle_->ReadData(buffer.data(), &size,
58 MOJO_READ_DATA_FLAG_NONE);
59 if (read_result == MOJO_RESULT_OK) {
60 std::copy(buffer.begin(), buffer.begin() + size,
61 std::back_inserter(data_));
62 }
63 } while (read_result == MOJO_RESULT_OK);
64
65 if (read_result == MOJO_RESULT_SHOULD_WAIT)
66 return;
67 }
68
69 if (result != MOJO_RESULT_CANCELLED)
70 watcher_.Cancel();
71
72 std::move(on_read_done_).Run();
73 }
74
75 ScopedDataPipeConsumerHandle consumer_handle_;
76 const size_t read_size_;
77 base::OnceClosure on_read_done_;
78 SimpleWatcher watcher_;
79 std::string data_;
80
81 DISALLOW_COPY_AND_ASSIGN(DataPipeReader);
82 };
83
84 class FileDataPipeProducerTest : public testing::Test {
85 public:
FileDataPipeProducerTest()86 FileDataPipeProducerTest() { CHECK(temp_dir_.CreateUniqueTempDir()); }
87
88 ~FileDataPipeProducerTest() override = default;
89
90 protected:
CreateTempFileWithContents(const std::string & contents)91 base::FilePath CreateTempFileWithContents(const std::string& contents) {
92 base::FilePath temp_file_path = temp_dir_.GetPath().AppendASCII(
93 base::StringPrintf("tmp%d", tmp_file_id_++));
94 base::File temp_file(temp_file_path,
95 base::File::FLAG_CREATE | base::File::FLAG_WRITE);
96 int bytes_written = temp_file.WriteAtCurrentPos(
97 contents.data(), static_cast<int>(contents.size()));
98 CHECK_EQ(static_cast<int>(contents.size()), bytes_written);
99 return temp_file_path;
100 }
101
WriteFromFileThenCloseWriter(std::unique_ptr<FileDataPipeProducer> producer,base::File file)102 static void WriteFromFileThenCloseWriter(
103 std::unique_ptr<FileDataPipeProducer> producer,
104 base::File file) {
105 FileDataPipeProducer* raw_producer = producer.get();
106 raw_producer->WriteFromFile(
107 std::move(file),
108 base::BindOnce([](std::unique_ptr<FileDataPipeProducer> producer,
109 MojoResult result) {},
110 std::move(producer)));
111 }
112
WriteFromFileThenCloseWriter(std::unique_ptr<FileDataPipeProducer> producer,base::File file,size_t max_bytes)113 static void WriteFromFileThenCloseWriter(
114 std::unique_ptr<FileDataPipeProducer> producer,
115 base::File file,
116 size_t max_bytes) {
117 FileDataPipeProducer* raw_producer = producer.get();
118 raw_producer->WriteFromFile(
119 std::move(file), max_bytes,
120 base::BindOnce([](std::unique_ptr<FileDataPipeProducer> producer,
121 MojoResult result) {},
122 std::move(producer)));
123 }
124
WriteFromPathThenCloseWriter(std::unique_ptr<FileDataPipeProducer> producer,const base::FilePath & path)125 static void WriteFromPathThenCloseWriter(
126 std::unique_ptr<FileDataPipeProducer> producer,
127 const base::FilePath& path) {
128 FileDataPipeProducer* raw_producer = producer.get();
129 raw_producer->WriteFromPath(
130 path, base::BindOnce([](std::unique_ptr<FileDataPipeProducer> producer,
131 MojoResult result) {},
132 std::move(producer)));
133 }
134
135 private:
136 base::test::ScopedTaskEnvironment task_environment_;
137 base::ScopedTempDir temp_dir_;
138 int tmp_file_id_ = 0;
139
140 DISALLOW_COPY_AND_ASSIGN(FileDataPipeProducerTest);
141 };
142
143 struct DataPipeObserverData {
144 int num_read_errors = 0;
145 size_t bytes_read = 0;
146 int done_called = 0;
147 };
148
149 class TestObserver : public FileDataPipeProducer::Observer {
150 public:
TestObserver(DataPipeObserverData * observer_data)151 explicit TestObserver(DataPipeObserverData* observer_data)
152 : observer_data_(observer_data) {}
153
OnBytesRead(const void * data,size_t num_bytes_read,base::File::Error read_result)154 void OnBytesRead(const void* data,
155 size_t num_bytes_read,
156 base::File::Error read_result) override {
157 base::AutoLock auto_lock(lock_);
158 if (read_result == base::File::FILE_OK)
159 observer_data_->bytes_read += num_bytes_read;
160 else
161 observer_data_->num_read_errors++;
162 }
163
OnDoneReading()164 void OnDoneReading() override {
165 base::AutoLock auto_lock(lock_);
166 observer_data_->done_called++;
167 }
168
169 private:
170 DataPipeObserverData* observer_data_;
171 // Observer may be called on any sequence.
172 base::Lock lock_;
173
174 DISALLOW_COPY_AND_ASSIGN(TestObserver);
175 };
176
TEST_F(FileDataPipeProducerTest,WriteFromFile)177 TEST_F(FileDataPipeProducerTest, WriteFromFile) {
178 const std::string kTestStringFragment = "Hello, world!";
179 constexpr size_t kNumRepetitions = 1000;
180 std::string test_string;
181 for (size_t i = 0; i < kNumRepetitions; ++i)
182 test_string += kTestStringFragment;
183
184 base::FilePath path = CreateTempFileWithContents(test_string);
185
186 base::RunLoop loop;
187 DataPipe pipe(16);
188 DataPipeReader reader(std::move(pipe.consumer_handle), 16,
189 loop.QuitClosure());
190
191 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
192 DataPipeObserverData observer_data;
193 auto observer = std::make_unique<TestObserver>(&observer_data);
194 WriteFromFileThenCloseWriter(
195 std::make_unique<FileDataPipeProducer>(std::move(pipe.producer_handle),
196 std::move(observer)),
197 std::move(file));
198 loop.Run();
199
200 EXPECT_EQ(test_string, reader.data());
201 EXPECT_EQ(0, observer_data.num_read_errors);
202 EXPECT_EQ(test_string.size(), observer_data.bytes_read);
203 EXPECT_EQ(1, observer_data.done_called);
204 }
205
TEST_F(FileDataPipeProducerTest,WriteFromFilePartial)206 TEST_F(FileDataPipeProducerTest, WriteFromFilePartial) {
207 const std::string kTestString = "abcdefghijklmnopqrstuvwxyz";
208 base::FilePath path = CreateTempFileWithContents(kTestString);
209 constexpr size_t kBytesToWrite = 7;
210
211 base::RunLoop loop;
212 DataPipe pipe(static_cast<uint32_t>(kTestString.size()));
213 DataPipeReader reader(std::move(pipe.consumer_handle), kTestString.size(),
214 loop.QuitClosure());
215
216 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
217 DataPipeObserverData observer_data;
218 auto observer = std::make_unique<TestObserver>(&observer_data);
219 WriteFromFileThenCloseWriter(
220 std::make_unique<FileDataPipeProducer>(std::move(pipe.producer_handle),
221 std::move(observer)),
222 std::move(file), kBytesToWrite);
223 loop.Run();
224
225 EXPECT_EQ(kTestString.substr(0, kBytesToWrite), reader.data());
226 EXPECT_EQ(0, observer_data.num_read_errors);
227 EXPECT_EQ(kBytesToWrite, observer_data.bytes_read);
228 EXPECT_EQ(1, observer_data.done_called);
229 }
230
TEST_F(FileDataPipeProducerTest,WriteFromInvalidFile)231 TEST_F(FileDataPipeProducerTest, WriteFromInvalidFile) {
232 base::FilePath path(FILE_PATH_LITERAL("<nonexistent-file>"));
233 constexpr size_t kBytesToWrite = 7;
234
235 base::RunLoop loop;
236 DataPipe pipe(kBytesToWrite);
237 DataPipeObserverData observer_data;
238 auto observer = std::make_unique<TestObserver>(&observer_data);
239 DataPipeReader reader(std::move(pipe.consumer_handle), kBytesToWrite,
240 loop.QuitClosure());
241
242 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
243 WriteFromFileThenCloseWriter(
244 std::make_unique<FileDataPipeProducer>(std::move(pipe.producer_handle),
245 std::move(observer)),
246 std::move(file), kBytesToWrite);
247 loop.Run();
248
249 EXPECT_EQ(0UL, reader.data().size());
250 EXPECT_EQ(0, observer_data.num_read_errors);
251 EXPECT_EQ(0UL, observer_data.bytes_read);
252 EXPECT_EQ(1, observer_data.done_called);
253 }
254
TEST_F(FileDataPipeProducerTest,WriteFromPath)255 TEST_F(FileDataPipeProducerTest, WriteFromPath) {
256 const std::string kTestStringFragment = "Hello, world!";
257 constexpr size_t kNumRepetitions = 1000;
258 std::string test_string;
259 for (size_t i = 0; i < kNumRepetitions; ++i)
260 test_string += kTestStringFragment;
261
262 base::FilePath path = CreateTempFileWithContents(test_string);
263
264 base::RunLoop loop;
265 DataPipe pipe(16);
266 DataPipeReader reader(std::move(pipe.consumer_handle), 16,
267 loop.QuitClosure());
268
269 DataPipeObserverData observer_data;
270 auto observer = std::make_unique<TestObserver>(&observer_data);
271 WriteFromPathThenCloseWriter(
272 std::make_unique<FileDataPipeProducer>(std::move(pipe.producer_handle),
273 std::move(observer)),
274 path);
275 loop.Run();
276
277 EXPECT_EQ(test_string, reader.data());
278 EXPECT_EQ(0, observer_data.num_read_errors);
279 EXPECT_EQ(test_string.size(), observer_data.bytes_read);
280 EXPECT_EQ(1, observer_data.done_called);
281 }
282
TEST_F(FileDataPipeProducerTest,TinyFile)283 TEST_F(FileDataPipeProducerTest, TinyFile) {
284 const std::string kTestString = ".";
285 base::FilePath path = CreateTempFileWithContents(kTestString);
286 base::RunLoop loop;
287 DataPipe pipe(16);
288 DataPipeReader reader(std::move(pipe.consumer_handle), 16,
289 loop.QuitClosure());
290 DataPipeObserverData observer_data;
291 auto observer = std::make_unique<TestObserver>(&observer_data);
292 WriteFromPathThenCloseWriter(
293 std::make_unique<FileDataPipeProducer>(std::move(pipe.producer_handle),
294 std::move(observer)),
295 path);
296 loop.Run();
297
298 EXPECT_EQ(kTestString, reader.data());
299 EXPECT_EQ(0, observer_data.num_read_errors);
300 EXPECT_EQ(kTestString.size(), observer_data.bytes_read);
301 EXPECT_EQ(1, observer_data.done_called);
302 }
303
TEST_F(FileDataPipeProducerTest,HugeFile)304 TEST_F(FileDataPipeProducerTest, HugeFile) {
305 // We want a file size that is many times larger than the data pipe size.
306 // 63MB is large enough, while being small enough to fit in a typical tmpfs.
307 constexpr size_t kHugeFileSize = 63 * 1024 * 1024;
308 constexpr uint32_t kDataPipeSize = 512 * 1024;
309
310 std::string test_string(kHugeFileSize, 'a');
311 for (size_t i = 0; i + 3 < test_string.size(); i += 4) {
312 test_string[i + 1] = 'b';
313 test_string[i + 2] = 'c';
314 test_string[i + 3] = 'd';
315 }
316 base::FilePath path = CreateTempFileWithContents(test_string);
317
318 base::RunLoop loop;
319 DataPipe pipe(kDataPipeSize);
320 DataPipeReader reader(std::move(pipe.consumer_handle), kDataPipeSize,
321 loop.QuitClosure());
322
323 DataPipeObserverData observer_data;
324 auto observer = std::make_unique<TestObserver>(&observer_data);
325 WriteFromPathThenCloseWriter(
326 std::make_unique<FileDataPipeProducer>(std::move(pipe.producer_handle),
327 std::move(observer)),
328 path);
329 loop.Run();
330
331 EXPECT_EQ(test_string, reader.data());
332 EXPECT_EQ(0, observer_data.num_read_errors);
333 EXPECT_EQ(kHugeFileSize, observer_data.bytes_read);
334 EXPECT_EQ(1, observer_data.done_called);
335 }
336
337 } // namespace
338 } // namespace mojo
339