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 <fcntl.h>
16
17 #include <fstream>
18
19 #include "contrib/brotli/sandboxed.h"
20 #include "contrib/brotli/utils/utils_brotli.h"
21 #include "contrib/brotli/utils/utils_brotli_dec.h"
22 #include "contrib/brotli/utils/utils_brotli_enc.h"
23 #include "sandboxed_api/util/path.h"
24 #include "sandboxed_api/util/status_matchers.h"
25
26 namespace {
27
28 using ::sapi::IsOk;
29
30 class BrotliBase : public testing::Test {
31 protected:
GetTestFilePath(const std::string & filename)32 std::string GetTestFilePath(const std::string& filename) {
33 return sapi::file::JoinPath(test_dir_, filename);
34 }
35
36 void SetUp() override;
37
38 std::unique_ptr<BrotliSandbox> sandbox_;
39 std::unique_ptr<BrotliEncoder> enc_;
40 std::unique_ptr<BrotliDecoder> dec_;
41 const char* test_dir_;
42 };
43
44 class BrotliMultiFile : public BrotliBase,
45 public testing::WithParamInterface<std::string> {};
46
SetUp()47 void BrotliBase::SetUp() {
48 sandbox_ = std::make_unique<BrotliSapiSandbox>();
49 ASSERT_THAT(sandbox_.get()->Init(), IsOk());
50
51 enc_ = std::make_unique<BrotliEncoder>(sandbox_.get());
52 ASSERT_TRUE(enc_.get()->IsInit());
53
54 dec_ = std::make_unique<BrotliDecoder>(sandbox_.get());
55 ASSERT_TRUE(dec_.get()->IsInit());
56
57 test_dir_ = getenv("TEST_FILES_DIR");
58 ASSERT_NE(test_dir_, nullptr);
59 }
60
TEST_F(BrotliBase,SetParamEnc)61 TEST_F(BrotliBase, SetParamEnc) {
62 ASSERT_THAT(enc_.get()->SetParameter(BROTLI_PARAM_QUALITY, 5), IsOk());
63 }
64
TEST_F(BrotliBase,SetParamDec)65 TEST_F(BrotliBase, SetParamDec) {
66 ASSERT_THAT(dec_.get()->SetParameter(
67 BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION, 100),
68 IsOk());
69 }
70
TEST_F(BrotliBase,Compress)71 TEST_F(BrotliBase, Compress) {
72 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufin,
73 ReadFile(GetTestFilePath("text")));
74
75 ASSERT_THAT(enc_.get()->Compress(bufin), IsOk());
76
77 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufout,
78 enc_.get()->TakeOutput());
79 ASSERT_LT(bufout.size(), bufin.size());
80 }
81
TEST_F(BrotliBase,CompressDecompress)82 TEST_F(BrotliBase, CompressDecompress) {
83 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufin,
84 ReadFile(GetTestFilePath("text")));
85
86 ASSERT_THAT(enc_.get()->Compress(bufin), IsOk());
87
88 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufcomp,
89 enc_.get()->TakeOutput());
90 ASSERT_LT(bufcomp.size(), bufin.size());
91
92 SAPI_ASSERT_OK_AND_ASSIGN(BrotliDecoderResult result,
93 dec_.get()->Decompress(bufcomp));
94 ASSERT_THAT(result, BROTLI_DECODER_RESULT_SUCCESS);
95
96 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufout,
97 dec_.get()->TakeOutput());
98 ASSERT_EQ(bufin, bufout);
99 }
100
TEST_F(BrotliBase,CompressStreamDecompress)101 TEST_F(BrotliBase, CompressStreamDecompress) {
102 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> buforig,
103 ReadFile(GetTestFilePath("text")));
104
105 for (auto it = buforig.begin(); it != buforig.end();) {
106 int nsize = std::min<size_t>(512, buforig.end() - it);
107 BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
108 if (it + nsize == buforig.end()) {
109 op = BROTLI_OPERATION_FINISH;
110 }
111 std::vector<uint8_t> bufin(it, it + nsize);
112
113 ASSERT_THAT(enc_.get()->Compress(bufin, op), IsOk());
114 it += nsize;
115 }
116
117 bool empty = false;
118 std::vector<uint8_t> bufcomp;
119 while (!empty) {
120 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> takebuf,
121 enc_.get()->TakeOutput());
122 empty = takebuf.empty();
123 if (!empty) {
124 bufcomp.insert(bufcomp.end(), takebuf.begin(), takebuf.end());
125 }
126 }
127
128 SAPI_ASSERT_OK_AND_ASSIGN(BrotliDecoderResult result,
129 dec_.get()->Decompress(bufcomp));
130 ASSERT_THAT(result, BROTLI_DECODER_RESULT_SUCCESS);
131
132 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufout,
133 dec_.get()->TakeOutput());
134 ASSERT_EQ(buforig, bufout);
135 }
136
TEST_P(BrotliMultiFile,Decompress)137 TEST_P(BrotliMultiFile, Decompress) {
138 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> buforig,
139 ReadFile(GetTestFilePath("text")));
140 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufin,
141 ReadFile(GetTestFilePath(GetParam())));
142
143 SAPI_ASSERT_OK_AND_ASSIGN(BrotliDecoderResult result,
144 dec_.get()->Decompress(bufin));
145 ASSERT_THAT(result, BROTLI_DECODER_RESULT_SUCCESS);
146
147 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufout,
148 dec_.get()->TakeOutput());
149 ASSERT_EQ(buforig, bufout);
150 }
151
TEST_P(BrotliMultiFile,DecompressCharStream)152 TEST_P(BrotliMultiFile, DecompressCharStream) {
153 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> buforig,
154 ReadFile(GetTestFilePath("text")));
155 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufcomp,
156 ReadFile(GetTestFilePath(GetParam())));
157
158 BrotliDecoderResult result;
159 std::vector<uint8_t> bufout;
160
161 for (auto it = bufcomp.begin(); it != bufcomp.end(); ++it) {
162 std::vector<uint8_t> tmp(it, it + 1);
163
164 SAPI_ASSERT_OK_AND_ASSIGN(result, dec_.get()->Decompress(tmp));
165 if (result == BROTLI_DECODER_RESULT_SUCCESS) {
166 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufuncomptmp,
167 dec_.get()->TakeOutput());
168 bufout.insert(bufout.end(), bufuncomptmp.begin(), bufuncomptmp.end());
169 }
170 }
171 ASSERT_THAT(result, BROTLI_DECODER_RESULT_SUCCESS);
172
173 ASSERT_EQ(buforig, bufout);
174 }
175
TEST_P(BrotliMultiFile,DecompressChunksStream)176 TEST_P(BrotliMultiFile, DecompressChunksStream) {
177 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> buforig,
178 ReadFile(GetTestFilePath("text")));
179 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufcomp,
180 ReadFile(GetTestFilePath(GetParam())));
181
182 std::vector<size_t> chunks = {128, 256, 13, 37, 99, 10, 42};
183
184 BrotliDecoderResult result;
185 std::vector<uint8_t> bufout;
186 std::vector<uint8_t>::iterator it = bufcomp.begin();
187
188 for (int i = 0; it != bufcomp.end(); ++i) {
189 size_t nsize =
190 std::min<size_t>(bufcomp.end() - it, chunks[i % chunks.size()]);
191 std::vector<uint8_t> tmp(it, it + nsize);
192 it += nsize;
193
194 SAPI_ASSERT_OK_AND_ASSIGN(result, dec_.get()->Decompress(tmp));
195 if (result == BROTLI_DECODER_RESULT_SUCCESS) {
196 SAPI_ASSERT_OK_AND_ASSIGN(std::vector<uint8_t> bufuncomptmp,
197 dec_.get()->TakeOutput());
198 bufout.insert(bufout.end(), bufuncomptmp.begin(), bufuncomptmp.end());
199 }
200 }
201
202 ASSERT_THAT(result, BROTLI_DECODER_RESULT_SUCCESS);
203 ASSERT_EQ(buforig, bufout);
204 }
205
206 INSTANTIATE_TEST_SUITE_P(BrotliBase, BrotliMultiFile,
207 testing::Values("text.full.brotli",
208 "text.chunk.brotli"));
209 } // namespace
210