summaryrefslogtreecommitdiffstats
path: root/unotest/source
diff options
context:
space:
mode:
Diffstat (limited to 'unotest/source')
-rw-r--r--unotest/source/cpp/bootstrapfixturebase.cxx43
-rw-r--r--unotest/source/cpp/directories.cxx66
-rw-r--r--unotest/source/cpp/filters-test.cxx170
-rw-r--r--unotest/source/cpp/getargument.cxx36
-rw-r--r--unotest/source/cpp/gettestargument.cxx34
-rw-r--r--unotest/source/cpp/macros_test.cxx232
-rw-r--r--unotest/source/cpp/officeconnection.cxx151
-rw-r--r--unotest/source/cpp/toabsolutefileurl.cxx61
-rw-r--r--unotest/source/cpp/unobootstrapprotector/unobootstrapprotector.cxx93
-rw-r--r--unotest/source/cpp/unoexceptionprotector/unoexceptionprotector.cxx81
-rw-r--r--unotest/source/java/org/openoffice/test/Argument.java27
-rw-r--r--unotest/source/java/org/openoffice/test/FileHelper.java46
-rw-r--r--unotest/source/java/org/openoffice/test/OfficeConnection.java285
-rw-r--r--unotest/source/java/org/openoffice/test/OfficeFileUrl.java33
-rw-r--r--unotest/source/java/org/openoffice/test/UnoApiTest.java47
-rw-r--r--unotest/source/java/org/openoffice/test/tools/DocumentType.java37
-rw-r--r--unotest/source/java/org/openoffice/test/tools/OfficeDocument.java198
-rw-r--r--unotest/source/java/org/openoffice/test/tools/OfficeDocumentView.java104
-rw-r--r--unotest/source/java/org/openoffice/test/tools/SpreadsheetDocument.java61
-rw-r--r--unotest/source/java/org/openoffice/test/tools/SpreadsheetView.java32
-rw-r--r--unotest/source/python/org/__init__.py0
-rw-r--r--unotest/source/python/org/libreoffice/__init__.py0
-rw-r--r--unotest/source/python/org/libreoffice/unittest.py29
-rw-r--r--unotest/source/python/org/libreoffice/unotest.py326
24 files changed, 2192 insertions, 0 deletions
diff --git a/unotest/source/cpp/bootstrapfixturebase.cxx b/unotest/source/cpp/bootstrapfixturebase.cxx
new file mode 100644
index 0000000000..dd92d3822f
--- /dev/null
+++ b/unotest/source/cpp/bootstrapfixturebase.cxx
@@ -0,0 +1,43 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+#include <config_features.h>
+
+#include <unotest/bootstrapfixturebase.hxx>
+#include <comphelper/processfactory.hxx>
+#if HAVE_FEATURE_SCRIPTING
+#include <basic/sbstar.hxx>
+#endif
+
+using namespace ::com::sun::star;
+
+// NB. this constructor is called before any tests are run, once for each
+// test function in a rather non-intuitive way. This is why all the 'real'
+// heavy lifting is deferred until setUp. setUp and tearDown are interleaved
+// between the tests as you might expect.
+test::BootstrapFixtureBase::BootstrapFixtureBase() {}
+
+test::BootstrapFixtureBase::~BootstrapFixtureBase() {}
+
+void test::BootstrapFixtureBase::setUp()
+{
+ m_xContext = comphelper::getProcessComponentContext();
+ m_xFactory = m_xContext->getServiceManager();
+ m_xSFactory.set(m_xFactory, uno::UNO_QUERY_THROW);
+}
+
+void test::BootstrapFixtureBase::tearDown()
+{
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::DetachAllDocBasicItems();
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/directories.cxx b/unotest/source/cpp/directories.cxx
new file mode 100644
index 0000000000..93bcd4daed
--- /dev/null
+++ b/unotest/source/cpp/directories.cxx
@@ -0,0 +1,66 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <cppunit/TestAssert.h>
+#include <osl/file.hxx>
+#include <unotest/directories.hxx>
+
+namespace
+{
+OUString getFileURLFromSystemPath(OUString const& path)
+{
+ OUString url;
+ osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath(path, url);
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, e);
+ if (!url.endsWith("/"))
+ {
+ url += "/";
+ }
+ return url;
+}
+}
+
+test::Directories::Directories()
+{
+ const char* pSrcRoot = getenv("SRC_ROOT");
+ CPPUNIT_ASSERT_MESSAGE("SRC_ROOT env variable not set", pSrcRoot != nullptr);
+ CPPUNIT_ASSERT_MESSAGE("SRC_ROOT env variable not set", pSrcRoot[0] != 0);
+ const char* pWorkdirRoot = getenv("WORKDIR_FOR_BUILD");
+ CPPUNIT_ASSERT_MESSAGE("$WORKDIR_FOR_BUILD env variable not set", pWorkdirRoot != nullptr);
+ CPPUNIT_ASSERT_MESSAGE("$WORKDIR_FOR_BUILD env variable not set", pWorkdirRoot[0] != 0);
+ m_aSrcRootPath = OUString::createFromAscii(pSrcRoot);
+ m_aSrcRootURL = getFileURLFromSystemPath(m_aSrcRootPath);
+
+ m_aWorkdirRootPath = OUString::createFromAscii(pWorkdirRoot);
+ m_aWorkdirRootURL = getFileURLFromSystemPath(m_aWorkdirRootPath);
+}
+
+OUString test::Directories::getURLFromSrc(std::u16string_view rPath) const
+{
+ return m_aSrcRootURL + rPath;
+}
+
+OUString test::Directories::getPathFromSrc(std::u16string_view rPath) const
+{
+ return m_aSrcRootPath + rPath;
+}
+
+OUString test::Directories::getURLFromWorkdir(std::u16string_view rPath) const
+{
+ return m_aWorkdirRootURL + rPath;
+}
+
+OUString test::Directories::getPathFromWorkdir(std::u16string_view rPath) const
+{
+ return m_aWorkdirRootPath + rPath;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/filters-test.cxx b/unotest/source/cpp/filters-test.cxx
new file mode 100644
index 0000000000..1eaaa62197
--- /dev/null
+++ b/unotest/source/cpp/filters-test.cxx
@@ -0,0 +1,170 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <set>
+#include <string_view>
+
+#include <unotest/filters-test.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <rtl/cipher.h>
+
+#include <cppunit/TestAssert.h>
+
+namespace test {
+
+static void decode(const OUString& rIn, const OUString &rOut)
+{
+ rtlCipher cipher = rtl_cipher_create(rtl_Cipher_AlgorithmARCFOUR, rtl_Cipher_ModeStream);
+ CPPUNIT_ASSERT_MESSAGE("cipher creation failed", cipher != nullptr);
+
+ //mcrypt --bare -a arcfour -o hex -k 435645 -s 3
+ const sal_uInt8 aKey[3] = {'C', 'V', 'E'};
+
+ rtlCipherError result = rtl_cipher_init(cipher, rtl_Cipher_DirectionDecode, aKey, std::size(aKey), nullptr, 0);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("cipher init failed", rtl_Cipher_E_None, result);
+
+ osl::File aIn(rIn);
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, aIn.open(osl_File_OpenFlag_Read));
+
+ osl::File aOut(rOut);
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, aOut.open(osl_File_OpenFlag_Write));
+
+ sal_uInt8 in[8192];
+ sal_uInt8 out[8192];
+ sal_uInt64 nBytesRead, nBytesWritten;
+ while(true)
+ {
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, aIn.read(in, sizeof(in), nBytesRead));
+ if (!nBytesRead)
+ break;
+ CPPUNIT_ASSERT_EQUAL(rtl_Cipher_E_None, rtl_cipher_decode(cipher, in, nBytesRead, out, sizeof(out)));
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, aOut.write(out, nBytesRead, nBytesWritten));
+ CPPUNIT_ASSERT_EQUAL(nBytesRead, nBytesWritten);
+ }
+
+ rtl_cipher_destroy(cipher);
+}
+
+void FiltersTest::recursiveScan(filterStatus nExpected,
+ const OUString &rFilter, const OUString &rURL,
+ const OUString &rUserData, SfxFilterFlags nFilterFlags,
+ SotClipboardFormatId nClipboardID, unsigned int nFilterVersion, bool bExport)
+{
+ osl::Directory aDir(rURL);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(OUString("Failed to open directory " + rURL).toUtf8().getStr(), osl::FileBase::E_None, aDir.open());
+ osl::DirectoryItem aItem;
+ osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL|osl_FileStatus_Mask_Type);
+ std::set<OUString> dirs;
+ std::set<OUString> files;
+ while (aDir.getNextItem(aItem) == osl::FileBase::E_None)
+ {
+ aItem.getFileStatus(aFileStatus);
+ OUString sURL = aFileStatus.getFileURL();
+ if (aFileStatus.getFileType() == osl::FileStatus::Directory)
+ {
+ dirs.insert(sURL);
+ }
+ else
+ {
+ files.insert(sURL);
+ }
+ }
+ for (auto const & sURL: dirs) {
+ recursiveScan(nExpected, rFilter, sURL, rUserData,
+ nFilterFlags, nClipboardID, nFilterVersion, bExport);
+ }
+ for (auto const & sURL: files) {
+ OUString sTmpFile;
+ bool bEncrypted = false;
+
+ sal_Int32 nLastSlash = sURL.lastIndexOf('/');
+
+ if ((nLastSlash != -1) && (nLastSlash+1 < sURL.getLength()))
+ {
+ //ignore .files
+ if (sURL[nLastSlash+1] == '.')
+ continue;
+
+ if (
+ (sURL.match("BID", nLastSlash+1)) ||
+ (sURL.match("CVE", nLastSlash+1)) ||
+ (sURL.match("EDB", nLastSlash+1)) ||
+ (sURL.match("RC4", nLastSlash+1)) // just means "encrypted"
+ )
+ {
+ bEncrypted = true;
+ }
+ }
+
+ OString aRes(
+ OString::Concat(bExport ? std::string_view("save") : std::string_view("load")) + " "
+ + OUStringToOString(sURL, osl_getThreadTextEncoding()));
+
+ OUString realUrl;
+ if (bEncrypted)
+ {
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, osl::FileBase::createTempFile(nullptr, nullptr, &sTmpFile));
+ decode(sURL, sTmpFile);
+ realUrl = sTmpFile;
+ }
+ else
+ {
+ realUrl = sURL;
+ }
+
+ //output name early, so in the case of a hang, the name of
+ //the hanging input file is visible
+ fprintf(stderr, "Testing %s:\n", aRes.getStr());
+ sal_uInt32 nStartTime = osl_getGlobalTimer();
+ bool bRes;
+ if (!bExport)
+ bRes = load(rFilter, realUrl, rUserData, nFilterFlags,
+ nClipboardID, nFilterVersion);
+ else
+ bRes = save(rFilter, realUrl, rUserData, nFilterFlags,
+ nClipboardID, nFilterVersion);
+ sal_uInt32 nEndTime = osl_getGlobalTimer();
+
+ if (bEncrypted)
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, osl::File::remove(sTmpFile));
+
+ fprintf(stderr, "Tested %s: %s (%" SAL_PRIuUINT32 "ms)\n",
+ aRes.getStr(), bRes?"Pass":"Fail", nEndTime-nStartTime);
+ if (nExpected == test::indeterminate)
+ continue;
+ filterStatus nResult = bRes ? test::pass : test::fail;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(aRes.getStr(), nExpected, nResult);
+ }
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, aDir.close());
+}
+
+void FiltersTest::testDir(const OUString &rFilter,
+ std::u16string_view rURL, const OUString &rUserData,
+ SfxFilterFlags nFilterFlags, SotClipboardFormatId nClipboardID,
+ unsigned int nFilterVersion, bool bExport)
+{
+ recursiveScan(test::pass, rFilter,
+ OUString::Concat(rURL) + "pass",
+ rUserData, nFilterFlags, nClipboardID, nFilterVersion, bExport);
+ recursiveScan(test::fail, rFilter,
+ OUString::Concat(rURL) + "fail",
+ rUserData, nFilterFlags, nClipboardID, nFilterVersion, bExport);
+ recursiveScan(test::indeterminate, rFilter,
+ OUString::Concat(rURL) + "indeterminate",
+ rUserData, nFilterFlags, nClipboardID, nFilterVersion, bExport);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/getargument.cxx b/unotest/source/cpp/getargument.cxx
new file mode 100644
index 0000000000..22cd39fc59
--- /dev/null
+++ b/unotest/source/cpp/getargument.cxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <unotest/getargument.hxx>
+
+namespace test
+{
+bool getArgument(std::u16string_view name, OUString* value)
+{
+ OSL_ASSERT(value != nullptr);
+ return rtl::Bootstrap::get(OUString::Concat("arg-") + name, *value);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/gettestargument.cxx b/unotest/source/cpp/gettestargument.cxx
new file mode 100644
index 0000000000..8d10b07cca
--- /dev/null
+++ b/unotest/source/cpp/gettestargument.cxx
@@ -0,0 +1,34 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include <unotest/getargument.hxx>
+#include <unotest/gettestargument.hxx>
+
+namespace test
+{
+bool getTestArgument(std::u16string_view name, OUString* value)
+{
+ return getArgument(Concat2View(OUString::Concat("testarg.") + name), value);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/macros_test.cxx b/unotest/source/cpp/macros_test.cxx
new file mode 100644
index 0000000000..3bb2a22a5d
--- /dev/null
+++ b/unotest/source/cpp/macros_test.cxx
@@ -0,0 +1,232 @@
+/* -*- 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/.
+ */
+
+#include <unotest/macros_test.hxx>
+
+#include <vector>
+
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+
+#include <basic/basrdll.hxx>
+#include <cppunit/TestAssert.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotest/directories.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <tools/datetime.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/scheduler.hxx>
+
+using namespace css;
+
+namespace unotest
+{
+MacrosTest::MacrosTest()
+ : mpDll(std::make_unique<BasicDLL>())
+{
+}
+
+MacrosTest::~MacrosTest() = default;
+
+uno::Reference<css::lang::XComponent>
+MacrosTest::loadFromDesktop(const OUString& rURL, const OUString& rDocService,
+ const uno::Sequence<beans::PropertyValue>& rExtraArgs)
+{
+ CPPUNIT_ASSERT_MESSAGE("no desktop", mxDesktop.is());
+ std::vector<beans::PropertyValue> args;
+ beans::PropertyValue aMacroValue;
+ aMacroValue.Name = "MacroExecutionMode";
+ aMacroValue.Handle = -1;
+ aMacroValue.Value <<= document::MacroExecMode::ALWAYS_EXECUTE_NO_WARN;
+ aMacroValue.State = beans::PropertyState_DIRECT_VALUE;
+ args.push_back(aMacroValue);
+
+ if (!rDocService.isEmpty())
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "DocumentService";
+ aValue.Handle = -1;
+ aValue.Value <<= rDocService;
+ aValue.State = beans::PropertyState_DIRECT_VALUE;
+ args.push_back(aValue);
+ }
+
+ args.insert(args.end(), rExtraArgs.begin(), rExtraArgs.end());
+
+ uno::Reference<lang::XComponent> xComponent = mxDesktop->loadComponentFromURL(
+ rURL, "_default", 0, comphelper::containerToSequence(args));
+ OUString sMessage = "loading failed: " + rURL;
+ CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sMessage, RTL_TEXTENCODING_UTF8).getStr(),
+ xComponent.is());
+ return xComponent;
+}
+
+css::uno::Any
+MacrosTest::dispatchCommand(const uno::Reference<lang::XComponent>& xComponent,
+ const OUString& rCommand,
+ const uno::Sequence<beans::PropertyValue>& rPropertyValues)
+{
+ uno::Reference<frame::XController> xController
+ = uno::Reference<frame::XModel>(xComponent, uno::UNO_QUERY_THROW)->getCurrentController();
+ CPPUNIT_ASSERT(xController.is());
+ uno::Reference<frame::XDispatchProvider> xFrame(xController->getFrame(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xFrame.is());
+
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference<frame::XDispatchHelper> xDispatchHelper(frame::DispatchHelper::create(xContext));
+ CPPUNIT_ASSERT(xDispatchHelper.is());
+
+ auto ret = xDispatchHelper->executeDispatch(xFrame, rCommand, OUString(), 0, rPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+
+ return ret;
+}
+
+std::unique_ptr<SvStream> MacrosTest::parseExportStream(const OUString& url,
+ const OUString& rStreamName)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext
+ = comphelper::getProcessComponentContext();
+ uno::Reference<packages::zip::XZipFileAccess2> const xZipNames(
+ packages::zip::ZipFileAccess::createWithURL(xComponentContext, url));
+ uno::Reference<io::XInputStream> const xInputStream(xZipNames->getByName(rStreamName),
+ uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ return pStream;
+}
+
+void MacrosTest::setUpNssGpg(const test::Directories& rDirectories, const OUString& rTestName)
+{
+ OUString aSourceDir = rDirectories.getURLFromSrc(u"/test/signing-keys/");
+ OUString aTargetDir
+ = rDirectories.getURLFromWorkdir(Concat2View("CppunitTest/" + rTestName + ".test.user"));
+
+ // Set up NSS database in workdir/CppunitTest/
+ osl::File::copy(aSourceDir + "cert9.db", aTargetDir + "/cert9.db");
+ osl::File::copy(aSourceDir + "key4.db", aTargetDir + "/key4.db");
+ osl::File::copy(aSourceDir + "pkcs11.txt", aTargetDir + "/pkcs11.txt");
+
+ // Make gpg use our own defined setup & keys
+ osl::File::copy(aSourceDir + "pubring.gpg", aTargetDir + "/pubring.gpg");
+ osl::File::copy(aSourceDir + "random_seed", aTargetDir + "/random_seed");
+ osl::File::copy(aSourceDir + "secring.gpg", aTargetDir + "/secring.gpg");
+ osl::File::copy(aSourceDir + "trustdb.gpg", aTargetDir + "/trustdb.gpg");
+
+ OUString aTargetPath;
+ osl::FileBase::getSystemPathFromFileURL(aTargetDir, aTargetPath);
+
+#ifdef _WIN32
+ // CryptoAPI test certificates
+ osl::File::copy(aSourceDir + "test.p7b", aTargetDir + "/test.p7b");
+ OUString caVar("LIBO_TEST_CRYPTOAPI_PKCS7");
+ osl_setEnvironment(caVar.pData, aTargetPath.pData);
+#else
+ OUString mozCertVar("MOZILLA_CERTIFICATE_FOLDER");
+ // explicit prefix with "sql:" needed for CentOS7 system NSS 3.67
+ osl_setEnvironment(mozCertVar.pData, OUString("sql:" + aTargetPath).pData);
+#endif
+ OUString gpgHomeVar("GNUPGHOME");
+ osl_setEnvironment(gpgHomeVar.pData, aTargetPath.pData);
+
+#if HAVE_GPGCONF_SOCKETDIR
+ auto const ldPath = std::getenv("LIBO_LD_PATH");
+ m_gpgconfCommandPrefix
+ = ldPath == nullptr ? OString() : OString::Concat("LD_LIBRARY_PATH=") + ldPath + " ";
+ OString path;
+ bool ok = aTargetPath.convertToString(&path, osl_getThreadTextEncoding(),
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR);
+ // if conversion fails, at least provide a best-effort conversion in the message here, for
+ // context
+ CPPUNIT_ASSERT_MESSAGE(OUStringToOString(aTargetPath, RTL_TEXTENCODING_UTF8).getStr(), ok);
+ m_gpgconfCommandPrefix += "GNUPGHOME=" + path + " " GPGME_GPGCONF;
+ // HAVE_GPGCONF_SOCKETDIR is only defined in configure.ac for Linux for now, so (a) std::system
+ // behavior will conform to POSIX (and the relevant env var to set is named LD_LIBRARY_PATH), and
+ // (b) gpgconf --create-socketdir should return zero:
+ OString cmd = m_gpgconfCommandPrefix + " --create-socketdir";
+ int res = std::system(cmd.getStr());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd.getStr(), 0, res);
+#else
+ (void)this;
+#endif
+}
+
+void MacrosTest::tearDownNssGpg()
+{
+#if HAVE_GPGCONF_SOCKETDIR
+ // HAVE_GPGCONF_SOCKETDIR is only defined in configure.ac for Linux for now, so (a) std::system
+ // behavior will conform to POSIX, and (b) gpgconf --remove-socketdir should return zero:
+ OString cmd = m_gpgconfCommandPrefix + " --remove-socketdir";
+ int res = std::system(cmd.getStr());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd.getStr(), 0, res);
+#else
+ (void)this;
+#endif
+}
+
+namespace
+{
+struct Valid
+{
+ DateTime now;
+ OUString subjectName;
+ const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& env;
+ Valid(const css::uno::Sequence<css::beans::PropertyValue>& rFilterData,
+ const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& rEnv)
+ : now(DateTime::SYSTEM)
+ , env(rEnv)
+ {
+ for (const auto& propVal : rFilterData)
+ {
+ if (propVal.Name == "SignCertificateSubjectName")
+ propVal.Value >>= subjectName;
+ }
+ }
+ bool operator()(const css::uno::Reference<css::security::XCertificate>& cert) const
+ {
+ if (!now.IsBetween(cert->getNotValidBefore(), cert->getNotValidAfter()))
+ return false;
+ if (!subjectName.isEmpty() && subjectName != cert->getSubjectName())
+ return false;
+ if (env->verifyCertificate(cert, {}) != css::security::CertificateValidity::VALID)
+ return false;
+ return true;
+ }
+};
+}
+
+bool MacrosTest::IsValid(const css::uno::Reference<css::security::XCertificate>& cert,
+ const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& env)
+{
+ const Valid test({}, env);
+ return test(cert);
+}
+
+css::uno::Reference<css::security::XCertificate> MacrosTest::GetValidCertificate(
+ const css::uno::Sequence<css::uno::Reference<css::security::XCertificate>>& certs,
+ const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& env,
+ const css::uno::Sequence<css::beans::PropertyValue>& rFilterData)
+{
+ if (auto it = std::find_if(certs.begin(), certs.end(), Valid(rFilterData, env));
+ it != certs.end())
+ return *it;
+ return {};
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/officeconnection.cxx b/unotest/source/cpp/officeconnection.cxx
new file mode 100644
index 0000000000..850b19440d
--- /dev/null
+++ b/unotest/source/cpp/officeconnection.cxx
@@ -0,0 +1,151 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/bridge/UnoUrlResolver.hpp>
+#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
+#include <com/sun/star/connection/NoConnectException.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/bootstrap.hxx>
+#include <cppunit/TestAssert.h>
+#include <osl/process.h>
+#include <osl/test/uniquepipename.hxx>
+#include <osl/time.h>
+#include <unotest/getargument.hxx>
+#include <unotest/officeconnection.hxx>
+#include <unotest/toabsolutefileurl.hxx>
+
+namespace test {
+
+OfficeConnection::OfficeConnection(): process_(nullptr) {}
+
+OfficeConnection::~OfficeConnection() {}
+
+void OfficeConnection::setUp() {
+ css::uno::Reference< css::bridge::XUnoUrlResolver > resolver(
+ css::bridge::UnoUrlResolver::create(
+ cppu::defaultBootstrap_InitialComponentContext()));
+ OUString desc;
+ OUString argSoffice;
+ CPPUNIT_ASSERT(
+ getArgument(
+ u"soffice",
+ &argSoffice));
+ if (argSoffice.match("path:")) {
+ desc = "pipe,name=" + osl::test::uniquePipeName("oootest");
+ OUString noquickArg("--quickstart=no");
+ OUString norestoreArg("--norestore");
+ OUString nologoArg("--nologo");
+ // disable use of the unix standalone splash screen app for the
+ // tests (probably not needed in combination with --headless?)
+ OUString headlessArg("--headless");
+ OUString acceptArg("--accept=" + desc + ";urp");
+ OUString argUser;
+ CPPUNIT_ASSERT(
+ getArgument(u"user", &argUser));
+ OUString userArg("-env:UserInstallation=" + toAbsoluteFileUrl(argUser));
+ OUString jreArg(
+ "-env:UNO_JAVA_JFW_ENV_JREHOME=true");
+ rtl_uString * args[] = {
+ noquickArg.pData, norestoreArg.pData,
+ nologoArg.pData, headlessArg.pData, acceptArg.pData, userArg.pData,
+ jreArg.pData };
+ rtl_uString ** envs = nullptr;
+ OUString argEnv;
+ if (getArgument(u"env", &argEnv))
+ {
+ envs = &argEnv.pData;
+ }
+ // coverity[callee_ptr_arith] - arith is fine
+ CPPUNIT_ASSERT_EQUAL(
+ osl_Process_E_None,
+ osl_executeProcess(
+ toAbsoluteFileUrl(
+ argSoffice.copy(RTL_CONSTASCII_LENGTH("path:"))).pData,
+ args, std::size(args), 0, nullptr, nullptr, envs, envs == nullptr ? 0 : 1,
+ &process_));
+ } else if (argSoffice.match("connect:")) {
+ desc = argSoffice.copy(RTL_CONSTASCII_LENGTH("connect:"));
+ } else {
+ CPPUNIT_FAIL(
+ "\"soffice\" argument starts with neither \"path:\" nor"
+ " \"connect:\"");
+ }
+ for (;;) {
+ try {
+ context_ =
+ css::uno::Reference< css::uno::XComponentContext >(
+ resolver->resolve(
+ "uno:" + desc + ";urp;StarOffice.ComponentContext"),
+ css::uno::UNO_QUERY_THROW);
+ break;
+ } catch (css::connection::NoConnectException &) {}
+ if (process_ != nullptr) {
+ TimeValue delay = { 1, 0 }; // 1 sec
+ CPPUNIT_ASSERT_EQUAL(
+ osl_Process_E_TimedOut,
+ osl_joinProcessWithTimeout(process_, &delay));
+ }
+ }
+}
+
+void OfficeConnection::tearDown() {
+ if (process_ == nullptr)
+ return;
+
+ if (context_.is()) {
+ css::uno::Reference< css::frame::XDesktop2 > desktop = css::frame::Desktop::create( context_ );
+ context_.clear();
+ try {
+ CPPUNIT_ASSERT(desktop->terminate());
+ desktop.clear();
+ } catch (css::lang::DisposedException &) {}
+ // it appears that DisposedExceptions can already happen while
+ // receiving the response of the terminate call
+ }
+ CPPUNIT_ASSERT_EQUAL(osl_Process_E_None, osl_joinProcess(process_));
+ oslProcessInfo info;
+ info.Size = sizeof info;
+ CPPUNIT_ASSERT_EQUAL(
+ osl_Process_E_None,
+ osl_getProcessInfo(process_, osl_Process_EXITCODE, &info));
+ CPPUNIT_ASSERT_EQUAL(oslProcessExitCode(0), info.Code);
+ osl_freeProcessHandle(process_);
+ process_ = nullptr; // guard against subsequent calls to isStillAlive
+}
+
+
+bool OfficeConnection::isStillAlive() const {
+ if (process_ == nullptr) {
+ // In case "soffice" argument starts with "connect:" we have no direct
+ // control over the liveness of the soffice.bin process (would need to
+ // directly monitor the bridge) so can only assume the best here:
+ return true;
+ }
+ TimeValue delay = { 0, 0 }; // 0 sec
+ oslProcessError e = osl_joinProcessWithTimeout(process_, &delay);
+ CPPUNIT_ASSERT(e == osl_Process_E_None || e == osl_Process_E_TimedOut);
+ return e == osl_Process_E_TimedOut;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/toabsolutefileurl.cxx b/unotest/source/cpp/toabsolutefileurl.cxx
new file mode 100644
index 0000000000..ea8b6f66c2
--- /dev/null
+++ b/unotest/source/cpp/toabsolutefileurl.cxx
@@ -0,0 +1,61 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/ustring.hxx>
+#include <unotest/toabsolutefileurl.hxx>
+
+namespace test {
+
+OUString toAbsoluteFileUrl(OUString const & relativePathname) {
+ OUString cwd;
+ oslProcessError e1 = osl_getProcessWorkingDir(&cwd.pData);
+ if (e1 != osl_Process_E_None) {
+ throw css::uno::RuntimeException(
+ "osl_getProcessWorkingDir failed with " + OUString::number(e1));
+ }
+ OUString url;
+ osl::FileBase::RC e2 = osl::FileBase::getFileURLFromSystemPath(
+ relativePathname, url);
+ if (e2 != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "osl::FileBase::getFileURLFromSystemPath(" +
+ relativePathname +
+ ") failed with " +
+ OUString::number(e2));
+ }
+ OUString absUrl;
+ e2 = osl::FileBase::getAbsoluteFileURL(cwd, url, absUrl);
+ if (e2 != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "osl::FileBase::getAbsoluteFileURL(" +
+ cwd + ", " + url +
+ ") failed with " +
+ OUString::number(e2));
+ }
+ return absUrl;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/unobootstrapprotector/unobootstrapprotector.cxx b/unotest/source/cpp/unobootstrapprotector/unobootstrapprotector.cxx
new file mode 100644
index 0000000000..5ad175b9d0
--- /dev/null
+++ b/unotest/source/cpp/unobootstrapprotector/unobootstrapprotector.cxx
@@ -0,0 +1,93 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Exception.hpp>
+
+#include <cppuhelper/bootstrap.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <sal/types.h>
+
+#include <cppunit/Protector.h>
+
+namespace {
+
+using namespace com::sun::star;
+
+//cppunit calls instantiates a new TextFixture for each test and calls setUp
+//and tearDown on that for every test in a fixture
+
+//We basically need to call dispose on our root component context to
+//shut down cleanly in the right order.
+
+//But we can't setup and tear down the root component context for
+//every test because all the uno singletons will be invalid after
+//the first dispose. So lets setup the default context once before
+//all tests are run, and tear it down once after all have finished
+
+class Prot : public CppUnit::Protector
+{
+public:
+ Prot();
+
+ virtual ~Prot() override;
+
+ Prot(const Prot&) = delete;
+ Prot& operator=(const Prot&) = delete;
+
+ virtual bool protect(
+ CppUnit::Functor const & functor,
+ CppUnit::ProtectorContext const & context) override;
+private:
+ uno::Reference<uno::XComponentContext> m_xContext;
+};
+
+
+Prot::Prot()
+ : m_xContext(cppu::defaultBootstrap_InitialComponentContext())
+{
+ uno::Reference<lang::XMultiComponentFactory> xFactory = m_xContext->getServiceManager();
+ uno::Reference<lang::XMultiServiceFactory> xSFactory(xFactory, uno::UNO_QUERY_THROW);
+
+ comphelper::setProcessServiceFactory(xSFactory);
+}
+
+bool Prot::protect(
+ CppUnit::Functor const & functor, CppUnit::ProtectorContext const &)
+{
+ return functor();
+}
+
+Prot::~Prot()
+{
+ uno::Reference< lang::XComponent >(m_xContext, uno::UNO_QUERY_THROW)->dispose();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT CppUnit::Protector * unobootstrapprotector()
+{
+ return new Prot;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/cpp/unoexceptionprotector/unoexceptionprotector.cxx b/unotest/source/cpp/unoexceptionprotector/unoexceptionprotector.cxx
new file mode 100644
index 0000000000..5a76cbdb11
--- /dev/null
+++ b/unotest/source/cpp/unoexceptionprotector/unoexceptionprotector.cxx
@@ -0,0 +1,81 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cstdlib>
+#include <string>
+#include <string_view>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppunit/Message.h>
+#include <osl/thread.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include <cppunit/Protector.h>
+
+namespace {
+
+// Best effort conversion:
+std::string convert(std::u16string_view s16) {
+ OString s8(OUStringToOString(s16, osl_getThreadTextEncoding()));
+ static_assert(sizeof (sal_Int32) <= sizeof (std::string::size_type), "got to be at least equal");
+ // ensure following cast is legitimate
+ return std::string(s8);
+}
+
+class Prot : public CppUnit::Protector
+{
+public:
+ Prot() {}
+
+ Prot(const Prot&) = delete;
+ Prot& operator=(const Prot&) = delete;
+
+ virtual bool protect(
+ CppUnit::Functor const & functor,
+ CppUnit::ProtectorContext const & context) override;
+};
+
+bool Prot::protect(
+ CppUnit::Functor const & functor, CppUnit::ProtectorContext const & context)
+{
+ try {
+ return functor();
+ } catch (const css::uno::Exception &e) {
+ css::uno::Any a(cppu::getCaughtException());
+ reportError(
+ context,
+ CppUnit::Message(
+ "An uncaught exception of type " + convert(a.getValueTypeName()),
+ convert(e.Message)));
+ }
+ return false;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT CppUnit::Protector *
+unoexceptionprotector() {
+ return std::getenv("CPPUNIT_PROPAGATE_EXCEPTIONS") == nullptr ? new Prot : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotest/source/java/org/openoffice/test/Argument.java b/unotest/source/java/org/openoffice/test/Argument.java
new file mode 100644
index 0000000000..294e144898
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/Argument.java
@@ -0,0 +1,27 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.test;
+
+public final class Argument {
+ public static String get(String name) {
+ return System.getProperty("org.openoffice.test.arg." + name);
+ }
+
+ private Argument() {}
+}
diff --git a/unotest/source/java/org/openoffice/test/FileHelper.java b/unotest/source/java/org/openoffice/test/FileHelper.java
new file mode 100644
index 0000000000..93907ed922
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/FileHelper.java
@@ -0,0 +1,46 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.test;
+
+/*
+ * Helper Functions for File handling
+ */
+public class FileHelper
+{
+ /*
+ * Concat a _sRelativePathToAdd to a _sPath and append a '/' to the _sPath only if need.
+ *
+ * @return a right concatenated path
+ */
+ public static String appendPath(String _sPath, String _sRelativePathToAdd)
+ {
+ String sNewPath = _sPath;
+ String fs = System.getProperty("file.separator");
+ if (_sPath.startsWith("file:"))
+ {
+ fs = "/"; // we use a file URL so only '/' is allowed.
+ }
+ if (! (sNewPath.endsWith("/") || sNewPath.endsWith("\\") ) )
+ {
+ sNewPath += fs;
+ }
+ sNewPath += _sRelativePathToAdd;
+ return sNewPath;
+ }
+}
diff --git a/unotest/source/java/org/openoffice/test/OfficeConnection.java b/unotest/source/java/org/openoffice/test/OfficeConnection.java
new file mode 100644
index 0000000000..0801da6e03
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/OfficeConnection.java
@@ -0,0 +1,285 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.test;
+
+import com.sun.star.bridge.UnoUrlResolver;
+import com.sun.star.bridge.XUnoUrlResolver;
+import com.sun.star.comp.helper.Bootstrap;
+import com.sun.star.connection.NoConnectException;
+import com.sun.star.frame.XDesktop;
+import com.sun.star.lang.DisposedException;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.UUID;
+import static org.junit.Assert.*;
+
+/** Start up and shut down an OOo instance.
+
+ Details about the OOo instance are tunneled in via
+ org.openoffice.test.arg... system properties.
+*/
+
+
+public final class OfficeConnection {
+ /** Start up an OOo instance.
+ */
+ public void setUp() throws Exception {
+ String sofficeArg = Argument.get("soffice");
+ if (sofficeArg.startsWith("path:")) {
+ description = "pipe,name=oootest" + UUID.randomUUID();
+ ProcessBuilder pb = new ProcessBuilder(
+ sofficeArg.substring("path:".length()), "--quickstart=no",
+ "--norestore", "--nologo", "--headless",
+ "--accept=" + description + ";urp",
+ "-env:UserInstallation=" + Argument.get("user"),
+ "-env:UNO_JAVA_JFW_ENV_JREHOME=true");
+ String workdirArg = Argument.get("workdir");
+ if (workdirArg != null) {
+ pb.directory(new File(workdirArg));
+ }
+ String envArg = Argument.get("env");
+ if (envArg != null) {
+ Map<String, String> env = pb.environment();
+ int i = envArg.indexOf('=');
+ if (i == -1) {
+ env.remove(envArg);
+ } else {
+ env.put(envArg.substring(0, i), envArg.substring(i + 1));
+ }
+ }
+ process = pb.start();
+ outForward = new Forward(process.getInputStream(), System.out);
+ outForward.start();
+ errForward = new Forward(process.getErrorStream(), System.err);
+ errForward.start();
+ } else if (sofficeArg.startsWith("connect:")) {
+ description = sofficeArg.substring("connect:".length());
+ } else {
+ fail(
+ "\"soffice\" argument \"" + sofficeArg +
+ " starts with neither \"path:\" nor \"connect:\"");
+ }
+ XUnoUrlResolver resolver = UnoUrlResolver.create(
+ Bootstrap.createInitialComponentContext(null));
+ for (;;) {
+ try {
+ context = UnoRuntime.queryInterface(
+ XComponentContext.class,
+ resolver.resolve(
+ "uno:" + description +
+ ";urp;StarOffice.ComponentContext"));
+ break;
+ } catch (NoConnectException e) {}
+ if (process != null) {
+ assertNull(waitForProcess(process, 1000)); // 1 sec
+ }
+ }
+ }
+
+ /** Shut down the OOo instance.
+ */
+ public void tearDown()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ boolean cleanTermination = false;
+ int code = 0;
+ try {
+ if (process != null) {
+ if (context != null) {
+ XDesktop desktop = null;
+ try {
+ XMultiComponentFactory factory =
+ context.getServiceManager();
+ assertNotNull(factory);
+ desktop = UnoRuntime.queryInterface(XDesktop.class,
+ factory.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", context));
+ } catch (DisposedException e) {
+ // it can happen that the Java bridge was disposed
+ // already, we want to ensure soffice.bin is killed
+ process.destroy();
+ }
+ context = null;
+ if (desktop != null) {
+ try {
+ boolean desktopTerminated = desktop.terminate();
+ if (!desktopTerminated) {
+ // in case terminate() fails we would wait
+ // forever for the process to die, so kill it
+ process.destroy();
+ }
+ assertTrue(desktopTerminated);
+ } catch (DisposedException e) {}
+ // it appears that DisposedExceptions can already happen
+ // while receiving the response of the terminate call
+ }
+ desktop = null;
+ } else {
+ process.destroy();
+ }
+ }
+ if (process != null) {
+ code = process.waitFor();
+ }
+ boolean outTerminated = outForward == null
+ || outForward.terminated();
+ boolean errTerminated = errForward == null
+ || errForward.terminated();
+ assertEquals(0, code);
+ cleanTermination = true;
+ assertTrue(outTerminated);
+ assertTrue(errTerminated);
+ } finally {
+ if (!cleanTermination) {
+ try {
+ String sofficeArg = Argument.get("soffice");
+ String workdir = Argument.get("workdir");
+ String postprocesscommand = Argument.get(
+ "postprocesscommand");
+ if (sofficeArg.startsWith("path:") && workdir != null
+ && postprocesscommand != null)
+ {
+ ProcessBuilder pb = new ProcessBuilder(
+ postprocesscommand,
+ sofficeArg.substring("path:".length()) + ".bin",
+ workdir, String.valueOf(code));
+ Process postprocess = pb.start();
+ Forward ppoutForward = new Forward(
+ postprocess.getInputStream(), System.out);
+ ppoutForward.start();
+ Forward pperrForward = new Forward(
+ postprocess.getErrorStream(), System.err);
+ pperrForward.start();
+ code = postprocess.waitFor();
+ if (code != 0) {
+ throw new PostprocessFailedException(code);
+ }
+ }
+ }
+ catch (IOException e) {
+ throw new PostprocessFailedException(e);
+ }
+ }
+ }
+ }
+
+ /** Obtain the component context of the running OOo instance.
+ */
+ public XComponentContext getComponentContext() {
+ return context;
+ }
+
+ //TODO: get rid of this hack for legacy qa/unoapi tests
+ public String getDescription() {
+ return description;
+ }
+
+ private static Integer waitForProcess(Process process, final int millis)
+ throws InterruptedException
+ {
+ final Thread t1 = Thread.currentThread();
+ Thread t2 = new Thread("waitForProcess") {
+ @Override
+ public void run() {
+ util.utils.pause(millis);
+ t1.interrupt();
+ }
+ };
+ boolean old = Thread.interrupted();
+ // clear interrupted status, get old status
+ t2.start();
+ int n = 0;
+ boolean done = false;
+ try {
+ n = process.waitFor();
+ done = true;
+ } catch (InterruptedException e) {}
+ t2.interrupt();
+ try {
+ t2.join();
+ } catch (InterruptedException e) {
+ t2.join();
+ }
+ Thread.interrupted(); // clear interrupted status
+ if (old) {
+ t1.interrupt(); // reset old status
+ }
+ return done ? Integer.valueOf(n) : null;
+ }
+
+ private static final class Forward extends Thread {
+ public Forward(InputStream in, PrintStream out) {
+ super("process output forwarder");
+ this.in = in;
+ this.out = out;
+ }
+
+ @Override
+ public void run() {
+ for (;;) {
+ byte[] buf = new byte[1024];
+ int n;
+ try {
+ n = in.read(buf);
+ } catch (IOException e) {
+ throw new RuntimeException("wrapping", e);
+ }
+ if (n == -1) {
+ break;
+ }
+ out.write(buf, 0, n);
+ }
+ done = true;
+ }
+
+ public boolean terminated() throws InterruptedException {
+ join();
+ return done;
+ }
+
+ private final InputStream in;
+ private final PrintStream out;
+ private boolean done = false;
+ }
+
+ private static final class PostprocessFailedException
+ extends RuntimeException
+ {
+ PostprocessFailedException(int exitCode) {
+ super("postprocessing failed with exit code " + exitCode);
+ }
+
+ PostprocessFailedException(IOException cause) {
+ super("postprocessing failed with IOException " + cause, cause);
+ }
+ }
+
+ private String description;
+ private Process process = null;
+ private Forward outForward = null;
+ private Forward errForward = null;
+ private XComponentContext context = null;
+}
+// vim:set et sw=4 sts=4:
diff --git a/unotest/source/java/org/openoffice/test/OfficeFileUrl.java b/unotest/source/java/org/openoffice/test/OfficeFileUrl.java
new file mode 100644
index 0000000000..f908f36428
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/OfficeFileUrl.java
@@ -0,0 +1,33 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.test;
+
+import java.io.File;
+
+/** Obtain the office-internal absolute file URL of a given file.
+ */
+public final class OfficeFileUrl {
+ public static String getAbsolute(File file) {
+ return file.getAbsoluteFile().toURI().toString().replaceFirst(
+ "\\A[Ff][Ii][Ll][Ee]:/(?=[^/]|\\z)", "file:///");
+ // file:/path -> file:///path
+ }
+
+ private OfficeFileUrl() {}
+}
diff --git a/unotest/source/java/org/openoffice/test/UnoApiTest.java b/unotest/source/java/org/openoffice/test/UnoApiTest.java
new file mode 100644
index 0000000000..44dd472319
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/UnoApiTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.test;
+
+import org.openoffice.Runner;
+import static org.junit.Assert.*;
+import org.junit.*;
+
+public final class UnoApiTest {
+ @Before public void setUp() throws Exception {
+ connection.setUp();
+ }
+
+ @After public void tearDown()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ connection.tearDown();
+ }
+
+ @Test public void test() throws Exception {
+ assertTrue("org.openoffice.Runner failed with params: -sce " + Argument.get("sce")
+ + "-xcl " + Argument.get("xcl")
+ + " -tdoc " + Argument.get("tdoc")
+ + " -cs " + connection.getDescription(),
+ Runner.run(
+ "-sce", Argument.get("sce"), "-xcl", Argument.get("xcl"), "-tdoc",
+ Argument.get("tdoc"), "-cs", connection.getDescription()));
+ }
+
+ private final OfficeConnection connection = new OfficeConnection();
+}
diff --git a/unotest/source/java/org/openoffice/test/tools/DocumentType.java b/unotest/source/java/org/openoffice/test/tools/DocumentType.java
new file mode 100644
index 0000000000..bb4745f7b3
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/tools/DocumentType.java
@@ -0,0 +1,37 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package org.openoffice.test.tools;
+
+/** a helper "enumeration class" for classifying a document type
+*/
+public class DocumentType extends com.sun.star.uno.Enum
+{
+ private DocumentType( int value )
+ {
+ super( value );
+ }
+
+ public static final DocumentType WRITER = new DocumentType(0);
+ public static final DocumentType CALC = new DocumentType(1);
+ public static final DocumentType DRAWING = new DocumentType(2);
+ public static final DocumentType XMLFORM = new DocumentType(3);
+ public static final DocumentType PRESENTATION = new DocumentType(4);
+ public static final DocumentType FORMULA = new DocumentType(5);
+ public static final DocumentType UNKNOWN = new DocumentType(-1);
+
+}
diff --git a/unotest/source/java/org/openoffice/test/tools/OfficeDocument.java b/unotest/source/java/org/openoffice/test/tools/OfficeDocument.java
new file mode 100644
index 0000000000..bd996c0115
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/tools/OfficeDocument.java
@@ -0,0 +1,198 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package org.openoffice.test.tools;
+
+import com.sun.star.beans.PropertyState;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.document.MacroExecMode;
+import com.sun.star.frame.XComponentLoader;
+import com.sun.star.frame.XController;
+import com.sun.star.frame.XModel;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.util.CloseVetoException;
+import com.sun.star.util.XCloseable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**************************************************************************/
+
+/**************************************************************************/
+/** provides a small wrapper around a document
+*/
+public class OfficeDocument
+{
+ /* ================================================================== */
+ /* ------------------------------------------------------------------ */
+ public OfficeDocument( XMultiServiceFactory orb, XComponent document )
+ {
+ m_orb = orb;
+ m_documentComponent = document;
+ }
+
+ /* ------------------------------------------------------------------ */
+ protected static XComponent implLoadAsComponent( XMultiServiceFactory orb, String documentOrFactoryURL ) throws com.sun.star.uno.Exception
+ {
+ return implLoadAsComponent( orb, documentOrFactoryURL, new PropertyValue[0] );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private static XComponent implLoadAsComponent( XMultiServiceFactory orb, String documentOrFactoryURL, final PropertyValue[] i_args ) throws com.sun.star.uno.Exception
+ {
+ XComponentLoader aLoader = UnoRuntime.queryInterface( XComponentLoader.class,
+ orb.createInstance( "com.sun.star.frame.Desktop" ) );
+
+ XComponent document = UnoRuntime.queryInterface( XComponent.class,
+ aLoader.loadComponentFromURL( documentOrFactoryURL, "_blank", 0, i_args )
+ );
+ return document;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private static OfficeDocument implLoadDocument( XMultiServiceFactory orb, String documentOrFactoryURL, final PropertyValue[] i_args ) throws com.sun.star.uno.Exception
+ {
+ XComponent document = implLoadAsComponent( orb, documentOrFactoryURL, i_args );
+
+ XServiceInfo xSI = UnoRuntime.queryInterface( XServiceInfo.class, document );
+ if ( xSI.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
+ return new SpreadsheetDocument( orb, document );
+ return new OfficeDocument( orb, document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public static OfficeDocument blankTextDocument( XMultiServiceFactory orb ) throws com.sun.star.uno.Exception
+ {
+ return blankDocument( orb, DocumentType.WRITER );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public static OfficeDocument blankDocument( XMultiServiceFactory orb, DocumentType eType ) throws com.sun.star.uno.Exception
+ {
+ final PropertyValue[] args = new PropertyValue[] {
+ new PropertyValue( "MacroExecutionMode", -1, MacroExecMode.ALWAYS_EXECUTE, PropertyState.DIRECT_VALUE )
+ };
+ return implLoadDocument( orb, getDocumentFactoryURL( eType ), args );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public boolean close()
+ {
+ try
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, m_documentComponent );
+ closeDoc.close( true );
+ return true;
+ }
+ catch ( CloseVetoException e )
+ {
+ Logger.getLogger( OfficeDocument.class.getName() ).log( Level.SEVERE, "closing the document was vetoed", e );
+ }
+ return false;
+ }
+
+ /* ================================================================== */
+ /* ------------------------------------------------------------------ */
+ public XComponent getDocument( )
+ {
+ return m_documentComponent;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the current view of the document
+ @return
+ the view component, queried for the interface described by aInterfaceClass
+ */
+ public OfficeDocumentView getCurrentView( )
+ {
+ // get the model interface for the document
+ XModel xDocModel = UnoRuntime.queryInterface( XModel.class, m_documentComponent );
+ // get the current controller for the document - as a controller is tied to a view,
+ // this gives us the currently active view for the document.
+ XController xController = xDocModel.getCurrentController();
+
+ if ( classify() == DocumentType.CALC )
+ return new SpreadsheetView( m_orb, xController );
+
+ return new OfficeDocumentView( m_orb, xController );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns a URL which can be used to create a document of a certain type
+ */
+ private static String getDocumentFactoryURL( DocumentType eType )
+ {
+ if ( eType == DocumentType.WRITER )
+ return "private:factory/swriter";
+ if ( eType == DocumentType.CALC )
+ return "private:factory/scalc";
+ if ( eType == DocumentType.DRAWING )
+ return "private:factory/sdraw";
+ if ( eType == DocumentType.XMLFORM )
+ return "private:factory/swriter?slot=21053";
+ if ( eType == DocumentType.PRESENTATION )
+ return "private:factory/simpress";
+ if ( eType == DocumentType.FORMULA )
+ return "private:factory/smath";
+ return "private:factory/swriter";
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** classifies a document
+ */
+ private DocumentType classify( )
+ {
+ XServiceInfo xSI = UnoRuntime.queryInterface( XServiceInfo.class, m_documentComponent );
+
+ if ( xSI.supportsService( "com.sun.star.text.TextDocument" ) )
+ return DocumentType.WRITER;
+ else if ( xSI.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
+ return DocumentType.CALC;
+ else if ( xSI.supportsService( "com.sun.star.drawing.DrawingDocument" ) )
+ return DocumentType.DRAWING;
+ else if ( xSI.supportsService( "com.sun.star.presentation.PresentationDocument" ) )
+ return DocumentType.PRESENTATION;
+ else if ( xSI.supportsService( "com.sun.star.formula.FormulaProperties" ) )
+ return DocumentType.FORMULA;
+
+ return DocumentType.UNKNOWN;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a component at the service factory provided by the document
+ */
+ public XInterface createInstance( String serviceSpecifier ) throws com.sun.star.uno.Exception
+ {
+ XMultiServiceFactory xORB = UnoRuntime.queryInterface( XMultiServiceFactory.class, m_documentComponent );
+ return (XInterface)xORB.createInstance( serviceSpecifier );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a component at the service factory provided by the document, queried for a given interface type
+ */
+ public <T> T createInstance( String i_serviceSpecifier, Class<T> i_interfaceClass ) throws com.sun.star.uno.Exception
+ {
+ return UnoRuntime.queryInterface( i_interfaceClass, createInstance( i_serviceSpecifier ) );
+ }
+
+ private final XMultiServiceFactory m_orb;
+ private XComponent m_documentComponent;
+}
+
diff --git a/unotest/source/java/org/openoffice/test/tools/OfficeDocumentView.java b/unotest/source/java/org/openoffice/test/tools/OfficeDocumentView.java
new file mode 100644
index 0000000000..c78537b713
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/tools/OfficeDocumentView.java
@@ -0,0 +1,104 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package org.openoffice.test.tools;
+
+/**************************************************************************/
+
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.frame.XController;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XDispatchProvider;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.util.URL;
+import com.sun.star.util.XURLTransformer;
+
+/**************************************************************************/
+/** provides a small wrapper around a document view
+*/
+public class OfficeDocumentView
+{
+ private final XMultiServiceFactory m_orb;
+ private final XController m_controller;
+
+ /* ------------------------------------------------------------------ */
+ final public XController getController()
+ {
+ return m_controller;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public OfficeDocumentView( XMultiServiceFactory orb, XController controller )
+ {
+ m_orb = orb;
+ m_controller = controller;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a dispatcher for the given URL, obtained at the current view of the document
+ @param aURL
+ a one-element array. The first element must contain a valid
+ <member scope="com.sun.star.util">URL::Complete</member> value. Upon return, the URL is correctly
+ parsed.
+ @return
+ the dispatcher for the URL in question
+ */
+ private XDispatch getDispatcher( URL[] aURL ) throws com.sun.star.uno.Exception
+ {
+ XDispatch xReturn = null;
+
+ // go get the dispatch provider of its frame
+ XDispatchProvider xProvider = UnoRuntime.queryInterface( XDispatchProvider.class, m_controller.getFrame() );
+ if ( null != xProvider )
+ {
+ // need a URLTransformer
+ XURLTransformer xTransformer = UnoRuntime.queryInterface( XURLTransformer.class,
+ m_orb.createInstance( "com.sun.star.util.URLTransformer" ) );
+ xTransformer.parseStrict( aURL );
+
+ xReturn = xProvider.queryDispatch( aURL[0], "", 0 );
+ }
+ return xReturn;
+ }
+
+
+ /* ------------------------------------------------------------------ */
+ /** dispatches the given URL into the view, if there's a dispatcher for it
+
+ @return
+ <TRUE/> if the URL was successfully dispatched
+ */
+ public boolean dispatch( String i_url ) throws com.sun.star.uno.Exception
+ {
+ return dispatch( i_url, new PropertyValue[0] );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean dispatch( final String i_url, final PropertyValue[] i_arguments ) throws com.sun.star.uno.Exception
+ {
+ URL[] completeURL = new URL[] { new URL() };
+ completeURL[0].Complete = i_url;
+ XDispatch dispatcher = getDispatcher( completeURL );
+ if ( dispatcher == null )
+ return false;
+
+ dispatcher.dispatch( completeURL[0], i_arguments );
+ return true;
+ }
+
+}
diff --git a/unotest/source/java/org/openoffice/test/tools/SpreadsheetDocument.java b/unotest/source/java/org/openoffice/test/tools/SpreadsheetDocument.java
new file mode 100644
index 0000000000..7db4291034
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/tools/SpreadsheetDocument.java
@@ -0,0 +1,61 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.test.tools;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XComponent;
+import com.sun.star.table.XCellRange;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.sheet.XSpreadsheetDocument;
+import com.sun.star.sheet.XSpreadsheets;
+import com.sun.star.uno.UnoRuntime;
+
+public class SpreadsheetDocument extends OfficeDocument
+{
+ /** Creates a new blank spreadsheet document */
+ /* ------------------------------------------------------------------ */
+ public SpreadsheetDocument( XMultiServiceFactory orb ) throws com.sun.star.uno.Exception
+ {
+ super( orb, implLoadAsComponent( orb, "private:factory/scalc" ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public SpreadsheetDocument( XMultiServiceFactory orb, XComponent document )
+ {
+ super( orb, document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns the sheets collection
+ */
+ private XSpreadsheets getSheets()
+ {
+ XSpreadsheetDocument spreadsheetDoc = UnoRuntime.queryInterface( XSpreadsheetDocument.class, getDocument() );
+ return spreadsheetDoc.getSheets();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns the sheet with the given index
+ */
+ public XCellRange getSheet( int index ) throws com.sun.star.uno.Exception
+ {
+ XIndexAccess sheets = UnoRuntime.queryInterface( XIndexAccess.class, getSheets() );
+ return UnoRuntime.queryInterface( XCellRange.class, sheets.getByIndex( index ) );
+ }
+}
diff --git a/unotest/source/java/org/openoffice/test/tools/SpreadsheetView.java b/unotest/source/java/org/openoffice/test/tools/SpreadsheetView.java
new file mode 100644
index 0000000000..21d3d23e4f
--- /dev/null
+++ b/unotest/source/java/org/openoffice/test/tools/SpreadsheetView.java
@@ -0,0 +1,32 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package org.openoffice.test.tools;
+
+import com.sun.star.frame.XController;
+import com.sun.star.lang.XMultiServiceFactory;
+
+public class SpreadsheetView extends OfficeDocumentView
+{
+
+ /** Creates a new instance of SpreadsheetView */
+ public SpreadsheetView( XMultiServiceFactory orb, XController controller )
+ {
+ super( orb, controller );
+ }
+
+}
diff --git a/unotest/source/python/org/__init__.py b/unotest/source/python/org/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/unotest/source/python/org/__init__.py
diff --git a/unotest/source/python/org/libreoffice/__init__.py b/unotest/source/python/org/libreoffice/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/__init__.py
diff --git a/unotest/source/python/org/libreoffice/unittest.py b/unotest/source/python/org/libreoffice/unittest.py
new file mode 100644
index 0000000000..f6f93080b4
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/unittest.py
@@ -0,0 +1,29 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/.
+#
+
+import gc
+import pyuno
+import unittest
+
+class LoTestResult(unittest.TextTestResult):
+ def stopTestRun(self):
+ # HACK calling gc.collect() to get rid of as many still existing UNO proxies to
+ # SwXTextDocument and friends as possible; those C++ classes' dtors call
+ # Application::GetSolarMutex via sw::UnoImplPtrDeleter, so the dtors must be called before
+ # DeInitVCL in the call to pyuno.private_deinitTestEnvironment(); any remaining proxies
+ # that are still referenced (UnoInProcess' self.xDoc in
+ # unotest/source/python/org/libreoffice/unotest.py, or per-class variables in the various
+ # PythonTests) need to be individually released (each marked as "HACK" in the code):
+ gc.collect()
+ pyuno.private_deinitTestEnvironment()
+
+if __name__ == '__main__':
+ unittest.main(module=None, testRunner=unittest.TextTestRunner(resultclass=LoTestResult))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/unotest/source/python/org/libreoffice/unotest.py b/unotest/source/python/org/libreoffice/unotest.py
new file mode 100644
index 0000000000..e27f9e145d
--- /dev/null
+++ b/unotest/source/python/org/libreoffice/unotest.py
@@ -0,0 +1,326 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/.
+#
+
+import pathlib
+import subprocess
+import time
+import uuid
+import argparse
+import os
+import shutil
+import urllib.parse
+import urllib.request
+
+try:
+ import pyuno
+ import uno
+except ImportError:
+ print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables")
+ print("to something like:")
+ print(" PYTHONPATH=/installation/opt/program")
+ print(" URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+ raise
+
+try:
+ from com.sun.star.document import XDocumentEventListener
+except ImportError:
+ print("UNO API class not found: try to set URE_BOOTSTRAP variable")
+ print("to something like:")
+ print(" URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+ raise
+
+try:
+ from urllib.parse import quote
+except ImportError:
+ from urllib import quote
+
+### utilities ###
+
+def mkPropertyValue(name, value):
+ return uno.createUnoStruct("com.sun.star.beans.PropertyValue", name, 0, value, 0)
+
+def mkPropertyValues(**kwargs):
+ '''mkPropertyValues(Name=Value, Name=Value,...) -> (PropertyValue, PropertyValue,...)
+ ex. : mkPropertyValues(Hidden=True, ReadOnly=False)'''
+ from com.sun.star.beans import PropertyValue
+ return tuple(PropertyValue(k,0,kwargs[k],0) for k in kwargs)
+
+def fileUrlToSystemPath(url):
+ return pyuno.fileUrlToSystemPath(url)
+
+def systemPathToFileUrl(systemPath):
+ return pyuno.systemPathToFileUrl(systemPath)
+
+### UNO utilities ###
+
+class OfficeConnection(object):
+
+ def __init__(self, args):
+ self.args = args
+ self.soffice = None
+ self.xContext = None
+ self.channel = None
+
+ def setUp(self):
+ try:
+ self.verbose = self.args["verbose"]
+ except KeyError:
+ self.verbose = False
+ try:
+ prog = self.args["program"]
+ except KeyError:
+ prog = os.getenv("SOFFICE_BIN")
+ if not (prog):
+ raise Exception("SOFFICE_BIN must be set")
+ channel = "pipe,name=pytest" + str(uuid.uuid1())
+ try:
+ userdir = self.args["userdir"]
+ except KeyError:
+ userdir = "file:///tmp"
+ if not(userdir.startswith("file://")):
+ raise Exception("--userdir must be file URL")
+ self.soffice = self.bootstrap(prog, userdir, channel)
+ return self.connect(channel)
+
+ def bootstrap(self, soffice, userdir, channel):
+ argv = [ soffice, "--accept=" + channel + ";urp",
+ "-env:UserInstallation=" + userdir,
+ "--quickstart=no",
+ "--norestore", "--nologo", "--headless"]
+ if "--valgrind" in self.args:
+ argv.append("--valgrind")
+ if self.verbose:
+ print ("starting LibreOffice with channel: ", channel)
+ return subprocess.Popen(argv)
+
+ def connect(self, channel):
+ xLocalContext = uno.getComponentContext()
+ xUnoResolver = xLocalContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", xLocalContext)
+ url = ("uno:%s;urp;StarOffice.ComponentContext" % channel)
+ if self.verbose:
+ print("Connecting to: ", url)
+ while True:
+ try:
+ self.xContext = xUnoResolver.resolve(url)
+ return self.xContext
+# except com.sun.star.connection.NoConnectException
+ except pyuno.getClass("com.sun.star.connection.NoConnectException"):
+ print("WARN: NoConnectException: sleeping...")
+ time.sleep(1)
+
+ def tearDown(self):
+ if self.soffice:
+ if self.xContext:
+ try:
+ if self.verbose:
+ print("tearDown: calling terminate()...")
+ xMgr = self.xContext.ServiceManager
+ xDesktop = xMgr.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", self.xContext)
+ xDesktop.terminate()
+ if self.verbose:
+ print("...done")
+# except com.sun.star.lang.DisposedException:
+ except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"):
+ print("caught UnknownPropertyException")
+ pass # ignore, also means disposed
+ except pyuno.getClass("com.sun.star.lang.DisposedException"):
+ print("caught DisposedException")
+ pass # ignore
+ else:
+ self.soffice.terminate()
+ ret = self.soffice.wait()
+ self.xContext = None
+ self.socket = None
+ self.soffice = None
+ if ret != 0:
+ raise Exception("Exit status indicates failure: " + str(ret))
+
+ def getContext(self):
+ return self.xContext
+
+class UnoRemoteConnection:
+ def __init__(self, args):
+ self.args = args
+ self.connection = None
+ def getContext(self):
+ return self.connection.xContext
+ def getDoc(self):
+ return self.xDoc
+ def setUp(self):
+ conn = OfficeConnection(self.args)
+ conn.setUp()
+ self.connection = conn
+ def openEmptyWriterDoc(self):
+ assert(self.connection)
+ smgr = self.getContext().ServiceManager
+ desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.getContext())
+ props = [("Hidden", True), ("ReadOnly", False)]
+ loadProps = tuple([mkPropertyValue(name, value) for (name, value) in props])
+ self.xDoc = desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, loadProps)
+ return self.xDoc
+
+ def checkProperties(self, obj, dict, test):
+ for k,v in dict.items():
+ obj.setPropertyValue(k, v)
+ value = obj.getPropertyValue(k)
+ test.assertEqual(value, v)
+
+ def postTest(self):
+ assert(self.connection)
+ def tearDown(self):
+ if self.connection:
+ try:
+ self.connection.tearDown()
+ finally:
+ self.connection = None
+
+havePonies = False
+
+class UnoInProcess:
+ def getContext(self):
+ return self.xContext
+ def getDoc(self):
+ return self.xDoc
+ def setUp(self):
+ self.xContext = pyuno.getComponentContext()
+ global havePonies
+ if not(havePonies):
+ pyuno.private_initTestEnvironment()
+ havePonies = True
+
+ def openEmptyWriterDoc(self):
+ return self.openEmptyDoc("private:factory/swriter")
+
+ def openEmptyCalcDoc(self):
+ return self.openEmptyDoc("private:factory/scalc")
+
+ def openEmptyDoc(self, url, bHidden = True, bReadOnly = False):
+ props = [("Hidden", bHidden), ("ReadOnly", bReadOnly)]
+ return self.__openDocFromURL(url, props)
+
+ def openTemplateFromTDOC(self, file):
+ return self.openDocFromTDOC(file, True)
+
+ def openDocFromTDOC(self, file, asTemplate = False):
+ path = makeCopyFromTDOC(file)
+ return self.openDocFromAbsolutePath(path, asTemplate)
+
+ def openDocFromAbsolutePath(self, file, asTemplate = False):
+ return self.openDocFromURL(pathlib.Path(file).as_uri(), asTemplate)
+
+ def openDocFromURL(self, url, asTemplate = False):
+ props = [("Hidden", True), ("ReadOnly", False), ("AsTemplate", asTemplate)]
+ return self.__openDocFromURL(url, props)
+
+ def __openDocFromURL(self, url, props):
+ assert(self.xContext)
+ smgr = self.getContext().ServiceManager
+ desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.getContext())
+ loadProps = tuple([mkPropertyValue(name, value) for (name, value) in props])
+ self.xDoc = desktop.loadComponentFromURL(url, "_blank", 0, loadProps)
+ assert(self.xDoc)
+ return self.xDoc
+
+ def checkProperties(self, obj, dict, test):
+ for k,v in dict.items():
+ obj.setPropertyValue(k, v)
+ value = obj.getPropertyValue(k)
+ test.assertEqual(value, v)
+
+ def setProperties(self, obj, dict):
+ for k,v in dict.items():
+ obj.setPropertyValue(k, v)
+
+ def postTest(self):
+ assert(self.xContext)
+ def tearDown(self):
+ if hasattr(self, 'xDoc'):
+ if self.xDoc:
+ self.xDoc.close(True)
+ # HACK in case self.xDoc holds a UNO proxy to an SwXTextDocument (whose dtor calls
+ # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be
+ # garbage-collected after VCL has already been deinitialized:
+ self.xDoc = None
+
+def simpleInvoke(connection, test):
+ try:
+ connection.preTest()
+ test.run(connection.getContext())
+ finally:
+ connection.postTest()
+
+def retryInvoke(connection, test):
+ tries = 5
+ while tries > 0:
+ try:
+ tries -= 1
+ try:
+ connection.preTest()
+ test.run(connection.getContext())
+ return
+ finally:
+ connection.postTest()
+ except KeyboardInterrupt:
+ raise # Ctrl+C should work
+ except:
+ print("retryInvoke: caught exception")
+ raise Exception("FAILED retryInvoke")
+
+def runConnectionTests(connection, invoker, tests):
+ try:
+ connection.setUp()
+ for test in tests:
+ invoker(connection, test)
+ finally:
+ connection.tearDown()
+
+def makeCopyFromTDOC(file):
+ src = os.getenv("TDOC")
+ assert(src is not None)
+ src = os.path.join(src, file)
+ dst = os.getenv("TestUserDir")
+ assert(dst is not None)
+ uri = urllib.parse.urlparse(dst)
+ assert(uri.scheme.casefold() == "file")
+ assert(uri.netloc == "" or uri.netloc.casefold() == "localhost")
+ assert(uri.params == "")
+ assert(uri.query == "")
+ assert(uri.fragment == "")
+ dst = urllib.request.url2pathname(uri.path)
+ dst = os.path.join(dst, "tmp", file)
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+ try:
+ os.remove(dst)
+ except FileNotFoundError:
+ pass
+ shutil.copyfile(src, dst)
+ return dst
+
+### tests ###
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser("Help utilities for testing LibreOffice")
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
+ #parser.add_argument("p", type=str, help="program name")
+ args = parser.parse_args()
+ if args.verbose:
+ verbose = True
+ con = PersistentConnection({"verbose" : args.verbose})
+ print("starting soffice ... ", end="")
+ con.setUp()
+ print("done")
+ con.get
+ print ("shutting down ... ", end="")
+ con.tearDown()
+ print("done")
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab: