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 
ACTION(SucceedIO)34 ACTION(SucceedIO) {
35   // Check that arg1 (count) can be converted
36   *arg2 = arg1;
37   return true;
38 }
39 
ACTION_P(SucceedPartialIO,bytes)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:
SetUp()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 
TEST_F(ExtentsFileTest,DestructorCloses)61 TEST_F(ExtentsFileTest, DestructorCloses) {
62   ExtentsFile file(std::move(mock_file_ptr_), {});
63 }
64 
TEST_F(ExtentsFileTest,CloseIsForwarded)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 
TEST_F(ExtentsFileTest,GetSizeSumExtents)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 
TEST_F(ExtentsFileTest,SeekToRightOffsets)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 
TEST_F(ExtentsFileTest,ReadAcrossAllExtents)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 
TEST_F(ExtentsFileTest,MultiReadAcrossAllExtents)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 
TEST_F(ExtentsFileTest,ReadSmallChunks)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 
TEST_F(ExtentsFileTest,ReadFailureFails)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 
TEST_F(ExtentsFileTest,ReadFails)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 
TEST_F(ExtentsFileTest,ReadPartialReadsAndEOF)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