Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 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 | |
Alex Deymo | ddf9db5 | 2017-03-02 16:10:41 -0800 | [diff] [blame] | 5 | #include "bsdiff/extents_file.h" |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 6 | |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 7 | #include <gmock/gmock.h> |
Alex Deymo | dcd423b | 2017-09-13 20:54:24 +0200 | [diff] [blame] | 8 | #include <gtest/gtest.h> |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 9 | #include <string> |
| 10 | #include <vector> |
| 11 | |
Alex Deymo | ddf9db5 | 2017-03-02 16:10:41 -0800 | [diff] [blame] | 12 | #include "bsdiff/file_interface.h" |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 13 | |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 14 | using std::vector; |
| 15 | using testing::AnyNumber; |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 16 | using testing::InSequence; |
Alex Deymo | dcd423b | 2017-09-13 20:54:24 +0200 | [diff] [blame] | 17 | using testing::Return; |
| 18 | using testing::StrictMock; |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 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()); |
Alex Deymo | daf3516 | 2015-10-14 20:43:15 -0700 | [diff] [blame] | 30 | MOCK_METHOD1(GetSize, bool(uint64_t*)); |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 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 | |
Alex Deymo | daf3516 | 2015-10-14 20:43:15 -0700 | [diff] [blame] | 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 | |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 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 | |
Sen Jiang | f822e6c | 2015-11-24 13:44:00 -0800 | [diff] [blame] | 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 | |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 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 | |
Amin Hassani | a65cff9 | 2020-03-23 11:37:24 -0700 | [diff] [blame] | 181 | char* buf = reinterpret_cast<char*>(0x1234); |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 182 | size_t bytes_read = 0; |
Amin Hassani | a65cff9 | 2020-03-23 11:37:24 -0700 | [diff] [blame] | 183 | EXPECT_TRUE(file.Read(buf, 100, &bytes_read)); |
Alex Deymo | 03f1deb | 2015-10-13 02:15:31 -0700 | [diff] [blame] | 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 |