#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
using std::ios;
static string test_word = "QuotesFromOscarWilde";
static string test_line = "It is always a silly thing to give advice, but to give good advice is fatal.";
static string test_long =
"One can survive everything, nowadays, except death, and live down everything except a good reputation.\n"
"One should always play fairly when one has the winning cards.\n"
"Patriotism is the virtue of the vicious.\n"
"Selfishness is not living as one wishes to live, it is asking others to live as one wishes to live.";
TEST(iostream_test, example_decorator_stack)
{
RwLoopBuffer rwbuf;
RwCounter rwcount(rwbuf, rwbuf);
IoStream rws(rwcount, rwcount);
std::stringstream sstr;
rws << test_line << endl;
rws << test_long << endl;
sstr << test_line << endl;
sstr << test_long << endl;
ASSERT_EQ(rwbuf.str(), sstr.str());
ASSERT_EQ(rwcount.write_count(), sstr.str().size());
ASSERT_EQ(rwcount.read_count(), 0);
string reads, tests;
while (sstr >> reads)
{
ASSERT_TRUE(rws >> tests);
ASSERT_EQ(reads, tests);
}
ASSERT_FALSE(rws >> tests);
ASSERT_EQ(rwcount.read_count(), sstr.str().size());
ASSERT_EQ(rwbuf.str().size(), 0);
}
struct iostreamTest : public testing::Test
{
std::vector<string> all;
iostreamTest()
{
all.push_back(test_word);
all.push_back(test_line);
all.push_back(test_long);
}
virtual ~iostreamTest() {}
void readwords_test(const string s, int recvbuf_size=10)
{
stringstream sstr(s);
RwLoopBuffer rw(s);
InStream ins(rw);
ins.recvbuf_size(recvbuf_size);
ASSERT_EQ(ins.recvbuf_size(), recvbuf_size);
cout << "readwords_test: " << s.substr(0,10) << "... size=" << s.size() << " recvbuf_size= " << ins.recvbuf_size() << endl;
string reads, tests;
while (sstr >> reads)
{
ASSERT_TRUE(ins >> tests);
ASSERT_EQ(reads, tests);
}
}
void readlines_test(const string s, int recvbuf_size=10)
{
stringstream sstr(s);
RwLoopBuffer rw(s);
InStream ins(rw);
ins.recvbuf_size(recvbuf_size);
ASSERT_EQ(ins.recvbuf_size(), recvbuf_size);
cout << "readlines_test: " << s.substr(0,10) << "... size=" << s.size() << " recvbuf_size= " << ins.recvbuf_size() << endl;
string reads, tests;
while (std::getline(sstr, reads))
{
ASSERT_TRUE(std::getline(ins, tests));
ASSERT_EQ(reads, tests);
}
}
void writewords_test(const string s, int sendbuf_size=10)
{
stringstream sstr(s);
RwLoopBuffer rw;
OutStream ous(rw);
ous.sendbuf_size(sendbuf_size);
ASSERT_EQ(ous.sendbuf_size(), sendbuf_size);
cout << "writewords_test: " << s.substr(0,10) << "... size=" << s.size() << " sendbuf_size= " << ous.sendbuf_size() << endl;
string reads;
stringstream writess;
while (sstr >> reads)
{
writess << reads;
ASSERT_TRUE(ous << reads);
}
writess.flush();
ous.flush();
ASSERT_EQ(writess.str(), rw.str());
}
void writelines_test(const string s, int sendbuf_size=10)
{
stringstream sstr(s);
RwLoopBuffer rw;
OutStream ous(rw);
ous.sendbuf_size(sendbuf_size);
ASSERT_EQ(ous.sendbuf_size(), sendbuf_size);
cout << "writelines_test: " << s.substr(0,10) << "... size=" << s.size() << " sendbuf_size= " << ous.sendbuf_size() << endl;
string reads;
stringstream writess;
while (std::getline(sstr, reads))
{
writess << reads << endl;
ASSERT_TRUE(ous << reads << endl);
}
writess.flush();
ous.flush();
ASSERT_EQ(writess.str(), rw.str());
}
void readchunks_test(const string s, int rbuf1=10, int rsz1=1024, int rbuf2=10)
{
RwLoopBuffer rw(s);
InStream ins(rw);
ins.recvbuf_size(rbuf1);
ASSERT_EQ(ins.recvbuf_size(), rbuf1);
int toread = s.size();
cout << "readchunks_test1: " << s.substr(0,10) << "... read=" << rsz1 << " toread=" << toread
<< " recvbuf_size= " << ins.recvbuf_size() << endl;
char bin1[rsz1];
ins.read(bin1, rsz1);
auto lastread = ins.gcount();
ASSERT_EQ(lastread, std::min(rsz1, toread));
ASSERT_EQ(memcmp(s.data(), bin1, lastread), 0);
toread -= lastread;
if (lastread < rsz1)
{
ASSERT_EQ((ins.rdstate()&std::ios_base::eofbit), std::ios_base::eofbit);
}
if (toread)
{
ins.recvbuf_size(rbuf2);
ASSERT_EQ(ins.recvbuf_size(), rbuf2);
cout << "readchunks_test2: " << s.substr(0,10) << "... read=" << toread << " toread=" << toread
<< " recvbuf_size= " << ins.recvbuf_size() << endl;
char bin2[toread];
ins.read(bin2, toread);
auto lastread2 = ins.gcount();
ASSERT_EQ(lastread2, toread);
ASSERT_EQ(memcmp(s.data()+lastread, bin2, lastread2), 0);
ASSERT_NE((ins.rdstate()&std::ios_base::eofbit), std::ios_base::eofbit);
}
}
void writechunks_test(const string s, int wbuf1=10, int wsz1=1024, int wbuf2=10)
{
RwLoopBuffer rw;
OutStream ous(rw);
ous.sendbuf_size(wbuf1);
ASSERT_EQ(ous.sendbuf_size(), wbuf1);
int towrite = s.size();
wsz1 = std::min(wsz1, towrite);
cout << "writechunks_test1: " << s.substr(0,10) << "... write=" << wsz1 << " towrite=" << towrite
<< " sendbuf_size= " << ous.sendbuf_size() << endl;
ASSERT_TRUE(ous.write(s.data(), wsz1));
towrite -= wsz1;
if (towrite)
{
ous.sendbuf_size(wbuf2);
ASSERT_EQ(ous.sendbuf_size(), wbuf2);
cout << "writechunks_test2: " << s.substr(0,10) << "... write=" << towrite << " towrite=" << towrite
<< " sendbuf_size= " << ous.sendbuf_size() << endl;
ASSERT_TRUE(ous.write(s.data()+wsz1, towrite));
}
ous.flush();
ASSERT_EQ(s, rw.str());
}
};
TEST_F(iostreamTest, read)
{
for (auto& s : all)
{
readwords_test(s);
}
for (auto& s : all)
{
readlines_test(s);
}
}
TEST_F(iostreamTest, write)
{
for (auto& s : all)
{
writewords_test(s);
}
for (auto& s : all)
{
writelines_test(s);
}
}
TEST_F(iostreamTest, readchunks)
{
for (auto& s : all)
{
readchunks_test(s);
readchunks_test(s, 10, 5, 20);
readchunks_test(s, 10, 8, 5);
}
}
TEST_F(iostreamTest, writechunks)
{
for (auto& s : all)
{
writechunks_test(s);
writechunks_test(s, 10, 5, 20);
writechunks_test(s, 10, 8, 5);
}
}
TEST(iostream_test, write_read)
{
string val("this\nis\na\ntest\n");
RwLoopBuffer rw;
IoStream ios(rw, rw);
ASSERT_TRUE(ios.write(val.data(), val.size()));
ios.flush();
char buf[sizeof(val)];
ASSERT_TRUE(ios.read(buf, val.size()));
ASSERT_EQ(ios.gcount(), val.size());
ASSERT_EQ(memcmp(buf, val.data(), val.size()), 0);
ASSERT_FALSE(ios.read(buf, val.size()));
}
TEST(iostream_test, stream_write_read)
{
string val("this\nis\na\ntest\n");
RwLoopBuffer rw;
IoStream ios(rw, rw);
ASSERT_TRUE(ios << val);
ios.flush();
string s;
ASSERT_TRUE(ios >> s);
ASSERT_EQ(s, "this");
ASSERT_TRUE(ios >> s);
ASSERT_EQ(s, "is");
ASSERT_TRUE(ios >> s);
ASSERT_EQ(s, "a");
ASSERT_TRUE(ios >> s);
ASSERT_EQ(s, "test");
}
string printstate(int state)
{
stringstream st;
st << "state: ";
if (state == 0) st << "good ";
if ((state&std::ios_base::badbit)==std::ios_base::badbit) st << "bad ";
if ((state&std::ios_base::failbit)==std::ios_base::failbit) st << "fail ";
if ((state&std::ios_base::eofbit)==std::ios_base::eofbit) st << "eof ";
return st.str();
}
TEST(iostream_test, write_buffer_states_test)
{
RwLoopBuffer rw;
OutStream st(rw, 10);
string s1 = "This is a test", s2 = " of the stream bit stuff.";
cout << "***OutStream test" << endl;
cout << "Before " << printstate(st.rdstate()) << endl;
ASSERT_EQ(st.rdstate(), 0);
st << s1;
cout << "write '"<<s1<<"' string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
ASSERT_EQ(st.rdstate(), 0);
ASSERT_EQ(rw.str(), s1.substr(0, 10));
st.setstate(std::ios_base::eofbit);
cout << "seteof string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
ASSERT_TRUE(st.eof());
st << s2;
cout << "write '"<<s2<<"' string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
ASSERT_TRUE(st.eof());
ASSERT_EQ(rw.str(), s1.substr(0, 10));
st.clear();
cout << "clear string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
st << s2;
cout << "write '"<<s2<<"' string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
st.flush();
cout << "flush string: '" << rw.str() << "' " << printstate(st.rdstate()) << endl;
ASSERT_EQ(st.rdstate(), 0);
ASSERT_EQ(rw.str(), s1+s2);
}
TEST(iostream_test, move_assign)
{
RwLoopBuffer rw;
IoStream io1(rw, rw);
io1 << "test";
io1.flush();
IoStream io2 = std::move(io1);
string s;
io2 >> s;
cout << "got: " << s << endl;
ASSERT_EQ(s, "test");
}
TEST(iostream_test, move_moveconstruct)
{
RwLoopBuffer rw;
IoStream io1(rw, rw);
io1 << "test";
io1.flush();
IoStream io2(std::move(io1));
string s;
io2 >> s;
cout << "got: " << s << endl;
ASSERT_EQ(s, "test");
}
TEST(instream_test, move_assign)
{
RwLoopBuffer rw("test");
InStream in1(rw);
InStream in2 = std::move(in1);
string s;
in2 >> s;
cout << "got: " << s << endl;
ASSERT_EQ(s, "test");
}
TEST(instream_test, move_construct)
{
RwLoopBuffer rw("test");
InStream in1(rw);
InStream in2(std::move(in1));
string s;
in2 >> s;
cout << "got: " << s << endl;
ASSERT_EQ(s, "test");
}
TEST(outstream_test, move_assign)
{
RwLoopBuffer rw;
OutStream ou1(rw);
OutStream ou2 = std::move(ou1);
ou2 << "test";
ou2.flush();
cout << "got: " << rw.str() << endl;
ASSERT_EQ(rw.str(), "test");
}
TEST(outstream_test, move_construct)
{
RwLoopBuffer rw;
OutStream ou1(rw);
OutStream ou2(std::move(ou1));
ou2 << "test";
ou2.flush();
cout << "got: " << rw.str() << endl;
ASSERT_EQ(rw.str(), "test");
}
TEST(fail_test, outstream_except)
{
string line("test");
struct ExReader : public Reader
{
bool fail;
ExReader() : fail(true) {}
size_t read(void* loc, size_t len)
{
if (fail) throw std::runtime_error("test");
return len;
}
};
struct ExWriter : public Writer
{
bool fail;
ExWriter() : fail(true) {}
size_t write(const void*, size_t len)
{
if (fail) throw std::runtime_error("test");
return len;
}
};
ExReader rd;
ExWriter wr;
InStream is(rd);
OutStream os(wr);
IoStream io(rd, wr);
is.read((char*)line.data(), 4);
ASSERT_TRUE(is.fail());
ASSERT_EQ(is.rdstate() & ios::badbit, ios::badbit);
ASSERT_EQ(is.recv_fail(), "test");
is.clear();
ASSERT_EQ(is.recv_fail(), "");
rd.fail = false;
is.read((char*)line.data(), 4);
ASSERT_FALSE(is.fail());
ASSERT_EQ(is.recv_fail(), "");
os << "test";
os.flush();
ASSERT_TRUE(os.fail());
ASSERT_EQ(os.rdstate() & ios::badbit, ios::badbit);
ASSERT_EQ(os.send_fail(), "test");
os.clear();
ASSERT_EQ(os.send_fail(), "");
wr.fail = false;
os << "test";
os.flush();
ASSERT_FALSE(os.fail());
ASSERT_EQ(os.send_fail(), "");
io.clear();
rd.fail = true;
wr.fail = true;
io.read((char*)line.data(), 4);
io << "test";
io.flush();
ASSERT_TRUE(io.fail());
ASSERT_EQ(io.rdstate() & ios::badbit, ios::badbit);
ASSERT_EQ(io.send_fail(), "");
ASSERT_EQ(io.recv_fail(), "test");
io.clear();
io << "test";
io.flush();
io.read((char*)line.data(), 4);
ASSERT_TRUE(io.fail());
ASSERT_EQ(io.send_fail(), "test");
ASSERT_EQ(io.recv_fail(), "");
io.clear();
rd.fail = false;
wr.fail = false;
io << "test";
io.flush();
io.read((char*)line.data(), 4);
ASSERT_FALSE(io.fail());
ASSERT_EQ(io.send_fail(), "");
ASSERT_EQ(io.recv_fail(), "");
}
TEST(shared_ptr_test, shared_io)
{
std::shared_ptr<RwLoopBuffer> buf(new RwLoopBuffer);
IoStream io(buf, buf);
for (char ch : test_long) io.put(ch);
io.flush();
ASSERT_EQ(test_long, buf->str());
string got;
for (char ch; io.get(ch);) got.push_back(ch);
ASSERT_EQ(test_long, got);
cout << "io buf references: " << buf.use_count() << endl;
ASSERT_EQ(buf.use_count(), 3);
}
TEST(shared_ptr_test, shared_in)
{
std::shared_ptr<RwLoopBuffer> buf(new RwLoopBuffer);
InStream io(buf);
buf->set(test_long.data(), test_long.size());
string got;
for (char ch; io.get(ch);) got.push_back(ch);
ASSERT_EQ(test_long, got);
cout << "in buf references: " << buf.use_count() << endl;
ASSERT_EQ(buf.use_count(), 2);
}
TEST(shared_ptr_test, shared_out)
{
std::shared_ptr<RwLoopBuffer> buf(new RwLoopBuffer);
OutStream io(buf);
for (char ch : test_long) io.put(ch);
io.flush();
ASSERT_EQ(test_long, buf->str());
cout << "out buf references: " << buf.use_count() << endl;
ASSERT_EQ(buf.use_count(), 2);
}
TEST(basic_stream_test, sync_n_flush)
{
RwLoopBuffer buf;
RwCounter count(buf, buf);
IoStream io(count, count, 10, 10);
for (unsigned i = 0; i < 7; i++) io.put(test_long[i]);
cout << "partial put buf size: " << buf.size() << endl;
cout << "partial put idx: " << buf.idx() << endl;
io.flush();
cout << "partial write buf size: " << buf.size() << endl;
cout << "partial write idx: " << buf.idx() << endl;
for (unsigned i = 7; i < test_long.size(); i++) io.put(test_long[i]);
io.flush();
cout << "full write buf size: " << buf.size() << endl;
cout << "full write idx: " << buf.idx() << endl;
string got;
char ch;
for (unsigned i = 0; i < 7 && io.get(ch); i++) got.push_back(ch);
cout << "partial read got: " << got << endl;
cout << "partial read buf size: " << buf.size() << endl;
cout << "partial read idx: " << buf.idx() << endl;
io.sync();
for (unsigned i = 0; i < 2 && io.get(ch); i++) got.push_back(ch);
cout << "sync & read 2 more got: " << got << endl;
cout << "sync & read 2 more buf size: " << buf.size() << endl;
cout << "sync & read 2 more idx: " << buf.idx() << endl;
while (io.get(ch)) got.push_back(ch);
cout << "full read got: " << got << endl;
cout << "full read buf size: " << buf.size() << endl;
cout << "full read idx: " << buf.idx() << endl;
}
Input stream wrapper for reader.
Input/output stream wrapper for reader/writer.
Output stream wrapper for writer.
Adds byte count to a read/write stream.
Loopback read/write stream buffer.
Base input/output stream classes.
Loopback read/write buffer.
Interface class for objects which can be read.
Interface class for objects which can be written.
TEST(inet_example, client_server_stream_test)
[Inet client server]