tdf#133368: update extensions website URLs and use https
[LibreOffice.git] / postprocess / qa / services.cxx
blob5ff9f63f522bffca4a5eea607b3d0a5e5a96ed53
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 <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 "/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
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 = {"com.sun.star.configuration.DefaultProvider"};
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 #if defined(_WIN32)
354 expImpl = "com.sun.star.datatransfer.clipboard.ClipboardW32";
355 #else
356 expImpl = "com.sun.star.datatransfer.VCLGenericClipboard";
357 #endif
358 #if !defined(_WIN32)
359 } else if (name == "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"
360 || name == "com.sun.star.datatransfer.dnd.XdndSupport")
362 expImpl = "com.sun.star.datatransfer.dnd.VclGenericDragSource";
363 expServs = {"com.sun.star.datatransfer.dnd.GenericDragSource"};
364 } else if (name == "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"
365 || name == "com.sun.star.datatransfer.dnd.XdndDropTarget")
367 expImpl = "com.sun.star.datatransfer.dnd.VclGenericDropTarget";
368 expServs = {"com.sun.star.datatransfer.dnd.GenericDropTarget"};
369 #endif
370 } else if (name == "com.sun.star.ui.dialogs.FolderPicker") {
371 // FolderPicker is a wrapper returning either a platform-specific or the
372 // generic OfficeFolderPicker:
373 #if defined(_WIN32)
374 expImpl = "com.sun.star.ui.dialogs.Win32FolderPicker";
375 expServs = {"com.sun.star.ui.dialogs.SystemFolderPicker"};
376 #else
377 expImpl = "com.sun.star.svtools.OfficeFolderPicker";
378 expServs = {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
379 #endif
380 } else if (expImpl == "com.sun.star.comp.Calc.SpreadsheetDocument") {
381 expImpl = "ScModelObj";
382 } else if (expImpl == "com.sun.star.comp.Draw.DrawingDocument"
383 || expImpl == "com.sun.star.comp.Draw.PresentationDocument")
385 expImpl = "SdXImpressDocument";
386 } else if (expImpl == "com.sun.star.comp.Writer.GlobalDocument"
387 || expImpl == "com.sun.star.comp.Writer.TextDocument"
388 || expImpl == "com.sun.star.comp.Writer.WebDocument")
390 expImpl = "SwXTextDocument";
392 CPPUNIT_ASSERT_EQUAL_MESSAGE(
393 (OString(
394 "instantiating \"" + msg(implementationName) + "\" via \""
395 + msg(name) + "\" reports wrong implementation name")
396 .getStr()),
397 expImpl, info->getImplementationName());
398 const css::uno::Sequence<OUString> servs(info->getSupportedServiceNames());
399 CPPUNIT_ASSERT_MESSAGE(
400 (OString(
401 "instantiating \"" + msg(implementationName) + "\" via \""
402 + msg(name) + "\" reports non-unique " + msg(servs))
403 .getStr()),
404 unique(servs));
405 // Some implementations like "com.sun.star.comp.Calc.SpreadsheetDocument"
406 // report sub-services like
407 // "com.sun.star.sheet.SpreadsheetDocumentSettings", and
408 // "com.sun.star.document.OfficeDocument" that are not listed in the
409 // .component file, so check for containment instead of equality:
410 CPPUNIT_ASSERT_MESSAGE(
411 (OString(
412 "instantiating \"" + msg(implementationName) + "\" via \""
413 + msg(name) + "\" reports " + msg(servs) + " different from "
414 + msg(expServs))
415 .getStr()),
416 contains(servs, expServs));
417 std::set<css::uno::Reference<css::reflection::XServiceTypeDescription>> allservs;
418 for (auto const & serv: servs) {
419 if (!typeManager->hasByHierarchicalName(serv)) {
420 std::cout
421 << "instantiating \"" << implementationName << "\" via \"" << name
422 << "\" supports fantasy service name \"" << serv << "\"\n";
423 continue;
425 addService(
426 css::uno::Reference<css::reflection::XServiceTypeDescription>(
427 typeManager->getByHierarchicalName(serv), css::uno::UNO_QUERY_THROW),
428 &allservs);
430 css::uno::Reference<css::beans::XPropertySetInfo> propsinfo;
431 for (auto const & serv: allservs) {
432 auto const props = serv->getProperties();
433 for (auto const & prop: props) {
434 auto const optional
435 = (prop->getPropertyFlags() & css::beans::PropertyAttribute::OPTIONAL) != 0;
436 if (!propsinfo.is()) {
437 css::uno::Reference<css::beans::XPropertySet> propset(inst, css::uno::UNO_QUERY);
438 if (!propset.is()) {
439 CPPUNIT_ASSERT_MESSAGE(
440 (OString(
441 "instantiating \"" + msg(implementationName) + "\" via \"" + msg(name)
442 + "\" reports service " + msg(serv->getName())
443 + " with non-optional property \"" + msg(prop->getName())
444 + "\" but does not implement css.uno.XPropertySet")
445 .getStr()),
446 optional);
447 continue;
449 propsinfo = propset->getPropertySetInfo();
450 if (!propsinfo.is()) {
451 //TODO: legal to return null in more cases? ("@returns NULL if the
452 // implementation cannot or will not provide information about the properties")
453 CPPUNIT_ASSERT_MESSAGE(
454 (OString(
455 "instantiating \"" + msg(implementationName) + "\" via \"" + msg(name)
456 + "\" reports service " + msg(serv->getName())
457 + " with non-optional property \"" + msg(prop->getName())
458 + "\" but css.uno.XPropertySet::getPropertySetInfo returns null")
459 .getStr()),
460 optional);
461 continue;
464 if (!propsinfo->hasPropertyByName(prop->getName())) {
465 static std::set<std::pair<OUString, OUString>> const denylist{
466 {"com.sun.star.comp.chart.DataSeries", "BorderDash"},
467 {"com.sun.star.comp.chart2.ChartDocumentWrapper", "UserDefinedAttributes"},
468 {"com.sun.star.comp.dbu.OColumnControlModel", "Tabstop"},
469 {"com.sun.star.comp.report.OFormattedField", "Align"},
470 {"com.sun.star.comp.report.OFormattedField", "BackgroundColor"},
471 {"com.sun.star.comp.report.OFormattedField", "Border"},
472 {"com.sun.star.comp.report.OFormattedField", "DefaultControl"},
473 {"com.sun.star.comp.report.OFormattedField", "EffectiveDefault"},
474 {"com.sun.star.comp.report.OFormattedField", "EffectiveMax"},
475 {"com.sun.star.comp.report.OFormattedField", "EffectiveMin"},
476 {"com.sun.star.comp.report.OFormattedField", "EffectiveValue"},
477 {"com.sun.star.comp.report.OFormattedField", "Enabled"},
478 {"com.sun.star.comp.report.OFormattedField", "FontEmphasisMark"},
479 {"com.sun.star.comp.report.OFormattedField", "FontRelief"},
480 {"com.sun.star.comp.report.OFormattedField", "HelpText"},
481 {"com.sun.star.comp.report.OFormattedField", "HelpURL"},
482 {"com.sun.star.comp.report.OFormattedField", "MaxTextLen"},
483 {"com.sun.star.comp.report.OFormattedField", "Printable"},
484 {"com.sun.star.comp.report.OFormattedField", "ReadOnly"},
485 {"com.sun.star.comp.report.OFormattedField", "Spin"},
486 {"com.sun.star.comp.report.OFormattedField", "Tabstop"},
487 {"com.sun.star.comp.report.OFormattedField", "Text"},
488 {"com.sun.star.comp.report.OFormattedField", "TextColor"},
489 {"com.sun.star.comp.report.OFormattedField", "TextLineColor"},
490 {"com.sun.star.comp.report.OFormattedField", "TreatAsNumber"},
491 {"stardiv.Toolkit.UnoControlRoadmapModel", "Interactive"}};
492 if (denylist.find({implementationName, prop->getName()}) != denylist.end()) {
493 continue;
495 CPPUNIT_ASSERT_MESSAGE(
496 (OString(
497 "instantiating \"" + msg(implementationName) + "\" via \"" + msg(name)
498 + "\" reports service " + msg(serv->getName())
499 + " with non-optional property \"" + msg(prop->getName())
500 + ("\" but css.uno.XPropertySet::getPropertySetInfo's hasPropertyByName"
501 " returns false"))
502 .getStr()),
503 optional);
509 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
513 CPPUNIT_PLUGIN_IMPLEMENT();
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */