pyuno,unotest,xmlsecurity: copy GPG test files for UITtest

The problem was that running UITest_xmlsecurity_gpg changed the files in
test/signing-keys/ ... prevent that by copying the directory in that
test, which is more complicated than initially expected.

Change-Id: Ie3be922e0b2e9dae49f9a70e35ad5270c90b4fc4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171322
Reviewed-by: Thorsten Behrens <thorsten.behrens@allotropia.de>
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Tested-by: Jenkins
This commit is contained in:
Michael Stahl 2024-07-31 14:42:57 +02:00
parent e3d5b3a5ea
commit 8ed755cfb0
10 changed files with 168 additions and 30 deletions

View file

@ -98,8 +98,8 @@ public:
// note: there is no tearDownX509
void setUpX509(const test::Directories& rDirectories, const OUString& rTestName);
void setUpGpg(const test::Directories& rDirectories, const OUString& rTestName);
void tearDownGpg();
static void setUpGpg(const test::Directories& rDirectories, std::u16string_view rTestName);
static void tearDownGpg();
static bool IsValid(const css::uno::Reference<css::security::XCertificate>& cert,
const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& env);
@ -113,9 +113,6 @@ protected:
private:
std::unique_ptr<BasicDLL> mpDll;
#if HAVE_GPGCONF_SOCKETDIR
OString m_gpgconfCommandPrefix;
#endif
};
}

View file

@ -222,6 +222,7 @@ struct RuntimeCargo
css::uno::Reference< css::beans::XIntrospection > xIntrospection;
PyRef dictUnoModule;
osl::Module testModule;
osl::Module unoTestModule;
bool valid;
ExceptionClassMap exceptionMap;
ClassSet interfaceSet;

View file

@ -383,6 +383,65 @@ static PyObject* deinitTestEnvironment(
return Py_None;
}
static PyObject* initTestEnvironmentGPG(
SAL_UNUSED_PARAMETER PyObject*, PyObject* args)
{
// this tries to set up certificate stores for unit tests
// which is only possible indirectly because pyuno is URE
// so load "unotest" library and invoke a function there to do the work
Runtime const runtime;
osl::Module & rModule(runtime.getImpl()->cargo->unoTestModule);
assert(!rModule.is());
try
{
char *const testlib = getenv("UNOTEST_LIB");
if (!testlib) { abort(); }
#ifdef _WIN32
OString const libname = OString(testlib, strlen(testlib))
.replaceAll(OString('/'), OString('\\'));
#else
OString const libname(testlib, strlen(testlib));
#endif
rModule.load(OStringToOUString(libname, osl_getThreadTextEncoding()),
SAL_LOADMODULE_LAZY | SAL_LOADMODULE_GLOBAL);
if (!rModule.is()) { abort(); }
oslGenericFunction const pFunc(rModule.getFunctionSymbol("test_init_gpg"));
if (!pFunc) { abort(); }
char * pTestDirURL;
if (!PyArg_ParseTuple(args, "s", &pTestDirURL)) { abort(); }
OUString const testDirURL(OUString::createFromAscii(pTestDirURL));
reinterpret_cast<void (SAL_CALL *)(OUString const&)>(pFunc)(testDirURL);
}
catch (const css::uno::Exception &)
{
abort();
}
return Py_None;
}
static PyObject* deinitTestEnvironmentGPG(
SAL_UNUSED_PARAMETER PyObject*, SAL_UNUSED_PARAMETER PyObject*)
{
Runtime const runtime;
osl::Module & rModule(runtime.getImpl()->cargo->unoTestModule);
if (rModule.is())
{
try
{
oslGenericFunction const pFunc(
rModule.getFunctionSymbol("test_deinit_gpg"));
if (!pFunc) { abort(); }
reinterpret_cast<void (SAL_CALL *)()>(pFunc)();
}
catch (const css::uno::Exception &)
{
abort();
}
}
return Py_None;
}
PyObject * extractOneStringArg( PyObject *args, char const *funcName )
{
if( !PyTuple_Check( args ) || PyTuple_Size( args) != 1 )
@ -852,6 +911,8 @@ struct PyMethodDef PyUNOModule_methods [] =
{
{"private_initTestEnvironment", initTestEnvironment, METH_VARARGS, nullptr},
{"private_deinitTestEnvironment", deinitTestEnvironment, METH_VARARGS, nullptr},
{"private_initTestEnvironmentGPG", initTestEnvironmentGPG, METH_VARARGS, nullptr},
{"private_deinitTestEnvironmentGPG", deinitTestEnvironmentGPG, METH_VARARGS, nullptr},
{"getComponentContext", getComponentContext, METH_VARARGS, nullptr},
#if defined __clang__
#pragma clang diagnostic push

View file

@ -41,9 +41,6 @@ gb_UITest_COMMAND = $(ICECREAM_RUN) $(gb_CppunitTest_coredumpctl_run) $(gb_Cppun
gb_TEST_ENV_VARS += LIBO_LANG=C
# GNUPGHOME is needed for tests using the LibreOffice GPG features.
gb_TEST_ENV_VARS += GNUPGHOME="$(SRCDIR)/test/signing-keys"
.PHONY : $(call gb_UITest_get_clean_target,%)
$(call gb_UITest_get_clean_target,%) :
$(call gb_Helper_abbreviate_dirs,\
@ -51,6 +48,9 @@ $(call gb_UITest_get_clean_target,%) :
ifneq ($(DISABLE_PYTHON),TRUE)
# dlopening unotest requires this
gb_UITest_PRECOMMAND = $(gb_PythonTest_PRECOMMAND)
# qadevOOo/qa/registrymodifications.xcu is copied to user profile directory to ensure en_US locale;
# this might be overwritten later when gb_UITest_use_config is set
.PHONY : $(call gb_UITest_get_target,%)
@ -73,6 +73,7 @@ else
$(DEFS) \
$(if $(filter WNT,$(OS)),SAL_LOG_FILE="$(dir $(call gb_UITest_get_target,$*))/soffice.out.log") \
TEST_LIB=$(call gb_Library_get_target,test) \
UNOTEST_LIB=$(call gb_Library_get_target,unotest) \
URE_BOOTSTRAP=vnd.sun.star.pathname:$(call gb_Helper_get_rcfile,$(INSTROOT)/$(LIBO_ETC_FOLDER)/fundamental) \
PYTHONPATH="$(PYPATH)" \
TestUserDir="$(call gb_Helper_make_url,$(dir $(call gb_UITest_get_target,$*)))" \

View file

@ -24,7 +24,7 @@ except ImportError:
def signal_handler(signal_num, frame):
signal_name = signal.Signals(signal_num).name
print(f'Signal handler called with signal {signal_name} ({signal_num})', flush=True)
#print(f'Signal handler called with signal {signal_name} ({signal_num})', flush=True)
class OfficeConnection:
def __init__(self, args):

View file

@ -145,27 +145,61 @@ void MacrosTest::setUpX509(const test::Directories& rDirectories, const OUString
#endif
}
void MacrosTest::setUpGpg(const test::Directories& rDirectories, const OUString& rTestName)
#if HAVE_GPGCONF_SOCKETDIR
// mutable global should be tolerable in test lib
static OString g_gpgconfCommandPrefix;
#endif
extern "C" {
SAL_DLLPUBLIC_EXPORT
void test_init_gpg(OUString const& rTargetDir)
{
OUString aSourceDir = rDirectories.getURLFromSrc(u"/test/signing-keys/");
OUString aTargetDir
= rDirectories.getURLFromWorkdir(Concat2View("CppunitTest/" + rTestName + ".test.user"));
const char* pSrcRoot = getenv("SRC_ROOT");
if (!pSrcRoot)
{
abort();
}
OUString const srcRootPath(OUString(pSrcRoot, strlen(pSrcRoot), osl_getThreadTextEncoding()));
OUString const sourcePath(srcRootPath + "/test/signing-keys/");
OUString aSourceDir;
osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath(sourcePath, aSourceDir);
if (osl::FileBase::E_None != e)
{
abort();
}
OUString aTargetPath;
osl::FileBase::getSystemPathFromFileURL(aTargetDir, aTargetPath);
osl::FileBase::getSystemPathFromFileURL(rTargetDir, aTargetPath);
auto const rc = osl::Directory::create(rTargetDir);
if (osl::FileBase::E_None != rc && osl::FileBase::E_EXIST != rc)
{
SAL_WARN("test", "creating target dir failed, aborting");
abort();
}
// 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");
if (osl::FileBase::E_None
!= osl::File::copy(aSourceDir + "pubring.gpg", rTargetDir + "/pubring.gpg")
|| osl::FileBase::E_None
!= osl::File::copy(aSourceDir + "random_seed", rTargetDir + "/random_seed")
|| osl::FileBase::E_None
!= osl::File::copy(aSourceDir + "secring.gpg", rTargetDir + "/secring.gpg")
|| osl::FileBase::E_None
!= osl::File::copy(aSourceDir + "trustdb.gpg", rTargetDir + "/trustdb.gpg"))
{
SAL_WARN("test", "copying files failed, aborting");
abort();
}
// note: this doesn't work for UITest because "os.environ" is a copy :(
OUString gpgHomeVar(u"GNUPGHOME"_ustr);
osl_setEnvironment(gpgHomeVar.pData, aTargetPath.pData);
#if HAVE_GPGCONF_SOCKETDIR
auto const ldPath = std::getenv("LIBO_LD_PATH");
m_gpgconfCommandPrefix
g_gpgconfCommandPrefix
= ldPath == nullptr ? OString() : OString::Concat("LD_LIBRARY_PATH=") + ldPath + " ";
OString path;
bool ok = aTargetPath.convertToString(&path, osl_getThreadTextEncoding(),
@ -173,32 +207,53 @@ void MacrosTest::setUpGpg(const test::Directories& rDirectories, const OUString&
| 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;
if (!ok)
{
SAL_WARN("test", "converting path failed, aborting: " << aTargetPath);
abort();
}
g_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";
OString cmd = g_gpgconfCommandPrefix + " --create-socketdir";
int res = std::system(cmd.getStr());
CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd.getStr(), 0, res);
if (res != 0)
{
SAL_WARN("test", "invoking gpgconf failed, aborting: " << cmd);
abort();
}
#else
(void)this;
(void)rTargetDir;
#endif
}
void MacrosTest::tearDownGpg()
SAL_DLLPUBLIC_EXPORT void test_deinit_gpg()
{
#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";
CPPUNIT_ASSERT(!g_gpgconfCommandPrefix.isEmpty());
OString cmd = g_gpgconfCommandPrefix + " --remove-socketdir";
int res = std::system(cmd.getStr());
CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd.getStr(), 0, res);
#else
(void)this;
g_gpgconfCommandPrefix.clear();
#endif
}
} // extern "C"
void MacrosTest::setUpGpg(const test::Directories& rDirectories,
std::u16string_view const rTestName)
{
OUString aTargetDir = rDirectories.getURLFromWorkdir(
Concat2View("CppunitTest/" + OUString(rTestName.data(), rTestName.size()) + ".test.user"));
return test_init_gpg(aTargetDir);
}
void MacrosTest::tearDownGpg() { return test_deinit_gpg(); }
namespace
{
struct Valid

View file

@ -17,4 +17,7 @@ $(eval $(call gb_UITest_set_defs,xmlsecurity_gpg, \
TDOC="$(SRCDIR)/xmlsecurity/qa/uitest/data" \
))
# oneprocess prevents setting GNUPGHOME
$(eval $(call gb_UITest_avoid_oneprocess,xmlsecurity_gpg))
# vim: set noet sw=4 ts=4:

View file

@ -9,8 +9,11 @@
from uitest.framework import UITestCase
from libreoffice.uno.propertyvalue import mkPropertyValues
import pyuno
from tempfile import TemporaryDirectory
from urllib.parse import urljoin, urlparse
from urllib.request import url2pathname
import os.path
@ -19,6 +22,23 @@ import os.path
# Requires the environment variable GNUPGHOME to be set correctly.
# See solenv/gbuild/UITest.mk
class GpgEncryptTest(UITestCase):
# should this be setUp() or setUpClass()?
# as setUp(), any test's change to the files should be overwritten before
# the next test, which could be an advantage.
def setUp(self):
testdir = os.getenv("TestUserDir")
certdir = urljoin(testdir, "signing-keys")
# this sets GNUPGHOME so do it before starting soffice
pyuno.private_initTestEnvironmentGPG(certdir)
# ugly: set var here again because "os.environ" is a copy :(
os.environ["GNUPGHOME"] = url2pathname(urlparse(certdir).path)
super().setUp()
def tearDown(self):
super().tearDown()
pyuno.private_deinitTestEnvironmentGPG()
def test_gpg_encrypt(self):
# TODO: Maybe deduplicate with sw/qa/uitest/writer_tests8/save_with_password_test_policy.py
with TemporaryDirectory() as tempdir:

View file

@ -92,7 +92,7 @@ void SigningTest::setUp()
UnoApiXmlTest::setUp();
MacrosTest::setUpX509(m_directories, u"xmlsecurity_signing"_ustr);
MacrosTest::setUpGpg(m_directories, u"xmlsecurity_signing"_ustr);
MacrosTest::setUpGpg(m_directories, std::u16string_view(u"xmlsecurity_signing"));
// Initialize crypto after setting up the environment variables.
mxSEInitializer = xml::crypto::SEInitializer::create(m_xContext);

View file

@ -57,7 +57,7 @@ void SigningTest2::setUp()
UnoApiXmlTest::setUp();
MacrosTest::setUpX509(m_directories, u"xmlsecurity_signing2"_ustr);
MacrosTest::setUpGpg(m_directories, u"xmlsecurity_signing2"_ustr);
MacrosTest::setUpGpg(m_directories, std::u16string_view(u"xmlsecurity_signing2"));
// Initialize crypto after setting up the environment variables.
mxSEInitializer = xml::crypto::SEInitializer::create(m_xContext);