Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : */
9 :
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).
20 :
21 : #include <sal/config.h>
22 :
23 : #include <algorithm>
24 : #include <cassert>
25 : #include <iostream>
26 : #include <map>
27 : #include <utility>
28 : #include <vector>
29 :
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>
40 :
41 : namespace {
42 :
43 16803 : OString msg(OUString const & string) {
44 16803 : return OUStringToOString(string, osl_getThreadTextEncoding());
45 : }
46 :
47 4168 : OString msg(css::uno::Sequence<OUString> const & strings) {
48 4168 : OStringBuffer buf("{");
49 11020 : for (sal_Int32 i = 0; i != strings.getLength(); ++i) {
50 6852 : if (i != 0) {
51 2684 : buf.append(", ");
52 : }
53 6852 : buf.append('"');
54 6852 : buf.append(msg(strings[i]));
55 6852 : buf.append('"');
56 : }
57 4168 : buf.append('}');
58 4168 : return buf.makeStringAndClear();
59 : }
60 :
61 1643 : bool unique(css::uno::Sequence<OUString> const & strings) {
62 : // Assumes small sequences for which quadratic algorithm is acceptable:
63 2572 : for (sal_Int32 i = 0; i < strings.getLength() - 1; ++i) {
64 3597 : for (sal_Int32 j = i + 1; j != strings.getLength(); ++j) {
65 2668 : if (strings[j] == strings[i]) {
66 0 : return false;
67 : }
68 : }
69 : }
70 1643 : return true;
71 : }
72 :
73 2207 : bool contains(
74 : css::uno::Sequence<OUString> const & strings, OUString const & string)
75 : {
76 3887 : for (sal_Int32 i = 0; i != strings.getLength(); ++i) {
77 3887 : if (string == strings[i]) {
78 2207 : return true;
79 : }
80 : }
81 0 : return false;
82 : }
83 :
84 594 : bool contains(
85 : css::uno::Sequence<OUString> const & strings1,
86 : css::uno::Sequence<OUString> const & strings2)
87 : {
88 : // Assumes small sequences for which quadratic algorithm is acceptable:
89 1464 : for (sal_Int32 i = 0; i != strings2.getLength(); ++i) {
90 870 : if (!contains(strings1, strings2[i])) {
91 0 : return false;
92 : }
93 : }
94 594 : return true;
95 : }
96 :
97 3 : class Test: public test::BootstrapFixture {
98 : public:
99 : void test();
100 :
101 2 : CPPUNIT_TEST_SUITE(Test);
102 1 : CPPUNIT_TEST(test);
103 5 : CPPUNIT_TEST_SUITE_END();
104 :
105 : private:
106 : void createInstance(
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);
111 : };
112 :
113 1 : void Test::test() {
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 1 : std::vector<OUString> blacklist;
129 1 : blacklist.push_back("com.sun.star.comp.report.OReportDefinition");
130 :
131 : // <https://bugs.documentfoundation.org/show_bug.cgi?id=89343>
132 : // "~SwXMailMerge() goes into endless SwCache::Check()":
133 1 : blacklist.push_back("SwXMailMerge");
134 :
135 : css::uno::Reference<css::container::XContentEnumerationAccess> enumAcc(
136 2 : m_xContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
137 : css::uno::Reference<css::container::XHierarchicalNameAccess> typeMgr(
138 1 : m_xContext->getValueByName(
139 1 : "/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
140 2 : css::uno::UNO_QUERY_THROW);
141 : css::uno::Sequence<OUString> serviceNames(
142 2 : m_xContext->getServiceManager()->getAvailableServiceNames());
143 725 : struct Constructor {
144 239 : Constructor(
145 : OUString const & theServiceName, bool theDefaultConstructor):
146 : serviceName(theServiceName),
147 239 : defaultConstructor(theDefaultConstructor)
148 239 : {}
149 : OUString serviceName;
150 : bool defaultConstructor;
151 : };
152 5245 : struct Implementation {
153 1049 : Implementation(
154 : css::uno::Reference<css::lang::XServiceInfo> const & theFactory,
155 : css::uno::Sequence<OUString> const & theServiceNames):
156 : factory(theFactory), serviceNames(theServiceNames),
157 1049 : accumulationBased(false)
158 1049 : {}
159 : css::uno::Reference<css::lang::XServiceInfo> const factory;
160 : css::uno::Sequence<OUString> const serviceNames;
161 : std::vector<Constructor> constructors;
162 : bool accumulationBased;
163 : };
164 2 : std::map<OUString, Implementation> impls;
165 989 : for (sal_Int32 i = 0; i != serviceNames.getLength(); ++i) {
166 : css::uno::Reference<css::container::XEnumeration> serviceImpls1(
167 988 : enumAcc->createContentEnumeration(serviceNames[i]),
168 988 : css::uno::UNO_SET_THROW);
169 1976 : std::vector<css::uno::Reference<css::lang::XServiceInfo>> serviceImpls2;
170 3313 : while (serviceImpls1->hasMoreElements()) {
171 : serviceImpls2.push_back(
172 : css::uno::Reference<css::lang::XServiceInfo>(
173 1337 : serviceImpls1->nextElement(), css::uno::UNO_QUERY_THROW));
174 : }
175 1976 : css::uno::Reference<css::reflection::XServiceTypeDescription2> desc;
176 988 : if (typeMgr->hasByHierarchicalName(serviceNames[i])) {
177 : desc.set(
178 590 : typeMgr->getByHierarchicalName(serviceNames[i]),
179 590 : css::uno::UNO_QUERY_THROW);
180 : }
181 988 : if (serviceImpls2.empty()) {
182 0 : if (desc.is()) {
183 0 : CPPUNIT_ASSERT_MESSAGE(
184 : (OString(
185 : "no implementations of single-interface--based \""
186 : + msg(serviceNames[i]) + "\"")
187 : .getStr()),
188 0 : !desc->isSingleInterfaceBased());
189 : std::cout
190 0 : << "accumulation-based service \"" << serviceNames[i]
191 0 : << "\" without implementations\n";
192 : } else {
193 : std::cout
194 0 : << "fantasy service name \"" << serviceNames[i]
195 0 : << "\" without implementations\n";
196 : }
197 : } else {
198 2325 : for (auto const & j: serviceImpls2) {
199 1337 : OUString name(j->getImplementationName());
200 1337 : auto k = impls.find(name);
201 1337 : if (k == impls.end()) {
202 : css::uno::Sequence<OUString> servs(
203 1049 : j->getSupportedServiceNames());
204 2098 : CPPUNIT_ASSERT_MESSAGE(
205 : (OString(
206 : "implementation \"" + msg(name)
207 : + "\" supports non-unique " + msg(servs))
208 : .getStr()),
209 1049 : unique(servs));
210 : k = impls.insert(
211 2098 : std::make_pair(name, Implementation(j, servs)))
212 2098 : .first;
213 : } else {
214 576 : CPPUNIT_ASSERT_MESSAGE(
215 : (OString(
216 : "multiple implementations named \"" + msg(name)
217 : + "\"")
218 : .getStr()),
219 288 : j == k->second.factory);
220 : }
221 2674 : CPPUNIT_ASSERT_MESSAGE(
222 : (OString(
223 : "implementation \"" + msg(name) + "\" supports "
224 : + msg(k->second.serviceNames) + " but not \""
225 : + msg(serviceNames[i]) + "\"")
226 : .getStr()),
227 1337 : contains(k->second.serviceNames, serviceNames[i]));
228 1337 : if (desc.is()) {
229 813 : if (desc->isSingleInterfaceBased()) {
230 322 : if (serviceImpls2.size() == 1) {
231 : css::uno::Sequence<
232 : css::uno::Reference<
233 : css::reflection::XServiceConstructorDescription>>
234 310 : ctors(desc->getConstructors());
235 392 : for (sal_Int32 l = 0; l != ctors.getLength(); ++l) {
236 321 : if (!ctors[l]->getParameters().hasElements()) {
237 239 : k->second.constructors.push_back(
238 : Constructor(
239 239 : serviceNames[i],
240 717 : ctors[l]->isDefaultConstructor()));
241 239 : break;
242 : }
243 310 : }
244 : }
245 : } else {
246 491 : k->second.accumulationBased = true;
247 : }
248 : } else {
249 : std::cout
250 524 : << "implementation \"" << name
251 524 : << "\" supports fantasy service name \""
252 1048 : << serviceNames[i] << "\"\n";
253 : }
254 1337 : }
255 : }
256 988 : }
257 2 : std::vector<css::uno::Reference<css::lang::XComponent>> comps;
258 1050 : for (auto const & i: impls) {
259 3147 : if (std::find(blacklist.begin(), blacklist.end(), i.first)
260 3147 : == blacklist.end())
261 : {
262 1047 : if (i.second.constructors.empty()) {
263 813 : if (i.second.accumulationBased) {
264 : createInstance(
265 356 : i.first, false, i.first, i.second.serviceNames, &comps);
266 : } else {
267 : std::cout
268 457 : << "no obvious way to instantiate implementation \""
269 914 : << i.first << "\"\n";
270 : }
271 : } else {
272 472 : for (auto const & j: i.second.constructors) {
273 : createInstance(
274 238 : j.serviceName, !j.defaultConstructor, i.first,
275 476 : i.second.serviceNames, &comps);
276 : }
277 : }
278 : }
279 : }
280 2 : SolarMutexReleaser rel;
281 260 : for (auto const & i: comps) {
282 259 : i->dispose();
283 1 : }
284 1 : }
285 :
286 594 : 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)
291 : {
292 : assert(components != nullptr);
293 594 : css::uno::Reference<css::uno::XInterface> inst;
294 : try {
295 594 : if (withArguments) {
296 75 : inst = m_xContext->getServiceManager()
297 50 : ->createInstanceWithArgumentsAndContext(
298 50 : name, css::uno::Sequence<css::uno::Any>(), m_xContext);
299 : } else {
300 1707 : inst = m_xContext->getServiceManager()->createInstanceWithContext(
301 1138 : name, m_xContext);
302 : }
303 0 : } catch (css::uno::Exception & e) {
304 0 : css::uno::Any a(cppu::getCaughtException());
305 0 : CPPUNIT_FAIL(
306 : OString(
307 : "instantiating \"" + msg(implementationName) + "\" via \""
308 : + msg(name) + "\" caused " + msg(a.getValueTypeName()) + " \""
309 : + msg(e.Message) + "\"")
310 0 : .getStr());
311 : }
312 1188 : CPPUNIT_ASSERT_MESSAGE(
313 : (OString(
314 : "instantiating \"" + msg(implementationName) + "\" via \""
315 : + msg(name) + "\" returned null reference")
316 : .getStr()),
317 594 : inst.is());
318 1188 : css::uno::Reference<css::lang::XComponent> comp(inst, css::uno::UNO_QUERY);
319 594 : if (comp.is()) {
320 259 : components->push_back(comp);
321 : }
322 : css::uno::Reference<css::lang::XServiceInfo> info(
323 1188 : inst, css::uno::UNO_QUERY);
324 1188 : CPPUNIT_ASSERT_MESSAGE(
325 : (OString(
326 : "instantiating \"" + msg(implementationName) + "\" via \""
327 : + msg(name) + "\" does not provide XServiceInfo")
328 : .getStr()),
329 594 : info.is());
330 1188 : OUString expImpl(implementationName);
331 1188 : css::uno::Sequence<OUString> expServs(serviceNames);
332 : // Special cases:
333 594 : if (name == "com.sun.star.comp.configuration.ConfigurationProvider") {
334 : // Instantiating a ConfigurationProvider with no or empty args must
335 : // return theDefaultProvider:
336 1 : expImpl = "com.sun.star.comp.configuration.DefaultProvider";
337 1 : expServs = {"com.sun.star.configuration.DefaultProvider"};
338 593 : } else if (name == "com.sun.star.datatransfer.clipboard.SystemClipboard") {
339 : // SystemClipboard is a wrapper returning either a platform-specific or
340 : // the generic VCLGenericClipboard:
341 : #if defined WNT
342 : expImpl = "com.sun.star.datatransfer.clipboard.ClipboardW32";
343 : #else
344 1 : expImpl = "com.sun.star.datatransfer.VCLGenericClipboard";
345 : #endif
346 : #if !defined WNT
347 1184 : } else if (name == "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"
348 592 : || name == "com.sun.star.datatransfer.dnd.XdndSupport")
349 : {
350 1 : expImpl = "com.sun.star.datatransfer.dnd.VclGenericDragSource";
351 1 : expServs = {"com.sun.star.datatransfer.dnd.GenericDragSource"};
352 1182 : } else if (name == "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"
353 591 : || name == "com.sun.star.datatransfer.dnd.XdndDropTarget")
354 : {
355 1 : expImpl = "com.sun.star.datatransfer.dnd.VclGenericDropTarget";
356 1 : expServs = {"com.sun.star.datatransfer.dnd.GenericDropTarget"};
357 : #endif
358 590 : } else if (name == "com.sun.star.ui.dialogs.FolderPicker") {
359 : // FolderPicker is a wrapper returning either a platform-specific or the
360 : // generic OfficeFolderPicker:
361 : #if defined WNT
362 : expImpl = "com.sun.star.ui.dialogs.Win32FolderPicker";
363 : expServs = {"com.sun.star.ui.dialogs.SystemFolderPicker"};
364 : #else
365 1 : expImpl = "com.sun.star.svtools.OfficeFolderPicker";
366 1 : expServs = {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
367 : #endif
368 : }
369 1188 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
370 : (OString(
371 : "instantiating \"" + msg(implementationName) + "\" via \""
372 : + msg(name) + "\" reports wrong implementation name")
373 : .getStr()),
374 594 : expImpl, info->getImplementationName());
375 1188 : css::uno::Sequence<OUString> servs(info->getSupportedServiceNames());
376 1188 : CPPUNIT_ASSERT_MESSAGE(
377 : (OString(
378 : "instantiating \"" + msg(implementationName) + "\" via \""
379 : + msg(name) + "\" reports non-unique " + msg(servs))
380 : .getStr()),
381 594 : unique(servs));
382 : // Some implementations like "com.sun.star.comp.Calc.SpreadsheetDocument"
383 : // report sub-services like
384 : // "com.sun.star.sheet.SpreadsheetDocumentSettings", and
385 : // "com.sun.star.document.OfficeDocument" that are not listed in the
386 : // .component file, so check for containment instead of equality:
387 1188 : CPPUNIT_ASSERT_MESSAGE(
388 : (OString(
389 : "instantiating \"" + msg(implementationName) + "\" via \""
390 : + msg(name) + "\" reports " + msg(servs) + " different from "
391 : + msg(expServs))
392 : .getStr()),
393 1188 : contains(servs, expServs));
394 594 : }
395 :
396 1 : CPPUNIT_TEST_SUITE_REGISTRATION(Test);
397 :
398 : }
399 :
400 4 : CPPUNIT_PLUGIN_IMPLEMENT();
401 :
402 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|