1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <config_validation.h>
12 #include <test/bootstrapfixture.hxx>
13 #include <vcl/errinf.hxx>
14 #include <sal/log.hxx>
15 #include <comphelper/processfactory.hxx>
17 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
18 #include <com/sun/star/ucb/XContentProvider.hpp>
19 #include <com/sun/star/ucb/XUniversalContentBroker.hpp>
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>
37 #include "setupvcl.hxx"
39 using namespace ::com::sun::star
;
41 static void aBasicErrorFunc( const OUString
&rErr
, const OUString
&rAction
)
43 OString aErr
= "Unexpected dialog: " +
44 OUStringToOString( rAction
, RTL_TEXTENCODING_ASCII_US
) +
46 OUStringToOString( rErr
, RTL_TEXTENCODING_ASCII_US
);
47 CPPUNIT_ASSERT_MESSAGE( aErr
.getStr(), false);
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
)
63 static void test_init_impl(bool bAssertOnDialog
, bool bNeedUCB
,
64 lang::XMultiServiceFactory
* pSFactory
)
67 ErrorRegistry::RegisterDisplay( aBasicErrorFunc
);
69 // Make GraphicConverter work, normally done in desktop::Desktop::Main()
70 Application::SetFilterHdl(
71 LINK(nullptr, test::BootstrapFixture
, ImplInitFilterHdl
));
75 // initialise unconfigured UCB:
76 uno::Reference
<ucb::XUniversalContentBroker
> xUcb(pSFactory
->createInstance(u
"com.sun.star.ucb.UniversalContentBroker"_ustr
), uno::UNO_QUERY_THROW
);
77 uno::Reference
<ucb::XContentProvider
> xFileProvider(pSFactory
->createInstance(u
"com.sun.star.ucb.FileContentProvider"_ustr
), uno::UNO_QUERY_THROW
);
78 xUcb
->registerContentProvider(xFileProvider
, u
"file"_ustr
, true);
79 uno::Reference
<ucb::XContentProvider
> xTdocProvider(pSFactory
->createInstance(u
"com.sun.star.ucb.TransientDocumentsContentProvider"_ustr
), uno::UNO_QUERY
);
80 if (xTdocProvider
.is())
82 xUcb
->registerContentProvider(xTdocProvider
, u
"vnd.sun.star.tdoc"_ustr
, true);
87 // this is called from pyuno
88 SAL_DLLPUBLIC_EXPORT
void test_init(lang::XMultiServiceFactory
*pFactory
)
92 ::comphelper::setProcessServiceFactory(pFactory
);
93 test::setUpVcl(true); // hard-code python tests to headless
94 test_init_impl(false, true, pFactory
);
96 catch (...) { abort(); }
99 // this is called from pyuno
100 SAL_DLLPUBLIC_EXPORT
void test_deinit()
107 void test::BootstrapFixture::setUp()
109 test::BootstrapFixtureBase::setUp();
111 test_init_impl(m_bAssertOnDialog
, m_bNeedUCB
, m_xSFactory
.get());
113 #if OSL_DEBUG_LEVEL > 0
114 Scheduler::ProcessEventsToIdle();
118 test::BootstrapFixture::~BootstrapFixture()
122 #if HAVE_EXPORT_VALIDATION
125 OString
loadFile(const OUString
& rURL
)
127 osl::File
aFile(rURL
);
128 osl::FileBase::RC eStatus
= aFile
.open(osl_File_OpenFlag_Read
);
129 CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None
, eStatus
);
131 aFile
.getSize(nSize
);
132 std::unique_ptr
<char[]> aBytes(new char[nSize
]);
133 sal_uInt64 nBytesRead
;
134 aFile
.read(aBytes
.get(), nSize
, nBytesRead
);
135 CPPUNIT_ASSERT_EQUAL(nSize
, nBytesRead
);
136 OString
aContent(aBytes
.get(), nBytesRead
);
141 constexpr std::u16string_view grand_total
= u
"Grand total of errors in submitted package: ";
143 OUString
filterOut(const OUString
& s
, std::u16string_view excludedSubstr
)
148 sal_Int32 pos
= result
.indexOf(excludedSubstr
);
151 sal_Int32 start
= result
.lastIndexOf('\n', pos
);
152 if (!result
.match("ERROR", start
+ 1))
153 return s
; // unexpected string format
154 sal_Int32 end
= result
.indexOf('\n', pos
);
155 result
= result
.replaceAt(start
, end
- start
, u
""_ustr
);
156 pos
= result
.lastIndexOf(grand_total
);
158 return s
; // unexpected string format
159 start
= end
= pos
+ grand_total
.size();
160 while (end
< result
.getLength() && rtl::isAsciiDigit(result
[end
]))
162 std::u16string_view aNumber
= result
.subView(start
, end
- start
);
163 sal_Int32 nErrors
= o3tl::toInt32(aNumber
) - 1;
164 result
= result
.replaceAt(start
, end
- start
, OUString::number(nErrors
));
169 OUString
filterValidationResults(const OUString
& s
)
172 // In ECMA-376-1 Second Edition, 2008, there is the following restriction for oleObj:
174 // <xsd:choice minOccurs="1" maxOccurs="1">
175 // <xsd:element name="embed" type="CT_OleObjectEmbed"/>
176 // <xsd:element name="link" type="CT_OleObjectLink"/>
177 // <xsd:element name="pic" type="CT_Picture"/>
180 // This makes simultaneous use of embed (or link) and pic impossible. This was obviously a
181 // mistake; and the following editions of standard fixed it: e.g., in ECMA-376-1:2016, that
184 // <xsd:choice minOccurs="1" maxOccurs="1">
185 // <xsd:element name="embed" type="CT_OleObjectEmbed"/>
186 // <xsd:element name="link" type="CT_OleObjectLink"/>
188 // <xsd:element name="pic" type="CT_Picture" minOccurs="1" maxOccurs="1"/>
190 // But officeotron only knows the old version...
191 result
= filterOut(result
, u
"Invalid content was found starting with element 'p:pic'. No child element is expected at this point.");
197 void test::BootstrapFixture::validate(const OUString
& rPath
, test::ValidationFormat eFormat
) const
199 #if HAVE_EXPORT_VALIDATION
201 if( eFormat
== test::OOXML
)
205 else if ( eFormat
== test::ODF
)
207 var
= "ODFVALIDATOR";
209 else if ( eFormat
== test::MSBINARY
)
211 #if HAVE_BFFVALIDATOR
212 var
= "BFFVALIDATOR";
214 // Binary Format Validator is disabled
219 oslProcessError e
= osl_getEnvironment(var
.pData
, &aValidator
.pData
);
220 CPPUNIT_ASSERT_EQUAL_MESSAGE(
221 OUString("cannot get env var " + var
).toUtf8().getStr(),
222 osl_Process_E_None
, e
);
223 CPPUNIT_ASSERT_MESSAGE(
224 OUString("empty get env var " + var
).toUtf8().getStr(),
225 !aValidator
.isEmpty());
227 if (eFormat
== test::ODF
)
229 // invoke without -e so that we know when something new is written
230 // in loext namespace that isn't yet in the custom schema
232 + m_directories
.getPathFromSrc(u
"/schema/libreoffice/OpenDocument-v1.4+libreoffice-manifest-schema.rng")
234 + m_directories
.getPathFromSrc(u
"/schema/libreoffice/OpenDocument-v1.4+libreoffice-dsig-schema.rng")
236 + m_directories
.getPathFromSrc(u
"/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng")
238 + m_directories
.getPathFromSrc(u
"/schema/mathml2/mathml2.xsd");
241 utl::TempFileNamed aOutput
;
242 aOutput
.EnableKillingFile();
243 OUString aOutputFile
= aOutput
.GetFileName();
244 OUString aCommand
= aValidator
+ " " + rPath
+ " > " + aOutputFile
+ " 2>&1";
247 // For now, this is only needed by some Linux ASan builds, so keep it simply and disable it on
248 // Windows (which doesn't support the relevant shell syntax for (un-)setting environment
251 if (test::getArgument(u
"env", &env
)) {
252 auto const n
= env
.indexOf('=');
254 aCommand
= "unset -v " + env
+ " && " + aCommand
;
256 aCommand
= env
+ " " + aCommand
;
261 SAL_INFO("test", "BootstrapFixture::validate: executing '" << aCommand
<< "'");
262 int returnValue
= system(OUStringToOString(aCommand
, RTL_TEXTENCODING_UTF8
).getStr());
264 OString aContentString
= loadFile(aOutput
.GetURL());
265 OUString aContentOUString
= OStringToOUString(aContentString
, RTL_TEXTENCODING_UTF8
);
267 if( eFormat
== test::OOXML
&& !aContentOUString
.isEmpty() )
269 aContentOUString
= filterValidationResults(aContentOUString
);
270 // check for validation errors here
271 sal_Int32 nIndex
= aContentOUString
.lastIndexOf(grand_total
);
274 SAL_WARN("test", "no summary line");
278 sal_Int32 nStartOfNumber
= nIndex
+ grand_total
.size();
279 std::u16string_view aNumber
= aContentOUString
.subView(nStartOfNumber
);
280 sal_Int32 nErrors
= o3tl::toInt32(aNumber
);
281 OString aMsg
= "validation error in OOXML export: Errors: " + OString::number(nErrors
);
284 SAL_WARN("test", aContentOUString
);
286 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), sal_Int32(0), nErrors
);
289 else if( eFormat
== test::ODF
&& !aContentOUString
.isEmpty() )
291 if( aContentOUString
.indexOf("Error") != -1 )
293 SAL_WARN("test", aContentOUString
);
294 CPPUNIT_FAIL(aContentString
.getStr());
297 CPPUNIT_ASSERT_EQUAL_MESSAGE(
299 "failed to execute: " + OUStringToOString(aCommand
, RTL_TEXTENCODING_UTF8
) + "\n"
300 + OUStringToOString(aContentOUString
, RTL_TEXTENCODING_UTF8
)).getStr(),
309 test::BootstrapFixture
, ImplInitFilterHdl
, ConvertData
&, rData
, bool)
311 return GraphicFilter::GetGraphicFilter().GetFilterCallback().Call( rData
);
314 bool test::BootstrapFixture::IsDefaultDPI()
316 return (Application::GetDefaultDevice()->GetDPIX() == 96
317 && Application::GetDefaultDevice()->GetDPIY() == 96);
320 std::pair
<double, double> test::BootstrapFixture::getDPIScaling()
322 return { Application::GetDefaultDevice()->GetDPIX() / 96.0,
323 Application::GetDefaultDevice()->GetDPIY() / 96.0 };
326 sal_uInt16
test::BootstrapFixture::getDefaultDeviceBitCount()
328 ScopedVclPtr
<VirtualDevice
> device
329 = VclPtr
<VirtualDevice
>::Create(DeviceFormat::WITHOUT_ALPHA
);
330 return device
->GetBitCount();
333 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */