Line data Source code
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 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #ifdef WNT
21 : #include <windows.h>
22 : #endif
23 :
24 : #include <cstdlib>
25 : #include <iostream>
26 : #include <string>
27 : #include <sal/types.h>
28 : #include "cppunittester/protectorfactory.hxx"
29 : #include "osl/module.h"
30 : #include "osl/module.hxx"
31 : #include "osl/thread.h"
32 : #include "rtl/process.h"
33 : #include "rtl/string.h"
34 : #include "rtl/string.hxx"
35 : #include "rtl/textcvt.h"
36 : #include "rtl/ustring.hxx"
37 : #include "sal/main.h"
38 :
39 : #include "cppunit/CompilerOutputter.h"
40 : #include "cppunit/Exception.h"
41 : #include "cppunit/TestFailure.h"
42 : #include "cppunit/TestResult.h"
43 : #include "cppunit/TestResultCollector.h"
44 : #include "cppunit/TestRunner.h"
45 : #include "cppunit/extensions/TestFactoryRegistry.h"
46 : #include "cppunit/plugin/PlugInManager.h"
47 : #include "cppunit/plugin/DynamicLibraryManagerException.h"
48 : #include "cppunit/portability/Stream.h"
49 :
50 : #include "boost/noncopyable.hpp"
51 : #include <boost/scoped_array.hpp>
52 : #include "boost/static_assert.hpp"
53 :
54 : namespace {
55 :
56 0 : void usageFailure() {
57 : std::cerr
58 : << ("Usage: cppunittester (--protector <shared-library-path>"
59 0 : " <function-symbol>)* <shared-library-path>")
60 0 : << std::endl;
61 0 : std::exit(EXIT_FAILURE);
62 : }
63 :
64 2616 : rtl::OUString getArgument(sal_Int32 index) {
65 2616 : rtl::OUString arg;
66 2616 : rtl_getAppCommandArg(index, &arg.pData);
67 2616 : return arg;
68 : }
69 :
70 0 : std::string convertLazy(rtl::OUString const & s16) {
71 0 : rtl::OString s8(rtl::OUStringToOString(s16, osl_getThreadTextEncoding()));
72 : BOOST_STATIC_ASSERT(sizeof (sal_Int32) <= sizeof (std::string::size_type));
73 : // ensure following cast is legitimate
74 : return std::string(
75 0 : s8.getStr(), static_cast< std::string::size_type >(s8.getLength()));
76 : }
77 :
78 : #if defined TIMETESTS
79 : //Output how long each test took
80 : class TimingListener
81 : : public CppUnit::TestListener
82 : , private boost::noncopyable
83 : {
84 : public:
85 : void startTest( CppUnit::Test *) SAL_OVERRIDE
86 : {
87 : m_nStartTime = osl_getGlobalTimer();
88 : }
89 :
90 : void endTest( CppUnit::Test *test ) SAL_OVERRIDE
91 : {
92 : sal_uInt32 nEndTime = osl_getGlobalTimer();
93 : std::cout << test->getName() << ": " << nEndTime-m_nStartTime
94 : << "ms" << std::endl;
95 : }
96 :
97 : private:
98 : sal_uInt32 m_nStartTime;
99 : };
100 : #endif
101 :
102 : #ifdef UNX
103 : #include <stdlib.h>
104 : // Setup an env variable so that temp file (or other) can
105 : // have a usefull value to identify the source
106 348 : class EyecatcherListener
107 : : public CppUnit::TestListener
108 : , private boost::noncopyable
109 : {
110 : public:
111 7758 : void startTest( CppUnit::Test* test) SAL_OVERRIDE
112 : {
113 7758 : boost::scoped_array<char> tn(new char [ test->getName().length() + 2 ]);
114 7758 : strcpy(tn.get(), test->getName().c_str());
115 7758 : int len = strlen(tn.get());
116 319450 : for(int i = 0; i < len; i++)
117 : {
118 311692 : if(!isalnum(tn[i]))
119 : {
120 30674 : tn[i] = '_';
121 : }
122 : }
123 7758 : tn[len] = '_';
124 7758 : tn[len + 1] = 0;
125 7758 : setenv("LO_TESTNAME", tn.get(), true);
126 7758 : }
127 :
128 7758 : void endTest( CppUnit::Test* /* test */ ) SAL_OVERRIDE
129 : {
130 7758 : }
131 : };
132 : #endif
133 :
134 348 : class LogFailuresAsTheyHappen : public CppUnit::TestListener
135 : {
136 : public:
137 0 : virtual void addFailure( const CppUnit::TestFailure &failure ) SAL_OVERRIDE
138 : {
139 0 : printFailureLocation( failure.sourceLine() );
140 0 : printFailedTestName( failure );
141 0 : printFailureMessage( failure );
142 0 : }
143 :
144 : private:
145 0 : void printFailureLocation( const CppUnit::SourceLine &sourceLine )
146 : {
147 0 : if ( !sourceLine.isValid() )
148 0 : std::cerr << "unknown:0:";
149 : else
150 0 : std::cerr << sourceLine.fileName() << ":" << sourceLine.lineNumber() << ":";
151 0 : }
152 :
153 0 : void printFailedTestName( const CppUnit::TestFailure &failure )
154 : {
155 0 : std::cerr << failure.failedTestName() << std::endl;
156 0 : }
157 :
158 0 : void printFailureMessage( const CppUnit::TestFailure &failure )
159 : {
160 0 : std::cerr << failure.thrownException()->message().shortDescription() << std::endl;
161 0 : std::cerr << failure.thrownException()->message().details() << std::endl;
162 0 : }
163 : };
164 :
165 : //Allow the whole uniting testing framework to be run inside a "Protector"
166 : //which knows about uno exceptions, so it can print the content of the
167 : //exception before falling over and dying
168 348 : class CPPUNIT_API ProtectedFixtureFunctor
169 : : public CppUnit::Functor
170 : , private boost::noncopyable
171 : {
172 : private:
173 : const std::string &testlib;
174 : const std::string &args;
175 : std::vector<CppUnit::Protector *> &protectors;
176 : CppUnit::TestResult &result;
177 : public:
178 348 : ProtectedFixtureFunctor(const std::string& testlib_, const std::string &args_, std::vector<CppUnit::Protector*> &protectors_, CppUnit::TestResult &result_)
179 : : testlib(testlib_)
180 : , args(args_)
181 : , protectors(protectors_)
182 348 : , result(result_)
183 : {
184 348 : }
185 348 : bool run() const
186 : {
187 : #ifdef DISABLE_DYNLOADING
188 :
189 : // NOTE: Running cppunit unit tests on iOS was something I did
190 : // only very early (several years ago) when starting porting
191 : // this stuff to iOS. The complicated mechanisms to do build
192 : // such unit test single executables have surely largely
193 : // bit-rotted or been semi-intentionally broken since. This
194 : // stuff here left for information only. --tml 2014.
195 :
196 : // For iOS cppunit plugins aren't really "plugins" (shared
197 : // libraries), but just static archives. In the real main
198 : // program of a cppunit app, which calls the lo_main() that
199 : // the SAL_IMPLEMENT_MAIN() below expands to, we specifically
200 : // call the initialize methods of the CppUnitTestPlugIns that
201 : // we statically link to the app executable.
202 : #else
203 348 : CppUnit::PlugInManager manager;
204 : try {
205 348 : manager.load(testlib, args);
206 0 : } catch (const CppUnit::DynamicLibraryManagerException &e) {
207 0 : std::cerr << "DynamicLibraryManagerException: \"" << e.what() << "\"\n";
208 : #ifdef WIN32
209 : const char *pPath = getenv ("PATH");
210 : if (pPath && strlen (pPath) > 256)
211 : {
212 : std::cerr << "Windows has significant build problems with long PATH variables ";
213 : std::cerr << "please check your PATH variable and re-autogen.\n";
214 : }
215 : #endif
216 0 : std::cerr << "Path is '" << getenv("PATH") << "'\n";
217 0 : return false;
218 : }
219 : #endif
220 :
221 988 : for (size_t i = 0; i < protectors.size(); ++i)
222 640 : result.pushProtector(protectors[i]);
223 :
224 : bool success;
225 : {
226 348 : CppUnit::TestResultCollector collector;
227 348 : result.addListener(&collector);
228 :
229 696 : LogFailuresAsTheyHappen logger;
230 348 : result.addListener(&logger);
231 :
232 : #ifdef TIMETESTS
233 : TimingListener timer;
234 : result.addListener(&timer);
235 : #endif
236 :
237 : #ifdef UNX
238 696 : EyecatcherListener eye;
239 348 : result.addListener(&eye);
240 : // set this to track down files created before first test method
241 696 : std::string lib(testlib.substr(testlib.rfind('/')+1));
242 348 : setenv("LO_TESTNAME", lib.c_str(), true);
243 : #endif
244 :
245 696 : CppUnit::TestRunner runner;
246 : runner.addTest(
247 348 : CppUnit::TestFactoryRegistry::getRegistry().makeTest());
248 348 : runner.run(result);
249 :
250 696 : CppUnit::CompilerOutputter outputter(&collector, CppUnit::stdCErr());
251 348 : outputter.setNoWrap();
252 348 : outputter.write();
253 696 : success = collector.wasSuccessful();
254 : }
255 :
256 988 : for (size_t i = 0; i < protectors.size(); ++i)
257 640 : result.popProtector();
258 :
259 348 : return success;
260 : }
261 0 : virtual bool operator()() const SAL_OVERRIDE
262 : {
263 0 : return run();
264 : }
265 : };
266 :
267 : }
268 :
269 696 : SAL_IMPLEMENT_MAIN() {
270 : #ifdef WNT
271 : //Disable Dr-Watson in order to crash simply without popup dialogs under
272 : //windows
273 : DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
274 : SetErrorMode(SEM_NOGPFAULTERRORBOX|dwMode);
275 : #ifdef _DEBUG // These functions are present only in the debgging runtime
276 : _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
277 : _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
278 : _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
279 : _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
280 : _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
281 : _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
282 : #endif
283 : #endif
284 :
285 348 : std::vector<CppUnit::Protector *> protectors;
286 696 : CppUnit::TestResult result;
287 696 : std::string args;
288 696 : std::string testlib;
289 348 : sal_uInt32 index = 0;
290 2032 : while (index < rtl_getAppCommandArgCount())
291 : {
292 1336 : rtl::OUString arg = getArgument(index);
293 1336 : if ( arg != "--protector" )
294 : {
295 696 : if (testlib.empty())
296 : {
297 348 : testlib = rtl::OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
298 348 : args += testlib;
299 : }
300 : else
301 : {
302 348 : args += ' ';
303 348 : args += rtl::OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
304 : }
305 696 : ++index;
306 696 : continue;
307 : }
308 640 : if (rtl_getAppCommandArgCount() - index < 3) {
309 0 : usageFailure();
310 : }
311 1280 : rtl::OUString lib(getArgument(index + 1));
312 1280 : rtl::OUString sym(getArgument(index + 2));
313 : #ifndef DISABLE_DYNLOADING
314 1280 : osl::Module mod(lib, SAL_LOADMODULE_GLOBAL);
315 640 : oslGenericFunction fn = mod.getFunctionSymbol(sym);
316 640 : mod.release();
317 : #else
318 : oslGenericFunction fn = 0;
319 : if (sym == "unoexceptionprotector")
320 : fn = (oslGenericFunction) unoexceptionprotector;
321 : else if (sym == "unobootstrapprotector")
322 : fn = (oslGenericFunction) unobootstrapprotector;
323 : else if (sym == "vclbootstrapprotector")
324 : fn = (oslGenericFunction) vclbootstrapprotector;
325 : else
326 : {
327 : std::cerr
328 : << "Only unoexceptionprotector or unobootstrapprotector protectors allowed"
329 : << std::endl;
330 : std::exit(EXIT_FAILURE);
331 : }
332 : #endif
333 : CppUnit::Protector *protector = fn == 0
334 : ? 0
335 640 : : (*reinterpret_cast< cppunittester::ProtectorFactory * >(fn))();
336 640 : if (protector == 0) {
337 : std::cerr
338 0 : << "Failure instantiating protector \"" << convertLazy(lib)
339 0 : << "\", \"" << convertLazy(sym) << '"' << std::endl;
340 0 : std::exit(EXIT_FAILURE);
341 : }
342 640 : protectors.push_back(protector);
343 640 : index+=3;
344 640 : }
345 :
346 696 : ProtectedFixtureFunctor tests(testlib, args, protectors, result);
347 348 : bool ok = tests.run();
348 :
349 696 : return ok ? EXIT_SUCCESS : EXIT_FAILURE;
350 1044 : }
351 :
352 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|