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/algorithm/string.hpp>
53 :
54 : #include <algorithm>
55 :
56 : namespace {
57 :
58 0 : void usageFailure() {
59 : std::cerr
60 : << ("Usage: cppunittester (--protector <shared-library-path>"
61 0 : " <function-symbol>)* <shared-library-path>")
62 0 : << std::endl;
63 0 : std::exit(EXIT_FAILURE);
64 : }
65 :
66 1514 : rtl::OUString getArgument(sal_Int32 index) {
67 1514 : rtl::OUString arg;
68 1514 : rtl_getAppCommandArg(index, &arg.pData);
69 1514 : return arg;
70 : }
71 :
72 0 : std::string convertLazy(rtl::OUString const & s16) {
73 0 : rtl::OString s8(rtl::OUStringToOString(s16, osl_getThreadTextEncoding()));
74 : static_assert(sizeof (sal_Int32) <= sizeof (std::string::size_type), "must be at least the same size");
75 : // ensure following cast is legitimate
76 : return std::string(
77 0 : s8.getStr(), static_cast< std::string::size_type >(s8.getLength()));
78 : }
79 :
80 : #if defined TIMETESTS
81 : //Output how long each test took
82 : class TimingListener
83 : : public CppUnit::TestListener
84 : , private boost::noncopyable
85 : {
86 : public:
87 : void startTest( CppUnit::Test *) SAL_OVERRIDE
88 : {
89 : m_nStartTime = osl_getGlobalTimer();
90 : }
91 :
92 : void endTest( CppUnit::Test *test ) SAL_OVERRIDE
93 : {
94 : sal_uInt32 nEndTime = osl_getGlobalTimer();
95 : std::cout << test->getName() << ": " << nEndTime-m_nStartTime
96 : << "ms" << std::endl;
97 : }
98 :
99 : private:
100 : sal_uInt32 m_nStartTime;
101 : };
102 : #endif
103 :
104 : #ifdef UNX
105 : #include <stdlib.h>
106 : // Setup an env variable so that temp file (or other) can
107 : // have a useful value to identify the source
108 195 : class EyecatcherListener
109 : : public CppUnit::TestListener
110 : , private boost::noncopyable
111 : {
112 : public:
113 4326 : void startTest( CppUnit::Test* test) SAL_OVERRIDE
114 : {
115 4326 : boost::scoped_array<char> tn(new char [ test->getName().length() + 2 ]);
116 4326 : strcpy(tn.get(), test->getName().c_str());
117 4326 : int len = strlen(tn.get());
118 174194 : for(int i = 0; i < len; i++)
119 : {
120 169868 : if(!isalnum(tn[i]))
121 : {
122 19568 : tn[i] = '_';
123 : }
124 : }
125 4326 : tn[len] = '_';
126 4326 : tn[len + 1] = 0;
127 4326 : setenv("LO_TESTNAME", tn.get(), true);
128 4326 : }
129 :
130 4326 : void endTest( CppUnit::Test* /* test */ ) SAL_OVERRIDE
131 : {
132 4326 : }
133 : };
134 : #endif
135 :
136 195 : class LogFailuresAsTheyHappen : public CppUnit::TestListener
137 : {
138 : public:
139 2 : virtual void addFailure( const CppUnit::TestFailure &failure ) SAL_OVERRIDE
140 : {
141 2 : printFailureLocation( failure.sourceLine() );
142 2 : printFailedTestName( failure );
143 2 : printFailureMessage( failure );
144 2 : }
145 :
146 : private:
147 2 : static void printFailureLocation( const CppUnit::SourceLine &sourceLine )
148 : {
149 2 : if ( !sourceLine.isValid() )
150 1 : std::cerr << "unknown:0:";
151 : else
152 1 : std::cerr << sourceLine.fileName() << ":" << sourceLine.lineNumber() << ":";
153 2 : }
154 :
155 2 : static void printFailedTestName( const CppUnit::TestFailure &failure )
156 : {
157 2 : std::cerr << failure.failedTestName() << std::endl;
158 2 : }
159 :
160 2 : static void printFailureMessage( const CppUnit::TestFailure &failure )
161 : {
162 2 : std::cerr << failure.thrownException()->message().shortDescription() << std::endl;
163 2 : std::cerr << failure.thrownException()->message().details() << std::endl;
164 2 : }
165 : };
166 :
167 : namespace {
168 :
169 0 : void addRecursiveTests(const std::vector<std::string>& test_names, CppUnit::Test* pTest, CppUnit::TestRunner& rRunner)
170 : {
171 0 : for (int i = 0; i < pTest->getChildTestCount(); ++i)
172 : {
173 0 : CppUnit::Test* pNewTest = pTest->getChildTestAt(i);
174 0 : addRecursiveTests(test_names, pNewTest, rRunner);
175 0 : if (std::find(test_names.begin(), test_names.end(), pNewTest->getName()) != test_names.end())
176 0 : rRunner.addTest(pNewTest);
177 : }
178 0 : }
179 :
180 : }
181 :
182 : //Allow the whole uniting testing framework to be run inside a "Protector"
183 : //which knows about uno exceptions, so it can print the content of the
184 : //exception before falling over and dying
185 195 : class CPPUNIT_API ProtectedFixtureFunctor
186 : : public CppUnit::Functor
187 : , private boost::noncopyable
188 : {
189 : private:
190 : const std::string &testlib;
191 : const std::string &args;
192 : std::vector<CppUnit::Protector *> &protectors;
193 : CppUnit::TestResult &result;
194 : public:
195 195 : ProtectedFixtureFunctor(const std::string& testlib_, const std::string &args_, std::vector<CppUnit::Protector*> &protectors_, CppUnit::TestResult &result_)
196 : : testlib(testlib_)
197 : , args(args_)
198 : , protectors(protectors_)
199 195 : , result(result_)
200 : {
201 195 : }
202 195 : bool run() const
203 : {
204 : #ifdef DISABLE_DYNLOADING
205 :
206 : // NOTE: Running cppunit unit tests on iOS was something I did
207 : // only very early (several years ago) when starting porting
208 : // this stuff to iOS. The complicated mechanisms to do build
209 : // such unit test single executables have surely largely
210 : // bit-rotted or been semi-intentionally broken since. This
211 : // stuff here left for information only. --tml 2014.
212 :
213 : // For iOS cppunit plugins aren't really "plugins" (shared
214 : // libraries), but just static archives. In the real main
215 : // program of a cppunit app, which calls the lo_main() that
216 : // the SAL_IMPLEMENT_MAIN() below expands to, we specifically
217 : // call the initialize methods of the CppUnitTestPlugIns that
218 : // we statically link to the app executable.
219 : #else
220 195 : CppUnit::PlugInManager manager;
221 : try {
222 195 : manager.load(testlib, args);
223 0 : } catch (const CppUnit::DynamicLibraryManagerException &e) {
224 0 : std::cerr << "DynamicLibraryManagerException: \"" << e.what() << "\"\n";
225 : #ifdef WIN32
226 : const char *pPath = getenv ("PATH");
227 : if (pPath && strlen (pPath) > 256)
228 : {
229 : std::cerr << "Windows has significant build problems with long PATH variables ";
230 : std::cerr << "please check your PATH variable and re-autogen.\n";
231 : }
232 : #endif
233 0 : std::cerr << "Path is '" << getenv("PATH") << "'\n";
234 0 : return false;
235 : }
236 : #endif
237 :
238 570 : for (size_t i = 0; i < protectors.size(); ++i)
239 375 : result.pushProtector(protectors[i]);
240 :
241 : bool success;
242 : {
243 195 : CppUnit::TestResultCollector collector;
244 195 : result.addListener(&collector);
245 :
246 390 : LogFailuresAsTheyHappen logger;
247 195 : result.addListener(&logger);
248 :
249 : #ifdef TIMETESTS
250 : TimingListener timer;
251 : result.addListener(&timer);
252 : #endif
253 :
254 : #ifdef UNX
255 390 : EyecatcherListener eye;
256 195 : result.addListener(&eye);
257 : // set this to track down files created before first test method
258 390 : std::string lib(testlib.substr(testlib.rfind('/')+1));
259 195 : setenv("LO_TESTNAME", lib.c_str(), true);
260 : #endif
261 :
262 195 : const char* pVal = getenv("CPPUNIT_TEST_NAME");
263 :
264 390 : CppUnit::TestRunner runner;
265 195 : if (pVal)
266 : {
267 0 : std::vector<std::string> test_names;
268 0 : boost::split(test_names, pVal, boost::is_any_of("\t "));
269 0 : CppUnit::Test* pTest = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
270 0 : addRecursiveTests(test_names, pTest, runner);
271 : }
272 : else
273 : {
274 195 : runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
275 : }
276 195 : runner.run(result);
277 :
278 390 : CppUnit::CompilerOutputter outputter(&collector, CppUnit::stdCErr());
279 195 : outputter.setNoWrap();
280 195 : outputter.write();
281 390 : success = collector.wasSuccessful();
282 : }
283 :
284 570 : for (size_t i = 0; i < protectors.size(); ++i)
285 375 : result.popProtector();
286 :
287 195 : return success;
288 : }
289 0 : virtual bool operator()() const SAL_OVERRIDE
290 : {
291 0 : return run();
292 : }
293 : };
294 :
295 : }
296 :
297 390 : SAL_IMPLEMENT_MAIN()
298 : {
299 195 : bool ok = false;
300 : try
301 : {
302 : #ifdef WNT
303 : //Disable Dr-Watson in order to crash simply without popup dialogs under
304 : //windows
305 : DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
306 : SetErrorMode(SEM_NOGPFAULTERRORBOX|dwMode);
307 : #ifdef _DEBUG // These functions are present only in the debgging runtime
308 : _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
309 : _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
310 : _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
311 : _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
312 : _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
313 : _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
314 : #endif
315 : #endif
316 :
317 195 : std::vector<CppUnit::Protector *> protectors;
318 390 : CppUnit::TestResult result;
319 390 : std::string args;
320 390 : std::string testlib;
321 195 : sal_uInt32 index = 0;
322 1154 : while (index < rtl_getAppCommandArgCount())
323 : {
324 764 : rtl::OUString arg = getArgument(index);
325 764 : if ( arg != "--protector" )
326 : {
327 389 : if (testlib.empty())
328 : {
329 195 : testlib = rtl::OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
330 195 : args += testlib;
331 : }
332 : else
333 : {
334 194 : args += ' ';
335 194 : args += rtl::OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
336 : }
337 389 : ++index;
338 389 : continue;
339 : }
340 375 : if (rtl_getAppCommandArgCount() - index < 3) {
341 0 : usageFailure();
342 : }
343 750 : rtl::OUString lib(getArgument(index + 1));
344 750 : rtl::OUString sym(getArgument(index + 2));
345 : #ifndef DISABLE_DYNLOADING
346 750 : osl::Module mod(lib, SAL_LOADMODULE_GLOBAL);
347 375 : oslGenericFunction fn = mod.getFunctionSymbol(sym);
348 375 : mod.release();
349 : #else
350 : oslGenericFunction fn = 0;
351 : if (sym == "unoexceptionprotector")
352 : fn = (oslGenericFunction) unoexceptionprotector;
353 : else if (sym == "unobootstrapprotector")
354 : fn = (oslGenericFunction) unobootstrapprotector;
355 : else if (sym == "vclbootstrapprotector")
356 : fn = (oslGenericFunction) vclbootstrapprotector;
357 : else
358 : {
359 : std::cerr
360 : << "Only unoexceptionprotector or unobootstrapprotector protectors allowed"
361 : << std::endl;
362 : std::exit(EXIT_FAILURE);
363 : }
364 : #endif
365 : CppUnit::Protector *protector = fn == 0
366 : ? 0
367 375 : : (*reinterpret_cast< cppunittester::ProtectorFactory * >(fn))();
368 375 : if (protector == 0) {
369 : std::cerr
370 0 : << "Failure instantiating protector \"" << convertLazy(lib)
371 0 : << "\", \"" << convertLazy(sym) << '"' << std::endl;
372 0 : std::exit(EXIT_FAILURE);
373 : }
374 375 : protectors.push_back(protector);
375 375 : index+=3;
376 375 : }
377 :
378 390 : ProtectedFixtureFunctor tests(testlib, args, protectors, result);
379 390 : ok = tests.run();
380 : }
381 0 : catch (const std::exception& e)
382 : {
383 : SAL_WARN("vcl.app", "Fatal exception: " << e.what());
384 : }
385 :
386 195 : return ok ? EXIT_SUCCESS : EXIT_FAILURE;
387 585 : }
388 :
389 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|