1 /** @file
2
3 Unit tests for BufferWriter.h.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "catch.hpp"
25 #include "tscore/BufferWriter.h"
26 #include <cstring>
27
28 namespace
29 {
30 std::string_view three[] = {"a", "", "bcd"};
31 }
32
33 TEST_CASE("BufferWriter::write(StringView)", "[BWWSV]")
34 {
35 class X : public ts::BufferWriter
36 {
37 size_t i, j;
38
39 public:
40 bool good;
41
X()42 X() : i(0), j(0), good(true) {}
43
44 X &
write(char c)45 write(char c) override
46 {
47 while (j == three[i].size()) {
48 ++i;
49 j = 0;
50 }
51
52 if ((i >= 3) or (c != three[i][j])) {
53 good = false;
54 }
55
56 ++j;
57
58 return *this;
59 }
60
61 bool
error() const62 error() const override
63 {
64 return false;
65 }
66
67 // Dummies.
68 const char *
data() const69 data() const override
70 {
71 return nullptr;
72 }
73 size_t
capacity() const74 capacity() const override
75 {
76 return 0;
77 }
78 size_t
extent() const79 extent() const override
80 {
81 return 0;
82 }
clip(size_t)83 X &clip(size_t) override { return *this; }
extend(size_t)84 X &extend(size_t) override { return *this; }
85 std::ostream &
operator >>(std::ostream & stream) const86 operator>>(std::ostream &stream) const override
87 {
88 return stream;
89 }
90 ssize_t
operator >>(int fd) const91 operator>>(int fd) const override
92 {
93 return 0;
94 }
95 };
96
97 X x;
98
99 static_cast<ts::BufferWriter &>(x).write(three[0]).write(three[1]).write(three[2]);
100
101 REQUIRE(x.good);
102 }
103
104 namespace
105 {
106 template <size_t N> using LBW = ts::LocalBufferWriter<N>;
107 }
108
109 TEST_CASE("Minimal Local Buffer Writer", "[BWLM]")
110 {
111 LBW<1> bw;
112
113 REQUIRE(!((bw.capacity() != 1) or (bw.size() != 0) or bw.error() or (bw.remaining() != 1)));
114
115 bw.write('#');
116
117 REQUIRE(!((bw.capacity() != 1) or (bw.size() != 1) or bw.error() or (bw.remaining() != 0)));
118
119 REQUIRE(bw.view() == "#");
120
121 bw.write('#');
122
123 REQUIRE(bw.error());
124
125 bw.reduce(1);
126
127 REQUIRE(!((bw.capacity() != 1) or (bw.size() != 1) or bw.error() or (bw.remaining() != 0)));
128
129 REQUIRE(bw.view() == "#");
130 }
131
132 namespace
133 {
134 template <class BWType>
135 bool
twice(BWType & bw)136 twice(BWType &bw)
137 {
138 if ((bw.capacity() != 20) or (bw.size() != 0) or bw.error() or (bw.remaining() != 20)) {
139 return false;
140 }
141
142 bw.write('T');
143
144 if ((bw.capacity() != 20) or (bw.size() != 1) or bw.error() or (bw.remaining() != 19)) {
145 return false;
146 }
147
148 if (bw.view() != "T") {
149 return false;
150 }
151
152 bw.write("he").write(' ').write("quick").write(' ').write("brown");
153
154 if ((bw.capacity() != 20) or bw.error() or (bw.remaining() != (21 - sizeof("The quick brown")))) {
155 return false;
156 }
157
158 if (bw.view() != "The quick brown") {
159 return false;
160 }
161
162 bw.reduce(0);
163
164 bw << "The" << ' ' << "quick" << ' ' << "brown";
165
166 if ((bw.capacity() != 20) or bw.error() or (bw.remaining() != (21 - sizeof("The quick brown")))) {
167 return false;
168 }
169
170 if (bw.view() != "The quick brown") {
171 return false;
172 }
173
174 bw.reduce(0);
175
176 bw.write("The", 3).write(' ').write("quick", 5).write(' ').write(std::string_view("brown", 5));
177
178 if ((bw.capacity() != 20) or bw.error() or (bw.remaining() != (21 - sizeof("The quick brown")))) {
179 return false;
180 }
181
182 if (bw.view() != "The quick brown") {
183 return false;
184 }
185
186 std::strcpy(bw.auxBuffer(), " fox");
187 bw.fill(sizeof(" fox") - 1);
188
189 if (bw.error()) {
190 return false;
191 }
192
193 if (bw.view() != "The quick brown fox") {
194 return false;
195 }
196
197 bw.write('x');
198
199 if (bw.error()) {
200 return false;
201 }
202
203 bw.write('x');
204
205 if (!bw.error()) {
206 return false;
207 }
208
209 bw.write('x');
210
211 if (!bw.error()) {
212 return false;
213 }
214
215 bw.reduce(sizeof("The quick brown fox") - 1);
216
217 if (bw.error()) {
218 return false;
219 }
220
221 if (bw.view() != "The quick brown fox") {
222 return false;
223 }
224
225 bw.reduce(sizeof("The quick brown") - 1);
226 bw.clip(bw.capacity() + 2 - (sizeof("The quick brown fox") - 1)).write(" fox");
227
228 if (bw.view() != "The quick brown f") {
229 return false;
230 }
231
232 if (!bw.error()) {
233 return false;
234 }
235
236 bw.extend(2).write("ox");
237
238 if (bw.error()) {
239 return false;
240 }
241
242 if (bw.view() != "The quick brown fox") {
243 return false;
244 }
245
246 return true;
247 }
248
249 } // end anonymous namespace
250
251 TEST_CASE("Concrete Buffer Writers 2", "[BWC2]")
252 {
253 LBW<20> bw;
254
255 REQUIRE(twice(bw));
256
257 char space[21];
258
259 space[20] = '!';
260
261 ts::FixedBufferWriter fbw(space, 20);
262
263 REQUIRE(twice(fbw));
264
265 REQUIRE(space[20] == '!');
266
267 LBW<20> bw20(bw);
268 LBW<30> bw30(bw); // test cross length constructors
269 LBW<10> bw10(bw);
270
271 REQUIRE(bw20.view() == "The quick brown fox");
272
273 bw30 = bw20;
274 REQUIRE(bw30.view() == "The quick brown fox");
275
276 bw10 = bw20;
277 REQUIRE(bw10.view() == "The quick ");
278 bw10.reduce(0);
279 bw10.write("01234567890123456789");
280 REQUIRE(bw10.extent() == 20);
281 REQUIRE(bw10.view() == "0123456789");
282 REQUIRE(bw10.remaining() == 0);
283 bw20 = bw10;
284 REQUIRE(bw20.view() == "0123456789");
285 REQUIRE(bw20.extent() == 10);
286 REQUIRE(bw20.size() == 10);
287
288 ts::FixedBufferWriter abw{bw20.auxWriter()};
289 REQUIRE(abw.remaining() == 10);
290 abw.write("abcdefghijklmnopqrstuvwxyz");
291 bw20.fill(abw.extent());
292 REQUIRE(bw20.size() == 20);
293 REQUIRE(bw20.extent() == 36);
294 REQUIRE(bw20.view() == "0123456789abcdefghij");
295 }
296
297 TEST_CASE("Discard Buffer Writer", "[BWD]")
298 {
299 char scratch[1] = {'!'};
300 ts::FixedBufferWriter bw(scratch, 0);
301
302 REQUIRE(bw.size() == 0);
303 REQUIRE(bw.extent() == 0);
304
305 bw.write('T');
306
307 REQUIRE(bw.size() == 0);
308 REQUIRE(bw.extent() == 1);
309
310 bw.write("he").write(' ').write("quick").write(' ').write("brown");
311
312 REQUIRE(bw.size() == 0);
313 REQUIRE(bw.extent() == (sizeof("The quick brown") - 1));
314
315 bw.reduce(0);
316
317 bw.write("The", 3).write(' ').write("quick", 5).write(' ').write(std::string_view("brown", 5));
318
319 REQUIRE(bw.size() == 0);
320 REQUIRE(bw.extent() == (sizeof("The quick brown") - 1));
321
322 bw.fill(sizeof(" fox") - 1);
323
324 REQUIRE(bw.size() == 0);
325 REQUIRE(bw.extent() == (sizeof("The quick brown fox") - 1));
326
327 bw.reduce(sizeof("The quick brown fox") - 1);
328
329 REQUIRE(bw.size() == 0);
330 REQUIRE(bw.extent() == (sizeof("The quick brown fox") - 1));
331
332 bw.reduce(sizeof("The quick brown") - 1);
333
334 REQUIRE(bw.size() == 0);
335 REQUIRE(bw.extent() == (sizeof("The quick brown") - 1));
336
337 // Make sure no actual writing.
338 //
339 REQUIRE(scratch[0] == '!');
340 }
341
342 TEST_CASE("LocalBufferWriter clip and extend")
343 {
344 ts::LocalBufferWriter<10> bw;
345
346 bw.clip(7);
347 bw << "aaaaaa";
348 REQUIRE(bw.view() == "aaa");
349
350 bw.extend(3);
351 bw << "bbbbbb";
352 REQUIRE(bw.view() == "aaabbb");
353
354 bw.extend(4);
355 bw.fill(static_cast<size_t>(snprintf(bw.auxBuffer(), bw.remaining(), "ccc")));
356 REQUIRE(bw.view() == "aaabbbccc");
357 }
358