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:
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 
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 
60   void Close() { EXPECT_TRUE(cfd_->Close()); }
61 
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 
69   void TearDown() override {
70     Close();
71     EXPECT_FALSE(cfd_->IsOpen());
72   }
73 
74  protected:
75   FileDescriptorPtr fd_{new EintrSafeFileDescriptor};
76   test_utils::ScopedTempFile temp_file_{"CachedFileDescriptor-file.XXXXXX"};
77   int value_{1};
78   FileDescriptorPtr cfd_;
79 };
80 
81 TEST_F(CachedFileDescriptorTest, IsOpenTest) {
82   EXPECT_TRUE(cfd_->IsOpen());
83 }
84 
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 
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 
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 
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 
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 
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 
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 
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