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