[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-3-0' - common/Unit.hpp net/Socket.hpp test/Makefile.am test/UnitWOPIDocumentConflict.cpp test/WopiTestServer.hpp wsd/LOOLWSD.cpp

Pranav Kant pranavk at collabora.co.uk
Thu Feb 15 13:16:52 UTC 2018


 common/Unit.hpp                   |    4 -
 net/Socket.hpp                    |    2 
 test/Makefile.am                  |   10 ++
 test/UnitWOPIDocumentConflict.cpp |  136 ++++++++++++++++++++++++++++++++++++++
 test/WopiTestServer.hpp           |   40 ++++++++++-
 wsd/LOOLWSD.cpp                   |    2 
 6 files changed, 188 insertions(+), 6 deletions(-)

New commits:
commit 292186d1b66bd7077a9a28dc590f423bf550f3a8
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Feb 9 00:21:54 2018 +0530

    document conflict: unit test
    
    Change-Id: I4ea310fe5adb198bc7b5e083f6bd4b0431c0cdef
    (cherry picked from commit 39f11ab4f7be708acc5e913d62e99321d8675357)
    Reviewed-on: https://gerrit.libreoffice.org/49757
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>

diff --git a/common/Unit.hpp b/common/Unit.hpp
index 97db1c39..5844cc3b 100644
--- a/common/Unit.hpp
+++ b/common/Unit.hpp
@@ -29,6 +29,8 @@ class WebSocketHandler;
 // Forward declaration to avoid pulling the world here.
 namespace Poco
 {
+    class MemoryInputStream;
+
     namespace Net
     {
         class HTTPServerRequest;
@@ -117,7 +119,7 @@ public:
     }
 
     /// Custom response to a http request.
-    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& /*request*/, std::shared_ptr<StreamSocket>& /*socket*/)
+    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& /*request*/, Poco::MemoryInputStream& /*message*/,std::shared_ptr<StreamSocket>& /*socket*/)
     {
         return false;
     }
diff --git a/net/Socket.hpp b/net/Socket.hpp
index da34b5e3..7e5035b4 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -501,7 +501,7 @@ public:
         } while (rc == -1 && errno == EINTR);
 
         if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
-            LOG_SYS("wakeup socket #" << fd << " is closd at wakeup?");
+            LOG_SYS("wakeup socket #" << fd << " is closed at wakeup?");
     }
 
     /// Wakeup the main polling loop in another thread
diff --git a/test/Makefile.am b/test/Makefile.am
index 1e5e16dc..81346fe0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -18,7 +18,8 @@ noinst_LTLIBRARIES = \
 	unit-admin.la unit-tilecache.la \
 	unit-fuzz.la unit-oob.la unit-oauth.la \
 	unit-wopi.la unit-wopi-saveas.la \
-	unit-wopi-ownertermination.la unit-wopi-versionrestore.la
+	unit-wopi-ownertermination.la unit-wopi-versionrestore.la \
+	unit-wopi-documentconflict.la
 
 
 MAGIC_TO_FORCE_SHLIB_CREATION = -rpath /dummy
@@ -88,6 +89,8 @@ unit_wopi_ownertermination_la_SOURCES = UnitWopiOwnertermination.cpp
 unit_wopi_ownertermination_la_LIBADD = $(CPPUNIT_LIBS)
 unit_wopi_versionrestore_la_SOURCES = UnitWOPIVersionRestore.cpp
 unit_wopi_versionrestore_la_LIBADD = $(CPPUNIT_LIBS)
+unit_wopi_documentconflict_la_SOURCES = UnitWOPIDocumentConflict.cpp
+unit_wopi_documentconflict_la_LIBADD = $(CPPUNIT_LIBS)
 
 if HAVE_LO_PATH
 SYSTEM_STAMP = @SYSTEMPLATE_PATH@/system_stamp
@@ -101,7 +104,10 @@ check-local:
 	./run_unit.sh --log-file test.log --trs-file test.trs
 # FIXME 2: unit-oob.la fails with symbol undefined:
 # UnitWSD::testHandleRequest(UnitWSD::TestRequest, UnitHTTPServerRequest&, UnitHTTPServerResponse&) ,
-TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la unit-oauth.la unit-wopi.la unit-wopi-saveas.la unit-wopi-ownertermination.la unit-wopi-versionrestore.la
+TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la \
+        unit-oauth.la unit-wopi.la unit-wopi-saveas.la \
+        unit-wopi-ownertermination.la unit-wopi-versionrestore.la \
+        unit-wopi-documentconflict.la
 # TESTS = unit-client.la
 # TESTS += unit-admin.la
 # TESTS += unit-storage.la
diff --git a/test/UnitWOPIDocumentConflict.cpp b/test/UnitWOPIDocumentConflict.cpp
new file mode 100644
index 00000000..0b1cbb8e
--- /dev/null
+++ b/test/UnitWOPIDocumentConflict.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "config.h"
+
+#include "WopiTestServer.hpp"
+#include "Log.hpp"
+#include "Unit.hpp"
+#include "UnitHTTP.hpp"
+#include "helpers.hpp"
+
+#include <Poco/Net/HTTPRequest.h>
+#include <Poco/Timestamp.h>
+
+/**
+ * This test asserts that the unsaved changes in the opened document are
+ * discarded in case document is changed in storage behind our back. We don't
+ * want to overwrite the document which is in storage when the user asks us to
+ * do so.
+ */
+class UnitWOPIDocumentConflict : public WopiTestServer
+{
+    enum class Phase
+    {
+        Load,
+        ModifyAndChangeStorageDoc,
+        LoadNewDocument,
+        Polling
+    } _phase;
+
+    enum class DocLoaded
+    {
+	Doc1,
+	Doc2
+    } _docLoaded;
+
+    const std::string _testName = "UnitWOPIDocumentConflict";
+
+public:
+    UnitWOPIDocumentConflict() :
+        _phase(Phase::Load)
+    {
+    }
+
+    void assertGetFileRequest(const Poco::Net::HTTPRequest& /*request*/) override
+    {
+	if (_docLoaded == DocLoaded::Doc2)
+	{
+	    // On second doc load, we should have the document in storage which
+	    // was changed beneath us, not the one which we modified by pressing 'a'
+	    if (_fileContent != "Modified content in storage")
+		exitTest(TestResult::Failed);
+	    else
+		exitTest(TestResult::Ok);
+	}
+    }
+
+    bool filterSendMessage(const char* data, const size_t len, const WSOpCode /* code */, const bool /* flush */, int& /*unitReturn*/) override
+    {
+        std::string message(data, len);
+        if (message == "error: cmd=storage kind=documentconflict")
+        {
+	    // we don't want to save current changes because doing so would
+	    // overwrite the document which was changed underneath us
+	    helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "closedocument", _testName.c_str());
+	    _phase = Phase::LoadNewDocument;
+        }
+
+        return false;
+    }
+
+    void invokeTest() override
+    {
+        switch (_phase)
+        {
+            case Phase::Load:
+            {
+                initWebsocket("/wopi/files/0?access_token=anything");
+		_docLoaded = DocLoaded::Doc1;
+
+                helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "load url=" + _wopiSrc, _testName.c_str());
+
+                _phase = Phase::ModifyAndChangeStorageDoc;
+                break;
+            }
+            case Phase::ModifyAndChangeStorageDoc:
+            {
+		// modify the currently opened document; type 'a'
+                helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "key type=input char=97 key=0", _testName.c_str());
+                helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "key type=up char=0 key=512", _testName.c_str());
+                SocketPoll::wakeupWorld();
+
+		// ModifiedStatus=true is a bit slow; let's sleep and hope that
+		// it is received before we wake up
+		std::this_thread::sleep_for(std::chrono::milliseconds(POLL_TIMEOUT_MS));
+
+		// change the document underneath, in storage
+		setFileContent("Modified content in storage");
+
+		// save the document; wsd should detect now that document has
+		// been changed underneath it and send us:
+		// "error: cmd=storage kind=documentconflict"
+		helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "save", _testName.c_str());
+
+                _phase = Phase::Polling;
+
+                break;
+            }
+	    case Phase::LoadNewDocument:
+            {
+		initWebsocket("/wopi/files/0?access_token=anything");
+		_docLoaded = DocLoaded::Doc2;
+                _phase = Phase::Polling;
+                break;
+            }
+            case Phase::Polling:
+            {
+                // just wait for the results
+                break;
+            }
+        }
+    }
+};
+
+UnitBase *unit_create_wsd(void)
+{
+    return new UnitWOPIDocumentConflict();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/WopiTestServer.hpp b/test/WopiTestServer.hpp
index 6efff2fb..651fef49 100644
--- a/test/WopiTestServer.hpp
+++ b/test/WopiTestServer.hpp
@@ -18,12 +18,18 @@
 #include <Poco/DateTimeFormat.h>
 #include <Poco/DateTimeFormatter.h>
 #include <Poco/JSON/Object.h>
+#include <Poco/MemoryStream.h>
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/URI.h>
 #include <Poco/Timestamp.h>
 
 class WopiTestServer : public UnitWSD
 {
+    enum class LOOLStatusCode
+    {
+        DocChanged = 1010
+    };
+
 protected:
     /// The WOPISrc URL.
     std::string _wopiSrc;
@@ -37,6 +43,13 @@ protected:
     /// Last modified time of the file
     Poco::Timestamp _fileLastModifiedTime;
 
+    /// Sets the file content to a given value and update the last file modified time
+    void setFileContent(const std::string& fileContent)
+    {
+        _fileContent = fileContent;
+        _fileLastModifiedTime = Poco::Timestamp();
+    }
+
 public:
     WopiTestServer(std::string fileContent = "Hello, world")
         : UnitWSD()
@@ -77,7 +90,7 @@ public:
 protected:
     /// Here we act as a WOPI server, so that we have a server that responds to
     /// the wopi requests without additional expensive setup.
-    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, std::shared_ptr<StreamSocket>& socket) override
+    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message, std::shared_ptr<StreamSocket>& socket) override
     {
         Poco::URI uriReq(request.getURI());
         LOG_INF("Fake wopi host request: " << uriReq.toString());
@@ -174,6 +187,31 @@ protected:
         {
             LOG_INF("Fake wopi host request, handling PutFile: " << uriReq.getPath());
 
+            std::string wopiTimestamp = request.get("X-LOOL-WOPI-Timestamp");
+            if (!wopiTimestamp.empty())
+            {
+                const std::string fileModifiedTime =
+                    Poco::DateTimeFormatter::format(Poco::DateTime(_fileLastModifiedTime),
+                                                    Poco::DateTimeFormat::ISO8601_FRAC_FORMAT);
+                if (wopiTimestamp != fileModifiedTime)
+                {
+                    std::ostringstream oss;
+                    oss << "HTTP/1.1 409 Conflict\r\n"
+                        << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                        << "\r\n"
+                        << "{\"LOOLStatusCode\":" << LOOLStatusCode::DocChanged << "}";
+
+                    socket->send(oss.str());
+                    socket->shutdown();
+                    return true;
+                }
+            }
+
+            std::streamsize size = request.getContentLength();
+            char buffer[size];
+            message.read(buffer, size);
+            setFileContent(buffer);
+
             assertPutFileRequest(request);
 
             std::ostringstream oss;
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index b713ac0c..9b89afb5 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1788,7 +1788,7 @@ private:
             std::vector<std::string> reqPathSegs;
             requestUri.getPathSegments(reqPathSegs);
 
-            if (UnitWSD::get().handleHttpRequest(request, socket))
+            if (UnitWSD::get().handleHttpRequest(request, message, socket))
             {
                 // Unit testing, nothing to do here
             }


More information about the Libreoffice-commits mailing list