1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 // Try to instantiate as many implementations as possible. Finds all
11 // implementations reachable via the service manager. If a given implementation
12 // is the only implementor of some service that has a zero-parameter
13 // constructor, instantiate the implementation through that service name. If a
14 // given implementation does not offer any such constructors (because it does not
15 // support any single-interface--based service, or because for each relevant
16 // service there are multiple implementations or it does not have an appropriate
17 // constructor) but does support at least one accumulation-based service, then
18 // instantiate it through its implementation name (a heuristic to identify
19 // instantiatable implementations that appears to work well).
21 #include <sal/config.h>
28 #include <string_view>
32 #include <com/sun/star/beans/PropertyAttribute.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/beans/XPropertySetInfo.hpp>
35 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
36 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
37 #include <com/sun/star/lang/XComponent.hpp>
38 #include <com/sun/star/lang/XServiceInfo.hpp>
39 #include <com/sun/star/reflection/XServiceConstructorDescription.hpp>
40 #include <com/sun/star/reflection/XServiceTypeDescription2.hpp>
41 #include <com/sun/star/frame/XDesktop.hpp>
42 #include <comphelper/sequence.hxx>
43 #include <cppuhelper/exc_hlp.hxx>
44 #include <rtl/strbuf.hxx>
45 #include <test/bootstrapfixture.hxx>
46 #include <vcl/svapp.hxx>
50 OString
msg(std::u16string_view string
) {
51 return OUStringToOString(string
, osl_getThreadTextEncoding());
54 OString
msg(css::uno::Sequence
<OUString
> const & strings
) {
55 OStringBuffer
buf("{");
56 for (sal_Int32 i
= 0; i
!= strings
.getLength(); ++i
) {
61 buf
.append(msg(strings
[i
]));
65 return buf
.makeStringAndClear();
68 bool unique(css::uno::Sequence
<OUString
> const & strings
) {
69 // Assumes small sequences for which quadratic algorithm is acceptable:
70 for (sal_Int32 i
= 0; i
< strings
.getLength() - 1; ++i
) {
71 for (sal_Int32 j
= i
+ 1; j
!= strings
.getLength(); ++j
) {
72 if (strings
[j
] == strings
[i
]) {
81 css::uno::Sequence
<OUString
> const & strings
, OUString
const & string
)
83 return comphelper::findValue(strings
, string
) != -1;
87 css::uno::Sequence
<OUString
> const & strings1
,
88 css::uno::Sequence
<OUString
> const & strings2
)
90 // Assumes small sequences for which quadratic algorithm is acceptable:
91 return std::all_of(strings2
.begin(), strings2
.end(),
92 [&strings1
](const OUString
& rStr
) { return contains(strings1
, rStr
); });
96 css::uno::Reference
<css::reflection::XServiceTypeDescription
> const & service
,
97 std::set
<css::uno::Reference
<css::reflection::XServiceTypeDescription
>> * allServices
)
99 assert(allServices
!= nullptr);
100 if (!allServices
->insert(service
).second
) {
103 const auto aMandatoryServices
= service
->getMandatoryServices();
104 for (auto const & serv
: aMandatoryServices
) {
105 addService(serv
, allServices
);
109 class Test
: public test::BootstrapFixture
{
113 CPPUNIT_TEST_SUITE(Test
);
115 CPPUNIT_TEST_SUITE_END();
119 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & typeManager
,
120 OUString
const & name
, bool withArguments
,
121 OUString
const & implementationName
,
122 css::uno::Sequence
<OUString
> const & serviceNames
,
123 std::vector
<css::uno::Reference
<css::lang::XComponent
>> * components
);
127 // On Windows, denylist the com.sun.star.comp.report.OReportDefinition
128 // implementation (reportdesign::OReportDefinition in
129 // reportdesign/source/core/api/ReportDefinition.cxx), as it spawns a thread
130 // that forever blocks in SendMessageW when no VCL event loop is running
131 // (reportdesign::<anon>::FactoryLoader::execute ->
132 // framework::Desktop::findFrame -> framework::TaskCreator::createTask ->
133 // <anon>::TaskCreatorService::createInstanceWithArguments ->
134 // <anon>::TaskCreatorService::impls_createContainerWindow ->
135 // <anon>::VCLXToolkit::createWindow ->
136 // <anon>::VCLXToolkit::ImplCreateWindow ->
137 // <anon>::VCLXToolkit::ImplCreateWindow -> WorkWindow::WorkWindow ->
138 // WorkWindow::ImplInit -> ImplBorderWindow::ImplBorderWindow ->
139 // ImplBorderWindow::ImplInit -> Window::ImplInit ->
140 // WinSalInstance::CreateFrame -> ImplSendMessage -> SendMessageW):
141 std::vector
<OUString
> denylist
;
142 denylist
.emplace_back("com.sun.star.comp.report.OReportDefinition");
144 // <https://bugs.documentfoundation.org/show_bug.cgi?id=89343>
145 // "~SwXMailMerge() goes into endless SwCache::Check()":
146 denylist
.emplace_back("SwXMailMerge");
148 css::uno::Reference
<css::container::XContentEnumerationAccess
> enumAcc(
149 m_xContext
->getServiceManager(), css::uno::UNO_QUERY_THROW
);
150 css::uno::Reference
<css::container::XHierarchicalNameAccess
> typeMgr(
151 m_xContext
->getValueByName(
152 "/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
153 css::uno::UNO_QUERY_THROW
);
154 const css::uno::Sequence
<OUString
> serviceNames(
155 m_xContext
->getServiceManager()->getAvailableServiceNames());
158 OUString
const & theServiceName
, bool theDefaultConstructor
):
159 serviceName(theServiceName
),
160 defaultConstructor(theDefaultConstructor
)
162 OUString serviceName
;
163 bool defaultConstructor
;
165 struct Implementation
{
167 css::uno::Reference
<css::lang::XServiceInfo
> const & theFactory
,
168 css::uno::Sequence
<OUString
> const & theServiceNames
):
169 factory(theFactory
), serviceNames(theServiceNames
),
170 accumulationBased(false)
172 css::uno::Reference
<css::lang::XServiceInfo
> const factory
;
173 css::uno::Sequence
<OUString
> const serviceNames
;
174 std::vector
<Constructor
> constructors
;
175 bool accumulationBased
;
177 std::map
<OUString
, Implementation
> impls
;
178 for (const auto& rServiceName
: serviceNames
) {
179 css::uno::Reference
<css::container::XEnumeration
> serviceImpls1(
180 enumAcc
->createContentEnumeration(rServiceName
),
181 css::uno::UNO_SET_THROW
);
182 std::vector
<css::uno::Reference
<css::lang::XServiceInfo
>> serviceImpls2
;
183 while (serviceImpls1
->hasMoreElements()) {
184 serviceImpls2
.emplace_back(
185 serviceImpls1
->nextElement(), css::uno::UNO_QUERY_THROW
);
187 css::uno::Reference
<css::reflection::XServiceTypeDescription2
> desc
;
188 if (typeMgr
->hasByHierarchicalName(rServiceName
)) {
190 typeMgr
->getByHierarchicalName(rServiceName
),
191 css::uno::UNO_QUERY_THROW
);
193 if (serviceImpls2
.empty()) {
195 CPPUNIT_ASSERT_MESSAGE(
197 "no implementations of single-interface--based \""
198 + msg(rServiceName
) + "\"")
200 !desc
->isSingleInterfaceBased());
202 << "accumulation-based service \"" << rServiceName
203 << "\" without implementations\n";
206 << "fantasy service name \"" << rServiceName
207 << "\" without implementations\n";
210 for (auto const & j
: serviceImpls2
) {
211 OUString
name(j
->getImplementationName());
212 auto k
= impls
.find(name
);
213 if (k
== impls
.end()) {
214 css::uno::Sequence
<OUString
> servs(
215 j
->getSupportedServiceNames());
216 CPPUNIT_ASSERT_MESSAGE(
218 "implementation \"" + msg(name
)
219 + "\" supports non-unique " + msg(servs
))
223 std::make_pair(name
, Implementation(j
, servs
)))
226 CPPUNIT_ASSERT_MESSAGE(
228 "multiple implementations named \"" + msg(name
)
231 bool(j
== k
->second
.factory
));
233 CPPUNIT_ASSERT_MESSAGE(
235 "implementation \"" + msg(name
) + "\" supports "
236 + msg(k
->second
.serviceNames
) + " but not \""
237 + msg(rServiceName
) + "\"")
239 contains(k
->second
.serviceNames
, rServiceName
));
241 if (desc
->isSingleInterfaceBased()) {
242 if (serviceImpls2
.size() == 1) {
243 const css::uno::Sequence
<
245 css::reflection::XServiceConstructorDescription
>>
246 ctors(desc
->getConstructors());
247 auto pCtor
= std::find_if(ctors
.begin(), ctors
.end(),
248 [](const auto& rCtor
) { return !rCtor
->getParameters().hasElements(); });
249 if (pCtor
!= ctors
.end())
250 k
->second
.constructors
.emplace_back(
252 (*pCtor
)->isDefaultConstructor());
255 k
->second
.accumulationBased
= true;
259 << "implementation \"" << name
260 << "\" supports fantasy service name \""
261 << rServiceName
<< "\"\n";
266 std::vector
<css::uno::Reference
<css::lang::XComponent
>> comps
;
267 for (auto const & i
: impls
) {
268 if (std::find(denylist
.begin(), denylist
.end(), i
.first
)
271 if (i
.second
.constructors
.empty()) {
272 if (i
.second
.accumulationBased
) {
274 typeMgr
, i
.first
, false, i
.first
, i
.second
.serviceNames
, &comps
);
277 << "no obvious way to instantiate implementation \""
278 << i
.first
<< "\"\n";
281 for (auto const & j
: i
.second
.constructors
) {
283 typeMgr
, j
.serviceName
, !j
.defaultConstructor
, i
.first
,
284 i
.second
.serviceNames
, &comps
);
289 SolarMutexReleaser rel
;
290 for (auto const & i
: comps
) {
291 // cannot call dispose() on XDesktop before calling terminate()
292 if (!css::uno::Reference
<css::frame::XDesktop
>(i
, css::uno::UNO_QUERY
))
297 void Test::createInstance(
298 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & typeManager
,
299 OUString
const & name
, bool withArguments
,
300 OUString
const & implementationName
,
301 css::uno::Sequence
<OUString
> const & serviceNames
,
302 std::vector
<css::uno::Reference
<css::lang::XComponent
>> * components
)
304 assert(components
!= nullptr);
305 css::uno::Reference
<css::uno::XInterface
> inst
;
308 inst
= m_xContext
->getServiceManager()
309 ->createInstanceWithArgumentsAndContext(
310 name
, css::uno::Sequence
<css::uno::Any
>(), m_xContext
);
312 inst
= m_xContext
->getServiceManager()->createInstanceWithContext(
315 } catch (css::uno::Exception
& e
) {
316 css::uno::Any
a(cppu::getCaughtException());
319 "instantiating \"" + msg(implementationName
) + "\" via \""
320 + msg(name
) + "\" caused " + msg(a
.getValueTypeName()) + " \""
321 + msg(e
.Message
) + "\"")
324 CPPUNIT_ASSERT_MESSAGE(
326 "instantiating \"" + msg(implementationName
) + "\" via \""
327 + msg(name
) + "\" returned null reference")
330 css::uno::Reference
<css::lang::XComponent
> comp(inst
, css::uno::UNO_QUERY
);
332 components
->push_back(comp
);
334 css::uno::Reference
<css::lang::XServiceInfo
> info(
335 inst
, css::uno::UNO_QUERY
);
336 CPPUNIT_ASSERT_MESSAGE(
338 "instantiating \"" + msg(implementationName
) + "\" via \""
339 + msg(name
) + "\" does not provide XServiceInfo")
342 OUString
expImpl(implementationName
);
343 css::uno::Sequence
<OUString
> expServs(serviceNames
);
345 if (name
== "com.sun.star.comp.configuration.ConfigurationProvider") {
346 // Instantiating a ConfigurationProvider with no or empty args must
347 // return theDefaultProvider:
348 expImpl
= "com.sun.star.comp.configuration.DefaultProvider";
349 expServs
= {"com.sun.star.configuration.DefaultProvider"};
350 } else if (name
== "com.sun.star.datatransfer.clipboard.SystemClipboard") {
351 // SystemClipboard is a wrapper returning either a platform-specific or
352 // the generic VCLGenericClipboard:
354 expImpl
= "com.sun.star.datatransfer.clipboard.ClipboardW32";
356 expImpl
= "com.sun.star.datatransfer.VCLGenericClipboard";
359 } else if (name
== "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"
360 || name
== "com.sun.star.datatransfer.dnd.XdndSupport")
362 expImpl
= "com.sun.star.datatransfer.dnd.VclGenericDragSource";
363 expServs
= {"com.sun.star.datatransfer.dnd.GenericDragSource"};
364 } else if (name
== "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"
365 || name
== "com.sun.star.datatransfer.dnd.XdndDropTarget")
367 expImpl
= "com.sun.star.datatransfer.dnd.VclGenericDropTarget";
368 expServs
= {"com.sun.star.datatransfer.dnd.GenericDropTarget"};
370 } else if (name
== "com.sun.star.ui.dialogs.FolderPicker") {
371 // FolderPicker is a wrapper returning either a platform-specific or the
372 // generic OfficeFolderPicker:
374 expImpl
= "com.sun.star.ui.dialogs.Win32FolderPicker";
375 expServs
= {"com.sun.star.ui.dialogs.SystemFolderPicker"};
377 expImpl
= "com.sun.star.svtools.OfficeFolderPicker";
378 expServs
= {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
380 } else if (expImpl
== "com.sun.star.comp.Calc.SpreadsheetDocument") {
381 expImpl
= "ScModelObj";
382 } else if (expImpl
== "com.sun.star.comp.Draw.DrawingDocument"
383 || expImpl
== "com.sun.star.comp.Draw.PresentationDocument")
385 expImpl
= "SdXImpressDocument";
386 } else if (expImpl
== "com.sun.star.comp.Writer.GlobalDocument"
387 || expImpl
== "com.sun.star.comp.Writer.TextDocument"
388 || expImpl
== "com.sun.star.comp.Writer.WebDocument")
390 expImpl
= "SwXTextDocument";
392 CPPUNIT_ASSERT_EQUAL_MESSAGE(
394 "instantiating \"" + msg(implementationName
) + "\" via \""
395 + msg(name
) + "\" reports wrong implementation name")
397 expImpl
, info
->getImplementationName());
398 const css::uno::Sequence
<OUString
> servs(info
->getSupportedServiceNames());
399 CPPUNIT_ASSERT_MESSAGE(
401 "instantiating \"" + msg(implementationName
) + "\" via \""
402 + msg(name
) + "\" reports non-unique " + msg(servs
))
405 // Some implementations like "com.sun.star.comp.Calc.SpreadsheetDocument"
406 // report sub-services like
407 // "com.sun.star.sheet.SpreadsheetDocumentSettings", and
408 // "com.sun.star.document.OfficeDocument" that are not listed in the
409 // .component file, so check for containment instead of equality:
410 CPPUNIT_ASSERT_MESSAGE(
412 "instantiating \"" + msg(implementationName
) + "\" via \""
413 + msg(name
) + "\" reports " + msg(servs
) + " different from "
416 contains(servs
, expServs
));
417 std::set
<css::uno::Reference
<css::reflection::XServiceTypeDescription
>> allservs
;
418 for (auto const & serv
: servs
) {
419 if (!typeManager
->hasByHierarchicalName(serv
)) {
421 << "instantiating \"" << implementationName
<< "\" via \"" << name
422 << "\" supports fantasy service name \"" << serv
<< "\"\n";
426 css::uno::Reference
<css::reflection::XServiceTypeDescription
>(
427 typeManager
->getByHierarchicalName(serv
), css::uno::UNO_QUERY_THROW
),
430 css::uno::Reference
<css::beans::XPropertySetInfo
> propsinfo
;
431 for (auto const & serv
: allservs
) {
432 auto const props
= serv
->getProperties();
433 for (auto const & prop
: props
) {
435 = (prop
->getPropertyFlags() & css::beans::PropertyAttribute::OPTIONAL
) != 0;
436 if (!propsinfo
.is()) {
437 css::uno::Reference
<css::beans::XPropertySet
> propset(inst
, css::uno::UNO_QUERY
);
439 CPPUNIT_ASSERT_MESSAGE(
441 "instantiating \"" + msg(implementationName
) + "\" via \"" + msg(name
)
442 + "\" reports service " + msg(serv
->getName())
443 + " with non-optional property \"" + msg(prop
->getName())
444 + "\" but does not implement css.uno.XPropertySet")
449 propsinfo
= propset
->getPropertySetInfo();
450 if (!propsinfo
.is()) {
451 //TODO: legal to return null in more cases? ("@returns NULL if the
452 // implementation cannot or will not provide information about the properties")
453 CPPUNIT_ASSERT_MESSAGE(
455 "instantiating \"" + msg(implementationName
) + "\" via \"" + msg(name
)
456 + "\" reports service " + msg(serv
->getName())
457 + " with non-optional property \"" + msg(prop
->getName())
458 + "\" but css.uno.XPropertySet::getPropertySetInfo returns null")
464 if (!propsinfo
->hasPropertyByName(prop
->getName())) {
465 static std::set
<std::pair
<OUString
, OUString
>> const denylist
{
466 {"com.sun.star.comp.chart.DataSeries", "BorderDash"},
467 {"com.sun.star.comp.chart2.ChartDocumentWrapper", "UserDefinedAttributes"},
468 {"com.sun.star.comp.dbu.OColumnControlModel", "Tabstop"},
469 {"com.sun.star.comp.report.OFormattedField", "Align"},
470 {"com.sun.star.comp.report.OFormattedField", "BackgroundColor"},
471 {"com.sun.star.comp.report.OFormattedField", "Border"},
472 {"com.sun.star.comp.report.OFormattedField", "DefaultControl"},
473 {"com.sun.star.comp.report.OFormattedField", "EffectiveDefault"},
474 {"com.sun.star.comp.report.OFormattedField", "EffectiveMax"},
475 {"com.sun.star.comp.report.OFormattedField", "EffectiveMin"},
476 {"com.sun.star.comp.report.OFormattedField", "EffectiveValue"},
477 {"com.sun.star.comp.report.OFormattedField", "Enabled"},
478 {"com.sun.star.comp.report.OFormattedField", "FontEmphasisMark"},
479 {"com.sun.star.comp.report.OFormattedField", "FontRelief"},
480 {"com.sun.star.comp.report.OFormattedField", "HelpText"},
481 {"com.sun.star.comp.report.OFormattedField", "HelpURL"},
482 {"com.sun.star.comp.report.OFormattedField", "MaxTextLen"},
483 {"com.sun.star.comp.report.OFormattedField", "Printable"},
484 {"com.sun.star.comp.report.OFormattedField", "ReadOnly"},
485 {"com.sun.star.comp.report.OFormattedField", "Spin"},
486 {"com.sun.star.comp.report.OFormattedField", "Tabstop"},
487 {"com.sun.star.comp.report.OFormattedField", "Text"},
488 {"com.sun.star.comp.report.OFormattedField", "TextColor"},
489 {"com.sun.star.comp.report.OFormattedField", "TextLineColor"},
490 {"com.sun.star.comp.report.OFormattedField", "TreatAsNumber"},
491 {"stardiv.Toolkit.UnoControlRoadmapModel", "Interactive"}};
492 if (denylist
.find({implementationName
, prop
->getName()}) != denylist
.end()) {
495 CPPUNIT_ASSERT_MESSAGE(
497 "instantiating \"" + msg(implementationName
) + "\" via \"" + msg(name
)
498 + "\" reports service " + msg(serv
->getName())
499 + " with non-optional property \"" + msg(prop
->getName())
500 + ("\" but css.uno.XPropertySet::getPropertySetInfo's hasPropertyByName"
509 CPPUNIT_TEST_SUITE_REGISTRATION(Test
);
513 CPPUNIT_PLUGIN_IMPLEMENT();
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */