• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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