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