bump product version to 5.0.4.1
[LibreOffice.git] / postprocess / qa / services.cxx
blob35e5b106d15c700ac9e5b5c4ec638f30a3d5d541
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 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>
23 #include <algorithm>
24 #include <cassert>
25 #include <iostream>
26 #include <map>
27 #include <utility>
28 #include <vector>
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>
41 namespace {
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) {
50 if (i != 0) {
51 buf.append(", ");
53 buf.append('"');
54 buf.append(msg(strings[i]));
55 buf.append('"');
57 buf.append('}');
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]) {
66 return false;
70 return true;
73 bool contains(
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]) {
78 return true;
81 return false;
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 for (sal_Int32 i = 0; i != strings2.getLength(); ++i) {
90 if (!contains(strings1, strings2[i])) {
91 return false;
94 return true;
97 class Test: public test::BootstrapFixture {
98 public:
99 void test();
101 CPPUNIT_TEST_SUITE(Test);
102 CPPUNIT_TEST(test);
103 CPPUNIT_TEST_SUITE_END();
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);
113 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 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());
143 struct Constructor {
144 Constructor(
145 OUString const & theServiceName, bool theDefaultConstructor):
146 serviceName(theServiceName),
147 defaultConstructor(theDefaultConstructor)
149 OUString serviceName;
150 bool defaultConstructor;
152 struct Implementation {
153 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])) {
177 desc.set(
178 typeMgr->getByHierarchicalName(serviceNames[i]),
179 css::uno::UNO_QUERY_THROW);
181 if (serviceImpls2.empty()) {
182 if (desc.is()) {
183 CPPUNIT_ASSERT_MESSAGE(
184 (OString(
185 "no implementations of single-interface--based \""
186 + msg(serviceNames[i]) + "\"")
187 .getStr()),
188 !desc->isSingleInterfaceBased());
189 std::cout
190 << "accumulation-based service \"" << serviceNames[i]
191 << "\" without implementations\n";
192 } else {
193 std::cout
194 << "fantasy service name \"" << serviceNames[i]
195 << "\" without implementations\n";
197 } else {
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(
205 (OString(
206 "implementation \"" + msg(name)
207 + "\" supports non-unique " + msg(servs))
208 .getStr()),
209 unique(servs));
210 k = impls.insert(
211 std::make_pair(name, Implementation(j, servs)))
212 .first;
213 } else {
214 CPPUNIT_ASSERT_MESSAGE(
215 (OString(
216 "multiple implementations named \"" + msg(name)
217 + "\"")
218 .getStr()),
219 j == k->second.factory);
221 CPPUNIT_ASSERT_MESSAGE(
222 (OString(
223 "implementation \"" + msg(name) + "\" supports "
224 + msg(k->second.serviceNames) + " but not \""
225 + msg(serviceNames[i]) + "\"")
226 .getStr()),
227 contains(k->second.serviceNames, serviceNames[i]));
228 if (desc.is()) {
229 if (desc->isSingleInterfaceBased()) {
230 if (serviceImpls2.size() == 1) {
231 css::uno::Sequence<
232 css::uno::Reference<
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(
238 Constructor(
239 serviceNames[i],
240 ctors[l]->isDefaultConstructor()));
241 break;
245 } else {
246 k->second.accumulationBased = true;
248 } else {
249 std::cout
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)
260 == blacklist.end())
262 if (i.second.constructors.empty()) {
263 if (i.second.accumulationBased) {
264 createInstance(
265 i.first, false, i.first, i.second.serviceNames, &comps);
266 } else {
267 std::cout
268 << "no obvious way to instantiate implementation \""
269 << i.first << "\"\n";
271 } else {
272 for (auto const & j: i.second.constructors) {
273 createInstance(
274 j.serviceName, !j.defaultConstructor, i.first,
275 i.second.serviceNames, &comps);
280 SolarMutexReleaser rel;
281 for (auto const & i: comps) {
282 i->dispose();
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;
294 try {
295 if (withArguments) {
296 inst = m_xContext->getServiceManager()
297 ->createInstanceWithArgumentsAndContext(
298 name, css::uno::Sequence<css::uno::Any>(), m_xContext);
299 } else {
300 inst = m_xContext->getServiceManager()->createInstanceWithContext(
301 name, m_xContext);
303 } catch (css::uno::Exception & e) {
304 css::uno::Any a(cppu::getCaughtException());
305 CPPUNIT_FAIL(
306 OString(
307 "instantiating \"" + msg(implementationName) + "\" via \""
308 + msg(name) + "\" caused " + msg(a.getValueTypeName()) + " \""
309 + msg(e.Message) + "\"")
310 .getStr());
312 CPPUNIT_ASSERT_MESSAGE(
313 (OString(
314 "instantiating \"" + msg(implementationName) + "\" via \""
315 + msg(name) + "\" returned null reference")
316 .getStr()),
317 inst.is());
318 css::uno::Reference<css::lang::XComponent> comp(inst, css::uno::UNO_QUERY);
319 if (comp.is()) {
320 components->push_back(comp);
322 css::uno::Reference<css::lang::XServiceInfo> info(
323 inst, css::uno::UNO_QUERY);
324 CPPUNIT_ASSERT_MESSAGE(
325 (OString(
326 "instantiating \"" + msg(implementationName) + "\" via \""
327 + msg(name) + "\" does not provide XServiceInfo")
328 .getStr()),
329 info.is());
330 OUString expImpl(implementationName);
331 css::uno::Sequence<OUString> expServs(serviceNames);
332 // Special cases:
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:
341 #if defined WNT
342 expImpl = "com.sun.star.datatransfer.clipboard.ClipboardW32";
343 #else
344 expImpl = "com.sun.star.datatransfer.VCLGenericClipboard";
345 #endif
346 #if !defined WNT
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"};
357 #endif
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:
361 #if defined WNT
362 expImpl = "com.sun.star.ui.dialogs.Win32FolderPicker";
363 expServs = {"com.sun.star.ui.dialogs.SystemFolderPicker"};
364 #else
365 expImpl = "com.sun.star.svtools.OfficeFolderPicker";
366 expServs = {"com.sun.star.ui.dialogs.OfficeFolderPicker"};
367 #endif
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(
381 (OString(
382 "instantiating \"" + msg(implementationName) + "\" via \""
383 + msg(name) + "\" reports wrong implementation name")
384 .getStr()),
385 expImpl, info->getImplementationName());
386 css::uno::Sequence<OUString> servs(info->getSupportedServiceNames());
387 CPPUNIT_ASSERT_MESSAGE(
388 (OString(
389 "instantiating \"" + msg(implementationName) + "\" via \""
390 + msg(name) + "\" reports non-unique " + msg(servs))
391 .getStr()),
392 unique(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(
399 (OString(
400 "instantiating \"" + msg(implementationName) + "\" via \""
401 + msg(name) + "\" reports " + msg(servs) + " different from "
402 + msg(expServs))
403 .getStr()),
404 contains(servs, expServs));
407 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
411 CPPUNIT_PLUGIN_IMPLEMENT();
413 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */