Bump version to 6.4-15
[LibreOffice.git] / postprocess / qa / services.cxx
blob3c0fa8f7c6cf95cb03bfcb7b197f89f697195610
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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>
23 #include <algorithm>
24 #include <cassert>
25 #include <iostream>
26 #include <map>
27 #include <set>
28 #include <utility>
29 #include <vector>
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>
46 namespace {
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) {
55 if (i != 0) {
56 buf.append(", ");
58 buf.append('"');
59 buf.append(msg(strings[i]));
60 buf.append('"');
62 buf.append('}');
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]) {
71 return false;
75 return true;
78 bool contains(
79 css::uno::Sequence<OUString> const & strings, OUString const & string)
81 return comphelper::findValue(strings, string) != -1;
84 bool contains(
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); });
93 void addService(
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) {
99 return;
101 const auto aMandatoryServices = service->getMandatoryServices();
102 for (auto const & serv : aMandatoryServices) {
103 addService(serv, allServices);
107 class Test: public test::BootstrapFixture {
108 public:
109 void test();
111 CPPUNIT_TEST_SUITE(Test);
112 CPPUNIT_TEST(test);
113 CPPUNIT_TEST_SUITE_END();
115 private:
116 void createInstance(
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);
124 void Test::test() {
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());
154 struct Constructor {
155 Constructor(
156 OUString const & theServiceName, bool theDefaultConstructor):
157 serviceName(theServiceName),
158 defaultConstructor(theDefaultConstructor)
160 OUString const serviceName;
161 bool const defaultConstructor;
163 struct Implementation {
164 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)) {
187 desc.set(
188 typeMgr->getByHierarchicalName(rServiceName),
189 css::uno::UNO_QUERY_THROW);
191 if (serviceImpls2.empty()) {
192 if (desc.is()) {
193 CPPUNIT_ASSERT_MESSAGE(
194 (OString(
195 "no implementations of single-interface--based \""
196 + msg(rServiceName) + "\"")
197 .getStr()),
198 !desc->isSingleInterfaceBased());
199 std::cout
200 << "accumulation-based service \"" << rServiceName
201 << "\" without implementations\n";
202 } else {
203 std::cout
204 << "fantasy service name \"" << rServiceName
205 << "\" without implementations\n";
207 } else {
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(
215 (OString(
216 "implementation \"" + msg(name)
217 + "\" supports non-unique " + msg(servs))
218 .getStr()),
219 unique(servs));
220 k = impls.insert(
221 std::make_pair(name, Implementation(j, servs)))
222 .first;
223 } else {
224 CPPUNIT_ASSERT_MESSAGE(
225 (OString(
226 "multiple implementations named \"" + msg(name)
227 + "\"")
228 .getStr()),
229 bool(j == k->second.factory));
231 CPPUNIT_ASSERT_MESSAGE(
232 (OString(
233 "implementation \"" + msg(name) + "\" supports "
234 + msg(k->second.serviceNames) + " but not \""
235 + msg(rServiceName) + "\"")
236 .getStr()),
237 contains(k->second.serviceNames, rServiceName));
238 if (desc.is()) {
239 if (desc->isSingleInterfaceBased()) {
240 if (serviceImpls2.size() == 1) {
241 const css::uno::Sequence<
242 css::uno::Reference<
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(
249 rServiceName,
250 (*pCtor)->isDefaultConstructor());
252 } else {
253 k->second.accumulationBased = true;
255 } else {
256 std::cout
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)
267 == blacklist.end())
269 if (i.second.constructors.empty()) {
270 if (i.second.accumulationBased) {
271 createInstance(
272 typeMgr, i.first, false, i.first, i.second.serviceNames, &comps);
273 } else {
274 std::cout
275 << "no obvious way to instantiate implementation \""
276 << i.first << "\"\n";
278 } else {
279 for (auto const & j: i.second.constructors) {
280 createInstance(
281 typeMgr, j.serviceName, !j.defaultConstructor, i.first,
282 i.second.serviceNames, &comps);
287 SolarMutexReleaser rel;
288 for (auto const & i: comps) {
289 i->dispose();
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;
302 try {
303 if (withArguments) {
304 inst = m_xContext->getServiceManager()
305 ->createInstanceWithArgumentsAndContext(
306 name, css::uno::Sequence<css::uno::Any>(), m_xContext);
307 } else {
308 inst = m_xContext->getServiceManager()->createInstanceWithContext(
309 name, m_xContext);
311 } catch (css::uno::Exception & e) {
312 css::uno::Any a(cppu::getCaughtException());
313 CPPUNIT_FAIL(
314 OString(
315 "instantiating \"" + msg(implementationName) + "\" via \""
316 + msg(name) + "\" caused " + msg(a.getValueTypeName()) + " \""
317 + msg(e.Message) + "\"")
318 .getStr());
320 CPPUNIT_ASSERT_MESSAGE(
321 (OString(
322 "instantiating \"" + msg(implementationName) + "\" via \""
323 + msg(name) + "\" returned null reference")
324 .getStr()),
325 inst.is());
326 css::uno::Reference<css::lang::XComponent> comp(inst, css::uno::UNO_QUERY);
327 if (comp.is()) {
328 components->push_back(comp);
330 css::uno::Reference<css::lang::XServiceInfo> info(
331 inst, css::uno::UNO_QUERY);
332 CPPUNIT_ASSERT_MESSAGE(
333 (OString(
334 "instantiating \"" + msg(implementationName) + "\" via \""
335 + msg(name) + "\" does not provide XServiceInfo")
336 .getStr()),
337 info.is());
338 OUString expImpl(implementationName);
339 css::uno::Sequence<OUString> expServs(serviceNames);
340 // Special cases:
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:
349 #if defined(_WIN32)
350 expImpl = "com.sun.star.datatransfer.clipboard.ClipboardW32";
351 #else
352 expImpl = "com.sun.star.datatransfer.VCLGenericClipboard";
353 #endif
354 #if !defined(_WIN32)
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"};
365 #endif
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:
369 #if defined(_WIN32)
370 expImpl = "com.sun.star.ui.dialogs.Win32FolderPicker";
371 expServs = {"com.sun.star.ui.dialogs.SystemFolderPicker"};
372 #else
373 expImpl = "com.sun.star.svtools.OfficeFolderPicker";
374 expServs = {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
375 #endif
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(
389 (OString(
390 "instantiating \"" + msg(implementationName) + "\" via \""
391 + msg(name) + "\" reports wrong implementation name")
392 .getStr()),
393 expImpl, info->getImplementationName());
394 const css::uno::Sequence<OUString> servs(info->getSupportedServiceNames());
395 CPPUNIT_ASSERT_MESSAGE(
396 (OString(
397 "instantiating \"" + msg(implementationName) + "\" via \""
398 + msg(name) + "\" reports non-unique " + msg(servs))
399 .getStr()),
400 unique(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(
407 (OString(
408 "instantiating \"" + msg(implementationName) + "\" via \""
409 + msg(name) + "\" reports " + msg(servs) + " different from "
410 + msg(expServs))
411 .getStr()),
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)) {
416 std::cout
417 << "instantiating \"" << implementationName << "\" via \"" << name
418 << "\" supports fantasy service name \"" << serv << "\"\n";
419 continue;
421 addService(
422 css::uno::Reference<css::reflection::XServiceTypeDescription>(
423 typeManager->getByHierarchicalName(serv), css::uno::UNO_QUERY_THROW),
424 &allservs);
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) {
430 auto const optional
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);
434 if (!propset.is()) {
435 CPPUNIT_ASSERT_MESSAGE(
436 (OString(
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")
441 .getStr()),
442 optional);
443 continue;
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(
450 (OString(
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")
455 .getStr()),
456 optional);
457 continue;
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()) {
489 continue;
491 CPPUNIT_ASSERT_MESSAGE(
492 (OString(
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"
497 " returns false"))
498 .getStr()),
499 optional);
505 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
509 CPPUNIT_PLUGIN_IMPLEMENT();
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */