xref: /aosp_15_r20/external/tink/cc/util/file_input_stream_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16 #include "tink/util/file_input_stream.h"
17 
18 #include <fcntl.h>
19 
20 #include <algorithm>
21 #include <cstdint>
22 #include <cstring>
23 #include <iostream>
24 #include <ostream>
25 #include <string>
26 
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "absl/memory/memory.h"
30 #include "absl/status/status.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/string_view.h"
33 #include "tink/internal/test_file_util.h"
34 #include "tink/subtle/random.h"
35 #include "tink/util/status.h"
36 #include "tink/util/test_matchers.h"
37 #include "tink/util/test_util.h"
38 
39 namespace crypto {
40 namespace tink {
41 namespace {
42 
43 using ::crypto::tink::test::IsOk;
44 using ::crypto::tink::test::IsOkAndHolds;
45 using ::crypto::tink::test::StatusIs;
46 
47 constexpr int kDefaultTestStreamSize = 100 * 1024;  // 100 KB.
48 
49 // Opens test file `filename` and returns a file descriptor to it.
OpenTestFileToRead(absl::string_view filename)50 util::StatusOr<int> OpenTestFileToRead(absl::string_view filename) {
51   std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename);
52   int fd = open(full_filename.c_str(), O_RDONLY);
53   if (fd == -1) {
54     return util::Status(absl::StatusCode::kInternal,
55                         absl::StrCat("Cannot open file ", full_filename,
56                                      " error: ", std::strerror(errno)));
57   }
58   return fd;
59 }
60 
61 // Reads the specified `input_stream` until no more bytes can be read,
62 // and puts the read bytes into `contents`.
63 // Returns the status of the last input_stream->Next()-operation.
ReadAll(util::FileInputStream * input_stream,std::string * contents)64 util::Status ReadAll(util::FileInputStream* input_stream,
65                      std::string* contents) {
66   contents->clear();
67   const void* buffer;
68   auto next_result = input_stream->Next(&buffer);
69   while (next_result.ok()) {
70     contents->append(static_cast<const char*>(buffer), next_result.value());
71     next_result = input_stream->Next(&buffer);
72   }
73   return next_result.status();
74 }
75 
76 using FileInputStreamTestDefaultBufferSize = testing::TestWithParam<int>;
77 
TEST_P(FileInputStreamTestDefaultBufferSize,ReadAllfFromInputStreamSucceeds)78 TEST_P(FileInputStreamTestDefaultBufferSize, ReadAllfFromInputStreamSucceeds) {
79   int stream_size = GetParam();
80   SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size));
81   std::string file_contents = subtle::Random::GetRandomBytes(stream_size);
82   std::string filename = absl::StrCat(
83       stream_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
84   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
85   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
86   ASSERT_THAT(input_fd.status(), IsOk());
87   EXPECT_EQ(stream_size, file_contents.size());
88   auto input_stream = absl::make_unique<util::FileInputStream>(*input_fd);
89   std::string stream_contents;
90   auto status = ReadAll(input_stream.get(), &stream_contents);
91   EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange));
92   EXPECT_EQ(status.message(), "EOF");
93   EXPECT_EQ(file_contents, stream_contents);
94 }
95 
96 INSTANTIATE_TEST_SUITE_P(FileInputStreamTest,
97                          FileInputStreamTestDefaultBufferSize,
98                          testing::ValuesIn({0, 10, 100, 1000, 10000, 100000,
99                                             1000000}));
100 
101 using FileInputStreamTestCustomBufferSizes = testing::TestWithParam<int>;
102 
TEST_P(FileInputStreamTestCustomBufferSizes,ReadAllWithCustomBufferSizeSucceeds)103 TEST_P(FileInputStreamTestCustomBufferSizes,
104        ReadAllWithCustomBufferSizeSucceeds) {
105   int buffer_size = GetParam();
106   SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size));
107   std::string file_contents =
108       subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
109   std::string filename = absl::StrCat(
110       buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
111   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
112   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
113   ASSERT_THAT(input_fd.status(), IsOk());
114   EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
115   auto input_stream =
116       absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
117   const void* buffer;
118   auto next_result = input_stream->Next(&buffer);
119   ASSERT_THAT(next_result, IsOk());
120   EXPECT_EQ(buffer_size, next_result.value());
121   EXPECT_EQ(file_contents.substr(0, buffer_size),
122             std::string(static_cast<const char*>(buffer), buffer_size));
123 }
124 
125 INSTANTIATE_TEST_SUITE_P(FileInputStreamTest,
126                          FileInputStreamTestCustomBufferSizes,
127                          testing::ValuesIn({1, 10, 100, 1000, 10000}));
128 
TEST(FileInputStreamTest,NextFailsIfFdIsInvalid)129 TEST(FileInputStreamTest, NextFailsIfFdIsInvalid) {
130   int buffer_size = 4 * 1024;
131   auto input_stream = absl::make_unique<util::FileInputStream>(-1, buffer_size);
132   const void* buffer = nullptr;
133   EXPECT_THAT(input_stream->Next(&buffer).status(),
134               StatusIs(absl::StatusCode::kInternal));
135 }
136 
TEST(FileInputStreamTest,NextFailsIfDataIsNull)137 TEST(FileInputStreamTest, NextFailsIfDataIsNull) {
138   int buffer_size = 4 * 1024;
139   std::string file_contents =
140       subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
141   std::string filename = absl::StrCat(
142       buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
143   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
144   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
145   ASSERT_THAT(input_fd.status(), IsOk());
146   EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
147   auto input_stream =
148       absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
149 
150   EXPECT_THAT(input_stream->Next(nullptr).status(),
151               StatusIs(absl::StatusCode::kInvalidArgument));
152 }
153 
TEST(FileInputStreamTest,NextReadsExactlyOneBlockOfData)154 TEST(FileInputStreamTest, NextReadsExactlyOneBlockOfData) {
155   int buffer_size = 4 * 1024;
156   std::string file_contents =
157       subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
158   std::string filename = absl::StrCat(
159       buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
160   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
161   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
162   ASSERT_THAT(input_fd.status(), IsOk());
163   EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
164   auto input_stream =
165       absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
166 
167   auto expected_file_content_block =
168       absl::string_view(file_contents).substr(0, buffer_size);
169   const void* buffer = nullptr;
170   util::StatusOr<int> next_result = input_stream->Next(&buffer);
171   ASSERT_THAT(next_result, IsOkAndHolds(buffer_size));
172   // Check that we advanced of buffer_size bytes.
173   EXPECT_EQ(input_stream->Position(), buffer_size);
174   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
175             expected_file_content_block);
176 }
177 
TEST(FileInputStreamTest,BackupForNegativeOrZeroBytesIsANoop)178 TEST(FileInputStreamTest, BackupForNegativeOrZeroBytesIsANoop) {
179   int buffer_size = 4 * 1024;
180   std::string file_contents =
181       subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
182   std::string filename = absl::StrCat(
183       buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
184   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
185   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
186   ASSERT_THAT(input_fd.status(), IsOk());
187   EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
188   auto input_stream =
189       absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
190   EXPECT_EQ(input_stream->Position(), 0);
191 
192   auto expected_file_content_block =
193       absl::string_view(file_contents).substr(0, buffer_size);
194   const void* buffer = nullptr;
195   ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
196   // Check that we advanced of buffer_size bytes.
197   EXPECT_EQ(input_stream->Position(), buffer_size);
198   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
199             expected_file_content_block);
200 
201   // The calls below are noops.
202   input_stream->BackUp(0);
203   EXPECT_EQ(input_stream->Position(), buffer_size);
204   input_stream->BackUp(-12);
205   EXPECT_EQ(input_stream->Position(), buffer_size);
206 
207   // A subsequent call to `Next` returns the 2nd block.
208   auto expected_2nd_file_content_block =
209       absl::string_view(file_contents).substr(buffer_size, buffer_size);
210   ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
211   // Check that we advanced of buffer_size bytes.
212   EXPECT_EQ(input_stream->Position(), 2 * buffer_size);
213   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
214             expected_2nd_file_content_block);
215 }
216 
TEST(FileInputStreamTest,BackupForLessThanOneBlockOfData)217 TEST(FileInputStreamTest, BackupForLessThanOneBlockOfData) {
218   int buffer_size = 4 * 1024;
219   std::string file_contents =
220       subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
221   std::string filename = absl::StrCat(
222       buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
223   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
224   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
225   ASSERT_THAT(input_fd.status(), IsOk());
226   EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
227   auto input_stream =
228       absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
229 
230   auto expected_file_content_block =
231       absl::string_view(file_contents).substr(0, buffer_size);
232   const void* buffer = nullptr;
233   ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
234   // Check that we advanced of buffer_size bytes.
235   EXPECT_EQ(input_stream->Position(), buffer_size);
236   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
237             expected_file_content_block);
238 
239   int64_t position_after_next = input_stream->Position();
240   // Number of bytes that were backed up.
241   int num_backed_up_bytes = 0;
242   input_stream->BackUp(0);  // This should be a noop.
243   EXPECT_EQ(input_stream->Position(), position_after_next);
244   input_stream->BackUp(-12);  // This should be a noop.
245   EXPECT_EQ(input_stream->Position(), position_after_next);
246   input_stream->BackUp(10);
247   num_backed_up_bytes += 10;
248   EXPECT_EQ(input_stream->Position(),
249             position_after_next - num_backed_up_bytes);
250   input_stream->BackUp(5);
251   num_backed_up_bytes += 5;
252   EXPECT_EQ(input_stream->Position(),
253             position_after_next - num_backed_up_bytes);
254 
255   // A subsequent call to Next should return only the backed up bytes.
256   auto expected_backed_up_bytes =
257       absl::string_view(file_contents)
258           .substr(buffer_size - num_backed_up_bytes, num_backed_up_bytes);
259   ASSERT_THAT(input_stream->Next(&buffer),
260               IsOkAndHolds(expected_backed_up_bytes.size()));
261   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer),
262                               expected_backed_up_bytes.size()),
263             expected_backed_up_bytes);
264 }
265 
266 // When backing up of a number of bytes larger than the size of a block, backup
267 // of one block.
TEST(FileInputStreamTest,BackupAtMostOfOneBlock)268 TEST(FileInputStreamTest, BackupAtMostOfOneBlock) {
269   int buffer_size = 4 * 1024;
270   std::string file_contents =
271       subtle::Random::GetRandomBytes(kDefaultTestStreamSize);
272   std::string filename = absl::StrCat(
273       buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin");
274   ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk());
275   util::StatusOr<int> input_fd = OpenTestFileToRead(filename);
276   ASSERT_THAT(input_fd.status(), IsOk());
277   EXPECT_EQ(kDefaultTestStreamSize, file_contents.size());
278   auto input_stream =
279       absl::make_unique<util::FileInputStream>(*input_fd, buffer_size);
280 
281   // Read two blocks of size buffer_size, then back up of more than buffer_size
282   // bytes.
283   auto expected_1st_file_content_block =
284       absl::string_view(file_contents).substr(0, buffer_size);
285   const void* buffer = nullptr;
286   ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
287   // Check that we advanced of buffer_size bytes.
288   EXPECT_EQ(input_stream->Position(), buffer_size);
289   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
290             expected_1st_file_content_block);
291 
292   auto expected_2nd_file_content_block =
293       absl::string_view(file_contents).substr(buffer_size, buffer_size);
294   ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
295   // Check that we advanced of buffer_size bytes.
296   EXPECT_EQ(input_stream->Position(), 2 * buffer_size);
297   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
298             expected_2nd_file_content_block);
299 
300   int64_t position_after_next = input_stream->Position();
301   EXPECT_EQ(input_stream->Position(), position_after_next);
302   input_stream->BackUp(10);
303   EXPECT_EQ(input_stream->Position(), position_after_next - 10);
304   input_stream->BackUp(buffer_size);
305   EXPECT_EQ(input_stream->Position(), position_after_next - buffer_size);
306 
307   // This call to Next is expected to read the second block again.
308   ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size));
309   EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size),
310             expected_2nd_file_content_block);
311 }
312 
313 }  // namespace
314 }  // namespace tink
315 }  // namespace crypto
316