summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp')
-rw-r--r--Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp676
1 files changed, 676 insertions, 0 deletions
diff --git a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
new file mode 100644
index 0000000..7d767d3
--- /dev/null
+++ b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2012 Mateusz Piękos
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+#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;
+
+class WhiteboardClientTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(WhiteboardClientTest);
+ CPPUNIT_TEST(testSynchronize_simplestSync);
+ CPPUNIT_TEST(testSynchronize_withoutTranslation);
+ CPPUNIT_TEST(testSynchronize_nonInterrupted);
+ CPPUNIT_TEST(testSynchronize_clientInterruption);
+ CPPUNIT_TEST(testSynchronize_serverInterruption);
+ CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations);
+ CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations2);
+ CPPUNIT_TEST_SUITE_END();
+public:
+
+ /*!
+ * /\
+ * \/
+ * \
+ */
+ void testSynchronize_simplestSync() {
+ 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);
+
+ //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;
+ clientOp = createInsertOperation("a", "0", 1);
+ WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(aElement);
+ WhiteboardInsertOperation::ref result;
+ checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
+
+ //Client receives server operation parented off "0", which isn't last client operation
+ //so it have to be transformed against local operations and then transformed value can
+ //be returned to draw
+ serverOp = createInsertOperation("b", "0", 1);
+ WhiteboardEllipseElement::ref bElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(bElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "b", "a", 2, bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives confirmation from the server about processed "a" operation, it had to
+ //be transformed against "b" on the server side to receive operation parented off "b".
+ //There aren't any waiting operations to send to server, this operation should return
+ //nothing
+ serverOp = createInsertOperation("a", "b", 1);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives local operation, it doesn't have to be transformed against anything
+ //but operation returned to send to the server should be parented off last server
+ //operation, which is "b"
+ clientOp = createInsertOperation("c", "b", 3);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(cElement);
+ checkOperation(client.handleLocalOperationReceived(clientOp), "c", "a", 3, cElement);
+
+ //Client receives confirmation from the server about processed "a" operation, it
+ //should be the same operation as it was sent because server didn't have to
+ //transform it
+ clientOp = createInsertOperation("c", "a", 3);
+ clientOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(clientOp);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Results:
+ //Client operations:
+ //ID pos
+ //0 0
+ //a 1
+ //b 2
+ //c 3
+ //
+ //Server operations:
+ //ID pos
+ //0 0
+ //b 1
+ //a 1
+ //c 3
+ //
+ //what gives 0abc on both sides
+ }
+
+ /*!
+ * /
+ * /
+ * \
+ */
+ void testSynchronize_withoutTranslation() {
+ 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);
+
+ //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 = createInsertOperation("c", "0", 1);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(cElement);
+ checkOperation(client.handleLocalOperationReceived(clientOp), "c", "0", 1, cElement);
+
+ //Client receives second local operation, client didn't receive ack about previous
+ //operation from the server so it can't be send.
+ clientOp = createInsertOperation("d", "c", 2);
+ WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(dElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp));
+
+ //Client receives confirmation about processing "c" operation, it should be the
+ //same as sent operation because it wasn't transformed, client could send now
+ //operation "d"
+ clientOp = createInsertOperation("c", "0", 1);
+ clientOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(clientOp);
+ checkOperation(pairResult.server, "d", "c", 2, dElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+ //Client receives confirmation about processing "d", it should be the same as
+ //sent operation. There aren't any operations in queue to send.
+ clientOp = createInsertOperation("d", "c", 2);
+ clientOp->setElement(dElement);
+ pairResult = client.handleServerOperationReceived(clientOp);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives new operation from server, it's parented off "d" which is at
+ //the end of local history so it doesn't have to be transformed, so operation
+ //to pass to window should be the same
+ serverOp = createInsertOperation("e", "d", 3);
+ WhiteboardEllipseElement::ref eElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(eElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ WhiteboardInsertOperation::ref result = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(pairResult.client);
+ CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast<WhiteboardInsertOperation>(pairResult.client));
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+
+ //Client operations:
+ //ID pos
+ //0 0
+ //c 1
+ //d 2
+ //e 3
+ //
+ //Server operations:
+ //ID pos
+ //0 0
+ //c 1
+ //d 2
+ //e 3
+ }
+
+ /*!
+ * /\
+ * / \
+ * \ /
+ * \/
+ */
+ void testSynchronize_nonInterrupted() {
+ 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);
+
+ //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;
+ clientOp = createInsertOperation("a", "0", 1);
+ WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(aElement);
+ checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
+
+ //Client receives second local operation, client didn't receive ack about previous
+ //operation from the server so it can't be send.
+ clientOp = createInsertOperation("b", "a", 2);
+ WhiteboardEllipseElement::ref bElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp));
+
+ //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", "0", 1);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "c", "b", 3, 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
+ serverOp = createInsertOperation("d", "c", 2);
+ WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(dElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "d", "c", 4, 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"
+ serverOp = createInsertOperation("a", "d", 1);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.server, "b", "a", 2, bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+
+ //Client receives confirmation about processing "b", there aren't any operations
+ //waiting so it should return nothing.
+ serverOp = createInsertOperation("b", "a", 2);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ 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.
+ }
+
+ /*!
+ * /\
+ * / \
+ * \ /
+ * / /
+ * \/
+ */
+ void testSynchronize_clientInterruption() {
+ 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);
+
+ //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;
+ clientOp = createInsertOperation("a", "0", 1);
+ WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(aElement);
+ checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
+
+ //Client receives second local operation, client didn't receive ack about previous
+ //operation from the server so it can't be send.
+ clientOp = createInsertOperation("b", "a", 2);
+ WhiteboardEllipseElement::ref bElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp));
+
+ //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", "0", 1);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "c", "b", 3, cElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives new local operation, client is still waiting for ack so, it
+ //should return nothing
+ clientOp = createInsertOperation("e", "a", 4);
+ WhiteboardEllipseElement::ref eElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(eElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp));
+
+ //Client receives new server operation, to add it to local history it should be transformed
+ //against result of previous transformations and operation "e", returned operation should
+ //be parented off "e", which was last local operation
+ serverOp = createInsertOperation("d", "c", 2);
+ WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(dElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "d", "e", 5, dElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives confirmation about processing "a", it had to be transformed against
+ //"c" and "d" and it is now parented off "d", returned value should be next operation
+ //which have to be send to server("b" parented off server version of "a").
+ serverOp = createInsertOperation("a", "d", 1);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.server, "b", "a", 2, bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+ //Client receives confirmation about processing "b", it is the same operation as sent because
+ //it didn't have to be transformed, returned value should be next operation
+ //which have to be send to server("e" parented off server version of "b").
+ serverOp = createInsertOperation("b", "a", 2);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.server, "e", "b", 4, eElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+ //Client receives confirmation about processing "b", it is the same operation as sent because
+ //it didn't have to be transformed, there aren't any operations to send so this function returns
+ //nothing
+ serverOp = createInsertOperation("e", "b", 4);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Result:
+ //Client operations:
+ //ID pos
+ //0 0
+ //a 1
+ //b 2
+ //c 3
+ //e 4
+ //d 5
+ //
+ //Server operations:
+ //0 0
+ //c 1
+ //d 2
+ //a 1
+ //b 2
+ //e 4
+ //what gives 0abced on both sides
+ }
+
+ /*!
+ * /\
+ * / /
+ * \ \
+ * \/
+ */
+ void testSynchronize_serverInterruption() {
+ 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);
+
+ //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;
+ clientOp = createInsertOperation("a", "0", 1);
+ WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(aElement);
+ checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
+
+ //Client receives second local operation, client didn't receive ack about previous
+ //operation from the server so it can't be send.
+ clientOp = createInsertOperation("b", "a", 2);
+ WhiteboardEllipseElement::ref bElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp));
+
+ //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", "0", 1);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "c", "b", 3, cElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives confirmation about processing "a", it had to be transformed against
+ //"c" and it is now parented off "c", returned value should be next operation
+ //which have to be send to server("b" parented off server version of "a").
+ serverOp = createInsertOperation("a", "c", 1);
+ serverOp->setElement(aElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.server, "b", "a", 2, bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+ //Client receives new server operation, to add it to local history it should be transformed
+ //against result of previous transformation(but only with transformation of "b"), returned
+ //operation should be parented off "c", which was last local operation
+ serverOp = createInsertOperation("d", "a", 3);
+ WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(dElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "d", "c", 4, dElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+ //Client receives confirmation about processing "b", it had to be transformed against
+ //"d" because both operations was parented off server version of "a".
+ //there aren't any operations to send so this function returns nothing.
+ serverOp = createInsertOperation("b", "d", 2);
+ serverOp->setElement(bElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ 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
+ //a 1
+ //d 3
+ //b 2
+ //
+ //what gives 0abcd on both sides
+
+ }
+
+ /*!
+ * /\
+ * / \
+ * \ /
+ * \/
+ */
+ void testSynchronize_nonInterruptedMixOperations() {
+ 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);
+
+ //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;
+ clientOp = createInsertOperation("a", "0", 1);
+ WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientOp->setElement(aElement);
+ checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
+
+ //Client receives second local operation, client didn't receive ack about previous
+ //operation from the server so it can't be send.
+ WhiteboardUpdateOperation::ref clientUpdateOp = createUpdateOperation("b", "a", 0);
+ WhiteboardEllipseElement::ref bElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ clientUpdateOp->setElement(bElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientUpdateOp));
+
+ //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
+ WhiteboardUpdateOperation::ref serverUpdateOp = createUpdateOperation("c", "0", 0);
+ WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverUpdateOp->setElement(cElement);
+ pairResult = client.handleServerOperationReceived(serverUpdateOp);
+ checkOperation(pairResult.client, "c", "b", 0, 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
+ serverOp = createInsertOperation("d", "c", 1);
+ WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+ serverOp->setElement(dElement);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.client, "d", "c", 2, 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"
+ serverOp = createInsertOperation("a", "d", 1);
+ pairResult = client.handleServerOperationReceived(serverOp);
+ checkOperation(pairResult.server, "b", "a", 0, cElement);
+ CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+
+ //Client receives confirmation about processing "b", there aren't any operations
+ //waiting so it should return nothing.
+ serverUpdateOp = createUpdateOperation("b", "a", 0);
+ pairResult = client.handleServerOperationReceived(serverUpdateOp);
+ 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.
+ }
+
+ /*!
+ * /\
+ * / \
+ * \ /
+ * \/
+ */
+ 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);
+ 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);
+ operation->setID(id);
+ operation->setPos(pos);
+ return operation;
+ }
+
+ WhiteboardUpdateOperation::ref createUpdateOperation(std::string id, std::string parent, int pos) {
+ WhiteboardUpdateOperation::ref operation = boost::make_shared<WhiteboardUpdateOperation>();
+ operation->setParentID(parent);
+ operation->setID(id);
+ operation->setPos(pos);
+ 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());
+ if (pos != -1) {
+ CPPUNIT_ASSERT_EQUAL(pos, operation->getPos());
+ }
+
+ if (element) {
+ WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
+ if (insertOp) {
+ CPPUNIT_ASSERT_EQUAL(element, insertOp->getElement());
+ }
+
+ WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(operation);
+ if (updateOp) {
+ CPPUNIT_ASSERT_EQUAL(element, updateOp->getElement());
+ }
+ }
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(WhiteboardClientTest);