#include <cstdlib>
#include <cerrno>
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
#include <list>
#include <sigc++/sigc++.h>
#include "jupiter_client.hpp"
#include "jupiter_server.hpp"
#include "operation.hpp"
#include "insert_operation.hpp"
#include "delete_operation.hpp"
#include "split_operation.hpp"
#include "no_operation.hpp"
#include "document.hpp"
typedef obby::operation<obby::document> operation;
typedef obby::record<obby::document> record;
typedef obby::jupiter_server<obby::document> jupiter_server;
typedef obby::jupiter_client<obby::document> jupiter_client;
struct record_wrapper { record* rec; const obby::user* from; };
std::vector<std::string> split_line(const std::string& line,
const std::string& separator)
{
std::vector<std::string> result;
std::string::size_type pos = 0, prev = 0;
while( (pos = line.find(separator, pos)) != std::string::npos)
{
result.push_back(line.substr(prev, pos - prev) );
pos += separator.length();
prev = pos;
}
result.push_back(line.substr(prev) );
return result;
}
void server_record(const record& rec,
const obby::user& to,
const obby::user* from,
std::vector<std::list<record_wrapper> >& recs)
{
record_wrapper wrapper = {
new record(rec.get_time(), rec.get_operation()),
from
};
// TODO: Discard from?
recs[to.get_id() - 1].push_back(wrapper);
}
void client_record(const record& rec,
const obby::user* from,
std::list<record_wrapper>& recs)
{
record_wrapper wrapper = {
new record(rec.get_time(), rec.get_operation()),
from
};
recs.push_back(wrapper);
}
void test(const std::string& line)
{
const obby::user* users[] = {
new obby::user(1, "user1", obby::colour(5, 5, 5) ),
new obby::user(2, "user2", obby::colour(25, 25, 25) ),
new obby::user(3, "user3", obby::colour(45, 45, 45) )
};
std::vector<std::string> fs = split_line(line, "|");
if(fs.size() != 3) throw std::runtime_error("Expected 3 sections");
std::string init = fs[0];
std::string result = fs[2];
const unsigned int clients = sizeof(users) / sizeof(users[0]);
obby::document::template_type templ;
obby::document serv_doc(templ);
std::vector<obby::document*> client_doc;
client_doc.resize(clients);
serv_doc.insert(0, init, NULL);
for(unsigned int i = 0; i < client_doc.size(); ++ i)
{
client_doc[i] = new obby::document(templ);
client_doc[i]->insert(0, init, NULL);
}
jupiter_server server(serv_doc);
std::vector<jupiter_client*> client_algos;
client_algos.resize(clients, NULL);
for(unsigned int i = 0; i < client_algos.size(); ++ i)
{
client_algos[i] = new jupiter_client(*client_doc[i]);
server.client_add(*users[i]);
}
std::list<record_wrapper> serv_rec;
std::vector<std::list<record_wrapper> > client_rec;
client_rec.resize(clients);
server.record_event().connect(
sigc::bind(
sigc::ptr_fun(&server_record),
sigc::ref(client_rec)
)
);
for(unsigned int i = 0; i < clients; ++ i)
{
client_algos[i]->record_event().connect(
sigc::bind(
sigc::ptr_fun(&client_record),
sigc::ref(serv_rec)
)
);
}
std::vector<std::string> ops = split_line(fs[1], ",");
for(unsigned int i = 0; i < ops.size(); ++ i)
{
if(ops[i].empty() ) continue;
std::vector<std::string> vs = split_line(ops[i], "->");
if(vs.size() != 2)
throw std::runtime_error("Expected Site->op");
unsigned long site = strtoul(vs[0].c_str(), NULL, 0);
if(site > clients)
{
throw std::runtime_error(
"Site must be between 0 and client count"
);
}
std::vector<std::string> op = split_line(vs[1], "(");
if(op.size() != 2)
throw std::runtime_error("Expected op(desc)");
if(op[0] != "ins" && op[0] != "del")
{
throw std::runtime_error(
"Unsupported operation: " + op[0]
);
}
std::auto_ptr<operation> new_op;
if(op[0] == "ins")
{
std::vector<std::string> desc = split_line(op[1], "@");
if(desc.size() != 2)
{
throw std::runtime_error(
"Expected ins(text@position"
);
}
errno = 0;
std::string text = desc[0];
obby::position pos = strtoul(desc[1].c_str(), NULL, 0);
if(errno != 0)
{
throw std::runtime_error(
"Expected numerical position"
);
}
new_op.reset(
new obby::insert_operation<obby::document>(
pos,
text
)
);
}
else
{
std::vector<std::string> desc = split_line(op[1], "-");
if(desc.size() != 2)
{
throw std::runtime_error(
"Expected del(from-to)"
);
}
errno = 0;
obby::position from = strtoul(desc[0].c_str(), NULL, 0);
if(errno != 0)
{
throw std::runtime_error(
"Expected numerical position"
);
}
obby::position to = strtoul(desc[1].c_str(), NULL, 0);
if(errno != 0)
{
throw std::runtime_error(
"Expected numerical position"
);
}
new_op.reset(
new obby::delete_operation<obby::document>(
from,
to-from
)
);
}
if(site == 0)
{
server.local_op(*new_op, NULL);
}
else
{
client_algos[site - 1]->local_op(
*new_op,
users[site - 1]
);
}
}
for(std::list<record_wrapper>::iterator iter = serv_rec.begin();
iter != serv_rec.end(); ++ iter)
{
server.remote_op(*iter->rec, iter->from);
}
for(unsigned int i = 0; i < clients; ++ i)
{
for(std::list<record_wrapper>::iterator iter =
client_rec[i].begin();
iter != client_rec[i].end(); ++ iter)
{
client_algos[i]->remote_op(*iter->rec, iter->from);
}
}
if(serv_doc.get_text() != result)
{
throw std::runtime_error(
"Server document \"" + serv_doc.get_text() + "\" "
"differs from expected result \"" + result + "\""
);
}
for(unsigned int i = 0; i < clients; ++ i)
{
if(client_doc[i]->get_text() != result)
{
throw std::runtime_error(
"Client document \"" +
client_doc[i]->get_text() + "\" from client " +
users[i]->get_name() + " differs from expected "
"result \"" + result + "\""
);
}
}
// TODO: Delete allocated memory
}
int main(int argc, char* argv[])
{
std::cout << argv[0] << std::endl;
std::cout << "Program to test the Jupiter implementation" << std::endl;
std::cout << "See the file \"base_file\" for how to build tests" << std::endl;
std::cout << std::endl;
std::string filename = "base_file";
if(argc >= 2) filename = argv[1];
std::ifstream file(filename.c_str());
if(!file)
{
std::cerr << "Could not open \"" << filename << "\"" << std::endl;
return EXIT_FAILURE;
}
std::string line;
unsigned int count = 0;
unsigned int success = 0;
unsigned int line_num = 0;
bool result = true;
while(std::getline(file, line) )
{
++ line_num;
if(line.empty() )
continue;
if(line[0] == '#')
continue;
++ count;
std::cout << "Test " << count << "(" << line_num << "): ";
try
{
test(line);
}
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
result = false;
continue;
}
std::cout << "passed!" << std::endl;
++ success;
}
return result ? EXIT_SUCCESS : EXIT_FAILURE;
}
syntax highlighted by Code2HTML, v. 0.9.1