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