1 //
2 // Copyright (C) 2017 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_consumer/cached_file_descriptor.h"
18
19 #include <fcntl.h>
20
21 #include <algorithm>
22 #include <string>
23 #include <vector>
24
25 #include <gtest/gtest.h>
26
27 #include "update_engine/common/test_utils.h"
28 #include "update_engine/common/utils.h"
29
30 using chromeos_update_engine::test_utils::ExpectVectorsEq;
31 using std::min;
32 using std::string;
33 using std::vector;
34
35 namespace chromeos_update_engine {
36
37 namespace {
38 const size_t kCacheSize = 100;
39 const size_t kFileSize = 1024;
40 const size_t kRandomIterations = 1000;
41 } // namespace
42
43 class CachedFileDescriptorTest : public ::testing::Test {
44 public:
Open()45 void Open() {
46 cfd_.reset(new CachedFileDescriptor(fd_, kCacheSize));
47 EXPECT_TRUE(cfd_->Open(temp_file_.path().c_str(), O_RDWR, 0600));
48 }
49
Write(uint8_t * buffer,size_t count)50 void Write(uint8_t* buffer, size_t count) {
51 size_t total_bytes_wrote = 0;
52 while (total_bytes_wrote < count) {
53 auto bytes_wrote =
54 cfd_->Write(buffer + total_bytes_wrote, count - total_bytes_wrote);
55 ASSERT_NE(bytes_wrote, -1);
56 total_bytes_wrote += bytes_wrote;
57 }
58 }
59
Close()60 void Close() { EXPECT_TRUE(cfd_->Close()); }
61
SetUp()62 void SetUp() override {
63 brillo::Blob zero_blob(kFileSize, 0);
64 EXPECT_TRUE(utils::WriteFile(
65 temp_file_.path().c_str(), zero_blob.data(), zero_blob.size()));
66 Open();
67 }
68
TearDown()69 void TearDown() override {
70 Close();
71 EXPECT_FALSE(cfd_->IsOpen());
72 }
73
74 protected:
75 FileDescriptorPtr fd_{new EintrSafeFileDescriptor};
76 ScopedTempFile temp_file_{"CachedFileDescriptor-file.XXXXXX"};
77 int value_{1};
78 FileDescriptorPtr cfd_;
79 };
80
TEST_F(CachedFileDescriptorTest,IsOpenTest)81 TEST_F(CachedFileDescriptorTest, IsOpenTest) {
82 EXPECT_TRUE(cfd_->IsOpen());
83 }
84
TEST_F(CachedFileDescriptorTest,SimpleWriteTest)85 TEST_F(CachedFileDescriptorTest, SimpleWriteTest) {
86 EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
87 brillo::Blob blob_in(kFileSize, value_);
88 Write(blob_in.data(), blob_in.size());
89 EXPECT_TRUE(cfd_->Flush());
90
91 brillo::Blob blob_out;
92 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
93 EXPECT_EQ(blob_in, blob_out);
94 }
95
TEST_F(CachedFileDescriptorTest,OneBytePerWriteTest)96 TEST_F(CachedFileDescriptorTest, OneBytePerWriteTest) {
97 EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
98 brillo::Blob blob_in(kFileSize, value_);
99 for (size_t idx = 0; idx < blob_in.size(); idx++) {
100 Write(&blob_in[idx], 1);
101 }
102 EXPECT_TRUE(cfd_->Flush());
103
104 brillo::Blob blob_out;
105 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
106 EXPECT_EQ(blob_in, blob_out);
107 }
108
TEST_F(CachedFileDescriptorTest,RandomWriteTest)109 TEST_F(CachedFileDescriptorTest, RandomWriteTest) {
110 EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
111
112 brillo::Blob blob_in(kFileSize, 0);
113 srand(time(nullptr));
114 uint32_t rand_seed;
115 for (size_t idx = 0; idx < kRandomIterations; idx++) {
116 // zero to full size available.
117 size_t start = rand_r(&rand_seed) % blob_in.size();
118 size_t size = rand_r(&rand_seed) % (blob_in.size() - start);
119 std::fill_n(&blob_in[start], size, idx % 256);
120 EXPECT_EQ(cfd_->Seek(start, SEEK_SET), static_cast<off64_t>(start));
121 Write(&blob_in[start], size);
122 }
123 EXPECT_TRUE(cfd_->Flush());
124
125 brillo::Blob blob_out;
126 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
127 EXPECT_EQ(blob_in, blob_out);
128 }
129
TEST_F(CachedFileDescriptorTest,SeekTest)130 TEST_F(CachedFileDescriptorTest, SeekTest) {
131 EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
132 EXPECT_EQ(cfd_->Seek(1, SEEK_SET), 1);
133 EXPECT_EQ(cfd_->Seek(kFileSize - 1, SEEK_SET),
134 static_cast<off64_t>(kFileSize - 1));
135 EXPECT_EQ(cfd_->Seek(kFileSize, SEEK_SET), static_cast<off64_t>(kFileSize));
136 EXPECT_EQ(cfd_->Seek(kFileSize + 1, SEEK_SET),
137 static_cast<off64_t>(kFileSize + 1));
138
139 EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
140 EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), 1);
141 EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), 2);
142 EXPECT_EQ(cfd_->Seek(kFileSize - 1, SEEK_SET),
143 static_cast<off64_t>(kFileSize - 1));
144 EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), static_cast<off64_t>(kFileSize));
145 EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), static_cast<off64_t>(kFileSize + 1));
146 }
147
TEST_F(CachedFileDescriptorTest,NoFlushTest)148 TEST_F(CachedFileDescriptorTest, NoFlushTest) {
149 EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
150 brillo::Blob blob_in(kFileSize, value_);
151 Write(blob_in.data(), blob_in.size());
152
153 brillo::Blob blob_out;
154 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
155 EXPECT_NE(blob_in, blob_out);
156 }
157
TEST_F(CachedFileDescriptorTest,CacheSizeWriteTest)158 TEST_F(CachedFileDescriptorTest, CacheSizeWriteTest) {
159 off64_t seek = 10;
160 brillo::Blob blob_in(kFileSize, 0);
161 std::fill_n(&blob_in[seek], kCacheSize, value_);
162 // We are writing exactly one cache size; Then it should be committed.
163 EXPECT_EQ(cfd_->Seek(seek, SEEK_SET), seek);
164 Write(&blob_in[seek], kCacheSize);
165
166 brillo::Blob blob_out;
167 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
168 EXPECT_EQ(blob_in, blob_out);
169 }
170
TEST_F(CachedFileDescriptorTest,UnderCacheSizeWriteTest)171 TEST_F(CachedFileDescriptorTest, UnderCacheSizeWriteTest) {
172 off64_t seek = 100;
173 size_t less_than_cache_size = kCacheSize - 1;
174 EXPECT_EQ(cfd_->Seek(seek, SEEK_SET), seek);
175 brillo::Blob blob_in(kFileSize, 0);
176 std::fill_n(&blob_in[seek], less_than_cache_size, value_);
177 // We are writing less than one cache size; then it should not be committed.
178 Write(&blob_in[seek], less_than_cache_size);
179
180 // Revert the changes in |blob_in|.
181 std::fill_n(&blob_in[seek], less_than_cache_size, 0);
182 brillo::Blob blob_out;
183 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
184 EXPECT_EQ(blob_in, blob_out);
185 }
186
TEST_F(CachedFileDescriptorTest,SeekAfterWriteTest)187 TEST_F(CachedFileDescriptorTest, SeekAfterWriteTest) {
188 off64_t seek = 100;
189 size_t less_than_cache_size = kCacheSize - 3;
190 EXPECT_EQ(cfd_->Seek(seek, SEEK_SET), seek);
191 brillo::Blob blob_in(kFileSize, 0);
192 std::fill_n(&blob_in[seek], less_than_cache_size, value_);
193 // We are writing less than one cache size; then it should not be committed.
194 Write(&blob_in[seek], less_than_cache_size);
195
196 // Then we seek, it should've written the cache after seek.
197 EXPECT_EQ(cfd_->Seek(200, SEEK_SET), 200);
198
199 brillo::Blob blob_out;
200 EXPECT_TRUE(utils::ReadFile(temp_file_.path(), &blob_out));
201 EXPECT_EQ(blob_in, blob_out);
202 }
203
204 } // namespace chromeos_update_engine
205