/*
Ousía
Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include "gtest/gtest.h"
#include
namespace ousia {
/* Test data */
// Generates some pseudo-random data
// (inspired by "Numerical Recipes, Third Edition", Chapter 7.17)
static std::vector generateData(size_t len)
{
const uint32_t B1 = 17;
const uint32_t B2 = 15;
const uint32_t B3 = 5;
uint32_t v = 0xF3A99148;
std::vector res;
for (size_t i = 0; i < len; i++) {
while (true) {
// Advance the random seed
v = v ^ (v >> B1);
v = v ^ (v << B2);
v = v ^ (v >> B3);
// Replace \n and \r in order to avoid line break processing by the
// CharReader
char c = v & 0xFF;
if (c != '\n' && c != '\r') {
res.push_back(c);
break;
}
}
}
return res;
}
// For performance tests only
// static constexpr size_t DATA_LENGTH = 16 * 1024 * 1024 + 795;
static constexpr size_t DATA_LENGTH = 256 * 1024 + 795;
static const std::vector DATA = generateData(DATA_LENGTH);
/* Buffer Test */
TEST(Buffer, simpleRead)
{
std::string testStr{"this is a test"};
// Create buffer with the test string
char c;
Buffer buf{testStr};
// Create a read cursor
Buffer::CursorId cursor = buf.createCursor();
// We're not at the end of the stream
ASSERT_FALSE(buf.atEnd(cursor));
// The cursor must be at zero
ASSERT_EQ(0U, buf.offset(cursor));
// Try to read the test string
std::string res;
while (buf.read(cursor, c)) {
res.append(&c, 1);
}
// The cursor must be at the end
ASSERT_TRUE(buf.atEnd(cursor));
// The cursor must be one byond the last byte
ASSERT_EQ(testStr.size(), buf.offset(cursor));
// The two strings must equal
ASSERT_EQ(testStr, res);
buf.deleteCursor(cursor);
}
TEST(Buffer, cursorManagement)
{
Buffer buf{""};
Buffer::CursorId c1 = buf.createCursor();
Buffer::CursorId c2 = buf.createCursor();
Buffer::CursorId c3 = buf.createCursor();
ASSERT_EQ(0U, c1);
ASSERT_EQ(1U, c2);
ASSERT_EQ(2U, c3);
buf.deleteCursor(c2);
Buffer::CursorId c4 = buf.createCursor();
ASSERT_EQ(1U, c4);
buf.deleteCursor(c1);
buf.deleteCursor(c3);
buf.deleteCursor(c4);
}
TEST(Buffer, twoCursors)
{
std::string testStr{"this is a test"};
// Create buffer with the test string
char c;
Buffer buf{testStr};
// Create two read cursors
Buffer::CursorId cur1 = buf.createCursor();
Buffer::CursorId cur2 = buf.createCursor();
ASSERT_FALSE(buf.atEnd(cur1));
ASSERT_FALSE(buf.atEnd(cur2));
// Try to read the test string with the first cursor
std::string res1;
while (buf.read(cur1, c)) {
res1.append(&c, 1);
}
// The first cursor must be at the end
ASSERT_TRUE(buf.atEnd(cur1));
ASSERT_FALSE(buf.atEnd(cur2));
// Try to read the test string with the second cursor
std::string res2;
while (buf.read(cur2, c)) {
res2.append(&c, 1);
}
// The first cursor must be at the end
ASSERT_TRUE(buf.atEnd(cur1));
ASSERT_TRUE(buf.atEnd(cur2));
// The two strings must equal
ASSERT_EQ(testStr, res1);
ASSERT_EQ(testStr, res2);
buf.deleteCursor(cur1);
buf.deleteCursor(cur2);
}
TEST(Buffer, copyCursors)
{
std::string testStr{"test1 test2 test3"};
// Create buffer with the test string
char c;
Buffer buf{testStr};
// Create two read cursors
Buffer::CursorId cur1 = buf.createCursor();
Buffer::CursorId cur2 = buf.createCursor();
ASSERT_FALSE(buf.atEnd(cur1));
ASSERT_FALSE(buf.atEnd(cur2));
// Read the first six characters with cursor one
std::string res1;
for (int i = 0; i < 6; i++) {
if (buf.read(cur1, c)) {
res1.append(&c, 1);
}
}
ASSERT_EQ("test1 ", res1);
ASSERT_FALSE(buf.atEnd(cur1));
// Copy cur1 to cur2, free cur1
buf.copyCursor(cur1, cur2);
buf.deleteCursor(cur1);
std::string res2;
for (int i = 0; i < 6; i++) {
if (buf.read(cur2, c)) {
res2.append(&c, 1);
}
}
ASSERT_EQ("test2 ", res2);
ASSERT_FALSE(buf.atEnd(cur2));
// Create a new cursor as copy of cur2
Buffer::CursorId cur3 = buf.createCursor(cur2);
std::string res3;
for (int i = 0; i < 6; i++) {
if (buf.read(cur3, c)) {
res3.append(&c, 1);
}
}
ASSERT_EQ("test3", res3);
ASSERT_TRUE(buf.atEnd(cur3));
buf.deleteCursor(cur1);
buf.deleteCursor(cur2);
buf.deleteCursor(cur3);
}
TEST(Buffer, moveCursor)
{
std::string testStr{"test1 test2 test3"};
// Create buffer with the test string
char c;
Buffer buf{testStr};
Buffer::CursorId cursor = buf.createCursor();
// Read the first six characters with cursor one
{
std::string res;
for (int i = 0; i < 6; i++) {
if (buf.read(cursor, c)) {
res.append(&c, 1);
}
}
ASSERT_EQ("test1 ", res);
}
// Move six bytes backward
ASSERT_EQ(-6, buf.moveCursor(cursor, -6));
{
std::string res;
for (int i = 0; i < 6; i++) {
if (buf.read(cursor, c)) {
res.append(&c, 1);
}
}
ASSERT_EQ("test1 ", res);
}
// Move more than six bytes backward
ASSERT_EQ(-6, buf.moveCursor(cursor, -1000));
{
std::string res;
for (int i = 0; i < 6; i++) {
if (buf.read(cursor, c)) {
res.append(&c, 1);
}
}
ASSERT_EQ("test1 ", res);
}
// Move six bytes forward
ASSERT_EQ(6, buf.moveCursor(cursor, 6));
{
std::string res;
for (int i = 0; i < 6; i++) {
if (buf.read(cursor, c)) {
res.append(&c, 1);
}
}
ASSERT_EQ("test3", res);
}
buf.deleteCursor(cursor);
}
struct VectorReadState {
size_t offs;
const std::vector &data;
VectorReadState(const std::vector &data) : offs(0), data(data) {}
};
static size_t readFromVector(char *buf, size_t size, void *userData)
{
VectorReadState &state = *(static_cast(userData));
size_t tar = std::min(state.offs + size, state.data.size());
for (size_t i = state.offs; i < tar; i++) {
*buf = state.data[i];
buf++;
}
size_t res = tar - state.offs;
state.offs = tar;
return res;
}
TEST(Buffer, simpleStream)
{
VectorReadState state(DATA);
Buffer buf{readFromVector, &state};
Buffer::CursorId cursor = buf.createCursor();
char c;
std::vector res;
while (buf.read(cursor, c)) {
res.push_back(c);
}
// We must be at the end of the buffer and the cursor offset must be set
// correctly
ASSERT_TRUE(buf.atEnd(cursor));
ASSERT_EQ(DATA_LENGTH, buf.offset(cursor));
// The read data and the original data must be equal
ASSERT_EQ(DATA, res);
buf.deleteCursor(cursor);
}
TEST(Buffer, streamTwoCursors)
{
VectorReadState state(DATA);
Buffer buf{readFromVector, &state};
Buffer::CursorId cur1 = buf.createCursor();
Buffer::CursorId cur2 = buf.createCursor();
char c;
std::vector res1;
while (buf.read(cur1, c)) {
res1.push_back(c);
}
ASSERT_TRUE(buf.atEnd(cur1));
ASSERT_FALSE(buf.atEnd(cur2));
ASSERT_EQ(DATA_LENGTH, buf.offset(cur1));
ASSERT_EQ(0U, buf.offset(cur2));
std::vector res2;
while (buf.read(cur2, c)) {
res2.push_back(c);
}
ASSERT_TRUE(buf.atEnd(cur1));
ASSERT_TRUE(buf.atEnd(cur2));
ASSERT_EQ(DATA_LENGTH, buf.offset(cur1));
ASSERT_EQ(DATA_LENGTH, buf.offset(cur2));
// The read data and the original data must be equal
ASSERT_EQ(DATA, res1);
ASSERT_EQ(DATA, res2);
buf.deleteCursor(cur1);
buf.deleteCursor(cur2);
}
TEST(Buffer, streamTwoCursorsMovingInterleaved)
{
VectorReadState state(DATA);
Buffer buf{readFromVector, &state};
Buffer::CursorId cur1 = buf.createCursor();
Buffer::CursorId cur2 = buf.createCursor();
char c;
std::vector res1;
std::vector res2;
while (!buf.atEnd(cur1) || !buf.atEnd(cur2)) {
for (int i = 0; i < 100; i++) {
if (buf.read(cur1, c)) {
res1.push_back(c);
}
}
for (int i = 0; i < 120; i++) {
if (buf.read(cur2, c)) {
res2.push_back(c);
}
}
// Move cur2 120 bytes backward and read the content again
res2.resize(res2.size() - 120);
ASSERT_EQ(-120, buf.moveCursor(cur2, -120));
for (int i = 0; i < 120; i++) {
if (buf.read(cur2, c)) {
res2.push_back(c);
}
}
// Move cur1 60 bytes forward and backward
buf.moveCursor(cur1, -buf.moveCursor(cur1, 60));
// Make sure the cursor position is correct
ASSERT_EQ(res1.size(), buf.offset(cur1));
ASSERT_EQ(res2.size(), buf.offset(cur2));
}
ASSERT_EQ(DATA_LENGTH, buf.offset(cur1));
ASSERT_EQ(DATA_LENGTH, buf.offset(cur2));
// The read data and the original data must be equal
ASSERT_EQ(DATA, res1);
ASSERT_EQ(DATA, res2);
buf.deleteCursor(cur1);
buf.deleteCursor(cur2);
}
TEST(Buffer, streamMoveForward)
{
VectorReadState state(DATA);
std::vector partialData;
partialData.resize(100);
std::copy(DATA.end() - partialData.size(), DATA.end(), partialData.begin());
Buffer buf{readFromVector, &state};
Buffer::CursorId cursor = buf.createCursor();
ASSERT_EQ(ssize_t(DATA_LENGTH) - 100,
buf.moveCursor(cursor, DATA_LENGTH - 100));
char c;
std::vector res;
while (buf.read(cursor, c)) {
res.push_back(c);
}
ASSERT_EQ(partialData, res);
buf.deleteCursor(cursor);
}
/* CharReader Test */
TEST(CharReader, simpleRead)
{
std::string testStr{"this is a test"};
char c;
// Feed a test string into the reader
CharReader reader{testStr};
// Try to read the test string
std::string res;
while (!reader.atEnd()) {
ASSERT_TRUE(reader.read(c));
res.append(&c, 1);
}
// The two strings must equal
ASSERT_EQ(testStr, res);
// Check the char reader offset
ASSERT_EQ(testStr.size(), reader.getOffset());
// If we call either read or peek, false is returned
ASSERT_FALSE(reader.read(c));
ASSERT_FALSE(reader.peek(c));
}
TEST(CharReader, simplePeek)
{
std::string testStr{"this is a test"};
char c;
// Feed a test string into the reader
CharReader reader{testStr};
// Try to read the test string
std::string res;
while (reader.peek(c)) {
res.append(&c, 1);
}
// Peeking does not trigger the "atEnd" flag
ASSERT_FALSE(reader.atEnd());
// The two strings must equal
ASSERT_EQ(testStr, res);
// We must now be at line 1, column 1 and NOT at the end of the stream
ASSERT_EQ(0U, reader.getOffset());
ASSERT_FALSE(reader.atEnd());
reader.consumePeek();
ASSERT_EQ(testStr.size(), reader.getOffset());
ASSERT_TRUE(reader.atEnd());
// If we call either read or peek, false is returned
ASSERT_FALSE(reader.read(c));
ASSERT_FALSE(reader.peek(c));
}
TEST(CharReader, linebreakSubstitution)
{
// Feed a test string into the reader and read all characters back
CharReader reader{"this\n\ris\n\rjust\na test\r\n\rtest\n\r"};
std::string res;
char c;
while (reader.read(c)) {
res.append(&c, 1);
}
// Test for equality
ASSERT_EQ("this\nis\njust\na test\n\ntest\n", res);
}
TEST(CharReader, stream)
{
// Copy the test data to a string stream
std::stringstream ss;
std::copy(DATA.begin(), DATA.end(), std::ostream_iterator(ss));
// Read the data back from the stream
std::vector res;
char c;
CharReader reader{ss};
while (reader.read(c)) {
res.push_back(c);
}
ASSERT_EQ(DATA_LENGTH, res.size());
ASSERT_EQ(DATA, res);
}
TEST(CharReader, fork)
{
std::string testStr{"first line\n\n\rsecond line\n\rlast line"};
// 0123456789 0 1 234567890123 4 5678901234
// 0 1 2 3
char c;
CharReader reader{testStr};
// Read a few characters
for (int i = 0; i < 4; i++)
reader.read(c);
// Peek a few characters
for (int i = 4; i < 7; i++)
reader.peek(c);
// Fork the reader
{
CharReaderFork fork = reader.fork();
ASSERT_EQ(4U, fork.getOffset());
fork.peek(c);
ASSERT_EQ('i', c);
fork.read(c);
ASSERT_EQ('t', c);
ASSERT_EQ(5U, fork.getOffset());
reader.read(c);
reader.read(c);
ASSERT_EQ(' ', c);
fork.commit();
}
ASSERT_EQ(5U, reader.getOffset());
}
}