1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qdir.h>
32#include <qlibrary.h>
33#include <QtCore/QRegularExpression>
34
35
36// Helper macros to let us know if some suffixes and prefixes are valid
37#define bundle_VALID false
38#define dylib_VALID false
39#define sl_VALID false
40#define a_VALID false
41#define so_VALID false
42#define dll_VALID false
43#define DLL_VALID false
44
45#if defined(Q_OS_DARWIN)
46# undef bundle_VALID
47# undef dylib_VALID
48# undef so_VALID
49# define bundle_VALID true
50# define dylib_VALID true
51# define so_VALID true
52# define SUFFIX ".dylib"
53# define PREFIX "lib"
54
55#elif defined(Q_OS_HPUX)
56# undef sl_VALID
57# define sl_VALID true
58# ifndef __ia64
59# define SUFFIX ".sl"
60# define PREFIX "lib"
61# else
62# undef so_VALID
63# define so_VALID true
64# define SUFFIX ".so"
65# define PREFIX "lib"
66# endif
67
68#elif defined(Q_OS_AIX)
69# undef a_VALID
70# undef so_VALID
71# define a_VALID true
72# define so_VALID true
73# define SUFFIX ".a"
74# define PREFIX "lib"
75
76#elif defined(Q_OS_WIN)
77# undef dll_VALID
78# define dll_VALID true
79# undef DLL_VALID
80# define DLL_VALID true
81# define SUFFIX ".dll"
82# define PREFIX ""
83
84#else // all other Unix
85# undef so_VALID
86# define so_VALID true
87# define SUFFIX ".so"
88# define PREFIX "lib"
89#endif
90
91QT_FORWARD_DECLARE_CLASS(QLibrary)
92class tst_QLibrary : public QObject
93{
94 Q_OBJECT
95
96enum QLibraryOperation {
97 Load = 1,
98 Unload = 2,
99 Resolve = 3,
100 OperationMask = 7,
101 DontSetFileName = 0x100
102};
103
104 QString sys_qualifiedLibraryName(const QString &fileName);
105
106 QString directory;
107#ifdef Q_OS_ANDROID
108 QSharedPointer<QTemporaryDir> temporaryDir;
109#endif
110private slots:
111 void initTestCase();
112
113 void load();
114 void load_data();
115 void library_data();
116 void resolve_data();
117 void resolve();
118 void unload_data();
119 void unload();
120 void unload_after_implicit_load();
121 void isLibrary_data();
122 void isLibrary();
123 void version_data();
124 void version();
125 void errorString_data();
126 void errorString();
127 void loadHints();
128 void loadHints_data();
129 void fileName_data();
130 void fileName();
131 void multipleInstancesForOneLibrary();
132};
133
134QString tst_QLibrary::sys_qualifiedLibraryName(const QString &fileName)
135{
136 return directory + QLatin1Char('/') + PREFIX + fileName + SUFFIX;
137}
138
139typedef int (*VersionFunction)(void);
140
141void tst_QLibrary::initTestCase()
142{
143#ifdef Q_OS_ANDROID
144 auto tempDir = QEXTRACTTESTDATA("android_test_data");
145
146 QVERIFY2(QDir::setCurrent(tempDir->path()), qPrintable("Could not chdir to " + tempDir->path()));
147
148 // copy :/library_path into ./library_path
149 QVERIFY(QDir().mkdir("library_path"));
150 QDirIterator iterator(":/library_path", QDirIterator::Subdirectories);
151 while (iterator.hasNext()) {
152 iterator.next();
153 QFileInfo sourceFileInfo(iterator.path());
154 QFileInfo targetFileInfo("./library_path/" + sourceFileInfo.fileName());
155 if (!targetFileInfo.exists()) {
156 QDir().mkpath(targetFileInfo.path());
157 QVERIFY(QFile::copy(sourceFileInfo.filePath(), targetFileInfo.filePath()));
158 }
159 }
160 directory = tempDir->path();
161 temporaryDir = std::move(tempDir);
162#elif !defined(Q_OS_WINRT)
163 // chdir to our testdata directory, and use relative paths in some tests.
164 QString testdatadir = QFileInfo(QFINDTESTDATA("library_path")).absolutePath();
165 QVERIFY2(QDir::setCurrent(testdatadir), qPrintable("Could not chdir to " + testdatadir));
166 directory = QCoreApplication::applicationDirPath();
167#elif defined(Q_OS_WINRT)
168 directory = QCoreApplication::applicationDirPath();
169#endif
170}
171
172void tst_QLibrary::version_data()
173{
174#ifdef Q_OS_ANDROID
175 QSKIP("Versioned .so files are not generated for Android, so this test is not applicable.");
176#endif
177 QTest::addColumn<QString>(name: "lib");
178 QTest::addColumn<int>(name: "loadversion");
179 QTest::addColumn<int>(name: "resultversion");
180
181 QTest::newRow( dataTag: "ok00, version 1" ) << "mylib" << 1 << 1;
182 QTest::newRow( dataTag: "ok00, version 2" ) << "mylib" << 2 << 2;
183 QTest::newRow( dataTag: "ok00, load without version" ) << "mylib" << -1 << 2;
184}
185
186void tst_QLibrary::version()
187{
188 QFETCH( QString, lib );
189 QFETCH( int, loadversion );
190 QFETCH( int, resultversion );
191
192#if !defined(Q_OS_AIX) && !defined(Q_OS_WIN)
193 QString appDir = directory;
194 QLibrary library( appDir + QLatin1Char('/') + lib, loadversion );
195 QVERIFY2(library.load(), qPrintable(library.errorString()));
196
197 VersionFunction fnVersion = (VersionFunction)library.resolve(symbol: "mylibversion");
198 QVERIFY(fnVersion);
199 QCOMPARE(fnVersion(), resultversion);
200 QVERIFY2(library.unload(), qPrintable(library.errorString()));
201#else
202 Q_UNUSED(lib);
203 Q_UNUSED(loadversion);
204 Q_UNUSED(resultversion);
205#endif
206}
207
208void tst_QLibrary::load_data()
209{
210 QTest::addColumn<QString>(name: "lib");
211 QTest::addColumn<bool>(name: "result");
212
213 QString appDir = directory;
214
215 QTest::newRow( dataTag: "ok00" ) << appDir + "/mylib" << true;
216 QTest::newRow( dataTag: "notexist" ) << appDir + "/nolib" << false;
217 QTest::newRow( dataTag: "badlibrary" ) << appDir + "/qlibrary.pro" << false;
218
219#ifdef Q_OS_MAC
220 QTest::newRow("ok (libmylib ver. 1)") << appDir + "/libmylib" <<true;
221#endif
222
223# if defined(Q_OS_WIN32)
224 QTest::newRow( "ok01 (with suffix)" ) << appDir + "/mylib.dll" << true;
225 QTest::newRow( "ok02 (with non-standard suffix)" ) << appDir + "/mylib.dl2" << true;
226 QTest::newRow( "ok03 (with many dots)" ) << appDir + "/system.qt.test.mylib.dll" << true;
227# elif defined Q_OS_UNIX
228 QTest::newRow( dataTag: "ok01 (with suffix)" ) << appDir + "/libmylib" SUFFIX << true;
229 QTest::newRow( dataTag: "ok02 (with non-standard suffix)" ) << appDir + "/libmylib.so2" << true;
230 QTest::newRow( dataTag: "ok03 (with many dots)" ) << appDir + "/system.qt.test.mylib.so" << true;
231# endif // Q_OS_UNIX
232}
233
234void tst_QLibrary::load()
235{
236 QFETCH( QString, lib );
237 QFETCH( bool, result );
238
239 QLibrary library( lib );
240 bool ok = library.load();
241 if ( result ) {
242 QVERIFY2( ok, qPrintable(library.errorString()) );
243 QVERIFY2( library.unload(), qPrintable(library.errorString()) );
244 } else {
245 QVERIFY( !ok );
246 }
247}
248
249void tst_QLibrary::unload_data()
250{
251 QTest::addColumn<QString>(name: "lib");
252 QTest::addColumn<bool>(name: "result");
253
254 QString appDir = directory;
255
256 QTest::newRow( dataTag: "mylib" ) << appDir + "/mylib" << true;
257 QTest::newRow( dataTag: "ok01" ) << appDir + "/nolib" << false;
258}
259
260void tst_QLibrary::unload()
261{
262 QFETCH( QString, lib );
263 QFETCH( bool, result );
264
265 QLibrary library( lib );
266 library.load();
267 bool ok = library.unload();
268 if ( result ) {
269 QVERIFY2( ok, qPrintable(library.errorString()) );
270 } else {
271 QVERIFY( !ok );
272 }
273}
274
275void tst_QLibrary::unload_after_implicit_load()
276{
277 QLibrary library( directory + "/mylib" );
278 QFunctionPointer p = library.resolve(symbol: "mylibversion");
279 QVERIFY(p); // Check if it was loaded
280 QVERIFY(library.isLoaded());
281 QVERIFY(library.unload());
282 QCOMPARE(library.isLoaded(), false);
283}
284
285void tst_QLibrary::resolve_data()
286{
287 QTest::addColumn<QString>(name: "lib");
288 QTest::addColumn<QString>(name: "symbol");
289 QTest::addColumn<bool>(name: "goodPointer");
290
291 QString appDir = directory;
292
293 QTest::newRow( dataTag: "ok00" ) << appDir + "/mylib" << QString("mylibversion") << true;
294 QTest::newRow( dataTag: "bad00" ) << appDir + "/mylib" << QString("nosym") << false;
295 QTest::newRow( dataTag: "bad01" ) << appDir + "/nolib" << QString("nosym") << false;
296}
297
298void tst_QLibrary::resolve()
299{
300 typedef int (*testFunc)();
301 QFETCH( QString, lib );
302 QFETCH( QString, symbol );
303 QFETCH( bool, goodPointer );
304
305 QLibrary library( lib );
306 testFunc func = (testFunc) library.resolve( symbol: symbol.toLatin1() );
307 if ( goodPointer ) {
308 QVERIFY( func != 0 );
309 } else {
310 QVERIFY( func == 0 );
311 }
312 library.unload();
313}
314
315void tst_QLibrary::library_data()
316{
317 QTest::addColumn<QString>(name: "lib");
318}
319
320void tst_QLibrary::isLibrary_data()
321{
322 QTest::addColumn<QString>(name: "filename");
323 QTest::addColumn<bool>(name: "valid");
324
325 // use the macros #defined at the top of the file
326 QTest::newRow(dataTag: "bad") << QString("mylib.bad") << false;
327 QTest::newRow(dataTag: ".a") << QString("mylib.a") << a_VALID;
328 QTest::newRow(dataTag: ".bundle") << QString("mylib.bundle") << bundle_VALID;
329 QTest::newRow(dataTag: ".dll") << QString("mylib.dll") << dll_VALID;
330 QTest::newRow(dataTag: ".DLL") << QString("MYLIB.DLL") << DLL_VALID;
331 QTest::newRow(dataTag: ".dl2" ) << QString("mylib.dl2") << false;
332 QTest::newRow(dataTag: ".dylib") << QString("mylib.dylib") << dylib_VALID;
333 QTest::newRow(dataTag: ".sl") << QString("mylib.sl") << sl_VALID;
334 QTest::newRow(dataTag: ".so") << QString("mylib.so") << so_VALID;
335 QTest::newRow(dataTag: ".so+version") << QString("mylib.so.0") << so_VALID;
336 QTest::newRow(dataTag: "version+.so") << QString("libc-2.7.so") << so_VALID;
337 QTest::newRow(dataTag: "version+.so+version") << QString("liboil-0.3.so.0.1.0") << so_VALID;
338
339 // special tests:
340#ifdef Q_OS_MAC
341 QTest::newRow("good (libmylib.1.0.0.dylib)") << QString("libmylib.1.0.0.dylib") << true;
342 QTest::newRow("good (libmylib.dylib)") << QString("libmylib.dylib") << true;
343 QTest::newRow("good (libmylib.so)") << QString("libmylib.so") << true;
344 QTest::newRow("good (libmylib.so.1.0.0)") << QString("libmylib.so.1.0.0") << true;
345
346 QTest::newRow("bad (libmylib.1.0.0.foo)") << QString("libmylib.1.0.0.foo") << false;
347#elif defined(Q_OS_WIN)
348 QTest::newRow("good (with many dots)" ) << "/system.qt.test.mylib.dll" << true;
349#endif
350}
351
352void tst_QLibrary::isLibrary()
353{
354 QFETCH( QString, filename );
355 QFETCH( bool, valid );
356
357 QCOMPARE(QLibrary::isLibrary(filename), valid);
358}
359
360void tst_QLibrary::errorString_data()
361{
362 QTest::addColumn<int>(name: "operation");
363 QTest::addColumn<QString>(name: "fileName");
364 QTest::addColumn<bool>(name: "success");
365 QTest::addColumn<QString>(name: "errorString");
366
367 QString appDir = directory;
368
369 QTest::newRow(dataTag: "bad load()") << (int)Load << QString("nosuchlib") << false << QString("Cannot load library nosuchlib: .*");
370 QTest::newRow(dataTag: "call errorString() on QLibrary with no d-pointer (crashtest)") << (int)(Load | DontSetFileName) << QString() << false << QString("Unknown error");
371 QTest::newRow(dataTag: "bad resolve") << (int)Resolve << appDir + "/mylib" << false << QString("Unknown error");
372 QTest::newRow(dataTag: "good resolve") << (int)Resolve << appDir + "/mylib" << true << QString("Unknown error");
373
374#ifdef Q_OS_WIN
375 QTest::newRow("bad load() with .dll suffix") << (int)Load << QString("nosuchlib.dll") << false << QString("Cannot load library nosuchlib.dll: The specified module could not be found.");
376// QTest::newRow("bad unload") << (int)Unload << QString("nosuchlib.dll") << false << QString("QLibrary::unload_sys: Cannot unload nosuchlib.dll (The specified module could not be found.)");
377#elif defined Q_OS_MAC
378#else
379 QTest::newRow(dataTag: "load invalid file") << (int)Load << QFINDTESTDATA("library_path/invalid.so") << false << QString("Cannot load library.*");
380#endif
381}
382
383void tst_QLibrary::errorString()
384{
385 QFETCH(int, operation);
386 QFETCH(QString, fileName);
387 QFETCH(bool, success);
388 QFETCH(QString, errorString);
389
390 QLibrary lib;
391 if (!(operation & DontSetFileName)) {
392 lib.setFileName(fileName);
393 }
394
395 bool ok = false;
396 switch (operation & OperationMask) {
397 case Load:
398 ok = lib.load();
399 break;
400 case Unload:
401 ok = lib.load(); //###
402 ok = lib.unload();
403 break;
404 case Resolve: {
405 ok = lib.load();
406 QCOMPARE(ok, true);
407 if (success) {
408 ok = lib.resolve(symbol: "mylibversion");
409 } else {
410 ok = lib.resolve(symbol: "nosuchsymbol");
411 }
412 break;}
413 default:
414 QFAIL(qPrintable(QString("Unknown operation: %1").arg(operation)));
415 break;
416 }
417#if QT_CONFIG(regularexpression)
418 QRegularExpression re(QRegularExpression::anchoredPattern(expression: errorString));
419 QString libErrorString = lib.errorString();
420 QVERIFY2(re.match(libErrorString).hasMatch(), qPrintable(libErrorString));
421#endif
422 QVERIFY(!lib.isLoaded() || lib.unload());
423 QCOMPARE(ok, success);
424}
425
426void tst_QLibrary::loadHints_data()
427{
428 QTest::addColumn<QString>(name: "lib");
429 QTest::addColumn<int>(name: "loadHints");
430 QTest::addColumn<bool>(name: "result");
431
432 QLibrary::LoadHints lh;
433
434 QString appDir = directory;
435
436 lh |= QLibrary::ResolveAllSymbolsHint;
437# if defined(Q_OS_WIN32) || defined(Q_OS_WINRT)
438 QTest::newRow( "ok01 (with suffix)" ) << appDir + "/mylib.dll" << int(lh) << true;
439 QTest::newRow( "ok02 (with non-standard suffix)" ) << appDir + "/mylib.dl2" << int(lh) << true;
440 QTest::newRow( "ok03 (with many dots)" ) << appDir + "/system.qt.test.mylib.dll" << int(lh) << true;
441# elif defined Q_OS_UNIX
442 QTest::newRow( dataTag: "ok01 (with suffix)" ) << appDir + "/libmylib" SUFFIX << int(lh) << true;
443 QTest::newRow( dataTag: "ok02 (with non-standard suffix)" ) << appDir + "/libmylib.so2" << int(lh) << true;
444 QTest::newRow( dataTag: "ok03 (with many dots)" ) << appDir + "/system.qt.test.mylib.so" << int(lh) << true;
445# endif // Q_OS_UNIX
446}
447
448void tst_QLibrary::loadHints()
449{
450 QFETCH( QString, lib );
451 QFETCH( int, loadHints);
452 QFETCH( bool, result );
453 //QLibrary library( lib );
454 QLibrary library;
455 QLibrary::LoadHints lh(loadHints);
456 if (int(loadHints) != 0) {
457 lh |= library.loadHints();
458 library.setLoadHints(lh);
459
460 // confirm that another QLibrary doesn't get affected - QTBUG-39642
461 QCOMPARE(QLibrary().loadHints(), QLibrary::LoadHints());
462 }
463 library.setFileName(lib);
464 QCOMPARE(library.loadHints(), lh);
465 bool ok = library.load();
466
467 // we can't change the hints anymore
468 library.setLoadHints(QLibrary::LoadHints());
469 QCOMPARE(library.loadHints(), lh);
470
471 // confirm that a new QLibrary inherits the hints too
472 QCOMPARE(QLibrary(lib).loadHints(), lh);
473
474 if ( result ) {
475 QVERIFY( ok );
476 QVERIFY(library.unload());
477 } else {
478 QVERIFY( !ok );
479 }
480}
481
482void tst_QLibrary::fileName_data()
483{
484 QTest::addColumn<QString>(name: "libName");
485 QTest::addColumn<QString>(name: "expectedFilename");
486
487 QTest::newRow( dataTag: "ok02" ) << sys_qualifiedLibraryName(fileName: QLatin1String("mylib"))
488 << sys_qualifiedLibraryName(fileName: QLatin1String("mylib"));
489#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
490 QTest::newRow( "ok03" ) << "user32"
491 << "USER32.dll";
492#endif
493}
494
495void tst_QLibrary::fileName()
496{
497 QFETCH( QString, libName);
498 QFETCH( QString, expectedFilename);
499
500 QLibrary lib(libName);
501 bool ok = lib.load();
502 QVERIFY2(ok, qPrintable(lib.errorString()));
503#if defined(Q_OS_WIN)
504 QCOMPARE(lib.fileName().toLower(), expectedFilename.toLower());
505#else
506 QCOMPARE(lib.fileName(), expectedFilename);
507#endif
508 QVERIFY(lib.unload());
509}
510
511void tst_QLibrary::multipleInstancesForOneLibrary()
512{
513 QString lib = directory + "/mylib";
514
515 {
516 QLibrary lib1(lib);
517 QLibrary lib2(lib);
518 QCOMPARE(lib1.isLoaded(), false);
519 QCOMPARE(lib2.isLoaded(), false);
520 lib1.load();
521 QCOMPARE(lib1.isLoaded(), true);
522 QCOMPARE(lib2.isLoaded(), true);
523 QCOMPARE(lib1.unload(), true);
524 QCOMPARE(lib1.isLoaded(), false);
525 QCOMPARE(lib2.isLoaded(), false);
526 lib1.load();
527 lib2.load();
528 QCOMPARE(lib1.isLoaded(), true);
529 QCOMPARE(lib2.isLoaded(), true);
530 QCOMPARE(lib1.unload(), false);
531 QCOMPARE(lib1.isLoaded(), true);
532 QCOMPARE(lib2.isLoaded(), true);
533 QCOMPARE(lib2.unload(), true);
534 QCOMPARE(lib1.isLoaded(), false);
535 QCOMPARE(lib2.isLoaded(), false);
536
537 // Finally; unload on that is already unloaded
538 QCOMPARE(lib1.unload(), false);
539 }
540
541 //now let's try with a 3rd one that will go out of scope
542 {
543 QLibrary lib1(lib);
544 QCOMPARE(lib1.isLoaded(), false);
545 lib1.load();
546 QCOMPARE(lib1.isLoaded(), true);
547 }
548 QLibrary lib2(lib);
549 //lib2 should be loaded because lib1 was loaded and never unloaded
550 QCOMPARE(lib2.isLoaded(), true);
551}
552
553QTEST_MAIN(tst_QLibrary)
554#include "tst_qlibrary.moc"
555

source code of qtbase/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp