summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Whiteboard')
-rw-r--r--Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h21
-rw-r--r--Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp101
-rw-r--r--Swiften/Whiteboard/WhiteboardClient.cpp9
-rw-r--r--Swiften/Whiteboard/WhiteboardTransformer.cpp81
-rw-r--r--Swiften/Whiteboard/WhiteboardTransformer.h8
5 files changed, 219 insertions, 1 deletions
diff --git a/Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h b/Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h
new file mode 100644
index 0000000..139ef0c
--- /dev/null
+++ b/Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2012 Mateusz Piękos
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Whiteboard/Operations/WhiteboardOperation.h>
+
+#include <Swiften/Whiteboard/Elements/WhiteboardElement.h>
+
+namespace Swift {
+ class WhiteboardDeleteOperation : public WhiteboardOperation {
+ public:
+ typedef boost::shared_ptr<WhiteboardDeleteOperation> ref;
+ public:
+ ~WhiteboardDeleteOperation() {
+ }
+ };
+}
diff --git a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
index 996d8a3..7526fdd 100644
--- a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
+++ b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
@@ -12,6 +12,7 @@
#include <Swiften/Whiteboard/WhiteboardClient.h>
#include <Swiften/Whiteboard/Operations/WhiteboardInsertOperation.h>
#include <Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h>
+#include <Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h>
#include <Swiften/Whiteboard/Elements/WhiteboardEllipseElement.h>
using namespace Swift;
@@ -24,6 +25,7 @@ class WhiteboardClientTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testSynchronize_clientInterruption);
CPPUNIT_TEST(testSynchronize_serverInterruption);
CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations);
+ CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations2);
CPPUNIT_TEST_SUITE_END();
public:
@@ -572,6 +574,97 @@ public:
//what gives 0abcd on both sides.
}
+ /*!
+ * /\
+ * / \
+ * \ /
+ * \/
+ */
+ void testSynchronize_nonInterruptedMixOperations2() {
+ WhiteboardClient client;
+ WhiteboardInsertOperation::ref serverOp;
+ serverOp = createInsertOperation("0", "", 0);
+ WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp);
+ CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast<WhiteboardInsertOperation>(pairResult.client));
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ serverOp = createInsertOperation("1", "0", 1);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast<WhiteboardInsertOperation>(pairResult.client));
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+ //Client receives first local operation, because it's parented off "0" which exists
+ //in server history and client doesn't wait for any operation ack from server,
+ //so this operation could be send
+ WhiteboardInsertOperation::ref clientOp;
+ WhiteboardUpdateOperation::ref clientUpdateOp;
+ WhiteboardDeleteOperation::ref clientDeleteOp;
+ clientUpdateOp = createUpdateOperation("a", "1", 0);
+ WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientUpdateOp->setElement(aElement);
+ checkOperation(client.handleLocalOperationReceived(clientUpdateOp), "a", "1", 0, aElement);
+
+ //Client receives second local operation, client didn't receive ack about previous
+ //operation from the server so it can't be send.
+ clientDeleteOp = createDeleteOperation("b", "a", 1);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientDeleteOp));
+
+ //Client receives new operation from server, it should be transformed against
+ //"a" and "b" before adding to local operations history because it's parented off "0".
+ //Because client is waiting for ack of "a", there is no operation to send to server
+ serverOp = createInsertOperation("c", "1", 2);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "c", "b", 1, cElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives new operation from server, it should be transformed against
+ //results of previous transformations, returned operation should be parented off
+ //"c" existing in local history.
+ //Because client is waiting for ack of "a", there is no operation to send to server
+ WhiteboardUpdateOperation::ref serverUpdateOp = createUpdateOperation("d", "c", 0);
+ WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverUpdateOp->setElement(dElement);
+ pairResult = client.handleServerOperationReceived(serverUpdateOp);
+ checkOperation(pairResult.client, "d", "c", 0, dElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives confirmation about processing "a", it should send next operation
+ //to server which is "b", but it should be version parented of transformed "a"
+ serverUpdateOp = createUpdateOperation("a", "d", 0);
+ pairResult = client.handleServerOperationReceived(serverUpdateOp);
+ checkOperation(pairResult.server, "b", "a", 1);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+
+ //Client receives confirmation about processing "b", there aren't any operations
+ //waiting so it should return nothing.
+ WhiteboardDeleteOperation::ref serverDeleteOp = createDeleteOperation("b", "a", 0);
+ serverDeleteOp->setOrigin(WhiteboardOperation::Other);
+ pairResult = client.handleServerOperationReceived(serverDeleteOp);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client operations:
+ //ID pos
+ //0 0
+ //a 1
+ //b 2
+ //c 3
+ //d 4
+ //
+ //Server operations:
+ //ID pos
+ //0 0
+ //c 1
+ //d 2
+ //a 1
+ //b 2
+ //
+ //what gives 0abcd on both sides.
+ }
+
+
WhiteboardInsertOperation::ref createInsertOperation(std::string id, std::string parent, int pos) {
WhiteboardInsertOperation::ref operation = boost::make_shared<WhiteboardInsertOperation>();
operation->setParentID(parent);
@@ -588,6 +681,14 @@ public:
return operation;
}
+ WhiteboardDeleteOperation::ref createDeleteOperation(std::string id, std::string parent, int pos) {
+ WhiteboardDeleteOperation::ref operation = boost::make_shared<WhiteboardDeleteOperation>();
+ operation->setParentID(parent);
+ operation->setID(id);
+ operation->setPos(pos);
+ return operation;
+ }
+
void checkOperation(WhiteboardOperation::ref operation, std::string id, std::string parent, int pos = -1, WhiteboardElement::ref element = WhiteboardElement::ref()) {
CPPUNIT_ASSERT_EQUAL(id, operation->getID());
CPPUNIT_ASSERT_EQUAL(parent, operation->getParentID());
diff --git a/Swiften/Whiteboard/WhiteboardClient.cpp b/Swiften/Whiteboard/WhiteboardClient.cpp
index 2dd9e45..70271e6 100644
--- a/Swiften/Whiteboard/WhiteboardClient.cpp
+++ b/Swiften/Whiteboard/WhiteboardClient.cpp
@@ -21,6 +21,10 @@ namespace Swift {
if (updateOp) {
op = boost::make_shared<WhiteboardUpdateOperation>(*updateOp.get());
}
+ WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast<WhiteboardDeleteOperation>(operation);
+ if (deleteOp) {
+ op = boost::make_shared<WhiteboardDeleteOperation>(*deleteOp.get());
+ }
if (bridge_.size() > 0) {
op->setParentID(bridge_.back()->getID());
@@ -37,6 +41,11 @@ namespace Swift {
if (updateOp) {
op = boost::make_shared<WhiteboardUpdateOperation>(*updateOp.get());
}
+ WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast<WhiteboardDeleteOperation>(operation);
+ if (deleteOp) {
+ op = boost::make_shared<WhiteboardDeleteOperation>(*deleteOp.get());
+ }
+
if (serverOperations_.size() > 0) {
op->setParentID(serverOperations_.back()->getID());
diff --git a/Swiften/Whiteboard/WhiteboardTransformer.cpp b/Swiften/Whiteboard/WhiteboardTransformer.cpp
index 32b1183..60963fd 100644
--- a/Swiften/Whiteboard/WhiteboardTransformer.cpp
+++ b/Swiften/Whiteboard/WhiteboardTransformer.cpp
@@ -13,6 +13,8 @@ namespace Swift {
WhiteboardInsertOperation::ref serverInsert = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(serverOp);
WhiteboardUpdateOperation::ref clientUpdate = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(clientOp);
WhiteboardUpdateOperation::ref serverUpdate = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(serverOp);
+ WhiteboardDeleteOperation::ref clientDelete = boost::dynamic_pointer_cast<WhiteboardDeleteOperation>(clientOp);
+ WhiteboardDeleteOperation::ref serverDelete = boost::dynamic_pointer_cast<WhiteboardDeleteOperation>(serverOp);
if (clientInsert && serverInsert) {
return transform(clientInsert, serverInsert);
} else if (clientUpdate && serverUpdate) {
@@ -21,6 +23,16 @@ namespace Swift {
return transform(clientInsert, serverUpdate);
} else if (clientUpdate && serverInsert) {
return transform(clientUpdate, serverInsert);
+ } else if (clientDelete && serverDelete) {
+ return transform(clientDelete, serverDelete);
+ } else if (clientInsert && serverDelete) {
+ return transform(clientInsert, serverDelete);
+ } else if (clientDelete && serverInsert) {
+ return transform(clientDelete, serverInsert);
+ } else if (clientUpdate && serverDelete) {
+ return transform(clientUpdate, serverDelete);
+ } else if (clientDelete && serverUpdate) {
+ return transform(clientDelete, serverUpdate);
} else {
return std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref>();
}
@@ -75,4 +87,73 @@ namespace Swift {
}
return result;
}
+
+ std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) {
+ std::pair<WhiteboardDeleteOperation::ref, WhiteboardDeleteOperation::ref> result;
+ result.first = boost::make_shared<WhiteboardDeleteOperation>(*serverOp.get());
+ result.first->setParentID(clientOp->getID());
+ result.second = boost::make_shared<WhiteboardDeleteOperation>(*clientOp.get());
+ result.second->setParentID(serverOp->getID());
+ if (clientOp->getPos() < serverOp->getPos()) {
+ result.first->setPos(result.first->getPos()-1);
+ } else if (clientOp->getPos() < serverOp->getPos()) {
+ result.second->setPos(result.second->getPos()-1);
+ }
+//TODO: situation with deletion of the same item
+ return result;
+ }
+
+ std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) {
+ std::pair<WhiteboardDeleteOperation::ref, WhiteboardInsertOperation::ref> result;
+ result.first = boost::make_shared<WhiteboardDeleteOperation>(*serverOp.get());
+ result.first->setParentID(clientOp->getID());
+ result.second = boost::make_shared<WhiteboardInsertOperation>(*clientOp.get());
+ result.second->setParentID(serverOp->getID());
+ if (clientOp->getPos() <= serverOp->getPos()) {
+ result.first->setPos(result.first->getPos()+1);
+ } else {
+ result.second->setPos(result.second->getPos()-1);
+ }
+ return result;
+ }
+
+ std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) {
+ std::pair<WhiteboardInsertOperation::ref, WhiteboardDeleteOperation::ref> result;
+ result.first = boost::make_shared<WhiteboardInsertOperation>(*serverOp.get());
+ result.first->setParentID(clientOp->getID());
+ result.second = boost::make_shared<WhiteboardDeleteOperation>(*clientOp.get());
+ result.second->setParentID(serverOp->getID());
+ if (serverOp->getPos() <= clientOp->getPos()) {
+ result.second->setPos(result.second->getPos()+1);
+ } else {
+ result.first->setPos(result.first->getPos()-1);
+ }
+ return result;
+ }
+
+ std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) {
+ std::pair<WhiteboardDeleteOperation::ref, WhiteboardUpdateOperation::ref> result;
+ result.first = boost::make_shared<WhiteboardDeleteOperation>(*serverOp.get());
+ result.first->setParentID(clientOp->getID());
+ result.second = boost::make_shared<WhiteboardUpdateOperation>(*clientOp.get());
+ result.second->setParentID(serverOp->getID());
+ if (clientOp->getPos() > serverOp->getPos()) {
+ result.second->setPos(result.second->getPos()-1);
+ }
+ return result;
+//TODO: situation with the same pos
+ }
+
+ std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) {
+ std::pair<WhiteboardUpdateOperation::ref, WhiteboardDeleteOperation::ref> result;
+ result.first = boost::make_shared<WhiteboardUpdateOperation>(*serverOp.get());
+ result.first->setParentID(clientOp->getID());
+ result.second = boost::make_shared<WhiteboardDeleteOperation>(*clientOp.get());
+ result.second->setParentID(serverOp->getID());
+ if (clientOp->getPos() < serverOp->getPos()) {
+ result.first->setPos(result.first->getPos()-1);
+ }
+ return result;
+//TODO: situation with the same pos
+ }
}
diff --git a/Swiften/Whiteboard/WhiteboardTransformer.h b/Swiften/Whiteboard/WhiteboardTransformer.h
index 0bab4d3..0ff5c7f 100644
--- a/Swiften/Whiteboard/WhiteboardTransformer.h
+++ b/Swiften/Whiteboard/WhiteboardTransformer.h
@@ -8,15 +8,21 @@
#include <Swiften/Whiteboard/Operations/WhiteboardInsertOperation.h>
#include <Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h>
+#include <Swiften/Whiteboard/Operations/WhiteboardDeleteOperation.h>
#include <utility>
namespace Swift {
class WhiteboardTransformer {
public:
-static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp);
+ static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp);
static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp);
static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp);
static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp);
static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardInsertOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp);
+ static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp);
+ static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardInsertOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp);
+ static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp);
+ static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp);
+ static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp);
};
}