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 contructors (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>
30 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
31 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
32 #include <com/sun/star/lang/XComponent.hpp>
33 #include <com/sun/star/lang/XServiceInfo.hpp>
34 #include <com/sun/star/reflection/XServiceConstructorDescription.hpp>
35 #include <com/sun/star/reflection/XServiceTypeDescription2.hpp>
36 #include <cppuhelper/exc_hlp.hxx>
37 #include <rtl/strbuf.hxx>
38 #include <test/bootstrapfixture.hxx>
39 #include <vcl/svapp.hxx>
43 OString
msg(OUString
const & string
) {
44 return OUStringToOString(string
, osl_getThreadTextEncoding());
47 OString
msg(css::uno::Sequence
<OUString
> const & strings
) {
48 OStringBuffer
buf("{");
49 for (sal_Int32 i
= 0; i
!= strings
.getLength(); ++i
) {
54 buf
.append(msg(strings
[i
]));
58 return buf
.makeStringAndClear();
61 bool unique(css::uno::Sequence
<OUString
> const & strings
) {
62 // Assumes small sequences for which quadratic algorithm is acceptable:
63 for (sal_Int32 i
= 0; i
< strings
.getLength() - 1; ++i
) {
64 for (sal_Int32 j
= i
+ 1; j
!= strings
.getLength(); ++j
) {
65 if (strings
[j
] == strings
[i
]) {
74 css::uno::Sequence
<OUString
> const & strings
, OUString
const & string
)
76 for (sal_Int32 i
= 0; i
!= strings
.getLength(); ++i
) {
77 if (string
== strings
[i
]) {
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 for (sal_Int32 i
= 0; i
!= strings2
.getLength(); ++i
) {
90 if (!contains(strings1
, strings2
[i
])) {
97 class Test
: public test::BootstrapFixture
{
101 CPPUNIT_TEST_SUITE(Test
);
103 CPPUNIT_TEST_SUITE_END();
107 OUString
const & name
, bool withArguments
,
108 OUString
const & implementationName
,
109 css::uno::Sequence
<OUString
> const & serviceNames
,
110 std::vector
<css::uno::Reference
<css::lang::XComponent
>> * components
);
114 // On Windows, blacklist the com.sun.star.comp.report.OReportDefinition
115 // implementation (reportdesign::OReportDefinition in
116 // reportdesign/source/core/api/ReportDefinition.cxx), as it spawns a thread
117 // that forever blocks in SendMessageW when no VCL event loop is running
118 // (reportdesign::<anon>::FactoryLoader::execute ->
119 // framework::Desktop::findFrame -> framework::TaskCreator::createTask ->
120 // <anon>::TaskCreatorService::createInstanceWithArguments ->
121 // <anon>::TaskCreatorService::impls_createContainerWindow ->
122 // <anon>::VCLXToolkit::createWindow ->
123 // <anon>::VCLXToolkit::ImplCreateWindow ->
124 // <anon>::VCLXToolkit::ImplCreateWindow -> WorkWindow::WorkWindow ->
125 // WorkWindow::ImplInit -> ImplBorderWindow::ImplBorderWindow ->
126 // ImplBorderWindow::ImplInit -> Window::ImplInit ->
127 // WinSalInstance::CreateFrame -> ImplSendMessage -> SendMessageW):
128 std::vector
<OUString
> blacklist
;
129 blacklist
.push_back("com.sun.star.comp.report.OReportDefinition");
131 // <https://bugs.documentfoundation.org/show_bug.cgi?id=89343>
132 // "~SwXMailMerge() goes into endless SwCache::Check()":
133 blacklist
.push_back("SwXMailMerge");
135 css::uno::Reference
<css::container::XContentEnumerationAccess
> enumAcc(
136 m_xContext
->getServiceManager(), css::uno::UNO_QUERY_THROW
);
137 css::uno::Reference
<css::container::XHierarchicalNameAccess
> typeMgr(
138 m_xContext
->getValueByName(
139 "/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
140 css::uno::UNO_QUERY_THROW
);
141 css::uno::Sequence
<OUString
> serviceNames(
142 m_xContext
->getServiceManager()->getAvailableServiceNames());
145 OUString
const & theServiceName
, bool theDefaultConstructor
):
146 serviceName(theServiceName
),
147 defaultConstructor(theDefaultConstructor
)
149 OUString serviceName
;
150 bool defaultConstructor
;
152 struct Implementation
{
154 css::uno::Reference
<css::lang::XServiceInfo
> const & theFactory
,
155 css::uno::Sequence
<OUString
> const & theServiceNames
):
156 factory(theFactory
), serviceNames(theServiceNames
),
157 accumulationBased(false)
159 css::uno::Reference
<css::lang::XServiceInfo
> const factory
;
160 css::uno::Sequence
<OUString
> const serviceNames
;
161 std::vector
<Constructor
> constructors
;
162 bool accumulationBased
;
164 std::map
<OUString
, Implementation
> impls
;
165 for (sal_Int32 i
= 0; i
!= serviceNames
.getLength(); ++i
) {
166 css::uno::Reference
<css::container::XEnumeration
> serviceImpls1(
167 enumAcc
->createContentEnumeration(serviceNames
[i
]),
168 css::uno::UNO_SET_THROW
);
169 std::vector
<css::uno::Reference
<css::lang::XServiceInfo
>> serviceImpls2
;
170 while (serviceImpls1
->hasMoreElements()) {
171 serviceImpls2
.push_back(
172 css::uno::Reference
<css::lang::XServiceInfo
>(
173 serviceImpls1
->nextElement(), css::uno::UNO_QUERY_THROW
));
175 css::uno::Reference
<css::reflection::XServiceTypeDescription2
> desc
;
176 if (typeMgr
->hasByHierarchicalName(serviceNames
[i
])) {
178 typeMgr
->getByHierarchicalName(serviceNames
[i
]),
179 css::uno::UNO_QUERY_THROW
);
181 if (serviceImpls2
.empty()) {
183 CPPUNIT_ASSERT_MESSAGE(
185 "no implementations of single-interface--based \""
186 + msg(serviceNames
[i
]) + "\"")
188 !desc
->isSingleInterfaceBased());
190 << "accumulation-based service \"" << serviceNames
[i
]
191 << "\" without implementations\n";
194 << "fantasy service name \"" << serviceNames
[i
]
195 << "\" without implementations\n";
198 for (auto const & j
: serviceImpls2
) {
199 OUString
name(j
->getImplementationName());
200 auto k
= impls
.find(name
);
201 if (k
== impls
.end()) {
202 css::uno::Sequence
<OUString
> servs(
203 j
->getSupportedServiceNames());
204 CPPUNIT_ASSERT_MESSAGE(
206 "implementation \"" + msg(name
)
207 + "\" supports non-unique " + msg(servs
))
211 std::make_pair(name
, Implementation(j
, servs
)))
214 CPPUNIT_ASSERT_MESSAGE(
216 "multiple implementations named \"" + msg(name
)
219 j
== k
->second
.factory
);
221 CPPUNIT_ASSERT_MESSAGE(
223 "implementation \"" + msg(name
) + "\" supports "
224 + msg(k
->second
.serviceNames
) + " but not \""
225 + msg(serviceNames
[i
]) + "\"")
227 contains(k
->second
.serviceNames
, serviceNames
[i
]));
229 if (desc
->isSingleInterfaceBased()) {
230 if (serviceImpls2
.size() == 1) {
233 css::reflection::XServiceConstructorDescription
>>
234 ctors(desc
->getConstructors());
235 for (sal_Int32 l
= 0; l
!= ctors
.getLength(); ++l
) {
236 if (!ctors
[l
]->getParameters().hasElements()) {
237 k
->second
.constructors
.push_back(
240 ctors
[l
]->isDefaultConstructor()));
246 k
->second
.accumulationBased
= true;
250 << "implementation \"" << name
251 << "\" supports fantasy service name \""
252 << serviceNames
[i
] << "\"\n";
257 std::vector
<css::uno::Reference
<css::lang::XComponent
>> comps
;
258 for (auto const & i
: impls
) {
259 if (std::find(blacklist
.begin(), blacklist
.end(), i
.first
)
262 if (i
.second
.constructors
.empty()) {
263 if (i
.second
.accumulationBased
) {
265 i
.first
, false, i
.first
, i
.second
.serviceNames
, &comps
);
268 << "no obvious way to instantiate implementation \""
269 << i
.first
<< "\"\n";
272 for (auto const & j
: i
.second
.constructors
) {
274 j
.serviceName
, !j
.defaultConstructor
, i
.first
,
275 i
.second
.serviceNames
, &comps
);
280 SolarMutexReleaser rel
;
281 for (auto const & i
: comps
) {
286 void Test::createInstance(
287 OUString
const & name
, bool withArguments
,
288 OUString
const & implementationName
,
289 css::uno::Sequence
<OUString
> const & serviceNames
,
290 std::vector
<css::uno::Reference
<css::lang::XComponent
>> * components
)
292 assert(components
!= nullptr);
293 css::uno::Reference
<css::uno::XInterface
> inst
;
296 inst
= m_xContext
->getServiceManager()
297 ->createInstanceWithArgumentsAndContext(
298 name
, css::uno::Sequence
<css::uno::Any
>(), m_xContext
);
300 inst
= m_xContext
->getServiceManager()->createInstanceWithContext(
303 } catch (css::uno::Exception
& e
) {
304 css::uno::Any
a(cppu::getCaughtException());
307 "instantiating \"" + msg(implementationName
) + "\" via \""
308 + msg(name
) + "\" caused " + msg(a
.getValueTypeName()) + " \""
309 + msg(e
.Message
) + "\"")
312 CPPUNIT_ASSERT_MESSAGE(
314 "instantiating \"" + msg(implementationName
) + "\" via \""
315 + msg(name
) + "\" returned null reference")
318 css::uno::Reference
<css::lang::XComponent
> comp(inst
, css::uno::UNO_QUERY
);
320 components
->push_back(comp
);
322 css::uno::Reference
<css::lang::XServiceInfo
> info(
323 inst
, css::uno::UNO_QUERY
);
324 CPPUNIT_ASSERT_MESSAGE(
326 "instantiating \"" + msg(implementationName
) + "\" via \""
327 + msg(name
) + "\" does not provide XServiceInfo")
330 OUString
expImpl(implementationName
);
331 css::uno::Sequence
<OUString
> expServs(serviceNames
);
333 if (name
== "com.sun.star.comp.configuration.ConfigurationProvider") {
334 // Instantiating a ConfigurationProvider with no or empty args must
335 // return theDefaultProvider:
336 expImpl
= "com.sun.star.comp.configuration.DefaultProvider";
337 expServs
= {"com.sun.star.configuration.DefaultProvider"};
338 } else if (name
== "com.sun.star.datatransfer.clipboard.SystemClipboard") {
339 // SystemClipboard is a wrapper returning either a platform-specific or
340 // the generic VCLGenericClipboard:
342 expImpl
= "com.sun.star.datatransfer.clipboard.ClipboardW32";
344 expImpl
= "com.sun.star.datatransfer.VCLGenericClipboard";
347 } else if (name
== "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"
348 || name
== "com.sun.star.datatransfer.dnd.XdndSupport")
350 expImpl
= "com.sun.star.datatransfer.dnd.VclGenericDragSource";
351 expServs
= {"com.sun.star.datatransfer.dnd.GenericDragSource"};
352 } else if (name
== "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"
353 || name
== "com.sun.star.datatransfer.dnd.XdndDropTarget")
355 expImpl
= "com.sun.star.datatransfer.dnd.VclGenericDropTarget";
356 expServs
= {"com.sun.star.datatransfer.dnd.GenericDropTarget"};
358 } else if (name
== "com.sun.star.ui.dialogs.FolderPicker") {
359 // FolderPicker is a wrapper returning either a platform-specific or the
360 // generic OfficeFolderPicker:
362 expImpl
= "com.sun.star.ui.dialogs.Win32FolderPicker";
363 expServs
= {"com.sun.star.ui.dialogs.SystemFolderPicker"};
365 expImpl
= "com.sun.star.svtools.OfficeFolderPicker";
366 expServs
= {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
368 } else if (expImpl
== "com.sun.star.comp.Calc.SpreadsheetDocument") {
369 expImpl
= "ScModelObj";
370 } else if (expImpl
== "com.sun.star.comp.Draw.DrawingDocument"
371 || expImpl
== "com.sun.star.comp.Draw.PresentationDocument")
373 expImpl
= "SdXImpressDocument";
374 } else if (expImpl
== "com.sun.star.comp.Writer.GlobalDocument"
375 || expImpl
== "com.sun.star.comp.Writer.TextDocument"
376 || expImpl
== "com.sun.star.comp.Writer.WebDocument")
378 expImpl
= "SwXTextDocument";
380 CPPUNIT_ASSERT_EQUAL_MESSAGE(
382 "instantiating \"" + msg(implementationName
) + "\" via \""
383 + msg(name
) + "\" reports wrong implementation name")
385 expImpl
, info
->getImplementationName());
386 css::uno::Sequence
<OUString
> servs(info
->getSupportedServiceNames());
387 CPPUNIT_ASSERT_MESSAGE(
389 "instantiating \"" + msg(implementationName
) + "\" via \""
390 + msg(name
) + "\" reports non-unique " + msg(servs
))
393 // Some implementations like "com.sun.star.comp.Calc.SpreadsheetDocument"
394 // report sub-services like
395 // "com.sun.star.sheet.SpreadsheetDocumentSettings", and
396 // "com.sun.star.document.OfficeDocument" that are not listed in the
397 // .component file, so check for containment instead of equality:
398 CPPUNIT_ASSERT_MESSAGE(
400 "instantiating \"" + msg(implementationName
) + "\" via \""
401 + msg(name
) + "\" reports " + msg(servs
) + " different from "
404 contains(servs
, expServs
));
407 CPPUNIT_TEST_SUITE_REGISTRATION(Test
);
411 CPPUNIT_PLUGIN_IMPLEMENT();
413 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */