1 // Copyright 2022 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 // https://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 #include <fstream>
16
17 #include "contrib/c-blosc/sandboxed.h"
18 #include "contrib/c-blosc/utils/utils_blosc.h"
19 #include "sandboxed_api/util/path.h"
20 #include "sandboxed_api/util/status_matchers.h"
21 #include "sandboxed_api/util/temp_file.h"
22
23 namespace {
24
25 using ::sapi::IsOk;
26
27 constexpr size_t kDefaultBlockSize = 19059;
28
CompareFiles(const std::string & name1,const std::string & name2)29 bool CompareFiles(const std::string& name1, const std::string& name2) {
30 std::ifstream f1(name1, std::ios::binary);
31 if (!f1.is_open()) {
32 return false;
33 }
34
35 std::ifstream f2(name2, std::ios::binary);
36 if (!f2.is_open()) {
37 return false;
38 }
39
40 while (!f1.eof() && !f2.eof()) {
41 char buf1[128];
42 char buf2[128];
43
44 f1.read(buf1, sizeof(buf1));
45 f2.read(buf2, sizeof(buf2));
46
47 if (f1.gcount() != f2.gcount()) {
48 return false;
49 }
50 if (memcmp(&buf1, &buf2, f2.gcount()) != 0) {
51 return false;
52 }
53 }
54
55 return f1.eof() == f2.eof();
56 }
57
GetTestFilePath(const std::string & filename)58 std::string GetTestFilePath(const std::string& filename) {
59 return sapi::file::JoinPath(getenv("TEST_FILES_DIR"), filename);
60 }
61
GetTemporaryFile(const std::string & filename)62 std::string GetTemporaryFile(const std::string& filename) {
63 absl::StatusOr<std::string> tmp_file =
64 sapi::CreateNamedTempFileAndClose(filename);
65 if (!tmp_file.ok()) {
66 return "";
67 }
68
69 return sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *tmp_file);
70 }
71
GetStreamSize(std::ifstream & stream)72 std::streamsize GetStreamSize(std::ifstream& stream) {
73 stream.seekg(0, std::ios_base::end);
74 std::streamsize ssize = stream.tellg();
75 stream.seekg(0, std::ios_base::beg);
76
77 return ssize;
78 }
79
80 class TestText : public testing::TestWithParam<std::string> {};
81
TEST(SandboxTest,CheckInit)82 TEST(SandboxTest, CheckInit) {
83 CbloscSapiSandbox sandbox;
84 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
85 CbloscApi api = CbloscApi(&sandbox);
86
87 ASSERT_THAT(api.blosc_init(), IsOk());
88 }
89
TEST(SandboxTest,CheckDestroy)90 TEST(SandboxTest, CheckDestroy) {
91 CbloscSapiSandbox sandbox;
92 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
93 CbloscApi api = CbloscApi(&sandbox);
94
95 ASSERT_THAT(api.blosc_init(), IsOk());
96 ASSERT_THAT(api.blosc_destroy(), IsOk());
97 }
98
TEST(SandboxTest,CheckGetNThreads)99 TEST(SandboxTest, CheckGetNThreads) {
100 CbloscSapiSandbox sandbox;
101 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
102 CbloscApi api = CbloscApi(&sandbox);
103
104 SAPI_ASSERT_OK_AND_ASSIGN(int nthreads, api.blosc_get_nthreads());
105
106 ASSERT_GT(nthreads, 0);
107 }
108
TEST(SandboxTest,CheckSetNThreads)109 TEST(SandboxTest, CheckSetNThreads) {
110 CbloscSapiSandbox sandbox;
111 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
112 CbloscApi api = CbloscApi(&sandbox);
113
114 int nthreads;
115 SAPI_ASSERT_OK_AND_ASSIGN(nthreads, api.blosc_get_nthreads());
116 ASSERT_NE(nthreads, 3);
117 ASSERT_THAT(api.blosc_set_nthreads(3), IsOk());
118 SAPI_ASSERT_OK_AND_ASSIGN(nthreads, api.blosc_get_nthreads());
119 ASSERT_EQ(nthreads, 3);
120 }
121
TEST(SandboxTest,CheckGetBlocksize)122 TEST(SandboxTest, CheckGetBlocksize) {
123 CbloscSapiSandbox sandbox;
124 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
125 CbloscApi api = CbloscApi(&sandbox);
126
127 SAPI_ASSERT_OK_AND_ASSIGN(size_t blocksize, api.blosc_get_blocksize());
128 ASSERT_NE(blocksize, kDefaultBlockSize);
129 }
130
TEST(SandboxTest,CheckSetBlocksize)131 TEST(SandboxTest, CheckSetBlocksize) {
132 CbloscSapiSandbox sandbox;
133 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
134 CbloscApi api = CbloscApi(&sandbox);
135
136 size_t blocksize;
137 SAPI_ASSERT_OK_AND_ASSIGN(blocksize, api.blosc_get_blocksize());
138 ASSERT_NE(blocksize, 1337);
139 ASSERT_THAT(api.blosc_set_blocksize(1337), IsOk());
140 SAPI_ASSERT_OK_AND_ASSIGN(blocksize, api.blosc_get_blocksize());
141 ASSERT_EQ(blocksize, 1337);
142 }
143
TEST_P(TestText,CheckSizes)144 TEST_P(TestText, CheckSizes) {
145 absl::Status status;
146 CbloscSapiSandbox sandbox;
147 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
148 CbloscApi api = CbloscApi(&sandbox);
149
150 std::string compressor(GetParam());
151
152 std::string origfile_s = GetTestFilePath("text");
153 std::string infile_s = GetTestFilePath(absl::StrCat("text.", compressor));
154
155 std::ifstream origfile(origfile_s, std::ios::binary);
156 ASSERT_TRUE(origfile.is_open());
157 ssize_t origsize = GetStreamSize(origfile);
158
159 std::ifstream infile(infile_s, std::ios::binary);
160 ASSERT_TRUE(infile.is_open());
161
162 std::streamsize insize = GetStreamSize(infile);
163 sapi::v::Array<uint8_t> inbuf(insize);
164 infile.read(reinterpret_cast<char*>(inbuf.GetData()), insize);
165
166 sapi::v::IntBase<size_t> nbytes;
167 sapi::v::IntBase<size_t> cbytes;
168 sapi::v::IntBase<size_t> blocksize;
169
170 ASSERT_THAT(api.blosc_cbuffer_sizes(inbuf.PtrBefore(), nbytes.PtrAfter(),
171 cbytes.PtrAfter(), blocksize.PtrAfter()),
172 IsOk());
173
174 ASSERT_EQ(nbytes.GetValue(), origsize);
175 ASSERT_EQ(cbytes.GetValue(), insize);
176 ASSERT_EQ(blocksize.GetValue(), kDefaultBlockSize);
177 }
178
TEST_P(TestText,CheckValidate)179 TEST_P(TestText, CheckValidate) {
180 absl::Status status;
181 CbloscSapiSandbox sandbox;
182 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
183 CbloscApi api = CbloscApi(&sandbox);
184
185 std::string compressor(GetParam());
186
187 std::string origfile_s = GetTestFilePath("text");
188 std::string infile_s = GetTestFilePath(absl::StrCat("text.", compressor));
189
190 std::ifstream origfile(origfile_s, std::ios::binary);
191 ASSERT_TRUE(origfile.is_open());
192 ssize_t origsize = GetStreamSize(origfile);
193
194 std::ifstream infile(infile_s, std::ios::binary);
195 ASSERT_TRUE(infile.is_open());
196
197 std::streamsize insize = GetStreamSize(infile);
198 sapi::v::Array<uint8_t> inbuf(insize);
199 infile.read(reinterpret_cast<char*>(inbuf.GetData()), insize);
200
201 sapi::v::IntBase<size_t> nbytes;
202
203 SAPI_ASSERT_OK_AND_ASSIGN(
204 int ret, api.blosc_cbuffer_validate(inbuf.PtrBefore(), inbuf.GetSize(),
205 nbytes.PtrAfter()));
206
207 ASSERT_GE(ret, 0);
208 ASSERT_EQ(nbytes.GetValue(), origsize);
209 }
210
TEST_P(TestText,SetCompress)211 TEST_P(TestText, SetCompress) {
212 CbloscSapiSandbox sandbox;
213 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
214 CbloscApi api = CbloscApi(&sandbox);
215
216 std::string compressor(GetParam());
217 SAPI_ASSERT_OK_AND_ASSIGN(
218 int ret, api.blosc_set_compressor(
219 sapi::v::ConstCStr(compressor.c_str()).PtrBefore()));
220 ASSERT_GE(ret, 0);
221
222 SAPI_ASSERT_OK_AND_ASSIGN(char* c_compressor_ret, api.blosc_get_compressor());
223 SAPI_ASSERT_OK_AND_ASSIGN(
224 std::string compressor_ret,
225 api.GetSandbox()->GetCString(sapi::v::RemotePtr(c_compressor_ret)));
226
227 EXPECT_EQ(compressor_ret, compressor);
228 }
229
TEST_P(TestText,Compress)230 TEST_P(TestText, Compress) {
231 CbloscSapiSandbox sandbox;
232 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
233 CbloscApi api = CbloscApi(&sandbox);
234
235 std::string compressor(GetParam());
236
237 std::string infile_s = GetTestFilePath("text");
238 std::string outfile_s = GetTemporaryFile(absl::StrCat("out", compressor));
239 ASSERT_FALSE(outfile_s.empty());
240
241 std::ifstream infile(infile_s, std::ios::binary);
242 ASSERT_TRUE(infile.is_open());
243
244 std::ofstream outfile(outfile_s, std::ios::binary);
245 ASSERT_TRUE(outfile.is_open());
246
247 absl::Status status = Compress(api, infile, outfile, 5, compressor, 5);
248 ASSERT_THAT(status, IsOk()) << "Unable to compress file";
249
250 ASSERT_LT(outfile.tellp(), infile.tellg());
251 }
252
TEST_P(TestText,Decompress)253 TEST_P(TestText, Decompress) {
254 absl::Status status;
255 CbloscSapiSandbox sandbox;
256 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
257 CbloscApi api = CbloscApi(&sandbox);
258
259 std::string compressor(GetParam());
260
261 std::string origfile_s = GetTestFilePath("text");
262 std::string infile_s = GetTestFilePath(absl::StrCat("text.", compressor));
263 std::string outfile_s = GetTemporaryFile(absl::StrCat("middle", compressor));
264 ASSERT_FALSE(outfile_s.empty());
265
266 std::ifstream infile(infile_s, std::ios::binary);
267 ASSERT_TRUE(infile.is_open());
268
269 std::ofstream outfile(outfile_s, std::ios::binary);
270 ASSERT_TRUE(outfile.is_open());
271
272 status = Decompress(api, infile, outfile, 5);
273 ASSERT_THAT(status, IsOk()) << "Unable to compress file";
274
275 ASSERT_GT(outfile.tellp(), infile.tellg());
276
277 ASSERT_TRUE(CompareFiles(origfile_s, outfile_s));
278 }
279
TEST_P(TestText,CompressDecompress)280 TEST_P(TestText, CompressDecompress) {
281 absl::Status status;
282 CbloscSapiSandbox sandbox;
283 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
284 CbloscApi api = CbloscApi(&sandbox);
285
286 std::string compressor(GetParam());
287
288 std::string infile_s = GetTestFilePath("text");
289 std::string middlefile_s =
290 GetTemporaryFile(absl::StrCat("middle", compressor));
291 ASSERT_FALSE(middlefile_s.empty());
292
293 std::ifstream infile(infile_s, std::ios::binary);
294 ASSERT_TRUE(infile.is_open());
295
296 std::ofstream outmiddlefile(middlefile_s, std::ios::binary);
297 ASSERT_TRUE(outmiddlefile.is_open());
298
299 status = Compress(api, infile, outmiddlefile, 5, compressor, 5);
300 ASSERT_THAT(status, IsOk()) << "Unable to compress file";
301
302 ASSERT_LT(outmiddlefile.tellp(), infile.tellg());
303
304 std::string outfile_s = GetTemporaryFile(absl::StrCat("out", compressor));
305 ASSERT_FALSE(outfile_s.empty());
306
307 std::ifstream inmiddlefile(middlefile_s, std::ios::binary);
308 ASSERT_TRUE(inmiddlefile.is_open());
309
310 std::ofstream outfile(outfile_s, std::ios::binary);
311 ASSERT_TRUE(outfile.is_open());
312
313 status = Decompress(api, inmiddlefile, outfile, 5);
314 ASSERT_THAT(status, IsOk()) << "Unable to compress file";
315
316 ASSERT_GT(outfile.tellp(), inmiddlefile.tellg());
317
318 ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
319 }
320
321 INSTANTIATE_TEST_SUITE_P(SandboxTest, TestText,
322 testing::Values("blosclz", "lz4", "lz4hc", "zlib",
323 "zstd"));
324
325 } // namespace
326