1// Copyright (c) 2011 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 <deque>
6#include <limits>
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/file_util.h"
12#include "base/files/scoped_temp_dir.h"
13#include "base/memory/weak_ptr.h"
14#include "base/message_loop/message_loop.h"
15#include "base/platform_file.h"
16#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
17#include "content/renderer/pepper/ppapi_unittest.h"
18#include "content/renderer/pepper/quota_file_io.h"
19
20using base::MessageLoopProxy;
21using base::PlatformFile;
22using base::PlatformFileError;
23
24namespace content {
25
26namespace {
27class QuotaMockDelegate : public QuotaFileIO::Delegate {
28 public:
29  typedef QuotaFileIO::Delegate::AvailableSpaceCallback Callback;
30
31  QuotaMockDelegate()
32      : available_space_(0),
33        will_update_count_(0),
34        file_thread_(MessageLoopProxy::current()),
35        weak_factory_(this) {
36  }
37  virtual ~QuotaMockDelegate() {}
38
39  virtual void QueryAvailableSpace(
40      const GURL& origin,
41      quota::StorageType type,
42      const Callback& callback) OVERRIDE {
43    DCHECK_EQ(false, callback.is_null());
44    MessageLoopProxy::current()->PostTask(
45        FROM_HERE, base::Bind(
46            &QuotaMockDelegate::RunAvailableSpaceCallback,
47            weak_factory_.GetWeakPtr(), callback));
48  }
49
50  virtual void WillUpdateFile(const GURL& file_path) OVERRIDE {
51    file_path_ = file_path;
52    ++will_update_count_;
53  }
54
55  virtual void DidUpdateFile(const GURL& file_path, int64_t delta) OVERRIDE {
56    ASSERT_EQ(file_path_, file_path);
57    ASSERT_GT(will_update_count_, 0);
58    --will_update_count_;
59    available_space_ -= delta;
60  }
61
62  virtual scoped_refptr<base::MessageLoopProxy>
63        GetFileThreadMessageLoopProxy() OVERRIDE {
64    return file_thread_;
65  }
66
67  void set_available_space(int64 available) { available_space_ = available; }
68  int64_t available_space() const { return available_space_; }
69
70 private:
71  void RunAvailableSpaceCallback(const Callback& callback) {
72    callback.Run(available_space_);
73  }
74
75  int64_t available_space_;
76  int will_update_count_;
77  GURL file_path_;
78  scoped_refptr<MessageLoopProxy> file_thread_;
79  base::WeakPtrFactory<QuotaMockDelegate> weak_factory_;
80};
81}  // namespace
82
83class QuotaFileIOTest : public PpapiUnittest {
84 public:
85  QuotaFileIOTest()
86      : delegate_(NULL),
87        weak_factory_(this) {}
88
89  virtual void SetUp() OVERRIDE {
90    PpapiUnittest::SetUp();
91    ASSERT_TRUE(dir_.CreateUniqueTempDir());
92    base::FilePath path;
93    ASSERT_TRUE(file_util::CreateTemporaryFileInDir(dir_.path(), &path));
94    int file_flags = base::PLATFORM_FILE_OPEN |
95                     base::PLATFORM_FILE_READ |
96                     base::PLATFORM_FILE_WRITE |
97                     base::PLATFORM_FILE_WRITE_ATTRIBUTES;
98    bool created = false;
99    file_ = base::kInvalidPlatformFileValue;
100    PlatformFileError error = base::PLATFORM_FILE_OK;
101    file_ = base::CreatePlatformFile(path, file_flags, &created, &error);
102    ASSERT_EQ(base::PLATFORM_FILE_OK, error);
103    ASSERT_NE(base::kInvalidPlatformFileValue, file_);
104    ASSERT_FALSE(created);
105    delegate_ = new QuotaMockDelegate;  // Owned by QuotaFileIO.
106    quota_file_io_.reset(new QuotaFileIO(
107        delegate_, file_, GURL(), PP_FILESYSTEMTYPE_LOCALTEMPORARY));
108  }
109
110  virtual void TearDown() OVERRIDE {
111    quota_file_io_.reset();
112    if (file_ != base::kInvalidPlatformFileValue)
113      base::ClosePlatformFile(file_);
114    PpapiUnittest::TearDown();
115  }
116
117 protected:
118  void WriteTestBody(bool will_operation) {
119    // Attempt to write zero bytes.
120    EXPECT_FALSE(quota_file_io_->Write(
121        0, "data", 0,
122        base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr())));
123    // Attempt to write negative number of bytes.
124    EXPECT_FALSE(quota_file_io_->Write(
125        0, "data", std::numeric_limits<int32_t>::min(),
126        base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr())));
127
128    delegate()->set_available_space(100);
129    std::string read_buffer;
130
131    // Write 8 bytes at offset 0 (-> length=8).
132    std::string data("12345678");
133    Write(0, data, will_operation);
134    base::MessageLoop::current()->RunUntilIdle();
135    ASSERT_EQ(1U, num_results());
136    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
137    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
138    EXPECT_EQ(100 - 8, delegate()->available_space());
139    reset_results();
140
141    if (will_operation) {
142      // WillWrite doesn't actually write.
143      EXPECT_EQ(0, GetPlatformFileSize());
144      // Adjust the actual file size to 'fake' write to proceed testing.
145      SetPlatformFileSize(8);
146    } else {
147      EXPECT_EQ(8, GetPlatformFileSize());
148      ReadPlatformFile(&read_buffer);
149      EXPECT_EQ(data, read_buffer);
150    }
151
152    // Write 5 bytes at offset 5 (-> length=10).
153    data = "55555";
154    Write(5, data, will_operation);
155    base::MessageLoop::current()->RunUntilIdle();
156    ASSERT_EQ(1U, num_results());
157    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
158    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
159    EXPECT_EQ(100 - 10, delegate()->available_space());
160    reset_results();
161
162    if (will_operation) {
163      EXPECT_EQ(8, GetPlatformFileSize());
164      SetPlatformFileSize(10);
165    } else {
166      EXPECT_EQ(10, GetPlatformFileSize());
167      ReadPlatformFile(&read_buffer);
168      EXPECT_EQ("1234555555", read_buffer);
169    }
170
171    // Write 7 bytes at offset 8 (-> length=15).
172    data = "9012345";
173    Write(8, data, will_operation);
174    base::MessageLoop::current()->RunUntilIdle();
175    ASSERT_EQ(1U, num_results());
176    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
177    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
178    EXPECT_EQ(100 - 15, delegate()->available_space());
179    reset_results();
180
181    if (will_operation) {
182      EXPECT_EQ(10, GetPlatformFileSize());
183      SetPlatformFileSize(15);
184    } else {
185      EXPECT_EQ(15, GetPlatformFileSize());
186      ReadPlatformFile(&read_buffer);
187      EXPECT_EQ("123455559012345", read_buffer);
188    }
189
190    // Write 2 bytes at offset 2 (-> length=15).
191    data = "33";
192    Write(2, data, will_operation);
193    base::MessageLoop::current()->RunUntilIdle();
194    ASSERT_EQ(1U, num_results());
195    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
196    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
197    EXPECT_EQ(100 - 15, delegate()->available_space());
198    reset_results();
199
200    if (will_operation) {
201      EXPECT_EQ(15, GetPlatformFileSize());
202    } else {
203      EXPECT_EQ(15, GetPlatformFileSize());
204      ReadPlatformFile(&read_buffer);
205      EXPECT_EQ("123355559012345", read_buffer);
206    }
207
208    // Write 4 bytes at offset 20 (-> length=24).
209    data = "XXXX";
210    Write(20, data, will_operation);
211    base::MessageLoop::current()->RunUntilIdle();
212    ASSERT_EQ(1U, num_results());
213    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
214    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
215    EXPECT_EQ(100 - 24, delegate()->available_space());
216    reset_results();
217
218    if (will_operation) {
219      EXPECT_EQ(15, GetPlatformFileSize());
220      SetPlatformFileSize(24);
221    } else {
222      EXPECT_EQ(24, GetPlatformFileSize());
223      ReadPlatformFile(&read_buffer);
224      EXPECT_EQ(std::string("123355559012345\0\0\0\0\0XXXX", 24), read_buffer);
225    }
226
227    delegate()->set_available_space(5);
228
229    // Quota error case.  Write 7 bytes at offset 23 (-> length is unchanged)
230    data = "ABCDEFG";
231    Write(23, data, will_operation);
232    base::MessageLoop::current()->RunUntilIdle();
233    ASSERT_EQ(1U, num_results());
234    EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front());
235    EXPECT_EQ(5, delegate()->available_space());
236    reset_results();
237
238    // Overlapping write.  Write 6 bytes at offset 2 (-> length is unchanged)
239    data = "ABCDEF";
240    Write(2, data, will_operation);
241    base::MessageLoop::current()->RunUntilIdle();
242    ASSERT_EQ(1U, num_results());
243    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
244    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
245    EXPECT_EQ(5, delegate()->available_space());
246    reset_results();
247
248    // Overlapping + extending the file size, but within the quota.
249    // Write 6 bytes at offset 23 (-> length=29).
250    Write(23, data, will_operation);
251    base::MessageLoop::current()->RunUntilIdle();
252    ASSERT_EQ(1U, num_results());
253    EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front());
254    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
255    EXPECT_EQ(0, delegate()->available_space());
256    reset_results();
257
258    if (!will_operation) {
259      EXPECT_EQ(29, GetPlatformFileSize());
260      ReadPlatformFile(&read_buffer);
261      EXPECT_EQ(std::string("12ABCDEF9012345\0\0\0\0\0XXXABCDEF", 29),
262                read_buffer);
263    }
264  }
265
266  void SetLengthTestBody(bool will_operation) {
267    delegate()->set_available_space(100);
268
269    SetLength(0, will_operation);
270    base::MessageLoop::current()->RunUntilIdle();
271    ASSERT_EQ(1U, num_results());
272    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
273    EXPECT_EQ(0, GetPlatformFileSize());
274    EXPECT_EQ(100, delegate()->available_space());
275    reset_results();
276
277    SetLength(8, will_operation);
278    base::MessageLoop::current()->RunUntilIdle();
279    ASSERT_EQ(1U, num_results());
280    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
281    EXPECT_EQ(100 - 8, delegate()->available_space());
282    reset_results();
283
284    if (will_operation) {
285      EXPECT_EQ(0, GetPlatformFileSize());
286      SetPlatformFileSize(8);
287    } else {
288      EXPECT_EQ(8, GetPlatformFileSize());
289    }
290
291    SetLength(16, will_operation);
292    base::MessageLoop::current()->RunUntilIdle();
293    ASSERT_EQ(1U, num_results());
294    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
295    EXPECT_EQ(100 - 16, delegate()->available_space());
296    reset_results();
297
298    if (will_operation) {
299      EXPECT_EQ(8, GetPlatformFileSize());
300      SetPlatformFileSize(16);
301    } else {
302      EXPECT_EQ(16, GetPlatformFileSize());
303    }
304
305    SetLength(4, will_operation);
306    base::MessageLoop::current()->RunUntilIdle();
307    ASSERT_EQ(1U, num_results());
308    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
309    EXPECT_EQ(100 - 4, delegate()->available_space());
310    reset_results();
311
312    if (will_operation) {
313      EXPECT_EQ(16, GetPlatformFileSize());
314      SetPlatformFileSize(4);
315    } else {
316      EXPECT_EQ(4, GetPlatformFileSize());
317    }
318
319    SetLength(0, will_operation);
320    base::MessageLoop::current()->RunUntilIdle();
321    ASSERT_EQ(1U, num_results());
322    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
323    EXPECT_EQ(100, delegate()->available_space());
324    reset_results();
325
326    if (will_operation) {
327      EXPECT_EQ(4, GetPlatformFileSize());
328      SetPlatformFileSize(0);
329    } else {
330      EXPECT_EQ(0, GetPlatformFileSize());
331    }
332
333    delegate()->set_available_space(5);
334
335    // Quota error case.
336    SetLength(7, will_operation);
337    base::MessageLoop::current()->RunUntilIdle();
338    ASSERT_EQ(1U, num_results());
339    EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front());
340    EXPECT_EQ(5, delegate()->available_space());
341    reset_results();
342  }
343
344  QuotaMockDelegate* delegate() {
345    return delegate_;
346  }
347
348  void Write(int64_t offset, const std::string& data, bool will_operation) {
349    if (will_operation) {
350      ASSERT_TRUE(quota_file_io_->WillWrite(
351          offset, data.size(),
352          base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr())));
353    } else {
354      ASSERT_TRUE(quota_file_io_->Write(
355          offset, data.c_str(), data.size(),
356          base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr())));
357    }
358  }
359
360  void SetLength(int64_t length, bool will_operation) {
361    if (will_operation) {
362      ASSERT_TRUE(quota_file_io_->WillSetLength(
363          length,
364          base::Bind(&QuotaFileIOTest::DidSetLength,
365                     weak_factory_.GetWeakPtr())));
366    } else {
367      ASSERT_TRUE(quota_file_io_->SetLength(
368          length,
369          base::Bind(&QuotaFileIOTest::DidSetLength,
370                     weak_factory_.GetWeakPtr())));
371    }
372  }
373
374  void DidWrite(PlatformFileError status, int bytes_written) {
375    status_.push_back(status);
376    bytes_written_.push_back(bytes_written);
377  }
378
379  void DidSetLength(PlatformFileError status) {
380    status_.push_back(status);
381  }
382
383  size_t num_results() const { return status_.size(); }
384  const std::deque<int>& bytes_written() const { return bytes_written_; }
385  const std::deque<PlatformFileError>& status() const { return status_; }
386
387  void reset_results() {
388    bytes_written_.clear();
389    status_.clear();
390  }
391
392  void pop_result() {
393    bytes_written_.pop_front();
394    status_.pop_front();
395  }
396
397  void ReadPlatformFile(std::string* data) {
398    data->clear();
399    char buf[256];
400    int32_t read_offset = 0;
401    for (;;) {
402      int rv = base::ReadPlatformFile(file_, read_offset, buf, sizeof(buf));
403      ASSERT_GE(rv, 0);
404      if (rv == 0)
405        break;
406      read_offset += rv;
407      data->append(buf, rv);
408    }
409  }
410
411  int64_t GetPlatformFileSize() {
412    base::PlatformFileInfo info;
413    EXPECT_TRUE(base::GetPlatformFileInfo(file_, &info));
414    return info.size;
415  }
416
417  void SetPlatformFileSize(int64_t length) {
418    EXPECT_TRUE(base::TruncatePlatformFile(file_, length));
419  }
420
421 private:
422  base::ScopedTempDir dir_;
423  PlatformFile file_;
424  scoped_ptr<QuotaFileIO> quota_file_io_;
425  std::deque<int> bytes_written_;
426  std::deque<PlatformFileError> status_;
427  QuotaMockDelegate* delegate_;
428  base::WeakPtrFactory<QuotaFileIOTest> weak_factory_;
429};
430
431TEST_F(QuotaFileIOTest, Write) {
432  WriteTestBody(false);
433}
434
435TEST_F(QuotaFileIOTest, WillWrite) {
436  WriteTestBody(true);
437}
438
439TEST_F(QuotaFileIOTest, SetLength) {
440  SetLengthTestBody(false);
441}
442
443TEST_F(QuotaFileIOTest, WillSetLength) {
444  SetLengthTestBody(true);
445}
446
447TEST_F(QuotaFileIOTest, ParallelWrites) {
448  delegate()->set_available_space(22);
449  std::string read_buffer;
450
451  const std::string data1[] = {
452    std::string("12345678"),
453    std::string("55555"),
454    std::string("9012345"),
455  };
456  Write(0, data1[0], false);
457  Write(5, data1[1], false);
458  Write(8, data1[2], false);
459  base::MessageLoop::current()->RunUntilIdle();
460
461  ASSERT_EQ(ARRAYSIZE_UNSAFE(data1), num_results());
462  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data1); ++i) {
463    EXPECT_EQ(static_cast<int>(data1[i].size()), bytes_written().front());
464    EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
465    pop_result();
466  }
467
468  EXPECT_EQ(22 - 15, delegate()->available_space());
469  EXPECT_EQ(15, GetPlatformFileSize());
470  ReadPlatformFile(&read_buffer);
471  EXPECT_EQ("123455559012345", read_buffer);
472
473  // The second write will fail for quota error.
474  const std::string data2[] = {
475    std::string("33"),
476    std::string("XXXX"),
477  };
478  Write(2, data2[0], false);
479  Write(20, data2[1], false);
480  base::MessageLoop::current()->RunUntilIdle();
481
482  ASSERT_EQ(ARRAYSIZE_UNSAFE(data2), num_results());
483  EXPECT_EQ(static_cast<int>(data2[0].size()), bytes_written().front());
484  EXPECT_EQ(base::PLATFORM_FILE_OK, status().front());
485  pop_result();
486  EXPECT_EQ(0, bytes_written().front());
487  EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front());
488  pop_result();
489
490  EXPECT_EQ(22 - 15, delegate()->available_space());
491  EXPECT_EQ(15, GetPlatformFileSize());
492  ReadPlatformFile(&read_buffer);
493  EXPECT_EQ("123355559012345", read_buffer);
494}
495
496}  // namespace content
497