#include <iostream>
#define protected public
#include "text.hpp"
#undef protected
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
using namespace obby;
namespace
{
class desc_error: public std::logic_error
{
public:
desc_error(const std::string& error_message):
std::logic_error(error_message) {}
};
const user* USERS[] = {
new user(1, "pi", obby::colour(255, 255, 0) ),
new user(2, "pa", obby::colour(255, 255, 0) ),
new user(3, "po", obby::colour(255, 255, 0) )
};
text make_text_from_desc(const std::string& desc)
{
text my_text;
if(desc.empty() )
return my_text;
std::string::size_type pos = 0;
const user* cur_user = NULL;
std::string::size_type prev = 0;
while( (pos = desc.find('[', pos)) != std::string::npos)
{
if(pos > prev)
{
if(cur_user == NULL)
throw desc_error("Unusered chunk");
my_text.m_chunks.push_back(
new text::chunk(
desc.substr(prev, pos - prev),
cur_user
)
);
}
text::size_type close = desc.find(']', pos);
if(close == std::string::npos)
throw std::logic_error("Missing ']'");
cur_user = USERS[desc[pos + 1] - '1'];
pos = prev = close + 1;
}
if(cur_user == NULL)
throw desc_error("Unusered chunk");
my_text.m_chunks.push_back(
new text::chunk(desc.substr(prev), cur_user)
);
return my_text;
}
std::string make_desc_from_text(const text& txt)
{
std::string str;
for(text::chunk_iterator iter = txt.chunk_begin();
iter != txt.chunk_end();
++ iter)
{
str += "[";
str += iter->get_author()->get_id() + '0';
str += "]";
str += iter->get_text();
}
return str;
}
struct insert_test {
static const char* NAME;
const char* first;
const text::size_type pos;
const char* second;
const char* expected;
text perform() const
{
text base(make_text_from_desc(first) );
base.insert(pos, make_text_from_desc(second) );
return base;
}
};
struct substr_test {
static const char* NAME;
const char* str;
const text::size_type pos;
const text::size_type len;
const char* expected;
text perform() const
{
text base(make_text_from_desc(str) );
return base.substr(pos, len);
}
};
struct erase_test {
static const char* NAME;
const char* str;
const text::size_type pos;
const text::size_type len;
const char* expected;
text perform() const
{
text base(make_text_from_desc(str) );
base.erase(pos, len);
return base;
}
};
struct append_test {
static const char* NAME;
const char* str;
const char* app;
const char* expected;
text perform() const
{
text base(make_text_from_desc(str) );
base.append(make_text_from_desc(app) );
return base;
}
};
struct prepend_test {
static const char* NAME;
const char* str;
const char* pre;
const char* expected;
text perform() const
{
text base(make_text_from_desc(str) );
base.prepend(make_text_from_desc(pre) );
return base;
}
};
const char* insert_test::NAME = "insert";
const char* substr_test::NAME = "substr";
const char* erase_test::NAME = "erase";
const char* append_test::NAME = "append";
const char* prepend_test::NAME = "prepend";
insert_test INSERT_TESTS[] = {
{ "", 0, "[1]bar", "[1]bar" },
{ "", 1, "[1]bar", NULL },
{ "[1]foo", 3, "[1]bar", "[1]foobar" },
{ "[1]foo", 0, "[1]bar", "[1]barfoo" },
{ "[1]for", 2, "[1]oba", "[1]foobar" },
{ "[1]foo", 0, "[2]bar", "[2]bar[1]foo" },
{ "[1]foo", 4, "[1]bar", NULL },
{ "[1]gnah", 3, "[2]gnah", "[1]gna[2]gnah[1]h" },
{ "[1]gnah", 4, "[2]gnah", "[1]gnah[2]gnah" },
{ "[1]b[2]a", 1, "[1]foo", "[1]bfoo[2]a" },
{ "[1]b[2]a", 1, "[2]foo", "[1]b[2]fooa" },
{ "[1]b[2]a", 1, "[1]f[2]oo", "[1]bf[2]ooa" },
{ "[1]b[2]a", 1, "[2]f[1]oo", "[1]b[2]f[1]oo[2]a" },
{ "[1]b[2]a", 1, "[1]f[2]o[1]o", "[1]bf[2]o[1]o[2]a" },
{ "[1]b[2]a", 1, "[2]f[1]o[2]o", "[1]b[2]f[1]o[2]oa" },
{ "[1]Die Frage[2] ist halt,[3] [2]ob[1] das so", 11, "[3]n", "[1]Die Frage[2] i[3]n[2]st halt,[3] [2]ob[1] das so" }
};
substr_test SUBSTR_TESTS[] = {
{ "", 0, 0, "" },
{ "[1]bar", 0, 3, "[1]bar" },
{ "[1]bar", 0, 1, "[1]b" },
{ "[1]bar", 0, 0, "" },
{ "[1]b[2]a[1]r", 0, 3, "[1]b[2]a[1]r" },
{ "[1]b[2]a[1]r", 0, 2, "[1]b[2]a" },
{ "[1]b[2]a[1]r", 1, 1, "[2]a" },
{ "[1]foo[2]bar", 0, 3, "[1]foo" },
{ "[1]foo[2]bar", 3, 3, "[2]bar" },
{ "[1]foo[2]bar", 1, 3, "[1]oo[2]b" },
{ "[1]foo[2]bar", 2, 3, "[1]o[2]ba" },
{ "[1]foo[2]bar", 0, 4, "[1]foo[2]b" },
{ "[1]foo[2]bar", 1, 4, "[1]oo[2]ba" },
{ "[1]foo[2]bar", 2, 4, "[1]o[2]bar" },
{ "[1]foo[2]bar", 1, 2, "[1]oo" },
{ "[1]foo[2]bar", 2, 2, "[1]o[2]b" },
{ "[1]foo[2]bar", 3, 2, "[2]ba" },
{ "[1]foo[2]bar", 5, 2, NULL },
{ "[1]foo[2]bar", 2, text::npos, "[1]o[2]bar" },
{ "[1]foo[2]bar", 3, text::npos, "[2]bar" }
};
erase_test ERASE_TESTS[] = {
{ "", 0, 0, "" },
{ "", 1, 0, NULL },
{ "", 0, 1, NULL },
{ "[1]foo", 0, 1, "[1]oo" },
{ "[1]foo", 1, 1, "[1]fo" },
{ "[1]foo", 1, 2, "[1]f" },
{ "[1]foo", 0, 3, "" },
{ "[1]foo[2]bar", 0, 1, "[1]oo[2]bar" },
{ "[1]foo[2]bar", 1, 1, "[1]fo[2]bar" },
{ "[1]foo[2]bar", 2, 1, "[1]fo[2]bar" },
{ "[1]foo[2]bar", 3, 1, "[1]foo[2]ar" },
{ "[1]foo[2]bar", 4, 1, "[1]foo[2]br" },
{ "[1]foo[2]bar", 5, 1, "[1]foo[2]ba" },
{ "[1]foo[2]bar", 0, 3, "[2]bar" },
{ "[1]foo[2]bar", 1, 3, "[1]f[2]ar" },
{ "[1]foo[2]bar", 2, 3, "[1]fo[2]r" },
{ "[1]foo[2]bar", 3, 3, "[1]foo" },
{ "[1]foo[2]bar[1]baz", 2, 3, "[1]fo[2]r[1]baz" },
{ "[1]foo[2]bar[1]baz", 3, 3, "[1]foobaz" },
{ "[1]foo[2]bar[1]baz", 4, 3, "[1]foo[2]b[1]az" },
{ "[1]foo[2]b[3]az[1]o", 2, 5, "[1]fo" },
{ "[1]foo[2]bar[3]baz[2]qux[3]fo[2]gneh[1]grah", 0, 10, "[2]ux[3]fo[2]gneh[1]grah" },
{ "[1]foo[2]bar", 2, obby::text::npos, "[1]fo" }
};
append_test APPEND_TESTS[] = {
{ "", "", "" },
{ "", "[1]bar", "[1]bar" },
{ "", "[1]bar[2]foo", "[1]bar[2]foo" },
{ "[1]foo[2]bar", "[1]bar[2]foo", "[1]foo[2]bar[1]bar[2]foo" },
{ "[1]foo[2]bar", "[2]bar[1]foo", "[1]foo[2]barbar[1]foo" }
};
prepend_test PREPEND_TESTS[] = {
{ "", "", "" },
{ "", "[1]bar", "[1]bar" },
{ "", "[1]bar[2]foo", "[1]bar[2]foo" },
{ "[1]foo[2]bar", "[1]bar[2]foo", "[1]bar[2]foo[1]foo[2]bar" },
{ "[1]foo[2]bar", "[2]bar[1]foo", "[2]bar[1]foofoo[2]bar" }
};
bool compare_text(const text& txt1, const text& txt2)
{
text::chunk_iterator iter1 = txt1.chunk_begin();
text::chunk_iterator iter2 = txt2.chunk_begin();
while(iter1 != txt1.chunk_end() && iter2 != txt2.chunk_end() )
{
if(iter1->get_author() != iter2->get_author() )
return false;
if(iter1->get_text() != iter2->get_text() )
return false;
++ iter1; ++ iter2;
}
bool match =
(iter1 == txt1.chunk_end() &&
iter2 == txt2.chunk_end());
if(match && (txt1 != txt2) )
{
throw std::logic_error(
"compare_text is equal, but txt1 == txt2 "
"not!\n" + make_desc_from_text(txt1) + " == " +
make_desc_from_text(txt2)
);
}
return match;
}
template<typename Test>
void test_generic(const Test& test)
{
try
{
text result = test.perform();
if(test.expected == NULL)
{
throw std::logic_error(
std::string(Test::NAME) + " should "
"fail, but it has not"
);
}
text exp(make_text_from_desc(test.expected) );
if(compare_text(result, exp) == false)
{
throw std::logic_error(
"Result does not match expectation:\n"
"Expected " + make_desc_from_text(exp) +
", got " + make_desc_from_text(result)
);
}
}
catch(desc_error& e)
{
throw e;
}
catch(std::logic_error& e)
{
if(test.expected != NULL)
throw e;
}
}
template<typename Test>
bool test_suite(const Test* tests, std::size_t num)
{
bool result = true;
for(std::size_t i = 0; i < num; ++ i)
{
try
{
test_generic(tests[i]);
}
catch(std::exception& e)
{
std::cerr << Test::NAME << " test #" << (i + 1)
<< " failed:\n" << e.what()
<< "\n" << std::endl;
result = false;
continue;
}
std::cerr << Test::NAME << " test #" << (i + 1)
<< " passed" << std::endl;
}
return result;
}
}
int main()
{
bool result = true;
result = test_suite(INSERT_TESTS, ARRAY_SIZE(INSERT_TESTS) ) &&
result;
result = test_suite(SUBSTR_TESTS, ARRAY_SIZE(SUBSTR_TESTS) ) &&
result;
result = test_suite(ERASE_TESTS, ARRAY_SIZE(ERASE_TESTS) ) &&
result;
result = test_suite(APPEND_TESTS, ARRAY_SIZE(APPEND_TESTS) ) &&
result;
result = test_suite(PREPEND_TESTS, ARRAY_SIZE(PREPEND_TESTS) ) &&
result;
return result ? EXIT_SUCCESS : EXIT_FAILURE;
}
syntax highlighted by Code2HTML, v. 0.9.1