xref: /aosp_15_r20/external/tink/cc/util/file_output_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 
17 #include "tink/util/file_output_stream.h"
18 
19 #include <fcntl.h>
20 
21 #include <algorithm>
22 #include <cstring>
23 #include <iostream>
24 #include <ostream>
25 #include <string>
26 
27 #include "gtest/gtest.h"
28 #include "absl/memory/memory.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/string_view.h"
31 #include "tink/internal/test_file_util.h"
32 #include "tink/subtle/random.h"
33 #include "tink/util/test_matchers.h"
34 #include "tink/util/test_util.h"
35 
36 namespace crypto {
37 namespace tink {
38 namespace {
39 
40 using ::crypto::tink::test::IsOk;
41 
42 // Opens test file `filename` and returns a file descriptor to it.
OpenTestFileToWrite(absl::string_view filename)43 util::StatusOr<int> OpenTestFileToWrite(absl::string_view filename) {
44   std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename);
45   mode_t mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
46   int fd = open(full_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
47   if (fd == -1) {
48     return util::Status(absl::StatusCode::kInternal,
49                         absl::StrCat("Cannot open file ", full_filename,
50                                      " error: ", std::strerror(errno)));
51   }
52   return fd;
53 }
54 
55 // Writes 'contents' the specified 'output_stream', and closes the stream.
56 // Returns the status of output_stream->Close()-operation, or a non-OK status
57 // of a prior output_stream->Next()-operation, if any.
WriteToStream(util::FileOutputStream * output_stream,absl::string_view contents)58 util::Status WriteToStream(util::FileOutputStream* output_stream,
59                            absl::string_view contents) {
60   void* buffer;
61   int pos = 0;
62   int remaining = contents.length();
63   int available_space = 0;
64   int available_bytes = 0;
65   while (remaining > 0) {
66     auto next_result = output_stream->Next(&buffer);
67     if (!next_result.ok()) return next_result.status();
68     available_space = next_result.value();
69     available_bytes = std::min(available_space, remaining);
70     memcpy(buffer, contents.data() + pos, available_bytes);
71     remaining -= available_bytes;
72     pos += available_bytes;
73   }
74   if (available_space > available_bytes) {
75     output_stream->BackUp(available_space - available_bytes);
76   }
77   return output_stream->Close();
78 }
79 
80 class FileOutputStreamTest : public ::testing::Test {
81 };
82 
TEST_F(FileOutputStreamTest,WritingStreams)83 TEST_F(FileOutputStreamTest, WritingStreams) {
84   for (auto stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) {
85     SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size));
86     std::string stream_contents = subtle::Random::GetRandomBytes(stream_size);
87     std::string filename = absl::StrCat(
88         stream_size, internal::GetTestFileNamePrefix(), "_test.bin");
89     ASSERT_THAT(internal::CreateTestFile(filename, stream_contents), IsOk());
90     util::StatusOr<int> output_fd = OpenTestFileToWrite(filename);
91     ASSERT_THAT(output_fd.status(), IsOk());
92     auto output_stream = absl::make_unique<util::FileOutputStream>(*output_fd);
93     auto status = WriteToStream(output_stream.get(), stream_contents);
94     EXPECT_TRUE(status.ok()) << status;
95     std::string file_contents = test::ReadTestFile(filename);
96     EXPECT_EQ(stream_size, file_contents.size());
97     EXPECT_EQ(stream_contents, file_contents);
98   }
99 }
100 
TEST_F(FileOutputStreamTest,CustomBufferSizes)101 TEST_F(FileOutputStreamTest, CustomBufferSizes) {
102   int stream_size = 1024 * 1024;
103   std::string stream_contents = subtle::Random::GetRandomBytes(stream_size);
104   for (auto buffer_size : {1, 10, 100, 1000, 10000, 100000, 1000000}) {
105     SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size));
106     std::string filename = absl::StrCat(
107         buffer_size, internal::GetTestFileNamePrefix(), "_test.bin");
108     ASSERT_THAT(internal::CreateTestFile(filename, stream_contents), IsOk());
109     util::StatusOr<int> output_fd = OpenTestFileToWrite(filename);
110     ASSERT_THAT(output_fd.status(), IsOk());
111     auto output_stream =
112         absl::make_unique<util::FileOutputStream>(*output_fd, buffer_size);
113     void* buffer;
114     auto next_result = output_stream->Next(&buffer);
115     EXPECT_TRUE(next_result.ok()) << next_result.status();
116     EXPECT_EQ(buffer_size, next_result.value());
117     output_stream->BackUp(buffer_size);
118     auto status = WriteToStream(output_stream.get(), stream_contents);
119     EXPECT_TRUE(status.ok()) << status;
120     std::string file_contents = test::ReadTestFile(filename);
121     EXPECT_EQ(stream_size, file_contents.size());
122     EXPECT_EQ(stream_contents, file_contents);
123   }
124 }
125 
126 
TEST_F(FileOutputStreamTest,BackupAndPosition)127 TEST_F(FileOutputStreamTest, BackupAndPosition) {
128   int stream_size = 1024 * 1024;
129   int buffer_size = 1234;
130   void* buffer;
131   std::string stream_contents = subtle::Random::GetRandomBytes(stream_size);
132   std::string filename =
133       absl::StrCat(buffer_size, internal::GetTestFileNamePrefix(), "_test.bin");
134   ASSERT_THAT(internal::CreateTestFile(filename, stream_contents), IsOk());
135   util::StatusOr<int> output_fd = OpenTestFileToWrite(filename);
136   ASSERT_THAT(output_fd.status(), IsOk());
137 
138   // Prepare the stream and do the first call to Next().
139   auto output_stream =
140       absl::make_unique<util::FileOutputStream>(*output_fd, buffer_size);
141   EXPECT_EQ(0, output_stream->Position());
142   auto next_result = output_stream->Next(&buffer);
143   EXPECT_TRUE(next_result.ok()) << next_result.status();
144   EXPECT_EQ(buffer_size, next_result.value());
145   EXPECT_EQ(buffer_size, output_stream->Position());
146   std::memcpy(buffer, stream_contents.data(), buffer_size);
147 
148   // BackUp several times, but in total fewer bytes than returned by Next().
149   int total_backup_size = 0;
150   for (auto backup_size : {0, 1, 5, 0, 10, 100, -42, 400, 20, -100}) {
151     SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size));
152     output_stream->BackUp(backup_size);
153     total_backup_size += std::max(0, backup_size);
154     EXPECT_EQ(buffer_size - total_backup_size, output_stream->Position());
155   }
156   EXPECT_LT(total_backup_size, next_result.value());
157 
158   // Call Next(), it should succeed.
159   next_result = output_stream->Next(&buffer);
160   EXPECT_TRUE(next_result.ok()) << next_result.status();
161 
162   // BackUp() some bytes, again fewer than returned by Next().
163   total_backup_size = 0;
164   for (auto backup_size : {0, 72, -94, 37, 82}) {
165     SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size));
166     output_stream->BackUp(backup_size);
167     total_backup_size += std::max(0, backup_size);
168     EXPECT_EQ(buffer_size - total_backup_size, output_stream->Position());
169   }
170   EXPECT_LT(total_backup_size, next_result.value());
171 
172   // Call Next(), it should succeed;
173   next_result = output_stream->Next(&buffer);
174   EXPECT_TRUE(next_result.ok()) << next_result.status();
175 
176   // Call Next() again, it should return a full block.
177   auto prev_position = output_stream->Position();
178   next_result = output_stream->Next(&buffer);
179   EXPECT_TRUE(next_result.ok()) << next_result.status();
180   EXPECT_EQ(buffer_size, next_result.value());
181   EXPECT_EQ(prev_position + buffer_size, output_stream->Position());
182   std::memcpy(buffer, stream_contents.data() + buffer_size, buffer_size);
183 
184   // BackUp a few times, with total over the returned buffer_size.
185   total_backup_size = 0;
186   for (auto backup_size :
187            {0, 72, -100, buffer_size / 2, 200, -25, buffer_size / 2, 42}) {
188     SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size));
189     output_stream->BackUp(backup_size);
190     total_backup_size = std::min(buffer_size,
191                                  total_backup_size + std::max(0, backup_size));
192     EXPECT_EQ(prev_position + buffer_size - total_backup_size,
193               output_stream->Position());
194   }
195   EXPECT_EQ(total_backup_size, buffer_size);
196   EXPECT_EQ(prev_position, output_stream->Position());
197 
198   // Call Next() again, it should return a full block.
199   next_result = output_stream->Next(&buffer);
200   EXPECT_TRUE(next_result.ok()) << next_result.status();
201   EXPECT_EQ(buffer_size, next_result.value());
202   EXPECT_EQ(prev_position + buffer_size, output_stream->Position());
203   std::memcpy(buffer, stream_contents.data() + buffer_size, buffer_size);
204 
205   // Write the remaining stream contents to stream.
206   auto status = WriteToStream(
207       output_stream.get(), stream_contents.substr(output_stream->Position()));
208   EXPECT_TRUE(status.ok()) << status;
209   std::string file_contents = test::ReadTestFile(filename);
210   EXPECT_EQ(stream_size, file_contents.size());
211   EXPECT_EQ(stream_contents, file_contents);
212 }
213 
214 }  // namespace
215 }  // namespace tink
216 }  // namespace crypto
217