[Libreoffice-commits] online.git: 6 commits - loleaflet/.gitignore loolwsd/configure.ac loolwsd/.gitignore loolwsd/LOOLKit.hpp loolwsd/LOOLWSD.cpp loolwsd/Makefile.am loolwsd/test loolwsd/Unit.cpp loolwsd/Unit.hpp loolwsd/Util.cpp

Michael Meeks michael.meeks at collabora.com
Thu Apr 7 18:39:36 UTC 2016


 loleaflet/.gitignore         |    1 
 loolwsd/.gitignore           |   44 +++++++++++---------
 loolwsd/LOOLKit.hpp          |    1 
 loolwsd/LOOLWSD.cpp          |   31 ++++++++++++--
 loolwsd/Makefile.am          |    4 +
 loolwsd/Unit.cpp             |   90 ++++++++++++++++++++++++++++++++++++++++++
 loolwsd/Unit.hpp             |   53 +++++++++++++++++++++++++
 loolwsd/Util.cpp             |   91 ++++++++++++++++++++++++++++++++-----------
 loolwsd/configure.ac         |   37 +++++++++++++++++
 loolwsd/test/.gitignore      |   10 ++--
 loolwsd/test/Makefile.am     |   50 ++++++++++++++++++++++-
 loolwsd/test/UnitPrefork.cpp |   45 +++++++++++++++++++++
 loolwsd/test/run_unit.sh     |   25 +++++++++++
 13 files changed, 430 insertions(+), 52 deletions(-)

New commits:
commit cd47b5e044893994a534c7b94b3fa11b87cfaeca
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Apr 7 17:43:26 2016 +0100

    Signal safe signal handlers.

diff --git a/loolwsd/Util.cpp b/loolwsd/Util.cpp
index 2d73562..678b1d7 100644
--- a/loolwsd/Util.cpp
+++ b/loolwsd/Util.cpp
@@ -18,6 +18,7 @@
 #include <random>
 #include <sstream>
 #include <string>
+#include <unistd.h>
 
 #include <png.h>
 
@@ -89,6 +90,8 @@ namespace rng
 }
 }
 
+static char LogPrefix[256] = { '\0' };
+
 namespace Log
 {
     static const Poco::Int64 epochStart = Poco::Timestamp().epochMicroseconds();
@@ -135,7 +138,6 @@ namespace Log
         return stream.str();
     }
 
-
     void initialize(const std::string& name)
     {
         Source.name = name;
@@ -143,6 +145,8 @@ namespace Log
         oss << Source.name << '-'
             << std::setw(5) << std::setfill('0') << Poco::Process::id();
         Source.id = oss.str();
+        assert (sizeof (LogPrefix) > strlen(oss.str().c_str()) + 1);
+        strcpy(LogPrefix, oss.str().c_str());
 
         auto channel = (isatty(fileno(stdout)) || std::getenv("LOOL_LOGCOLOR")
                      ? static_cast<Poco::Channel*>(new Poco::ColorConsoleChannel())
@@ -356,19 +360,38 @@ namespace Util
         }
     }
 
+    // We need a signal safe means of writing messages
+    //   $ man 7 signal
+    static
+    void log_signal(const char *message)
+    {
+        while (true) {
+            int length = strlen(message);
+            int written = write (STDERR_FILENO, message, length);
+            if (written < 0)
+            {
+                if (errno == EINTR)
+                    continue; // ignore.
+                else
+                    break;
+            }
+            message += written;
+            if (message[0] == '\0')
+                break;
+        }
+    }
+
     static
     void handleTerminationSignal(const int signal)
     {
         if (!TerminationFlag)
         {
-            // Poco::Log takes a lock that isn't recursive.
-            // If we are signaled while having that lock,
-            // logging again will deadlock on it.
             TerminationFlag = true;
 
-            Log::info() << "Termination signal received: "
-                        << Util::signalName(signal) << " "
-                        << strsignal(signal) << Log::end;
+            log_signal(LogPrefix);
+            log_signal(" Termination signal received: ");
+            log_signal(strsignal(signal));
+            log_signal("\n");
         }
     }
 
@@ -386,19 +409,19 @@ namespace Util
         sigaction(SIGHUP, &action, nullptr);
     }
 
+    static char FatalGdbString[256] = { '\0' };
+
     static
     void handleFatalSignal(const int signal)
     {
-        Log::error() << "Fatal signal received: "
-                     << Util::signalName(signal) << " "
-                     << strsignal(signal) << Log::end;
+        log_signal(LogPrefix);
+        log_signal(" Fatal signal received: ");
+        log_signal(strsignal(signal));
+        log_signal("\n");
 
         if (std::getenv("LOOL_DEBUG"))
         {
-            Log::error() << "\nFatal signal! Attach debugger with:\n"
-                         << "sudo gdb --pid=" << Poco::Process::id() << "\n or \n"
-                         << "sudo gdb --q --n --ex 'thread apply all backtrace full' --batch --pid="
-                         << Poco::Process::id() << "\n" << Log::end;
+            log_signal(FatalGdbString);
             sleep(30);
         }
 
@@ -426,6 +449,16 @@ namespace Util
         sigaction(SIGABRT, &action, NULL);
         sigaction(SIGILL, &action, NULL);
         sigaction(SIGFPE, &action, NULL);
+
+        // prepare this in advance just in case.
+        std::ostringstream stream;
+        stream << "\nFatal signal! Attach debugger with:\n"
+               << "sudo gdb --pid=" << Poco::Process::id() << "\n or \n"
+               << "sudo gdb --q --n --ex 'thread apply all backtrace full' --batch --pid="
+               << Poco::Process::id() << "\n";
+        std::string streamStr = stream.str();
+        assert (sizeof (FatalGdbString) > strlen(streamStr.c_str()) + 1);
+        strcpy(FatalGdbString, streamStr.c_str());
     }
 
     int getChildStatus(const int code)
commit a454a3c407825cf5abe150f59765733b79c051df
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed Apr 6 19:50:55 2016 +0100

    Improve test API, and handle failure better.

diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 95ab3e5..3c497db 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -164,9 +164,9 @@ void forkChildren(int number)
 void preForkChildren()
 {
     std::unique_lock<std::mutex> lock(newChildrenMutex);
-    int nPreSpawn = LOOLWSD::NumPreSpawnedChildren;
-    UnitHooks::get().preSpawnCount(nPreSpawn);
-    forkChildren(nPreSpawn);
+    int numPreSpawn = LOOLWSD::NumPreSpawnedChildren;
+    UnitHooks::get().preSpawnCount(numPreSpawn);
+    forkChildren(numPreSpawn);
 }
 
 std::shared_ptr<ChildProcess> getNewChild()
@@ -662,6 +662,7 @@ public:
             newChildren.emplace_back(std::make_shared<ChildProcess>(pid, ws));
             Log::info("Have " + std::to_string(newChildren.size()) + " children.");
             newChildrenCV.notify_one();
+            UnitHooks::get().newChild();
             return;
         }
 
@@ -1543,8 +1544,12 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     Poco::Net::uninitializeSSL();
     Poco::Crypto::uninitializeCrypto();
 
-    Log::info("Process finished.");
-    return Application::EXIT_OK;
+    Log::info("Process [loolwsd] finished.");
+
+    int returnValue = Application::EXIT_OK;
+    UnitHooks::get().returnValue(returnValue);
+
+    return returnValue;
 }
 
 POCO_SERVER_MAIN(LOOLWSD)
diff --git a/loolwsd/Unit.cpp b/loolwsd/Unit.cpp
index 82718ce..2a9448c 100644
--- a/loolwsd/Unit.cpp
+++ b/loolwsd/Unit.cpp
@@ -13,6 +13,9 @@
 #include "Util.hpp"
 #include "Unit.hpp"
 
+#include <Poco/Util/Application.h>
+using Poco::Util::Application;
+
 UnitHooks *UnitHooks::_global = nullptr;
 
 UnitHooks *UnitHooks::linkAndCreateUnit(const std::string &unitLibPath)
@@ -56,9 +59,32 @@ UnitHooks::UnitHooks()
 
 UnitHooks::~UnitHooks()
 {
-    if (_dlHandle)
-        dlclose(_dlHandle);
+// FIXME: we should really clean-up properly.
+//    if (_dlHandle)
+//        dlclose(_dlHandle);
     _dlHandle = NULL;
 }
 
+void UnitHooks::exitTest(TestResult result)
+{
+    _setRetValue = true;
+    _retValue = result == TestResult::TEST_OK ?
+        Application::EXIT_OK : Application::EXIT_SOFTWARE;
+    TerminationFlag = true;
+}
+
+/// Tweak the return value from LOOLWSD.
+void UnitHooks::returnValue(int &retValue)
+{
+    if (_setRetValue)
+        retValue = _retValue;
+}
+
+// FIXME: trigger the timeout.
+void UnitHooks::timeout()
+{
+    Log::error("Test timed out - failing.");
+    exitTest(TestResult::TEST_TIMED_OUT);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/Unit.hpp b/loolwsd/Unit.hpp
index 36c6af6..dc53d9f 100644
--- a/loolwsd/Unit.hpp
+++ b/loolwsd/Unit.hpp
@@ -21,10 +21,16 @@ extern "C" { UnitHooks *unit_create(void); }
 class UnitHooks
 {
     void *_dlHandle;
+    bool _setRetValue;
+    int  _retValue;
     static UnitHooks *_global;
 
     void setHandle(void *dlHandle) { _dlHandle = dlHandle; }
     static UnitHooks *linkAndCreateUnit(const std::string &unitLibPath);
+protected:
+    enum TestResult { TEST_FAILED, TEST_OK, TEST_TIMED_OUT };
+    /// Encourages loolwsd to exit with this value (unless hooked)
+    void exitTest(TestResult result);
 public:
              UnitHooks();
     virtual ~UnitHooks();
@@ -32,7 +38,14 @@ public:
     /// Load unit test hook shared library from this path
     static bool init(const std::string &unitLibPath);
 
+    /// Tweak the count of pre-spawned kits.
 	virtual void preSpawnCount(int & /* numPrefork */) {}
+    /// Tweak the return value from LOOLWSD.
+	virtual void returnValue(int & /* retValue */);
+    /// When a new child kit process reports
+    virtual void newChild() {}
+    /// If the test times out
+    virtual void timeout();
 };
 
 #endif // LOOL_UNIT_HPP
diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index 412606c..d7a17a3 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -1,3 +1,12 @@
+# variables for tests
+jails=${top_builddir}/test/jails
+systemplate=${top_builddir}/test/systemplate
+export jails
+export systemplate
+export LO_PATH
+
+AUTOMAKE_OPTION = serial-tests
+
 check_PROGRAMS = test
 
 AM_CXXFLAGS = $(CPPUNIT_CFLAGS)
@@ -17,11 +26,6 @@ unit_prefork_la_SOURCES = \
 	UnitPrefork.cpp
 unit_prefork_la_LDFLAGS = -module
 
-jails=${top_builddir}/test/jails
-systemplate=${top_builddir}/test/systemplate
-export jails
-export systemplate
-
 ${systemplate}/system_stamp :
 	rm -rf ${systemplate}
 	${top_srcdir}/loolwsd-systemplate-setup ${systemplate} ${LO_PATH} && touch ${systemplate}/system_stamp
@@ -29,17 +33,25 @@ ${systemplate}/system_stamp :
 ${jails} :
 	mkdir -p ${jails}
 
-run_tests : ${systemplate}/system_stamp ${jails}
-	${top_srcdir}/test/run_unit.sh "${LO_PATH}"
+${top_builddir}/test/run_unit.sh.log ${top_builddir}/test/run_unit.sh.trs : \
+	${systemplate}/system_stamp ${jails} \
+	${top_srcdir}/test/run_unit.sh \
+	${top_builddir}/loolwsd \
+	${top_builddir}/loolforkit \
+	unit-prefork.la
+	if ${top_srcdir}/test/run_unit.sh; then \
+	   touch ${top_builddir}/test/run_unit.sh.log; \
+	fi
 
 if HAVE_LO_PATH
-TESTS = run_tests $(top_builddir)/test/test
+TESTS = ${top_builddir}/test/run_unit.sh ${top_builddir}/test/test
 
 clean-local:
 	rm -rf ${top_builddir}/test/jails
 	rm -rf ${systemplate}
+	rm -f ${top_builddir}/test/unit_stamp;
 else
-TESTS = $(top_builddir)/test/test
+TESTS = ${top_builddir}/test/test
 endif
 
 EXTRA_DIST = data/hello.odt data/hello.txt $(test_SOURCES) run_unit.sh
diff --git a/loolwsd/test/UnitPrefork.cpp b/loolwsd/test/UnitPrefork.cpp
index 3d93d4d..6bca7b9 100644
--- a/loolwsd/test/UnitPrefork.cpp
+++ b/loolwsd/test/UnitPrefork.cpp
@@ -13,16 +13,28 @@
 #include "Util.hpp"
 #include "Unit.hpp"
 
-class UnitPrefork : public UnitHooks {
+class UnitPrefork : public UnitHooks
+{
+    int _numStarted;
+    const int _numToPrefork;
 public:
     UnitPrefork()
+        : _numStarted(0),
+          _numToPrefork(20)
     {
     }
-    virtual void preSpawnCount(int &numPrefork)
+    virtual void preSpawnCount(int &numPrefork) override
     {
-        numPrefork = 20;
+        numPrefork = _numToPrefork;
         Log::error("Hello world");
     }
+
+    virtual void newChild() override
+    {
+        _numStarted++;
+        if (_numStarted >= _numToPrefork + 1)
+            exitTest(TestResult::TEST_OK);
+    }
 };
 
 UnitHooks *unit_create(void)
diff --git a/loolwsd/test/run_unit.sh b/loolwsd/test/run_unit.sh
index d8ec7f8..8f2a79a 100755
--- a/loolwsd/test/run_unit.sh
+++ b/loolwsd/test/run_unit.sh
@@ -2,11 +2,24 @@
 
 export LOOL_LOGLEVEL=trace
 
-for test in prefork; do
-    echo "Running test: $test";
-    if ../loolwsd --systemplate=${systemplate} --lotemplate="$1" --childroot="${jails}" --unitlib=".libs/unit-$test.so" >& "${jails}/$test.log"; then
+mkdir -p test_output
+
+# result logging
+echo > run_unit.sh.trs
+
+for tst in prefork; do
+    tst_log="test_output/$tst.log"
+    echo "Running test: $tst | $tst_log";
+    if ../loolwsd --systemplate=${systemplate} --lotemplate="${LO_PATH}" --childroot="${jails}" --unitlib=".libs/unit-$tst.so" >& "$tst_log"; then
+	echo "Test $tst passed."
+	echo ":test-result: PASS $tst" >> run_unit.sh.trs
     else
+	cat "$tst_log"
+        echo "============================================================="
+	echo "Test failed on unit: $tst re-run with:"
+	echo "   $ gdb --args ../loolwsd --systemplate=${systemplate} --lotemplate=\"${LO_PATH}\" --childroot=\"${jails}\" --unitlib=\".libs/unit-$tst.so\""
+        echo "============================================================="
+	echo ":test-result: FAIL $tst" >> run_unit.sh.trs
     fi
 done
 
-
commit 024fd2ee8d639dc52c9038c4e54a7705a9709b4c
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Tue Apr 5 23:04:40 2016 +0100

    Scripting to create and populate jails automatically.
    
    'make check' now builds a system image, and runs tests inside it.

diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index 94de2de..412606c 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -17,14 +17,31 @@ unit_prefork_la_SOURCES = \
 	UnitPrefork.cpp
 unit_prefork_la_LDFLAGS = -module
 
+jails=${top_builddir}/test/jails
+systemplate=${top_builddir}/test/systemplate
+export jails
+export systemplate
+
+${systemplate}/system_stamp :
+	rm -rf ${systemplate}
+	${top_srcdir}/loolwsd-systemplate-setup ${systemplate} ${LO_PATH} && touch ${systemplate}/system_stamp
+
+${jails} :
+	mkdir -p ${jails}
+
+run_tests : ${systemplate}/system_stamp ${jails}
+	${top_srcdir}/test/run_unit.sh "${LO_PATH}"
+
 if HAVE_LO_PATH
-TESTS = $(top_builddir)/test/test $(top_builddir)/loolwsd/test/run_unit.sh
+TESTS = run_tests $(top_builddir)/test/test
+
+clean-local:
+	rm -rf ${top_builddir}/test/jails
+	rm -rf ${systemplate}
 else
 TESTS = $(top_builddir)/test/test
 endif
 
 EXTRA_DIST = data/hello.odt data/hello.txt $(test_SOURCES) run_unit.sh
 
-clean-hook:
-	@echo "rm -Rf $(top_builddir)/test/jails"
 
diff --git a/loolwsd/test/run_unit.sh b/loolwsd/test/run_unit.sh
new file mode 100755
index 0000000..d8ec7f8
--- /dev/null
+++ b/loolwsd/test/run_unit.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+export LOOL_LOGLEVEL=trace
+
+for test in prefork; do
+    echo "Running test: $test";
+    if ../loolwsd --systemplate=${systemplate} --lotemplate="$1" --childroot="${jails}" --unitlib=".libs/unit-$test.so" >& "${jails}/$test.log"; then
+    else
+    fi
+done
+
+
commit ad13dca8f89b94fe8013fd6d6fb91087d9b9a9a4
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Tue Apr 5 17:41:10 2016 +0100

    First cut at adding integration and unit test hooks into loolwsd.
    
    Add new configure parameter --with-lo-path= which can be used to
    auto-populate a system template as required.

diff --git a/loolwsd/LOOLKit.hpp b/loolwsd/LOOLKit.hpp
index 3a4ca67..4f1bcc3 100644
--- a/loolwsd/LOOLKit.hpp
+++ b/loolwsd/LOOLKit.hpp
@@ -1,3 +1,4 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * This file is part of the LibreOffice project.
  *
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index d8669d4..95ab3e5 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -92,6 +92,7 @@
 #include "Storage.hpp"
 #include "IoUtil.hpp"
 #include "Util.hpp"
+#include "Unit.hpp"
 
 using namespace LOOLProtocol;
 
@@ -163,7 +164,9 @@ void forkChildren(int number)
 void preForkChildren()
 {
     std::unique_lock<std::mutex> lock(newChildrenMutex);
-    forkChildren(LOOLWSD::NumPreSpawnedChildren);
+    int nPreSpawn = LOOLWSD::NumPreSpawnedChildren;
+    UnitHooks::get().preSpawnCount(nPreSpawn);
+    forkChildren(nPreSpawn);
 }
 
 std::shared_ptr<ChildProcess> getNewChild()
@@ -954,6 +957,7 @@ std::string LOOLWSD::LoSubPath = "lo";
 std::string LOOLWSD::FileServerRoot;
 std::string LOOLWSD::AdminCreds;
 bool LOOLWSD::AllowLocalStorage = false;
+std::string UnitTestLibrary;
 
 unsigned int LOOLWSD::NumPreSpawnedChildren = 0;
 bool LOOLWSD::DoTest = false;
@@ -1139,9 +1143,15 @@ void LOOLWSD::defineOptions(OptionSet& optionSet)
     optionSet.addOption(Option("test", "", "Interactive testing.")
                         .required(false)
                         .repeatable(false));
+
+    optionSet.addOption(Option("unitlib", "", "Unit testing library path.")
+                        .required(false)
+                        .repeatable(false)
+                        .argument("unitlib"));
 }
 
-void LOOLWSD::handleOption(const std::string& optionName, const std::string& value)
+void LOOLWSD::handleOption(const std::string& optionName,
+                           const std::string& value)
 {
     ServerApplication::handleOption(optionName, value);
 
@@ -1175,6 +1185,8 @@ void LOOLWSD::handleOption(const std::string& optionName, const std::string& val
         AdminCreds = value;
     else if (optionName == "allowlocalstorage")
         AllowLocalStorage = true;
+    else if (optionName == "unitlib")
+        UnitTestLibrary = value;
     else if (optionName == "test")
         LOOLWSD::DoTest = true;
 }
@@ -1223,6 +1235,12 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
         return Application::EXIT_USAGE;
     }
 
+    if (!UnitHooks::init(UnitTestLibrary))
+    {
+        Log::error("Failed to load unit test library");
+        return Application::EXIT_USAGE;
+    }
+
     initializeSSL();
 
     char *locale = setlocale(LC_ALL, nullptr);
diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am
index 0731b3e..c555b88 100644
--- a/loolwsd/Makefile.am
+++ b/loolwsd/Makefile.am
@@ -5,7 +5,7 @@ bin_PROGRAMS = loolwsd loolforkit loolmap loolmount
 dist_bin_SCRIPTS = loolwsd-systemplate-setup discovery.xml
 
 AM_CPPFLAGS = -pthread
-AM_LDFLAGS = -pthread
+AM_LDFLAGS = -pthread -Wl,-E
 
 AM_ETAGSFLAGS = --c++-kinds=+p --fields=+iaS --extra=+q -R --totals=yes *
 AM_CTAGSFLAGS = $(AM_ETAGSFLAGS)
@@ -25,6 +25,7 @@ loolwsd_SOURCES = Admin.cpp \
                   MasterProcessSession.cpp \
                   Storage.cpp \
                   TileCache.cpp \
+		  Unit.cpp \
                   $(shared_sources)
 
 noinst_PROGRAMS = connect \
@@ -73,6 +74,7 @@ noinst_HEADERS = Admin.hpp \
                  Rectangle.hpp \
                  Storage.hpp \
                  TileCache.hpp \
+		 Unit.hpp \
                  Util.hpp \
                  bundled/include/LibreOfficeKit/LibreOfficeKit.h \
                  bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h \
diff --git a/loolwsd/Unit.cpp b/loolwsd/Unit.cpp
new file mode 100644
index 0000000..82718ce
--- /dev/null
+++ b/loolwsd/Unit.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 <dlfcn.h>
+#include <ftw.h>
+#include <cassert>
+#include "Util.hpp"
+#include "Unit.hpp"
+
+UnitHooks *UnitHooks::_global = nullptr;
+
+UnitHooks *UnitHooks::linkAndCreateUnit(const std::string &unitLibPath)
+{
+    void *dlHandle = dlopen(unitLibPath.c_str(), RTLD_GLOBAL|RTLD_NOW);
+    if (!dlHandle)
+    {
+        Log::error("Failed to load " + unitLibPath + ": " + std::string(dlerror()));
+        return NULL;
+    }
+
+    CreateUnitHooksFunction* createHooks;
+    createHooks = (CreateUnitHooksFunction *)dlsym(dlHandle, CREATE_UNIT_HOOKS_SYMBOL);
+    if (!createHooks)
+    {
+        Log::error("No " CREATE_UNIT_HOOKS_SYMBOL " symbol in " + unitLibPath);
+        return NULL;
+    }
+    UnitHooks *pHooks = createHooks();
+
+    if (pHooks)
+        pHooks->setHandle(dlHandle);
+
+    return pHooks;
+}
+
+bool UnitHooks::init(const std::string &unitLibPath)
+{
+    if (!unitLibPath.empty())
+        _global = linkAndCreateUnit(unitLibPath);
+    else
+        _global = new UnitHooks();
+
+    return _global != NULL;
+}
+
+UnitHooks::UnitHooks()
+    : _dlHandle(NULL)
+{
+}
+
+UnitHooks::~UnitHooks()
+{
+    if (_dlHandle)
+        dlclose(_dlHandle);
+    _dlHandle = NULL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/Unit.hpp b/loolwsd/Unit.hpp
new file mode 100644
index 0000000..36c6af6
--- /dev/null
+++ b/loolwsd/Unit.hpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ */
+#ifndef LOOL_UNIT_HPP
+#define LOOL_UNIT_HPP
+
+#include <string>
+
+class UnitHooks;
+
+#define CREATE_UNIT_HOOKS_SYMBOL "unit_create"
+typedef UnitHooks *(CreateUnitHooksFunction)();
+extern "C" { UnitHooks *unit_create(void); }
+
+/// Derive your unit test / hooks from me.
+class UnitHooks
+{
+    void *_dlHandle;
+    static UnitHooks *_global;
+
+    void setHandle(void *dlHandle) { _dlHandle = dlHandle; }
+    static UnitHooks *linkAndCreateUnit(const std::string &unitLibPath);
+public:
+             UnitHooks();
+    virtual ~UnitHooks();
+	static UnitHooks &get() { return *_global; }
+    /// Load unit test hook shared library from this path
+    static bool init(const std::string &unitLibPath);
+
+	virtual void preSpawnCount(int & /* numPrefork */) {}
+};
+
+#endif // LOOL_UNIT_HPP
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/configure.ac b/loolwsd/configure.ac
index 1066ff6..d50fce2 100644
--- a/loolwsd/configure.ac
+++ b/loolwsd/configure.ac
@@ -4,6 +4,7 @@
 AC_PREREQ([2.69])
 
 AC_INIT([loolwsd], [1.5.5], [libreoffice at lists.freedesktop.org])
+LT_INIT([disable-static])
 
 AM_INIT_AUTOMAKE([1.11 silent-rules subdir-objects])
 
@@ -43,6 +44,10 @@ AC_ARG_WITH([lokit-path],
             AS_HELP_STRING([--with-lokit-path=<path>],
                            [Path to the "include" directory with the LibreOfficeKit headers]))
 
+AC_ARG_WITH([lo-path],
+            AS_HELP_STRING([--with-lo-path=<path>],
+                           [Path to a working installation directory or instdir of LibreOffice]))
+
 AC_ARG_WITH([poco-includes],
             AS_HELP_STRING([--with-poco-includes=<path>],
                            [Path to the "include" directory with the Poco headers]))
@@ -82,6 +87,30 @@ AS_IF([test -z "$with_poco_libs"],
 with_lokit_path=`readlink -f $with_lokit_path`
 AS_IF([test -n "$with_lokit_path"],
       [CPPFLAGS="$CPPFLAGS -I${with_lokit_path}"])
+lokit_msg="$with_lokit_path"
+
+dnl
+dnl lo_path is not required; but useful for testing.
+dnl
+LO_PATH=
+have_lo_path=false
+AC_MSG_CHECKING([whether to run tests against a LibreOffice])
+if test -n "$with_lo_path"; then
+   version_file="$with_lo_path/program/versionrc"
+   if test -f $version_file; then
+     LO_PATH="$with_lo_path"
+     have_lo_path=true
+     lo_msg="test against $LO_PATH"
+     AC_MSG_RESULT([yes])
+   else
+     AC_MSG_ERROR([LibreOffice install looks dodgy, missing $version_file])
+   fi
+else
+   lo_msg="no integration tests"
+   AC_MSG_RESULT([no])
+fi
+AC_SUBST(LO_PATH)
+AM_CONDITIONAL(HAVE_LO_PATH,[test "$have_lo_path" = "true"])
 
 AS_IF([test -n "$with_poco_includes"],
       [CPPFLAGS="$CPPFLAGS -I${with_poco_includes}"])
@@ -177,4 +206,12 @@ AC_OUTPUT
 
 AC_LANG_POP
 
+echo "
+Configuration:
+    Source code location:   ${srcdir}
+    Build location:         ${top_builddir}
+    LOKit path              ${lokit_msg}
+    LO integration tests    ${lo_msg}
+"
+
 dnl vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index e99adb9..94de2de 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -2,12 +2,29 @@ check_PROGRAMS = test
 
 AM_CXXFLAGS = $(CPPUNIT_CFLAGS)
 
+lib_LTLIBRARIES = unit-prefork.la
+
+AM_CPPFLAGS = -pthread -I$(top_srcdir)
+
 test_CPPFLAGS = -DTDOC=\"$(top_srcdir)/test/data\"
 
 test_LDADD = $(CPPUNIT_LIBS)
 
 test_SOURCES = httpposttest.cpp httpwstest.cpp test.cpp ../LOOLProtocol.cpp
 
-EXTRA_DIST = data/hello.odt data/hello.txt $(test_SOURCES)
+# unit test modules:
+unit_prefork_la_SOURCES = \
+	UnitPrefork.cpp
+unit_prefork_la_LDFLAGS = -module
+
+if HAVE_LO_PATH
+TESTS = $(top_builddir)/test/test $(top_builddir)/loolwsd/test/run_unit.sh
+else
+TESTS = $(top_builddir)/test/test
+endif
+
+EXTRA_DIST = data/hello.odt data/hello.txt $(test_SOURCES) run_unit.sh
+
+clean-hook:
+	@echo "rm -Rf $(top_builddir)/test/jails"
 
-TESTS = test
diff --git a/loolwsd/test/UnitPrefork.cpp b/loolwsd/test/UnitPrefork.cpp
new file mode 100644
index 0000000..3d93d4d
--- /dev/null
+++ b/loolwsd/test/UnitPrefork.cpp
@@ -0,0 +1,33 @@
+/* -*- 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 <dlfcn.h>
+#include <ftw.h>
+#include <cassert>
+#include "Util.hpp"
+#include "Unit.hpp"
+
+class UnitPrefork : public UnitHooks {
+public:
+    UnitPrefork()
+    {
+    }
+    virtual void preSpawnCount(int &numPrefork)
+    {
+        numPrefork = 20;
+        Log::error("Hello world");
+    }
+};
+
+UnitHooks *unit_create(void)
+{
+    return new UnitPrefork();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit b6ab8982e6921b60fa785baf47480420f6d97601
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Apr 7 13:13:03 2016 +0100

    Guard logger so it can be used during static destructors safely.

diff --git a/loolwsd/Util.cpp b/loolwsd/Util.cpp
index c58202b..2d73562 100644
--- a/loolwsd/Util.cpp
+++ b/loolwsd/Util.cpp
@@ -92,8 +92,21 @@ namespace rng
 namespace Log
 {
     static const Poco::Int64 epochStart = Poco::Timestamp().epochMicroseconds();
-    static std::string SourceName;
-    static std::string SourceId;
+    // help avoid destruction ordering issues.
+    struct StaticNames {
+        bool inited;
+        std::string name;
+        std::string id;
+        StaticNames() :
+            inited(true)
+        {
+        }
+        ~StaticNames()
+        {
+            inited = false;
+        }
+    };
+    static StaticNames Source;
 
     std::string logPrefix()
     {
@@ -108,7 +121,8 @@ namespace Log
         usec %= (one_s);
 
         std::ostringstream stream;
-        stream << Log::SourceId << '-' << std::setw(2) << std::setfill('0')
+        stream << (Source.inited ? Source.id : std::string())
+               << '-' << std::setw(2) << std::setfill('0')
                << (Poco::Thread::current() ? Poco::Thread::current()->id() : 0) << ' '
                << std::setw(2) << hours << ':' << std::setw(2) << minutes << ':'
                << std::setw(2) << seconds << "." << std::setw(6) << usec
@@ -124,16 +138,16 @@ namespace Log
 
     void initialize(const std::string& name)
     {
-        SourceName = name;
+        Source.name = name;
         std::ostringstream oss;
-        oss << SourceName << '-'
+        oss << Source.name << '-'
             << std::setw(5) << std::setfill('0') << Poco::Process::id();
-        SourceId = oss.str();
+        Source.id = oss.str();
 
         auto channel = (isatty(fileno(stdout)) || std::getenv("LOOL_LOGCOLOR")
                      ? static_cast<Poco::Channel*>(new Poco::ColorConsoleChannel())
                      : static_cast<Poco::Channel*>(new Poco::ConsoleChannel()));
-        auto& logger = Poco::Logger::create(SourceName, channel, Poco::Message::PRIO_TRACE);
+        auto& logger = Poco::Logger::create(Source.name, channel, Poco::Message::PRIO_TRACE);
 
         // Configure the logger.
         // TODO: This should come from a file.
@@ -149,7 +163,7 @@ namespace Log
 
     Poco::Logger& logger()
     {
-        return Poco::Logger::get(SourceName);
+        return Poco::Logger::get(Source.inited ? Source.name : std::string());
     }
 
     void trace(const std::string& msg)
commit 72a0926b4a5ca36f52aaeac33aeb650ec49b41ae
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed Apr 6 17:39:44 2016 +0100

    Hush - update gitignores.

diff --git a/loleaflet/.gitignore b/loleaflet/.gitignore
index 1423824..ddcd766 100644
--- a/loleaflet/.gitignore
+++ b/loleaflet/.gitignore
@@ -12,6 +12,7 @@ _site
 dist/*.js
 dist/admin/*.js
 plugins/draw-0.2.4/dist/*.js
+plugins/draw-0.2.4/npm-debug.log
 coverage/
 /load_test_out
 /spec/data/load_test
diff --git a/loolwsd/.gitignore b/loolwsd/.gitignore
index 60d6cd2..fa27586 100644
--- a/loolwsd/.gitignore
+++ b/loolwsd/.gitignore
@@ -3,25 +3,29 @@
 \#*
 
 # Autofoo
-/.deps
-/Makefile
-/Makefile.in
-/aclocal.m4
-/autom4te.cache
-/config.h
-/config.h.in
-/config.log
-/config.status
-/configure
-/compile
-/depcomp
-/install-sh
-/missing
-/stamp-h1
-/systemplate
-/test-driver
-/jails
-/loolwsd.spec
+.deps
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.h
+config.h.in
+config.log
+config.guess
+config.status
+config.sub
+configure
+compile
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+systemplate
+test-driver
+jails
+loolwsd.spec
 
 *.o
 *.exe
@@ -36,6 +40,8 @@ loolwsd
 loolforkit
 loolmount
 loolmap
+loolmount
+looltool
 sockettransporttest
 
 # Debug output
diff --git a/loolwsd/test/.gitignore b/loolwsd/test/.gitignore
index 81defda..2016409 100644
--- a/loolwsd/test/.gitignore
+++ b/loolwsd/test/.gitignore
@@ -1,9 +1,11 @@
 # Autofoo
-/.deps
-/Makefile
-/Makefile.in
+.deps
+Makefile
+Makefile.in
 *.log
 *.trs
-
+.libs
+*.la
+*.lo
 *.o
 test


More information about the Libreoffice-commits mailing list