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>
31 #include <com/sun/star/beans/PropertyAttribute.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/beans/XPropertySetInfo.hpp>
34 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
35 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
36 #include <com/sun/star/lang/XComponent.hpp>
37 #include <com/sun/star/lang/XServiceInfo.hpp>
38 #include <com/sun/star/reflection/XServiceConstructorDescription.hpp>
39 #include <com/sun/star/reflection/XServiceTypeDescription2.hpp>
40 #include <comphelper/sequence.hxx>
41 #include <cppuhelper/exc_hlp.hxx>
42 #include <rtl/strbuf.hxx>
43 #include <test/bootstrapfixture.hxx>
44 #include <vcl/svapp.hxx>
48 OString
msg(OUString
const & string
) {
49 return OUStringToOString(string
, osl_getThreadTextEncoding());
52 OString
msg(css::uno::Sequence
<OUString
> const & strings
) {
53 OStringBuffer
buf("{");
54 for (sal_Int32 i
= 0; i
!= strings
.getLength(); ++i
) {
59 buf
.append(msg(strings
[i
]));
63 return buf
.makeStringAndClear();
66 bool unique(css::uno::Sequence
<OUString
> const & strings
) {
67 // Assumes small sequences for which quadratic algorithm is acceptable:
68 for (sal_Int32 i
= 0; i
< strings
.getLength() - 1; ++i
) {
69 for (sal_Int32 j
= i
+ 1; j
!= strings
.getLength(); ++j
) {
70 if (strings
[j
] == strings
[i
]) {
79 css::uno::Sequence
<OUString
> const & strings
, OUString
const & string
)
81 return comphelper::findValue(strings
, string
) != -1;
85 css::uno::Sequence
<OUString
> const & strings1
,
86 css::uno::Sequence
<OUString
> const & strings2
)
88 // Assumes small sequences for which quadratic algorithm is acceptable:
89 return std::all_of(strings2
.begin(), strings2
.end(),
90 [&strings1
](const OUString
& rStr
) { return contains(strings1
, rStr
); });
94 css::uno::Reference
<css::reflection::XServiceTypeDescription
> const & service
,
95 std::set
<css::uno::Reference
<css::reflection::XServiceTypeDescription
>> * allServices
)
97 assert(allServices
!= nullptr);
98 if (!allServices
->insert(service
).second
) {
101 const auto aMandatoryServices
= service
->getMandatoryServices();
102 for (auto const & serv
: aMandatoryServices
) {
103 addService(serv
, allServices
);
107 class Test
: public test::BootstrapFixture
{
111 CPPUNIT_TEST_SUITE(Test
);
113 CPPUNIT_TEST_SUITE_END();
117 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & typeManager
,
118 OUString
const & name
, bool withArguments
,
119 OUString
const & implementationName
,
120 css::uno::Sequence
<OUString
> const & serviceNames
,
121 std::vector
<css::uno::Reference
<css::lang::XComponent
>> * components
);
125 // On Windows, blacklist the com.sun.star.comp.report.OReportDefinition
126 // implementation (reportdesign::OReportDefinition in
127 // reportdesign/source/core/api/ReportDefinition.cxx), as it spawns a thread
128 // that forever blocks in SendMessageW when no VCL event loop is running
129 // (reportdesign::<anon>::FactoryLoader::execute ->
130 // framework::Desktop::findFrame -> framework::TaskCreator::createTask ->
131 // <anon>::TaskCreatorService::createInstanceWithArguments ->
132 // <anon>::TaskCreatorService::impls_createContainerWindow ->
133 // <anon>::VCLXToolkit::createWindow ->
134 // <anon>::VCLXToolkit::ImplCreateWindow ->
135 // <anon>::VCLXToolkit::ImplCreateWindow -> WorkWindow::WorkWindow ->
136 // WorkWindow::ImplInit -> ImplBorderWindow::ImplBorderWindow ->
137 // ImplBorderWindow::ImplInit -> Window::ImplInit ->
138 // WinSalInstance::CreateFrame -> ImplSendMessage -> SendMessageW):
139 std::vector
<OUString
> blacklist
;
140 blacklist
.emplace_back("com.sun.star.comp.report.OReportDefinition");
142 // <https://bugs.documentfoundation.org/show_bug.cgi?id=89343>
143 // "~SwXMailMerge() goes into endless SwCache::Check()":
144 blacklist
.emplace_back("SwXMailMerge");
146 css::uno::Reference
<css::container::XContentEnumerationAccess
> enumAcc(
147 m_xContext
->getServiceManager(), css::uno::UNO_QUERY_THROW
);
148 css::uno::Reference
<css::container::XHierarchicalNameAccess
> typeMgr(
149 m_xContext
->getValueByName(
150 "/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
151 css::uno::UNO_QUERY_THROW
);
152 const css::uno::Sequence
<OUString
> serviceNames(
153 m_xContext
->getServiceManager()->getAvailableServiceNames());
156 OUString
const & theServiceName
, bool theDefaultConstructor
):
157 serviceName(theServiceName
),
158 defaultConstructor(theDefaultConstructor
)
160 OUString
const serviceName
;
161 bool const defaultConstructor
;
163 struct Implementation
{
165 css::uno::Reference
<css::lang::XServiceInfo
> const & theFactory
,
166 css::uno::Sequence
<OUString
> const & theServiceNames
):
167 factory(theFactory
), serviceNames(theServiceNames
),
168 accumulationBased(false)
170 css::uno::Reference
<css::lang::XServiceInfo
> const factory
;
171 css::uno::Sequence
<OUString
> const serviceNames
;
172 std::vector
<Constructor
> constructors
;
173 bool accumulationBased
;
175 std::map
<OUString
, Implementation
> impls
;
176 for (const auto& rServiceName
: serviceNames
) {
177 css::uno::Reference
<css::container::XEnumeration
> serviceImpls1(
178 enumAcc
->createContentEnumeration(rServiceName
),
179 css::uno::UNO_SET_THROW
);
180 std::vector
<css::uno::Reference
<css::lang::XServiceInfo
>> serviceImpls2
;
181 while (serviceImpls1
->hasMoreElements()) {
182 serviceImpls2
.emplace_back(
183 serviceImpls1
->nextElement(), css::uno::UNO_QUERY_THROW
);
185 css::uno::Reference
<css::reflection::XServiceTypeDescription2
> desc
;
186 if (typeMgr
->hasByHierarchicalName(rServiceName
)) {
188 typeMgr
->getByHierarchicalName(rServiceName
),
189 css::uno::UNO_QUERY_THROW
);
191 if (serviceImpls2
.empty()) {
193 CPPUNIT_ASSERT_MESSAGE(
195 "no implementations of single-interface--based \""
196 + msg(rServiceName
) + "\"")
198 !desc
->isSingleInterfaceBased());
200 << "accumulation-based service \"" << rServiceName
201 << "\" without implementations\n";
204 << "fantasy service name \"" << rServiceName
205 << "\" without implementations\n";
208 for (auto const & j
: serviceImpls2
) {
209 OUString
name(j
->getImplementationName());
210 auto k
= impls
.find(name
);
211 if (k
== impls
.end()) {
212 css::uno::Sequence
<OUString
> servs(
213 j
->getSupportedServiceNames());
214 CPPUNIT_ASSERT_MESSAGE(
216 "implementation \"" + msg(name
)
217 + "\" supports non-unique " + msg(servs
))
221 std::make_pair(name
, Implementation(j
, servs
)))
224 CPPUNIT_ASSERT_MESSAGE(
226 "multiple implementations named \"" + msg(name
)
229 bool(j
== k
->second
.factory
));
231 CPPUNIT_ASSERT_MESSAGE(
233 "implementation \"" + msg(name
) + "\" supports "
234 + msg(k
->second
.serviceNames
) + " but not \""
235 + msg(rServiceName
) + "\"")
237 contains(k
->second
.serviceNames
, rServiceName
));
239 if (desc
->isSingleInterfaceBased()) {
240 if (serviceImpls2
.size() == 1) {
241 const css::uno::Sequence
<
243 css::reflection::XServiceConstructorDescription
>>
244 ctors(desc
->getConstructors());
245 auto pCtor
= std::find_if(ctors
.begin(), ctors
.end(),
246 [](const auto& rCtor
) { return !rCtor
->getParameters().hasElements(); });
247 if (pCtor
!= ctors
.end())
248 k
->second
.constructors
.emplace_back(
250 (*pCtor
)->isDefaultConstructor());
253 k
->second
.accumulationBased
= true;
257 << "implementation \"" << name
258 << "\" supports fantasy service name \""
259 << rServiceName
<< "\"\n";
264 std::vector
<css::uno::Reference
<css::lang::XComponent
>> comps
;
265 for (auto const & i
: impls
) {
266 if (std::find(blacklist
.begin(), blacklist
.end(), i
.first
)
269 if (i
.second
.constructors
.empty()) {
270 if (i
.second
.accumulationBased
) {
272 typeMgr
, i
.first
, false, i
.first
, i
.second
.serviceNames
, &comps
);
275 << "no obvious way to instantiate implementation \""
276 << i
.first
<< "\"\n";
279 for (auto const & j
: i
.second
.constructors
) {
281 typeMgr
, j
.serviceName
, !j
.defaultConstructor
, i
.first
,
282 i
.second
.serviceNames
, &comps
);
287 SolarMutexReleaser rel
;
288 for (auto const & i
: comps
) {
293 void Test::createInstance(
294 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & typeManager
,
295 OUString
const & name
, bool withArguments
,
296 OUString
const & implementationName
,
297 css::uno::Sequence
<OUString
> const & serviceNames
,
298 std::vector
<css::uno::Reference
<css::lang::XComponent
>> * components
)
300 assert(components
!= nullptr);
301 css::uno::Reference
<css::uno::XInterface
> inst
;
304 inst
= m_xContext
->getServiceManager()
305 ->createInstanceWithArgumentsAndContext(
306 name
, css::uno::Sequence
<css::uno::Any
>(), m_xContext
);
308 inst
= m_xContext
->getServiceManager()->createInstanceWithContext(
311 } catch (css::uno::Exception
& e
) {
312 css::uno::Any
a(cppu::getCaughtException());
315 "instantiating \"" + msg(implementationName
) + "\" via \""
316 + msg(name
) + "\" caused " + msg(a
.getValueTypeName()) + " \""
317 + msg(e
.Message
) + "\"")
320 CPPUNIT_ASSERT_MESSAGE(
322 "instantiating \"" + msg(implementationName
) + "\" via \""
323 + msg(name
) + "\" returned null reference")
326 css::uno::Reference
<css::lang::XComponent
> comp(inst
, css::uno::UNO_QUERY
);
328 components
->push_back(comp
);
330 css::uno::Reference
<css::lang::XServiceInfo
> info(
331 inst
, css::uno::UNO_QUERY
);
332 CPPUNIT_ASSERT_MESSAGE(
334 "instantiating \"" + msg(implementationName
) + "\" via \""
335 + msg(name
) + "\" does not provide XServiceInfo")
338 OUString
expImpl(implementationName
);
339 css::uno::Sequence
<OUString
> expServs(serviceNames
);
341 if (name
== "com.sun.star.comp.configuration.ConfigurationProvider") {
342 // Instantiating a ConfigurationProvider with no or empty args must
343 // return theDefaultProvider:
344 expImpl
= "com.sun.star.comp.configuration.DefaultProvider";
345 expServs
= {"com.sun.star.configuration.DefaultProvider"};
346 } else if (name
== "com.sun.star.datatransfer.clipboard.SystemClipboard") {
347 // SystemClipboard is a wrapper returning either a platform-specific or
348 // the generic VCLGenericClipboard:
350 expImpl
= "com.sun.star.datatransfer.clipboard.ClipboardW32";
352 expImpl
= "com.sun.star.datatransfer.VCLGenericClipboard";
355 } else if (name
== "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"
356 || name
== "com.sun.star.datatransfer.dnd.XdndSupport")
358 expImpl
= "com.sun.star.datatransfer.dnd.VclGenericDragSource";
359 expServs
= {"com.sun.star.datatransfer.dnd.GenericDragSource"};
360 } else if (name
== "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"
361 || name
== "com.sun.star.datatransfer.dnd.XdndDropTarget")
363 expImpl
= "com.sun.star.datatransfer.dnd.VclGenericDropTarget";
364 expServs
= {"com.sun.star.datatransfer.dnd.GenericDropTarget"};
366 } else if (name
== "com.sun.star.ui.dialogs.FolderPicker") {
367 // FolderPicker is a wrapper returning either a platform-specific or the
368 // generic OfficeFolderPicker:
370 expImpl
= "com.sun.star.ui.dialogs.Win32FolderPicker";
371 expServs
= {"com.sun.star.ui.dialogs.SystemFolderPicker"};
373 expImpl
= "com.sun.star.svtools.OfficeFolderPicker";
374 expServs
= {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
376 } else if (expImpl
== "com.sun.star.comp.Calc.SpreadsheetDocument") {
377 expImpl
= "ScModelObj";
378 } else if (expImpl
== "com.sun.star.comp.Draw.DrawingDocument"
379 || expImpl
== "com.sun.star.comp.Draw.PresentationDocument")
381 expImpl
= "SdXImpressDocument";
382 } else if (expImpl
== "com.sun.star.comp.Writer.GlobalDocument"
383 || expImpl
== "com.sun.star.comp.Writer.TextDocument"
384 || expImpl
== "com.sun.star.comp.Writer.WebDocument")
386 expImpl
= "SwXTextDocument";
388 CPPUNIT_ASSERT_EQUAL_MESSAGE(
390 "instantiating \"" + msg(implementationName
) + "\" via \""
391 + msg(name
) + "\" reports wrong implementation name")
393 expImpl
, info
->getImplementationName());
394 const css::uno::Sequence
<OUString
> servs(info
->getSupportedServiceNames());
395 CPPUNIT_ASSERT_MESSAGE(
397 "instantiating \"" + msg(implementationName
) + "\" via \""
398 + msg(name
) + "\" reports non-unique " + msg(servs
))
401 // Some implementations like "com.sun.star.comp.Calc.SpreadsheetDocument"
402 // report sub-services like
403 // "com.sun.star.sheet.SpreadsheetDocumentSettings", and
404 // "com.sun.star.document.OfficeDocument" that are not listed in the
405 // .component file, so check for containment instead of equality:
406 CPPUNIT_ASSERT_MESSAGE(
408 "instantiating \"" + msg(implementationName
) + "\" via \""
409 + msg(name
) + "\" reports " + msg(servs
) + " different from "
412 contains(servs
, expServs
));
413 std::set
<css::uno::Reference
<css::reflection::XServiceTypeDescription
>> allservs
;
414 for (auto const & serv
: servs
) {
415 if (!typeManager
->hasByHierarchicalName(serv
)) {
417 << "instantiating \"" << implementationName
<< "\" via \"" << name
418 << "\" supports fantasy service name \"" << serv
<< "\"\n";
422 css::uno::Reference
<css::reflection::XServiceTypeDescription
>(
423 typeManager
->getByHierarchicalName(serv
), css::uno::UNO_QUERY_THROW
),
426 css::uno::Reference
<css::beans::XPropertySetInfo
> propsinfo
;
427 for (auto const & serv
: allservs
) {
428 auto const props
= serv
->getProperties();
429 for (auto const & prop
: props
) {
431 = (prop
->getPropertyFlags() & css::beans::PropertyAttribute::OPTIONAL
) != 0;
432 if (!propsinfo
.is()) {
433 css::uno::Reference
<css::beans::XPropertySet
> propset(inst
, css::uno::UNO_QUERY
);
435 CPPUNIT_ASSERT_MESSAGE(
437 "instantiating \"" + msg(implementationName
) + "\" via \"" + msg(name
)
438 + "\" reports service " + msg(serv
->getName())
439 + " with non-optional property \"" + msg(prop
->getName())
440 + "\" but does not implement css.uno.XPropertySet")
445 propsinfo
= propset
->getPropertySetInfo();
446 if (!propsinfo
.is()) {
447 //TODO: legal to return null in more cases? ("@returns NULL if the
448 // implementation cannot or will not provide information about the properties")
449 CPPUNIT_ASSERT_MESSAGE(
451 "instantiating \"" + msg(implementationName
) + "\" via \"" + msg(name
)
452 + "\" reports service " + msg(serv
->getName())
453 + " with non-optional property \"" + msg(prop
->getName())
454 + "\" but css.uno.XPropertySet::getPropertySetInfo returns null")
460 if (!propsinfo
->hasPropertyByName(prop
->getName())) {
461 static std::set
<std::pair
<OUString
, OUString
>> const blacklist
{
462 {"com.sun.star.comp.chart.DataSeries", "BorderDash"},
463 {"com.sun.star.comp.chart2.ChartDocumentWrapper", "UserDefinedAttributes"},
464 {"com.sun.star.comp.dbu.OColumnControlModel", "Tabstop"},
465 {"com.sun.star.comp.report.OFormattedField", "Align"},
466 {"com.sun.star.comp.report.OFormattedField", "BackgroundColor"},
467 {"com.sun.star.comp.report.OFormattedField", "Border"},
468 {"com.sun.star.comp.report.OFormattedField", "DefaultControl"},
469 {"com.sun.star.comp.report.OFormattedField", "EffectiveDefault"},
470 {"com.sun.star.comp.report.OFormattedField", "EffectiveMax"},
471 {"com.sun.star.comp.report.OFormattedField", "EffectiveMin"},
472 {"com.sun.star.comp.report.OFormattedField", "EffectiveValue"},
473 {"com.sun.star.comp.report.OFormattedField", "Enabled"},
474 {"com.sun.star.comp.report.OFormattedField", "FontEmphasisMark"},
475 {"com.sun.star.comp.report.OFormattedField", "FontRelief"},
476 {"com.sun.star.comp.report.OFormattedField", "HelpText"},
477 {"com.sun.star.comp.report.OFormattedField", "HelpURL"},
478 {"com.sun.star.comp.report.OFormattedField", "MaxTextLen"},
479 {"com.sun.star.comp.report.OFormattedField", "Printable"},
480 {"com.sun.star.comp.report.OFormattedField", "ReadOnly"},
481 {"com.sun.star.comp.report.OFormattedField", "Spin"},
482 {"com.sun.star.comp.report.OFormattedField", "Tabstop"},
483 {"com.sun.star.comp.report.OFormattedField", "Text"},
484 {"com.sun.star.comp.report.OFormattedField", "TextColor"},
485 {"com.sun.star.comp.report.OFormattedField", "TextLineColor"},
486 {"com.sun.star.comp.report.OFormattedField", "TreatAsNumber"},
487 {"stardiv.Toolkit.UnoControlRoadmapModel", "Interactive"}};
488 if (blacklist
.find({implementationName
, prop
->getName()}) != blacklist
.end()) {
491 CPPUNIT_ASSERT_MESSAGE(
493 "instantiating \"" + msg(implementationName
) + "\" via \"" + msg(name
)
494 + "\" reports service " + msg(serv
->getName())
495 + " with non-optional property \"" + msg(prop
->getName())
496 + ("\" but css.uno.XPropertySet::getPropertySetInfo's hasPropertyByName"
505 CPPUNIT_TEST_SUITE_REGISTRATION(Test
);
509 CPPUNIT_PLUGIN_IMPLEMENT();
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */