• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 Google LLC
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/cc/python_output_stream.h"
18 
19 #include <algorithm>
20 #include <memory>
21 #include <string>
22 #include <utility>
23 
24 #include "gtest/gtest.h"
25 #include "absl/memory/memory.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/string_view.h"
28 #include "tink/subtle/random.h"
29 #include "tink/util/status.h"
30 #include "tink/util/statusor.h"
31 #include "tink/cc/test_util.h"
32 
33 namespace crypto {
34 namespace tink {
35 namespace {
36 
37 // Writes 'contents' to the specified 'output_stream', and closes the stream.
38 // Returns the status of output_stream->Close()-operation, or a non-OK status
39 // of a prior output_stream->Next()-operation, if any.
WriteToStream(PythonOutputStream * output_stream,absl::string_view contents)40 util::Status WriteToStream(PythonOutputStream* output_stream,
41                            absl::string_view contents) {
42   void* buffer;
43   int pos = 0;
44   int remaining = contents.length();
45   int available_space = 0;
46   int available_bytes = 0;
47   while (remaining > 0) {
48     auto next_result = output_stream->Next(&buffer);
49     if (!next_result.ok()) return next_result.status();
50     available_space = next_result.value();
51     available_bytes = std::min(available_space, remaining);
52     memcpy(buffer, contents.data() + pos, available_bytes);
53     remaining -= available_bytes;
54     pos += available_bytes;
55   }
56   if (available_space > available_bytes) {
57     output_stream->BackUp(available_space - available_bytes);
58   }
59   return output_stream->Close();
60 }
61 
TEST(PythonOutputStreamTest,WritingStreams)62 TEST(PythonOutputStreamTest, WritingStreams) {
63   for (size_t stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) {
64     SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size));
65     std::string stream_contents = subtle::Random::GetRandomBytes(stream_size);
66     auto output = absl::make_unique<test::TestWritableObject>();
67     std::string* output_buffer = output->GetBuffer();
68     auto output_stream =
69         absl::make_unique<PythonOutputStream>(std::move(output));
70     auto status = WriteToStream(output_stream.get(), stream_contents);
71     EXPECT_TRUE(status.ok()) << status;
72     EXPECT_EQ(stream_size, output_buffer->size());
73     EXPECT_EQ(stream_contents, *output_buffer);
74   }
75 }
76 
TEST(PythonOutputStreamTest,CustomBufferSizes)77 TEST(PythonOutputStreamTest, CustomBufferSizes) {
78   int stream_size = 1024 * 1024;
79   std::string stream_contents = subtle::Random::GetRandomBytes(stream_size);
80   for (int buffer_size : {1, 10, 100, 1000, 10000, 100000, 1000000}) {
81     SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size));
82     auto output = absl::make_unique<test::TestWritableObject>();
83     std::string* output_buffer = output->GetBuffer();
84     auto output_stream =
85         absl::make_unique<PythonOutputStream>(std::move(output), buffer_size);
86     void* buffer;
87     auto next_result = output_stream->Next(&buffer);
88     EXPECT_TRUE(next_result.ok()) << next_result.status();
89     EXPECT_EQ(buffer_size, next_result.value());
90     output_stream->BackUp(buffer_size);
91     auto status = WriteToStream(output_stream.get(), stream_contents);
92     EXPECT_TRUE(status.ok()) << status;
93     EXPECT_EQ(stream_size, output_buffer->size());
94     EXPECT_EQ(stream_contents, *output_buffer);
95   }
96 }
97 
TEST(PythonOutputStreamTest,BackupAndPosition)98 TEST(PythonOutputStreamTest, BackupAndPosition) {
99   int stream_size = 1024 * 1024;
100   int buffer_size = 1234;
101   void* buffer;
102   std::string stream_contents = subtle::Random::GetRandomBytes(stream_size);
103   auto output = absl::make_unique<test::TestWritableObject>();
104   std::string* output_buffer = output->GetBuffer();
105 
106   // Prepare the stream and do the first call to Next().
107   auto output_stream =
108       absl::make_unique<PythonOutputStream>(std::move(output), buffer_size);
109   EXPECT_EQ(0, output_stream->Position());
110   auto next_result = output_stream->Next(&buffer);
111   EXPECT_TRUE(next_result.ok()) << next_result.status();
112   EXPECT_EQ(buffer_size, next_result.value());
113   EXPECT_EQ(buffer_size, output_stream->Position());
114   std::memcpy(buffer, stream_contents.data(), buffer_size);
115 
116   // BackUp several times, but in total fewer bytes than returned by Next().
117   int total_backup_size = 0;
118   for (int backup_size : {0, 1, 5, 0, 10, 100, -42, 400, 20, -100}) {
119     SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size));
120     output_stream->BackUp(backup_size);
121     total_backup_size += std::max(backup_size, 0);
122     EXPECT_EQ(buffer_size - total_backup_size, output_stream->Position());
123   }
124   EXPECT_LT(total_backup_size, next_result.value());
125 
126   // Call Next(), it should succeed.
127   next_result = output_stream->Next(&buffer);
128   EXPECT_TRUE(next_result.ok()) << next_result.status();
129 
130   // BackUp() some bytes, again fewer than returned by Next().
131   total_backup_size = 0;
132   for (int backup_size : {0, 72, -94, 37, 82}) {
133     SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size));
134     output_stream->BackUp(backup_size);
135     total_backup_size += std::max(0, backup_size);
136     EXPECT_EQ(buffer_size - total_backup_size, output_stream->Position());
137   }
138   EXPECT_LT(total_backup_size, next_result.value());
139 
140   // Call Next(), it should succeed;
141   next_result = output_stream->Next(&buffer);
142   EXPECT_TRUE(next_result.ok()) << next_result.status();
143 
144   // Call Next() again, it should return a full block.
145   auto prev_position = output_stream->Position();
146   next_result = output_stream->Next(&buffer);
147   EXPECT_TRUE(next_result.ok()) << next_result.status();
148   EXPECT_EQ(buffer_size, next_result.value());
149   EXPECT_EQ(prev_position + buffer_size, output_stream->Position());
150   std::memcpy(buffer, stream_contents.data() + buffer_size, buffer_size);
151 
152   // BackUp a few times, with total over the returned buffer_size.
153   total_backup_size = 0;
154   for (int backup_size :
155            {0, 72, -100, buffer_size / 2, 200, -25, buffer_size / 2, 42}) {
156     SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size));
157     output_stream->BackUp(backup_size);
158     total_backup_size = std::min(buffer_size,
159                                  total_backup_size + std::max(backup_size, 0));
160     EXPECT_EQ(prev_position + buffer_size - total_backup_size,
161               output_stream->Position());
162   }
163   EXPECT_EQ(total_backup_size, buffer_size);
164   EXPECT_EQ(prev_position, output_stream->Position());
165 
166   // Call Next() again, it should return a full block.
167   next_result = output_stream->Next(&buffer);
168   EXPECT_TRUE(next_result.ok()) << next_result.status();
169   EXPECT_EQ(buffer_size, next_result.value());
170   EXPECT_EQ(prev_position + buffer_size, output_stream->Position());
171   std::memcpy(buffer, stream_contents.data() + buffer_size, buffer_size);
172 
173   // Write the remaining stream contents to stream.
174   auto status = WriteToStream(
175       output_stream.get(), stream_contents.substr(output_stream->Position()));
176   EXPECT_TRUE(status.ok()) << status;
177   EXPECT_EQ(stream_size, output_buffer->size());
178   EXPECT_EQ(stream_contents, *output_buffer);
179 }
180 
181 }  // namespace
182 }  // namespace tink
183 }  // namespace crypto
184