scclib
Stable Cloud Computing C++ Library
iostream.cc
Go to the documentation of this file.
1 /*
2 BSD 3-Clause License
3 
4 Copyright (c) 2022, Stable Cloud Computing, Inc.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 1. Redistributions of source code must retain the above copyright notice, this
10  list of conditions and the following disclaimer.
11 
12 2. Redistributions in binary form must reproduce the above copyright notice,
13  this list of conditions and the following disclaimer in the documentation
14  and/or other materials provided with the distribution.
15 
16 3. Neither the name of the copyright holder nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <util/iostream.h>
32 #include <util/rwloopbuf.h>
33 #include <util/rwcounter.h>
34 #include <gtest/gtest.h>
35 #include <iostream>
36 #include <string>
37 #include <vector>
38 #include <sstream>
39 
46 using std::cout;
47 using std::endl;
48 using std::string;
49 using std::stringstream;
50 using std::ios;
56 using scc::util::Reader;
57 using scc::util::Writer;
58 
60 static string test_word = "QuotesFromOscarWilde";
61 static string test_line = "It is always a silly thing to give advice, but to give good advice is fatal.";
62 static string test_long =
63  "One can survive everything, nowadays, except death, and live down everything except a good reputation.\n"
64  "One should always play fairly when one has the winning cards.\n"
65  "Patriotism is the virtue of the vicious.\n"
66  "Selfishness is not living as one wishes to live, it is asking others to live as one wishes to live.";
67 
68 TEST(iostream_test, example_decorator_stack)
69 {
70  RwLoopBuffer rwbuf; // loopback stream buffer
71  RwCounter rwcount(rwbuf, rwbuf); // decorator provides read() and write() for the stream buffer
72  IoStream rws(rwcount, rwcount); // provides std::iostream
73 
74  std::stringstream sstr; // std::stringstream is a std::iostream
75 
76  // verify stream write behavior
77 
78  rws << test_line << endl; // std::endl writes a newline char and flushes the stream
79  rws << test_long << endl;
80  sstr << test_line << endl;
81  sstr << test_long << endl;
82 
83  ASSERT_EQ(rwbuf.str(), sstr.str());
84 
85  ASSERT_EQ(rwcount.write_count(), sstr.str().size());
86  ASSERT_EQ(rwcount.read_count(), 0);
87 
88  // verify stream read behavior
89 
90  string reads, tests;
91  while (sstr >> reads)
92  {
93  ASSERT_TRUE(rws >> tests);
94  ASSERT_EQ(reads, tests);
95  }
96 
97  ASSERT_FALSE(rws >> tests); // verify the RwLoopBuffer stream is done as well
98 
99  ASSERT_EQ(rwcount.read_count(), sstr.str().size());
100 
101  ASSERT_EQ(rwbuf.str().size(), 0); // the loopback buffer is now empty
102 }
103 
104 struct iostreamTest : public testing::Test
105 {
106  std::vector<string> all;
107  iostreamTest()
108  {
109  all.push_back(test_word);
110  all.push_back(test_line);
111  all.push_back(test_long);
112  }
113  virtual ~iostreamTest() {}
114 
115  void readwords_test(const string s, int recvbuf_size=10)
116  {
117  stringstream sstr(s);
118  RwLoopBuffer rw(s);
119  InStream ins(rw);
120  ins.recvbuf_size(recvbuf_size);
121 
122  ASSERT_EQ(ins.recvbuf_size(), recvbuf_size);
123 
124  cout << "readwords_test: " << s.substr(0,10) << "... size=" << s.size() << " recvbuf_size= " << ins.recvbuf_size() << endl;
125 
126  string reads, tests;
127  while (sstr >> reads)
128  {
129  ASSERT_TRUE(ins >> tests);
130  ASSERT_EQ(reads, tests);
131  }
132  }
133  void readlines_test(const string s, int recvbuf_size=10)
134  {
135  stringstream sstr(s);
136  RwLoopBuffer rw(s);
137  InStream ins(rw);
138  ins.recvbuf_size(recvbuf_size);
139 
140  ASSERT_EQ(ins.recvbuf_size(), recvbuf_size);
141 
142  cout << "readlines_test: " << s.substr(0,10) << "... size=" << s.size() << " recvbuf_size= " << ins.recvbuf_size() << endl;
143 
144  string reads, tests;
145  while (std::getline(sstr, reads))
146  {
147  ASSERT_TRUE(std::getline(ins, tests));
148  ASSERT_EQ(reads, tests);
149  }
150  }
151  void writewords_test(const string s, int sendbuf_size=10)
152  {
153  stringstream sstr(s);
154  RwLoopBuffer rw;
155  OutStream ous(rw);
156  ous.sendbuf_size(sendbuf_size);
157 
158  ASSERT_EQ(ous.sendbuf_size(), sendbuf_size);
159 
160  cout << "writewords_test: " << s.substr(0,10) << "... size=" << s.size() << " sendbuf_size= " << ous.sendbuf_size() << endl;
161 
162  string reads;
163  stringstream writess;
164  while (sstr >> reads)
165  {
166  writess << reads;
167  ASSERT_TRUE(ous << reads);
168  }
169  writess.flush();
170  ous.flush();
171  ASSERT_EQ(writess.str(), rw.str());
172  }
173  void writelines_test(const string s, int sendbuf_size=10)
174  {
175  stringstream sstr(s);
176  RwLoopBuffer rw;
177  OutStream ous(rw);
178  ous.sendbuf_size(sendbuf_size);
179 
180  ASSERT_EQ(ous.sendbuf_size(), sendbuf_size);
181 
182  cout << "writelines_test: " << s.substr(0,10) << "... size=" << s.size() << " sendbuf_size= " << ous.sendbuf_size() << endl;
183 
184  string reads;
185  stringstream writess;
186  while (std::getline(sstr, reads))
187  {
188  writess << reads << endl;
189  ASSERT_TRUE(ous << reads << endl);
190  }
191  writess.flush();
192  ous.flush();
193  ASSERT_EQ(writess.str(), rw.str());
194  }
195  void readchunks_test(const string s, int rbuf1=10, int rsz1=1024, int rbuf2=10)
196  {
197  RwLoopBuffer rw(s);
198  InStream ins(rw);
199 
200  ins.recvbuf_size(rbuf1);
201  ASSERT_EQ(ins.recvbuf_size(), rbuf1);
202 
203  int toread = s.size();
204  cout << "readchunks_test1: " << s.substr(0,10) << "... read=" << rsz1 << " toread=" << toread
205  << " recvbuf_size= " << ins.recvbuf_size() << endl;
206  char bin1[rsz1];
207  ins.read(bin1, rsz1);
208  auto lastread = ins.gcount();
209  ASSERT_EQ(lastread, std::min(rsz1, toread));
210  ASSERT_EQ(memcmp(s.data(), bin1, lastread), 0);
211  toread -= lastread;
212  if (lastread < rsz1)
213  {
214  ASSERT_EQ((ins.rdstate()&std::ios_base::eofbit), std::ios_base::eofbit);
215  }
216 
217  if (toread)
218  {
219  ins.recvbuf_size(rbuf2);
220  ASSERT_EQ(ins.recvbuf_size(), rbuf2);
221 
222  cout << "readchunks_test2: " << s.substr(0,10) << "... read=" << toread << " toread=" << toread
223  << " recvbuf_size= " << ins.recvbuf_size() << endl;
224  char bin2[toread];
225  ins.read(bin2, toread);
226  auto lastread2 = ins.gcount();
227  ASSERT_EQ(lastread2, toread);
228  ASSERT_EQ(memcmp(s.data()+lastread, bin2, lastread2), 0);
229  ASSERT_NE((ins.rdstate()&std::ios_base::eofbit), std::ios_base::eofbit);
230  }
231  }
232  void writechunks_test(const string s, int wbuf1=10, int wsz1=1024, int wbuf2=10)
233  {
234  RwLoopBuffer rw;
235  OutStream ous(rw);
236 
237  ous.sendbuf_size(wbuf1);
238  ASSERT_EQ(ous.sendbuf_size(), wbuf1);
239 
240  int towrite = s.size();
241  wsz1 = std::min(wsz1, towrite);
242  cout << "writechunks_test1: " << s.substr(0,10) << "... write=" << wsz1 << " towrite=" << towrite
243  << " sendbuf_size= " << ous.sendbuf_size() << endl;
244  ASSERT_TRUE(ous.write(s.data(), wsz1));
245 
246  towrite -= wsz1;
247 
248  if (towrite)
249  {
250  ous.sendbuf_size(wbuf2);
251  ASSERT_EQ(ous.sendbuf_size(), wbuf2);
252 
253  cout << "writechunks_test2: " << s.substr(0,10) << "... write=" << towrite << " towrite=" << towrite
254  << " sendbuf_size= " << ous.sendbuf_size() << endl;
255 
256  ASSERT_TRUE(ous.write(s.data()+wsz1, towrite));
257  }
258  ous.flush();
259  ASSERT_EQ(s, rw.str());
260  }
261 };
262 
263 TEST_F(iostreamTest, read)
264 {
265  for (auto& s : all)
266  {
267  readwords_test(s);
268  }
269  for (auto& s : all)
270  {
271  readlines_test(s);
272  }
273 }
274 
275 TEST_F(iostreamTest, write)
276 {
277  for (auto& s : all)
278  {
279  writewords_test(s);
280  }
281  for (auto& s : all)
282  {
283  writelines_test(s);
284  }
285 }
286 
287 TEST_F(iostreamTest, readchunks)
288 {
289  for (auto& s : all)
290  {
291  readchunks_test(s); // buf 10, read all
292  readchunks_test(s, 10, 5, 20); // buf 10, read 5, buf 20, read all
293  readchunks_test(s, 10, 8, 5); // buf 10, read 8, buf 5, read all
294  }
295 }
296 
297 TEST_F(iostreamTest, writechunks)
298 {
299  for (auto& s : all)
300  {
301  writechunks_test(s); // buf 10, write all
302  writechunks_test(s, 10, 5, 20); // buf 10, write 5, buf 20, write all
303  writechunks_test(s, 10, 8, 5); // buf 10, write 8, buf 5, write all
304  }
305 }
306 
307 TEST(iostream_test, write_read)
308 {
309  string val("this\nis\na\ntest\n");
310  RwLoopBuffer rw;
311  IoStream ios(rw, rw);
312 
313  ASSERT_TRUE(ios.write(val.data(), val.size()));
314  ios.flush();
315  char buf[sizeof(val)];
316  ASSERT_TRUE(ios.read(buf, val.size()));
317  ASSERT_EQ(ios.gcount(), val.size());
318  ASSERT_EQ(memcmp(buf, val.data(), val.size()), 0);
319  ASSERT_FALSE(ios.read(buf, val.size()));
320 }
321 
322 TEST(iostream_test, stream_write_read)
323 {
324  string val("this\nis\na\ntest\n");
325  RwLoopBuffer rw;
326  IoStream ios(rw, rw);
327 
328  ASSERT_TRUE(ios << val);
329  ios.flush();
330  string s;
331  ASSERT_TRUE(ios >> s);
332  ASSERT_EQ(s, "this");
333  ASSERT_TRUE(ios >> s);
334  ASSERT_EQ(s, "is");
335  ASSERT_TRUE(ios >> s);
336  ASSERT_EQ(s, "a");
337  ASSERT_TRUE(ios >> s);
338  ASSERT_EQ(s, "test");
339 }
340 string printstate(int state)
341 {
342  stringstream st;
343  st << "state: ";
344  if (state == 0) st << "good ";
345  if ((state&std::ios_base::badbit)==std::ios_base::badbit) st << "bad ";
346  if ((state&std::ios_base::failbit)==std::ios_base::failbit) st << "fail ";
347  if ((state&std::ios_base::eofbit)==std::ios_base::eofbit) st << "eof ";
348  return st.str();
349 }
350 
351 TEST(iostream_test, write_buffer_states_test)
352 {
353  RwLoopBuffer rw;
354  OutStream st(rw, 10);
355 
356  string s1 = "This is a test", s2 = " of the stream bit stuff.";
357 
358  cout << "***OutStream test" << endl;
359  cout << "Before " << printstate(st.rdstate()) << endl;
360  ASSERT_EQ(st.rdstate(), 0);
361  st << s1;
362  cout << "write '"<<s1<<"' string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
363  ASSERT_EQ(st.rdstate(), 0);
364  ASSERT_EQ(rw.str(), s1.substr(0, 10));
365  st.setstate(std::ios_base::eofbit);
366  cout << "seteof string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
367  ASSERT_TRUE(st.eof());
368  st << s2;
369  cout << "write '"<<s2<<"' string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
370  ASSERT_TRUE(st.eof());
371  ASSERT_EQ(rw.str(), s1.substr(0, 10));
372  st.clear();
373  cout << "clear string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
374  st << s2;
375  cout << "write '"<<s2<<"' string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
376  st.flush();
377  cout << "flush string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
378  ASSERT_EQ(st.rdstate(), 0);
379  ASSERT_EQ(rw.str(), s1+s2);
380 }
381 
382 TEST(iostream_test, move_assign)
383 {
384  RwLoopBuffer rw;
385  IoStream io1(rw, rw);
386  io1 << "test";
387  io1.flush();
388  IoStream io2 = std::move(io1);
389  string s;
390  io2 >> s;
391  cout << "got: " << s << endl;
392  ASSERT_EQ(s, "test");
393 }
394 
395 TEST(iostream_test, move_moveconstruct)
396 {
397  RwLoopBuffer rw;
398  IoStream io1(rw, rw);
399  io1 << "test";
400  io1.flush();
401  IoStream io2(std::move(io1));
402  string s;
403  io2 >> s;
404  cout << "got: " << s << endl;
405  ASSERT_EQ(s, "test");
406 }
407 
408 TEST(instream_test, move_assign)
409 {
410  RwLoopBuffer rw("test");
411  InStream in1(rw);
412  InStream in2 = std::move(in1);
413  string s;
414  in2 >> s;
415  cout << "got: " << s << endl;
416  ASSERT_EQ(s, "test");
417 }
418 
419 TEST(instream_test, move_construct)
420 {
421  RwLoopBuffer rw("test");
422  InStream in1(rw);
423  InStream in2(std::move(in1));
424  string s;
425  in2 >> s;
426  cout << "got: " << s << endl;
427  ASSERT_EQ(s, "test");
428 }
429 
430 TEST(outstream_test, move_assign)
431 {
432  RwLoopBuffer rw;
433  OutStream ou1(rw);
434  OutStream ou2 = std::move(ou1);
435  ou2 << "test";
436  ou2.flush();
437  cout << "got: " << rw.str() << endl;
438  ASSERT_EQ(rw.str(), "test");
439 }
440 
441 TEST(outstream_test, move_construct)
442 {
443  RwLoopBuffer rw;
444  OutStream ou1(rw);
445  OutStream ou2(std::move(ou1));
446  ou2 << "test";
447  ou2.flush();
448  cout << "got: " << rw.str() << endl;
449  ASSERT_EQ(rw.str(), "test");
450 }
451 
452 TEST(fail_test, outstream_except)
453 {
454  string line("test");
455 
456  struct ExReader : public Reader
457  {
458  bool fail;
459  ExReader() : fail(true) {}
460  size_t read(void* loc, size_t len)
461  {
462  if (fail) throw std::runtime_error("test");
463  return len;
464  }
465  };
466  struct ExWriter : public Writer
467  {
468  bool fail;
469  ExWriter() : fail(true) {}
470  size_t write(const void*, size_t len)
471  {
472  if (fail) throw std::runtime_error("test");
473  return len;
474  }
475  };
476 
477  ExReader rd;
478  ExWriter wr;
479  InStream is(rd);
480  OutStream os(wr);
481  IoStream io(rd, wr);
482 
483  is.read((char*)line.data(), 4);
484 
485  ASSERT_TRUE(is.fail());
486  ASSERT_EQ(is.rdstate() & ios::badbit, ios::badbit);
487  ASSERT_EQ(is.recv_fail(), "test");
488 
489  is.clear();
490  ASSERT_EQ(is.recv_fail(), "");
491  rd.fail = false;
492  is.read((char*)line.data(), 4);
493 
494  ASSERT_FALSE(is.fail());
495  ASSERT_EQ(is.recv_fail(), "");
496 
497  os << "test";
498  os.flush();
499 
500  ASSERT_TRUE(os.fail());
501  ASSERT_EQ(os.rdstate() & ios::badbit, ios::badbit);
502  ASSERT_EQ(os.send_fail(), "test");
503 
504  os.clear();
505  ASSERT_EQ(os.send_fail(), "");
506  wr.fail = false;
507  os << "test";
508  os.flush();
509 
510  ASSERT_FALSE(os.fail());
511  ASSERT_EQ(os.send_fail(), "");
512 
513  io.clear();
514  rd.fail = true;
515  wr.fail = true;
516 
517  io.read((char*)line.data(), 4);
518 
519  io << "test";
520  io.flush();
521 
522  ASSERT_TRUE(io.fail());
523  ASSERT_EQ(io.rdstate() & ios::badbit, ios::badbit);
524  ASSERT_EQ(io.send_fail(), "");
525  ASSERT_EQ(io.recv_fail(), "test");
526 
527  io.clear();
528 
529  io << "test";
530  io.flush();
531 
532  io.read((char*)line.data(), 4);
533 
534  ASSERT_TRUE(io.fail());
535  ASSERT_EQ(io.send_fail(), "test");
536  ASSERT_EQ(io.recv_fail(), "");
537 
538  io.clear();
539  rd.fail = false;
540  wr.fail = false;
541 
542  io << "test";
543  io.flush();
544 
545  io.read((char*)line.data(), 4);
546 
547  ASSERT_FALSE(io.fail());
548  ASSERT_EQ(io.send_fail(), "");
549  ASSERT_EQ(io.recv_fail(), "");
550 }
551 
552 TEST(shared_ptr_test, shared_io)
553 {
554  std::shared_ptr<RwLoopBuffer> buf(new RwLoopBuffer);
555  IoStream io(buf, buf);
556 
557  for (char ch : test_long) io.put(ch);
558  io.flush();
559 
560  ASSERT_EQ(test_long, buf->str());
561 
562  string got;
563  for (char ch; io.get(ch);) got.push_back(ch);
564 
565  ASSERT_EQ(test_long, got);
566 
567  cout << "io buf references: " << buf.use_count() << endl;
568  ASSERT_EQ(buf.use_count(), 3);
569 }
570 
571 TEST(shared_ptr_test, shared_in)
572 {
573  std::shared_ptr<RwLoopBuffer> buf(new RwLoopBuffer);
574  InStream io(buf);
575 
576  buf->set(test_long.data(), test_long.size());
577 
578  string got;
579  for (char ch; io.get(ch);) got.push_back(ch);
580 
581  ASSERT_EQ(test_long, got);
582 
583  cout << "in buf references: " << buf.use_count() << endl;
584  ASSERT_EQ(buf.use_count(), 2);
585 }
586 
587 TEST(shared_ptr_test, shared_out)
588 {
589  std::shared_ptr<RwLoopBuffer> buf(new RwLoopBuffer);
590  OutStream io(buf);
591 
592  for (char ch : test_long) io.put(ch);
593  io.flush();
594 
595  ASSERT_EQ(test_long, buf->str());
596 
597  cout << "out buf references: " << buf.use_count() << endl;
598  ASSERT_EQ(buf.use_count(), 2);
599 }
600 
601 TEST(basic_stream_test, sync_n_flush)
602 {
603  RwLoopBuffer buf;
604  RwCounter count(buf, buf);
605  IoStream io(count, count, 10, 10);
606 
607  for (unsigned i = 0; i < 7; i++) io.put(test_long[i]);
608 
609  cout << "partial put buf size: " << buf.size() << endl;
610  cout << "partial put idx: " << buf.idx() << endl;
611 
612 // ASSERT_TRUE(buf.empty());
613 // ASSERT_EQ(buf.idx(), 0);
614 
615  io.flush(); // flush the write buffer
616 
617  cout << "partial write buf size: " << buf.size() << endl;
618  cout << "partial write idx: " << buf.idx() << endl;
619 
620 // ASSERT_EQ(buf.str(), test_long.substr(0, 7));
621 // ASSERT_EQ(buf.idx(), 0);
622 
623  for (unsigned i = 7; i < test_long.size(); i++) io.put(test_long[i]);
624  io.flush();
625  cout << "full write buf size: " << buf.size() << endl;
626  cout << "full write idx: " << buf.idx() << endl;
627 
628 // ASSERT_EQ(buf.str(), test_long);
629 // ASSERT_EQ(buf.idx(), 0);
630 
631  string got;
632  char ch;
633  for (unsigned i = 0; i < 7 && io.get(ch); i++) got.push_back(ch);
634  cout << "partial read got: " << got << endl;
635  cout << "partial read buf size: " << buf.size() << endl;
636  cout << "partial read idx: " << buf.idx() << endl;
637 
638  io.sync(); // sync the read buffer
639 
640  for (unsigned i = 0; i < 2 && io.get(ch); i++) got.push_back(ch);
641 
642  cout << "sync & read 2 more got: " << got << endl;
643  cout << "sync & read 2 more buf size: " << buf.size() << endl;
644  cout << "sync & read 2 more idx: " << buf.idx() << endl;
645 
646  while (io.get(ch)) got.push_back(ch);
647 
648  cout << "full read got: " << got << endl;
649  cout << "full read buf size: " << buf.size() << endl;
650  cout << "full read idx: " << buf.idx() << endl;
651 }
Input stream wrapper for reader.
Definition: iostream.h:59
Input/output stream wrapper for reader/writer.
Definition: iostream.h:157
Output stream wrapper for writer.
Definition: iostream.h:108
Adds byte count to a read/write stream.
Definition: rwcounter.h:135
Loopback read/write stream buffer.
Definition: rwloopbuf.h:57
std::string str()
Read as a string.
Definition: rwloopbuf.h:143
Base input/output stream classes.
Read/write counter.
Loopback read/write buffer.
Interface class for objects which can be read.
Definition: iobase.h:67
virtual size_t read(void *, size_t)=0
Read interface.
Interface class for objects which can be written.
Definition: iobase.h:86
virtual size_t write(const void *, size_t)=0
Write interface.
TEST(inet_example, client_server_stream_test)
[Inet client server]
Definition: inet.cc:521