curl: upgrade to 8.11.1
[LibreOffice.git] / postprocess / qa / services.cxx
blob7e83206cc38995f9845a9edce27d697aae2c3f09
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 // instantiable 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 <string_view>
29 #include <utility>
30 #include <vector>
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>
48 namespace {
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) {
57 if (i != 0) {
58 buf.append(", ");
60 buf.append('"');
61 buf.append(msg(strings[i]));
62 buf.append('"');
64 buf.append('}');
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]) {
73 return false;
77 return true;
80 bool contains(
81 css::uno::Sequence<OUString> const & strings, OUString const & string)
83 return comphelper::findValue(strings, string) != -1;
86 bool contains(
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); });
95 void addService(
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) {
101 return;
103 const auto aMandatoryServices = service->getMandatoryServices();
104 for (auto const & serv : aMandatoryServices) {
105 addService(serv, allServices);
109 class Test: public test::BootstrapFixture {
110 public:
111 void test();
113 CPPUNIT_TEST_SUITE(Test);
114 CPPUNIT_TEST(test);
115 CPPUNIT_TEST_SUITE_END();
117 private:
118 void createInstance(
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);
126 void Test::test() {
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 u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"_ustr),
153 css::uno::UNO_QUERY_THROW);
154 const css::uno::Sequence<OUString> serviceNames(
155 m_xContext->getServiceManager()->getAvailableServiceNames());
156 struct Constructor {
157 Constructor(
158 OUString const & theServiceName, bool theDefaultConstructor):
159 serviceName(theServiceName),
160 defaultConstructor(theDefaultConstructor)
162 OUString serviceName;
163 bool defaultConstructor;
165 struct Implementation {
166 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)) {
189 desc.set(
190 typeMgr->getByHierarchicalName(rServiceName),
191 css::uno::UNO_QUERY_THROW);
193 if (serviceImpls2.empty()) {
194 if (desc.is()) {
195 CPPUNIT_ASSERT_MESSAGE(
196 (OString(
197 "no implementations of single-interface--based \""
198 + msg(rServiceName) + "\"")
199 .getStr()),
200 !desc->isSingleInterfaceBased());
201 std::cout
202 << "accumulation-based service \"" << rServiceName
203 << "\" without implementations\n";
204 } else {
205 std::cout
206 << "fantasy service name \"" << rServiceName
207 << "\" without implementations\n";
209 } else {
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(
217 (OString(
218 "implementation \"" + msg(name)
219 + "\" supports non-unique " + msg(servs))
220 .getStr()),
221 unique(servs));
222 k = impls.insert(
223 std::make_pair(name, Implementation(j, servs)))
224 .first;
225 } else {
226 CPPUNIT_ASSERT_MESSAGE(
227 (OString(
228 "multiple implementations named \"" + msg(name)
229 + "\"")
230 .getStr()),
231 bool(j == k->second.factory));
233 CPPUNIT_ASSERT_MESSAGE(
234 (OString(
235 "implementation \"" + msg(name) + "\" supports "
236 + msg(k->second.serviceNames) + " but not \""
237 + msg(rServiceName) + "\"")
238 .getStr()),
239 contains(k->second.serviceNames, rServiceName));
240 if (desc.is()) {
241 if (desc->isSingleInterfaceBased()) {
242 if (serviceImpls2.size() == 1) {
243 const css::uno::Sequence<
244 css::uno::Reference<
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(
251 rServiceName,
252 (*pCtor)->isDefaultConstructor());
254 } else {
255 k->second.accumulationBased = true;
257 } else {
258 std::cout
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)
269 == denylist.end())
271 if (i.second.constructors.empty()) {
272 if (i.second.accumulationBased) {
273 createInstance(
274 typeMgr, i.first, false, i.first, i.second.serviceNames, &comps);
275 } else {
276 std::cout
277 << "no obvious way to instantiate implementation \""
278 << i.first << "\"\n";
280 } else {
281 for (auto const & j: i.second.constructors) {
282 createInstance(
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))
293 i->dispose();
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;
306 try {
307 if (withArguments) {
308 inst = m_xContext->getServiceManager()
309 ->createInstanceWithArgumentsAndContext(
310 name, css::uno::Sequence<css::uno::Any>(), m_xContext);
311 } else {
312 inst = m_xContext->getServiceManager()->createInstanceWithContext(
313 name, m_xContext);
315 } catch (css::uno::Exception & e) {
316 css::uno::Any a(cppu::getCaughtException());
317 CPPUNIT_FAIL(
318 OString(
319 "instantiating \"" + msg(implementationName) + "\" via \""
320 + msg(name) + "\" caused " + msg(a.getValueTypeName()) + " \""
321 + msg(e.Message) + "\"")
322 .getStr());
324 CPPUNIT_ASSERT_MESSAGE(
325 (OString(
326 "instantiating \"" + msg(implementationName) + "\" via \""
327 + msg(name) + "\" returned null reference")
328 .getStr()),
329 inst.is());
330 css::uno::Reference<css::lang::XComponent> comp(inst, css::uno::UNO_QUERY);
331 if (comp.is()) {
332 components->push_back(comp);
334 css::uno::Reference<css::lang::XServiceInfo> info(
335 inst, css::uno::UNO_QUERY);
336 CPPUNIT_ASSERT_MESSAGE(
337 (OString(
338 "instantiating \"" + msg(implementationName) + "\" via \""
339 + msg(name) + "\" does not provide XServiceInfo")
340 .getStr()),
341 info.is());
342 OUString expImpl(implementationName);
343 css::uno::Sequence<OUString> expServs(serviceNames);
344 // Special cases:
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 = {u"com.sun.star.configuration.DefaultProvider"_ustr};
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:
353 expImpl = "com.sun.star.datatransfer.VCLGenericClipboard";
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 = {u"com.sun.star.datatransfer.dnd.GenericDragSource"_ustr};
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 = {u"com.sun.star.datatransfer.dnd.GenericDropTarget"_ustr};
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. In headless mode it is always the
369 // generic one.
370 expImpl = "com.sun.star.svtools.OfficeFolderPicker";
371 expServs = {u"com.sun.star.ui.dialogs.OfficeFolderPicker"_ustr};
372 } else if (expImpl == "com.sun.star.comp.Calc.SpreadsheetDocument") {
373 expImpl = "ScModelObj";
374 } else if (expImpl == "com.sun.star.comp.Draw.DrawingDocument"
375 || expImpl == "com.sun.star.comp.Draw.PresentationDocument")
377 expImpl = "SdXImpressDocument";
378 } else if (expImpl == "com.sun.star.comp.Writer.GlobalDocument"
379 || expImpl == "com.sun.star.comp.Writer.TextDocument"
380 || expImpl == "com.sun.star.comp.Writer.WebDocument")
382 expImpl = "SwXTextDocument";
384 CPPUNIT_ASSERT_EQUAL_MESSAGE(
385 (OString(
386 "instantiating \"" + msg(implementationName) + "\" via \""
387 + msg(name) + "\" reports wrong implementation name")
388 .getStr()),
389 expImpl, info->getImplementationName());
390 const css::uno::Sequence<OUString> servs(info->getSupportedServiceNames());
391 CPPUNIT_ASSERT_MESSAGE(
392 (OString(
393 "instantiating \"" + msg(implementationName) + "\" via \""
394 + msg(name) + "\" reports non-unique " + msg(servs))
395 .getStr()),
396 unique(servs));
397 // Some implementations like "com.sun.star.comp.Calc.SpreadsheetDocument"
398 // report sub-services like
399 // "com.sun.star.sheet.SpreadsheetDocumentSettings", and
400 // "com.sun.star.document.OfficeDocument" that are not listed in the
401 // .component file, so check for containment instead of equality:
402 CPPUNIT_ASSERT_MESSAGE(
403 (OString(
404 "instantiating \"" + msg(implementationName) + "\" via \""
405 + msg(name) + "\" reports " + msg(servs) + " different from "
406 + msg(expServs))
407 .getStr()),
408 contains(servs, expServs));
409 std::set<css::uno::Reference<css::reflection::XServiceTypeDescription>> allservs;
410 for (auto const & serv: servs) {
411 if (!typeManager->hasByHierarchicalName(serv)) {
412 std::cout
413 << "instantiating \"" << implementationName << "\" via \"" << name
414 << "\" supports fantasy service name \"" << serv << "\"\n";
415 continue;
417 addService(
418 css::uno::Reference<css::reflection::XServiceTypeDescription>(
419 typeManager->getByHierarchicalName(serv), css::uno::UNO_QUERY_THROW),
420 &allservs);
422 css::uno::Reference<css::beans::XPropertySetInfo> propsinfo;
423 for (auto const & serv: allservs) {
424 auto const props = serv->getProperties();
425 for (auto const & prop: props) {
426 auto const optional
427 = (prop->getPropertyFlags() & css::beans::PropertyAttribute::OPTIONAL) != 0;
428 if (!propsinfo.is()) {
429 css::uno::Reference<css::beans::XPropertySet> propset(inst, css::uno::UNO_QUERY);
430 if (!propset.is()) {
431 CPPUNIT_ASSERT_MESSAGE(
432 (OString(
433 "instantiating \"" + msg(implementationName) + "\" via \"" + msg(name)
434 + "\" reports service " + msg(serv->getName())
435 + " with non-optional property \"" + msg(prop->getName())
436 + "\" but does not implement css.uno.XPropertySet")
437 .getStr()),
438 optional);
439 continue;
441 propsinfo = propset->getPropertySetInfo();
442 if (!propsinfo.is()) {
443 //TODO: legal to return null in more cases? ("@returns NULL if the
444 // implementation cannot or will not provide information about the properties")
445 CPPUNIT_ASSERT_MESSAGE(
446 (OString(
447 "instantiating \"" + msg(implementationName) + "\" via \"" + msg(name)
448 + "\" reports service " + msg(serv->getName())
449 + " with non-optional property \"" + msg(prop->getName())
450 + "\" but css.uno.XPropertySet::getPropertySetInfo returns null")
451 .getStr()),
452 optional);
453 continue;
456 if (!propsinfo->hasPropertyByName(prop->getName())) {
457 static std::set<std::pair<OUString, OUString>> const denylist{
458 {"com.sun.star.comp.chart.DataSeries", "BorderDash"},
459 {"com.sun.star.comp.chart2.ChartDocumentWrapper", "UserDefinedAttributes"},
460 {"com.sun.star.comp.dbu.OColumnControlModel", "Tabstop"},
461 {"com.sun.star.comp.report.OFormattedField", "Align"},
462 {"com.sun.star.comp.report.OFormattedField", "BackgroundColor"},
463 {"com.sun.star.comp.report.OFormattedField", "Border"},
464 {"com.sun.star.comp.report.OFormattedField", "DefaultControl"},
465 {"com.sun.star.comp.report.OFormattedField", "EffectiveDefault"},
466 {"com.sun.star.comp.report.OFormattedField", "EffectiveMax"},
467 {"com.sun.star.comp.report.OFormattedField", "EffectiveMin"},
468 {"com.sun.star.comp.report.OFormattedField", "EffectiveValue"},
469 {"com.sun.star.comp.report.OFormattedField", "Enabled"},
470 {"com.sun.star.comp.report.OFormattedField", "FontEmphasisMark"},
471 {"com.sun.star.comp.report.OFormattedField", "FontRelief"},
472 {"com.sun.star.comp.report.OFormattedField", "HelpText"},
473 {"com.sun.star.comp.report.OFormattedField", "HelpURL"},
474 {"com.sun.star.comp.report.OFormattedField", "MaxTextLen"},
475 {"com.sun.star.comp.report.OFormattedField", "Printable"},
476 {"com.sun.star.comp.report.OFormattedField", "ReadOnly"},
477 {"com.sun.star.comp.report.OFormattedField", "Spin"},
478 {"com.sun.star.comp.report.OFormattedField", "Tabstop"},
479 {"com.sun.star.comp.report.OFormattedField", "Text"},
480 {"com.sun.star.comp.report.OFormattedField", "TextColor"},
481 {"com.sun.star.comp.report.OFormattedField", "TextLineColor"},
482 {"com.sun.star.comp.report.OFormattedField", "TreatAsNumber"},
483 {"stardiv.Toolkit.UnoControlRoadmapModel", "Interactive"}};
484 if (denylist.find({implementationName, prop->getName()}) != denylist.end()) {
485 continue;
487 CPPUNIT_ASSERT_MESSAGE(
488 (OString(
489 "instantiating \"" + msg(implementationName) + "\" via \"" + msg(name)
490 + "\" reports service " + msg(serv->getName())
491 + " with non-optional property \"" + msg(prop->getName())
492 + ("\" but css.uno.XPropertySet::getPropertySetInfo's hasPropertyByName"
493 " returns false"))
494 .getStr()),
495 optional);
501 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
505 CPPUNIT_PLUGIN_IMPLEMENT();
507 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */