Home | History | Annotate | Download | only in bsdiff
      1 // Copyright 2015 The Chromium OS 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 "bsdiff/extents_file.h"
      6 
      7 #include <gmock/gmock.h>
      8 #include <gtest/gtest.h>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "bsdiff/file_interface.h"
     13 
     14 using std::vector;
     15 using testing::AnyNumber;
     16 using testing::InSequence;
     17 using testing::Return;
     18 using testing::StrictMock;
     19 using testing::_;
     20 
     21 namespace bsdiff {
     22 
     23 // Mock class for the underlying file interface.
     24 class MockFile : public FileInterface {
     25  public:
     26   MOCK_METHOD3(Read, bool(void*, size_t, size_t*));
     27   MOCK_METHOD3(Write, bool(const void*, size_t, size_t*));
     28   MOCK_METHOD1(Seek, bool(off_t));
     29   MOCK_METHOD0(Close, bool());
     30   MOCK_METHOD1(GetSize, bool(uint64_t*));
     31 };
     32 
     33 ACTION(SucceedIO) {
     34   // Check that arg1 (count) can be converted
     35   *arg2 = arg1;
     36   return true;
     37 }
     38 
     39 ACTION_P(SucceedPartialIO, bytes) {
     40   // Check that arg1 (count) can be converted
     41   *arg2 = bytes;
     42   return true;
     43 }
     44 
     45 class ExtentsFileTest : public testing::Test {
     46  protected:
     47   void SetUp() {
     48     mock_file_ = new StrictMock<MockFile>();
     49     mock_file_ptr_.reset(mock_file_);
     50     // The destructor of the ExtentsFile will call Close once.
     51     EXPECT_CALL(*mock_file_, Close()).WillOnce(Return(true));
     52   }
     53 
     54   // Pointer to the underlying File owned by the ExtentsFile under test. This
     55   // pointer is invalidated whenever the ExtentsFile is destroyed.
     56   StrictMock<MockFile>* mock_file_;
     57   std::unique_ptr<FileInterface> mock_file_ptr_;
     58 };
     59 
     60 TEST_F(ExtentsFileTest, DestructorCloses) {
     61   ExtentsFile file(std::move(mock_file_ptr_), {});
     62 }
     63 
     64 TEST_F(ExtentsFileTest, CloseIsForwarded) {
     65   ExtentsFile file(std::move(mock_file_ptr_), {});
     66   EXPECT_TRUE(file.Close());
     67   EXPECT_CALL(*mock_file_, Close()).WillOnce(Return(false));
     68 }
     69 
     70 TEST_F(ExtentsFileTest, GetSizeSumExtents) {
     71   ExtentsFile file(std::move(mock_file_ptr_),
     72                    {ex_t{10, 5}, ex_t{20, 5}, {25, 2}});
     73   uint64_t size;
     74   EXPECT_TRUE(file.GetSize(&size));
     75   EXPECT_EQ(12U, size);
     76 }
     77 
     78 TEST_F(ExtentsFileTest, SeekToRightOffsets) {
     79   ExtentsFile file(std::move(mock_file_ptr_),
     80                    {ex_t{10, 5}, ex_t{20, 5}, {25, 2}});
     81   vector<std::pair<off_t, off_t>> tests = {
     82       // Seek to the beginning of the file.
     83       {0, 10},
     84       // Seek to the middle of a extent.
     85       {3, 13},
     86       {11, 26},
     87       // Seek to the extent boundary.
     88       {5, 20},  // Seeks to the first byte in the second extent.
     89       {10, 25},
     90   };
     91   for (const auto& offset_pair : tests) {
     92     // We use a failing Read() call to trigger the actual seek call to the
     93     // underlying file.
     94     EXPECT_CALL(*mock_file_, Seek(offset_pair.second)).WillOnce(Return(true));
     95     EXPECT_CALL(*mock_file_, Read(_, _, _)).WillOnce(Return(false));
     96 
     97     EXPECT_TRUE(file.Seek(offset_pair.first));
     98     size_t bytes_read;
     99     EXPECT_FALSE(file.Read(nullptr, 1, &bytes_read));
    100   }
    101 
    102   // Seeking to the end of the file is ok, but not past it.
    103   EXPECT_TRUE(file.Seek(12));
    104   EXPECT_FALSE(file.Seek(13));
    105 
    106   EXPECT_FALSE(file.Seek(-1));
    107 }
    108 
    109 TEST_F(ExtentsFileTest, ReadAcrossAllExtents) {
    110   ExtentsFile file(std::move(mock_file_ptr_),
    111                    {ex_t{10, 5}, ex_t{20, 7}, {27, 3}});
    112   InSequence s;
    113   char* buf = reinterpret_cast<char*>(0x1234);
    114 
    115   EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
    116   EXPECT_CALL(*mock_file_, Read(buf, 5, _)).WillOnce(SucceedIO());
    117   EXPECT_CALL(*mock_file_, Seek(20)).WillOnce(Return(true));
    118   EXPECT_CALL(*mock_file_, Read(buf + 5, 7, _)).WillOnce(SucceedIO());
    119   EXPECT_CALL(*mock_file_, Seek(27)).WillOnce(Return(true));
    120   EXPECT_CALL(*mock_file_, Read(buf + 12, 3, _)).WillOnce(SucceedIO());
    121 
    122   // FileExtents::Read() should read everything in one shot, by reading all
    123   // the little chunks. Note that it doesn't attempt to read past the end of the
    124   // FileExtents.
    125   size_t bytes_read = 0;
    126   EXPECT_TRUE(file.Read(buf, 100, &bytes_read));
    127   EXPECT_EQ(15U, bytes_read);
    128 }
    129 
    130 TEST_F(ExtentsFileTest, MultiReadAcrossAllExtents) {
    131   ExtentsFile file(std::move(mock_file_ptr_),
    132                    {ex_t{10, 5}, ex_t{20, 7}, {27, 3}});
    133   InSequence s;
    134   char* buf = reinterpret_cast<char*>(0x1234);
    135 
    136   EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
    137   EXPECT_CALL(*mock_file_, Read(buf, 2, _)).WillOnce(SucceedIO());
    138   EXPECT_CALL(*mock_file_, Seek(12)).WillOnce(Return(true));
    139   EXPECT_CALL(*mock_file_, Read(buf, 3, _)).WillOnce(SucceedIO());
    140   EXPECT_CALL(*mock_file_, Seek(20)).WillOnce(Return(true));
    141   EXPECT_CALL(*mock_file_, Read(buf + 3, 5, _)).WillOnce(SucceedIO());
    142   EXPECT_CALL(*mock_file_, Seek(25)).WillOnce(Return(true));
    143   EXPECT_CALL(*mock_file_, Read(buf, 2, _)).WillOnce(SucceedIO());
    144   EXPECT_CALL(*mock_file_, Seek(27)).WillOnce(Return(true));
    145   EXPECT_CALL(*mock_file_, Read(buf + 2, 3, _)).WillOnce(SucceedIO());
    146 
    147   size_t bytes_read = 0;
    148   EXPECT_TRUE(file.Read(buf, 2, &bytes_read));
    149   EXPECT_EQ(2U, bytes_read);
    150   EXPECT_TRUE(file.Read(buf, 8, &bytes_read));
    151   EXPECT_EQ(8U, bytes_read);
    152   EXPECT_TRUE(file.Read(buf, 100, &bytes_read));
    153   EXPECT_EQ(5U, bytes_read);
    154 }
    155 
    156 TEST_F(ExtentsFileTest, ReadSmallChunks) {
    157   ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
    158   InSequence s;
    159   char* buf = reinterpret_cast<char*>(0x1234);
    160 
    161   EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
    162   EXPECT_CALL(*mock_file_, Read(buf, 1, _)).WillOnce(SucceedIO());
    163   EXPECT_CALL(*mock_file_, Seek(20)).WillOnce(Return(true));
    164   // We expect to read only part of the second extent.
    165   EXPECT_CALL(*mock_file_, Read(buf + 1, 1, _)).WillOnce(SucceedIO());
    166 
    167   size_t bytes_read = 0;
    168   EXPECT_TRUE(file.Read(buf, 2, &bytes_read));
    169   EXPECT_EQ(2U, bytes_read);
    170 }
    171 
    172 TEST_F(ExtentsFileTest, ReadFailureFails) {
    173   ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
    174   EXPECT_CALL(*mock_file_, Seek(_))
    175       .Times(AnyNumber())
    176       .WillRepeatedly(Return(true));
    177   EXPECT_CALL(*mock_file_, Read(_, 1, _)).WillOnce(SucceedIO());
    178   // A second read that fails will succeed if there was partial data read.
    179   EXPECT_CALL(*mock_file_, Read(_, 10, _)).WillOnce(Return(false));
    180 
    181   size_t bytes_read = 0;
    182   EXPECT_TRUE(file.Read(nullptr, 100, &bytes_read));
    183   EXPECT_EQ(1U, bytes_read);
    184 }
    185 
    186 TEST_F(ExtentsFileTest, ReadFails) {
    187   ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
    188   EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
    189   EXPECT_CALL(*mock_file_, Read(_, 1, _)).WillOnce(Return(false));
    190   size_t bytes_read;
    191   EXPECT_FALSE(file.Read(nullptr, 1, &bytes_read));
    192 }
    193 
    194 TEST_F(ExtentsFileTest, ReadPartialReadsAndEOF) {
    195   ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
    196   EXPECT_CALL(*mock_file_, Seek(_))
    197       .Times(AnyNumber())
    198       .WillRepeatedly(Return(true));
    199   char* buf = reinterpret_cast<char*>(0x1234);
    200   InSequence s;
    201   EXPECT_CALL(*mock_file_, Read(buf, 1, _)).WillOnce(SucceedIO());
    202   EXPECT_CALL(*mock_file_, Read(buf + 1, _, _)).WillOnce(SucceedPartialIO(3));
    203   EXPECT_CALL(*mock_file_, Read(buf + 4, _, _)).WillOnce(SucceedPartialIO(0));
    204 
    205   size_t bytes_read = 0;
    206   EXPECT_TRUE(file.Read(buf, 100, &bytes_read));
    207   EXPECT_EQ(4U, bytes_read);
    208 }
    209 
    210 }  // namespace bsdiff
    211