#include #include #include #include #include #include #include #include #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 operation; typedef obby::record record; typedef obby::jupiter_server jupiter_server; typedef obby::jupiter_client jupiter_client; struct record_wrapper { record* rec; const obby::user* from; }; std::vector split_line(const std::string& line, const std::string& separator) { std::vector 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 >& 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& 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 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 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 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 serv_rec; std::vector > 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 ops = split_line(fs[1], ","); for(unsigned int i = 0; i < ops.size(); ++ i) { if(ops[i].empty() ) continue; std::vector 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 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 new_op; if(op[0] == "ins") { std::vector 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( pos, text ) ); } else { std::vector 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( 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::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::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; }