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