postprocess/CppunitTest_singletons.mk | 36 ++++++++++++ postprocess/Module_postprocess.mk | 4 + postprocess/qa/singletons.cxx | 98 ++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+)
New commits: commit 26e03a6571e61ba3589996f0fc19d16d495080e6 Author: Neil Roberts <[email protected]> AuthorDate: Mon Feb 23 14:16:50 2026 +0100 Commit: Neil Roberts <[email protected]> CommitDate: Mon Feb 23 16:39:43 2026 +0100 Add a unit test that constructs every service-based singleton via class name The idea is to check that all of the class names match a singleton declared in a component somewhere and that they return an object that implements the advertised interface. Otherwise the generated getter for the singleton will fail with a runtime exception. Change-Id: I1352e7392e33e9182962e6279fcb62cf3b58352a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200056 Reviewed-by: Neil Roberts <[email protected]> Tested-by: Jenkins diff --git a/postprocess/CppunitTest_singletons.mk b/postprocess/CppunitTest_singletons.mk new file mode 100644 index 000000000000..a8eccf83e31e --- /dev/null +++ b/postprocess/CppunitTest_singletons.mk @@ -0,0 +1,36 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,singletons)) + +$(eval $(call gb_CppunitTest_add_exception_objects,singletons, \ + postprocess/qa/singletons \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,singletons, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + test \ + unotest \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,singletons)) +$(eval $(call gb_CppunitTest_use_api,singletons,oovbaapi)) + +$(eval $(call gb_CppunitTest_use_ure,singletons)) +$(eval $(call gb_CppunitTest_use_vcl,singletons)) + +$(eval $(call gb_CppunitTest_use_rdb,singletons,services)) + +$(eval $(call gb_CppunitTest_use_configuration,singletons)) + +# vim: set noet sw=4 ts=4: diff --git a/postprocess/Module_postprocess.mk b/postprocess/Module_postprocess.mk index 01aeb11afaf3..2f7290b23094 100644 --- a/postprocess/Module_postprocess.mk +++ b/postprocess/Module_postprocess.mk @@ -63,4 +63,8 @@ $(eval $(call gb_Module_add_check_targets,postprocess,\ )) endif +$(eval $(call gb_Module_add_check_targets,postprocess,\ + CppunitTest_singletons \ +)) + # vim: set noet sw=4 ts=4: diff --git a/postprocess/qa/singletons.cxx b/postprocess/qa/singletons.cxx new file mode 100644 index 000000000000..0745aa36ea75 --- /dev/null +++ b/postprocess/qa/singletons.cxx @@ -0,0 +1,98 @@ +/* -*- 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/. + */ + +// Tries to get every interface-based singleton registered in the type description manager using its +// type name. See tdf#170930 for an example of a mistake that can make this fail. + +#include <sal/config.h> + +#include <com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp> +#include <com/sun/star/reflection/XSingletonTypeDescription2.hpp> +#include <test/bootstrapfixture.hxx> + +namespace +{ +constexpr OUString TYPE_DESCRIPTION_MANAGER + = u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"_ustr; + +class Test : public test::BootstrapFixture +{ +public: + void test(); + + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); +}; + +void Test::test() +{ + css::uno::Reference<css::reflection::XTypeDescriptionEnumerationAccess> xTDMgr; + + m_xContext->getValueByName(TYPE_DESCRIPTION_MANAGER) >>= xTDMgr; + + if (!xTDMgr.is()) + throw css::uno::RuntimeException("cannot get singleton " + TYPE_DESCRIPTION_MANAGER); + + css::uno::Sequence<css::uno::TypeClass> aTypes = { css::uno::TypeClass_SINGLETON }; + + css::uno::Reference<css::reflection::XTypeDescriptionEnumeration> xTypeEnum + = xTDMgr->createTypeDescriptionEnumeration( + "", aTypes, css::reflection::TypeDescriptionSearchDepth_INFINITE); + + while (true) + { + css::uno::Reference<css::reflection::XTypeDescription> xType; + + try + { + xType = xTypeEnum->nextTypeDescription(); + } + catch (css::container::NoSuchElementException&) + { + break; + } + + css::uno::Reference<css::reflection::XSingletonTypeDescription2> xSingleton( + xType, css::uno::UNO_QUERY); + + // Skip singletons that are not interface-based + if (!xSingleton.is() || !xSingleton->isInterfaceBased()) + continue; + + OUString sName = "/singletons/" + xSingleton->getName(); + + css::uno::Reference<css::uno::XInterface> xImpl; + + m_xContext->getValueByName(sName) >>= xImpl; + + if (!xImpl.is()) + throw css::uno::RuntimeException("cannot get singleton " + sName); + + css::uno::Reference<css::reflection::XTypeDescription> xInterface + = xSingleton->getInterface(); + + css::uno::Type interfaceType(xInterface->getTypeClass(), xInterface->getName()); + + // Make sure that the returned object supports the interface of the singleton + if (!xImpl->queryInterface(interfaceType).hasValue()) + { + throw css::uno::RuntimeException("Singleton " + xSingleton->getName() + + " doesn’t support the " + xInterface->getName() + + " interface"); + } + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
