xref: /core/test/source/bootstrapfixture.cxx (revision 5200a736)
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 #include <config_validation.h>
11 
12 #include <test/bootstrapfixture.hxx>
13 #include <vcl/errinf.hxx>
14 #include <sal/log.hxx>
15 #include <comphelper/processfactory.hxx>
16 
17 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
18 #include <com/sun/star/ucb/XContentProvider.hpp>
19 #include <com/sun/star/ucb/XUniversalContentBroker.hpp>
20 
21 #include <vcl/outdev.hxx>
22 #include <vcl/svapp.hxx>
23 #include <tools/link.hxx>
24 #include <vcl/graphicfilter.hxx>
25 #include <osl/file.hxx>
26 #include <osl/process.h>
27 #include <unotest/getargument.hxx>
28 #include <unotools/tempfile.hxx>
29 #include <vcl/salgtype.hxx>
30 #include <vcl/scheduler.hxx>
31 #include <vcl/virdev.hxx>
32 #include <o3tl/string_view.hxx>
33 
34 #include <memory>
35 #include <cstring>
36 
37 #include "setupvcl.hxx"
38 
39 using namespace ::com::sun::star;
40 
41 static void aBasicErrorFunc( const OUString &rErr, const OUString &rAction )
42 {
43     OString aErr = "Unexpected dialog: " +
44         OUStringToOString( rAction, RTL_TEXTENCODING_ASCII_US ) +
45         " Error: " +
46         OUStringToOString( rErr, RTL_TEXTENCODING_ASCII_US );
47     CPPUNIT_ASSERT_MESSAGE( aErr.getStr(), false);
48 }
49 
50 // NB. this constructor is called before any tests are run, once for each
51 // test function in a rather non-intuitive way. This is why all the 'real'
52 // heavy lifting is deferred until setUp. setUp and tearDown are interleaved
53 // between the tests as you might expect.
54 test::BootstrapFixture::BootstrapFixture( bool bAssertOnDialog, bool bNeedUCB )
55     : m_bNeedUCB( bNeedUCB )
56     , m_bAssertOnDialog( bAssertOnDialog )
57 {
58 }
59 
60 extern "C"
61 {
62 
63 static void test_init_impl(bool bAssertOnDialog, bool bNeedUCB,
64         lang::XMultiServiceFactory * pSFactory)
65 {
66     if (bAssertOnDialog)
67         ErrorRegistry::RegisterDisplay( aBasicErrorFunc );
68 
69     // Make GraphicConverter work, normally done in desktop::Desktop::Main()
70     Application::SetFilterHdl(
71             LINK(nullptr, test::BootstrapFixture, ImplInitFilterHdl));
72 
73     if (bNeedUCB)
74     {
75         // initialise unconfigured UCB:
76         uno::Reference<ucb::XUniversalContentBroker> xUcb(pSFactory->createInstance("com.sun.star.ucb.UniversalContentBroker"), uno::UNO_QUERY_THROW);
77         uno::Reference<ucb::XContentProvider> xFileProvider(pSFactory->createInstance("com.sun.star.ucb.FileContentProvider"), uno::UNO_QUERY_THROW);
78         xUcb->registerContentProvider(xFileProvider, "file", true);
79         uno::Reference<ucb::XContentProvider> xTdocProvider(pSFactory->createInstance("com.sun.star.ucb.TransientDocumentsContentProvider"), uno::UNO_QUERY);
80         if (xTdocProvider.is())
81         {
82             xUcb->registerContentProvider(xTdocProvider, "vnd.sun.star.tdoc", true);
83         }
84     }
85 }
86 
87 // this is called from pyuno
88 SAL_DLLPUBLIC_EXPORT void test_init(lang::XMultiServiceFactory *pFactory)
89 {
90     try
91     {
92         ::comphelper::setProcessServiceFactory(pFactory);
93         test::setUpVcl(true); // hard-code python tests to headless
94         test_init_impl(false, true, pFactory);
95     }
96     catch (...) { abort(); }
97 }
98 
99 // this is called from pyuno
100 SAL_DLLPUBLIC_EXPORT void test_deinit()
101 {
102     DeInitVCL();
103 }
104 
105 } // extern "C"
106 
107 void test::BootstrapFixture::setUp()
108 {
109     test::BootstrapFixtureBase::setUp();
110 
111     test_init_impl(m_bAssertOnDialog, m_bNeedUCB, m_xSFactory.get());
112 
113 #if OSL_DEBUG_LEVEL > 0
114     Scheduler::ProcessEventsToIdle();
115 #endif
116 
117     mxComponentContext.set(comphelper::getComponentContext(getMultiServiceFactory()));
118 }
119 
120 test::BootstrapFixture::~BootstrapFixture()
121 {
122 }
123 
124 #if HAVE_EXPORT_VALIDATION
125 namespace {
126 
127 OString loadFile(const OUString& rURL)
128 {
129     osl::File aFile(rURL);
130     osl::FileBase::RC eStatus = aFile.open(osl_File_OpenFlag_Read);
131     CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, eStatus);
132     sal_uInt64 nSize;
133     aFile.getSize(nSize);
134     std::unique_ptr<char[]> aBytes(new char[nSize]);
135     sal_uInt64 nBytesRead;
136     aFile.read(aBytes.get(), nSize, nBytesRead);
137     CPPUNIT_ASSERT_EQUAL(nSize, nBytesRead);
138     OString aContent(aBytes.get(), nBytesRead);
139 
140     return aContent;
141 }
142 
143 }
144 #endif
145 
146 void test::BootstrapFixture::validate(const OUString& rPath, test::ValidationFormat eFormat) const
147 {
148 #if HAVE_EXPORT_VALIDATION
149     OUString var;
150     if( eFormat == test::OOXML )
151     {
152         var = "OFFICEOTRON";
153     }
154     else if ( eFormat == test::ODF )
155     {
156         var = "ODFVALIDATOR";
157     }
158     else if ( eFormat == test::MSBINARY )
159     {
160 #if HAVE_BFFVALIDATOR
161         var = "BFFVALIDATOR";
162 #else
163         // Binary Format Validator is disabled
164         return;
165 #endif
166     }
167     OUString aValidator;
168     oslProcessError e = osl_getEnvironment(var.pData, &aValidator.pData);
169     CPPUNIT_ASSERT_EQUAL_MESSAGE(
170         OUString("cannot get env var " + var).toUtf8().getStr(),
171         osl_Process_E_None, e);
172     CPPUNIT_ASSERT_MESSAGE(
173         OUString("empty get env var " + var).toUtf8().getStr(),
174         !aValidator.isEmpty());
175 
176     if (eFormat == test::ODF)
177     {
178         // invoke without -e so that we know when something new is written
179         // in loext namespace that isn't yet in the custom schema
180         aValidator += " -M "
181             + m_directories.getPathFromSrc(u"/schema/libreoffice/OpenDocument-v1.3+libreoffice-manifest-schema.rng")
182             + " -D "
183             + m_directories.getPathFromSrc(u"/schema/libreoffice/OpenDocument-v1.3+libreoffice-dsig-schema.rng")
184             + " -O "
185             + m_directories.getPathFromSrc(u"/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng")
186             + " -m "
187             + m_directories.getPathFromSrc(u"/schema/mathml2/mathml2.xsd");
188     }
189 
190     utl::TempFile aOutput;
191     aOutput.EnableKillingFile();
192     OUString aOutputFile = aOutput.GetFileName();
193     OUString aCommand = aValidator + " " + rPath + " > " + aOutputFile + " 2>&1";
194 
195 #if !defined _WIN32
196     // For now, this is only needed by some Linux ASan builds, so keep it simply and disable it on
197     // Windows (which doesn't support the relevant shell syntax for (un-)setting environment
198     // variables).
199     OUString env;
200     if (test::getArgument(u"env", &env)) {
201         auto const n = env.indexOf('=');
202         if (n == -1) {
203             aCommand = "unset -v " + env + " && " + aCommand;
204         } else {
205             aCommand = env + " " + aCommand;
206         }
207     }
208 #endif
209 
210     SAL_INFO("test", "BootstrapFixture::validate: executing '" << aCommand << "'");
211     int returnValue = system(OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8).getStr());
212 
213     OString aContentString = loadFile(aOutput.GetURL());
214     OUString aContentOUString = OStringToOUString(aContentString, RTL_TEXTENCODING_UTF8);
215 
216     if( eFormat == test::OOXML && !aContentOUString.isEmpty() )
217     {
218         // check for validation errors here
219         sal_Int32 nIndex = aContentOUString.lastIndexOf("Grand total of errors in submitted package: ");
220         if(nIndex == -1)
221         {
222             SAL_WARN("test", "no summary line");
223         }
224         else
225         {
226             sal_Int32 nStartOfNumber = nIndex + std::strlen("Grand total of errors in submitted package: ");
227             std::u16string_view aNumber = aContentOUString.subView(nStartOfNumber);
228             sal_Int32 nErrors = o3tl::toInt32(aNumber);
229             OString aMsg = "validation error in OOXML export: Errors: " + OString::number(nErrors);
230             if(nErrors)
231             {
232                 SAL_WARN("test", aContentOUString);
233             }
234             CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), sal_Int32(0), nErrors);
235         }
236     }
237     else if( eFormat == test::ODF && !aContentOUString.isEmpty() )
238     {
239         if( aContentOUString.indexOf("Error") != -1 )
240         {
241             SAL_WARN("test", aContentOUString);
242             CPPUNIT_FAIL(aContentString.getStr());
243         }
244     }
245     CPPUNIT_ASSERT_EQUAL_MESSAGE(
246         OString(
247             "failed to execute: " + OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8) + "\n"
248             + OUStringToOString(aContentOUString, RTL_TEXTENCODING_UTF8)).getStr(),
249         0, returnValue);
250 #else
251     (void)rPath;
252     (void)eFormat;
253 #endif
254 }
255 
256 IMPL_STATIC_LINK(
257         test::BootstrapFixture, ImplInitFilterHdl, ConvertData&, rData, bool)
258 {
259     return GraphicFilter::GetGraphicFilter().GetFilterCallback().Call( rData );
260 }
261 
262 bool test::BootstrapFixture::IsDefaultDPI()
263 {
264     return (Application::GetDefaultDevice()->GetDPIX() == 96
265             && Application::GetDefaultDevice()->GetDPIY() == 96);
266 }
267 
268 std::pair<double, double> test::BootstrapFixture::getDPIScaling()
269 {
270     return { Application::GetDefaultDevice()->GetDPIX() / 96.0,
271              Application::GetDefaultDevice()->GetDPIY() / 96.0 };
272 }
273 
274 sal_uInt16 test::BootstrapFixture::getDefaultDeviceBitCount()
275 {
276     ScopedVclPtr<VirtualDevice> device
277         = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
278     return device->GetBitCount();
279 }
280 
281 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
282