2017-12-20 14:54:05 +01:00
/*!
* \ brief Unit tests for \ ref Env
*
2019-05-22 10:08:38 +02:00
* \ copyright Copyright ( c ) 2017 - 2019 Governikus GmbH & Co . KG , Germany
2017-12-20 14:54:05 +01:00
*/
# include "Env.h"
2019-01-03 15:06:22 +01:00
# include "LogHandler.h"
2017-12-20 14:54:05 +01:00
# include <QScopedPointer>
2019-01-03 15:06:22 +01:00
# include <QtConcurrent/QtConcurrentRun>
2017-12-20 14:54:05 +01:00
# include <QtTest>
using namespace governikus ;
namespace
{
2019-01-03 15:06:22 +01:00
struct TestEmptyTmp
{
} ;
2017-12-20 14:54:05 +01:00
class AbstractTestPodInstance
{
public :
virtual ~ AbstractTestPodInstance ( )
{
}
virtual QString dummy ( ) = 0 ;
} ;
class AbstractTestInstance
{
Q_GADGET
public :
virtual ~ AbstractTestInstance ( )
{
}
virtual QString dummy ( ) = 0 ;
} ;
class AbstractTestInstanceImpl
: public AbstractTestInstance
{
Q_GADGET
public :
virtual QString dummy ( ) override
{
return QStringLiteral ( " impl " ) ;
}
} ;
class MockedAbstractTestInstance
: public AbstractTestInstance
{
Q_GADGET
public :
virtual QString dummy ( ) override
{
return QStringLiteral ( " mocked " ) ;
}
} ;
class TestInstance
{
Q_GADGET
protected :
TestInstance ( ) = default ;
virtual ~ TestInstance ( )
{
}
public :
static TestInstance & getInstance ( )
{
static TestInstance instance ;
return instance ;
}
virtual QString something ( )
{
return QStringLiteral ( " orig " ) ;
}
} ;
class TestMockedInstance
: public TestInstance
{
public :
virtual QString something ( ) override
{
return QStringLiteral ( " mocked " ) ;
}
} ;
class TestSharedInstance
: public QObject
{
Q_OBJECT
public :
virtual QString something ( )
{
return QStringLiteral ( " orig " ) ;
}
} ;
class TestMockedSharedInstance
: public TestSharedInstance
{
Q_OBJECT
public :
virtual QString something ( ) override
{
return QStringLiteral ( " mocked " ) ;
}
} ;
class TestAbstractUnmanagedInstance
{
Q_GADGET
public :
virtual ~ TestAbstractUnmanagedInstance ( ) = default ;
virtual QString something ( ) = 0 ;
} ;
class TestUnmanagedInstance
: public TestAbstractUnmanagedInstance
{
Q_GADGET
public :
virtual QString something ( ) override
{
return QStringLiteral ( " TestUnmanagedInstance " ) ;
}
} ;
class TestMoveCtorAssign
: public QObject
{
Q_OBJECT
private :
QString mData ;
public :
TestMoveCtorAssign ( const QString & pData )
: mData ( pData )
{
}
TestMoveCtorAssign ( TestMoveCtorAssign & & pCopy )
{
mData = std : : move ( pCopy . mData ) ;
}
TestMoveCtorAssign & operator = ( TestMoveCtorAssign & & pCopy )
{
mData = std : : move ( pCopy . mData ) ;
return * this ;
}
QString data ( )
{
return mData ;
}
} ;
2019-01-03 15:06:22 +01:00
class TestSingleInCtor
{
Q_GADGET
public :
TestSingleInCtor ( )
{
Env : : create < TestEmptyTmp > ( ) ;
Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
}
static TestSingleInCtor & getInstance ( )
{
static TestSingleInCtor Instance ;
return Instance ;
}
} ;
} // namespace
Q_DECLARE_METATYPE ( std : : function < TestEmptyTmp ( ) > )
2017-12-20 14:54:05 +01:00
namespace governikus
{
2019-01-03 15:06:22 +01:00
template < > TestAbstractUnmanagedInstance * singleton < TestAbstractUnmanagedInstance > ( )
2017-12-20 14:54:05 +01:00
{
static TestUnmanagedInstance instance ;
return & instance ;
}
2019-01-03 15:06:22 +01:00
template < > AbstractTestInstance * singleton < AbstractTestInstance > ( )
2017-12-20 14:54:05 +01:00
{
2019-01-03 15:06:22 +01:00
static AbstractTestInstanceImpl instance ;
return & instance ;
2017-12-20 14:54:05 +01:00
}
template < > AbstractTestInstance * createNewObject < AbstractTestInstance * > ( )
{
return new AbstractTestInstanceImpl ;
}
template < > AbstractTestInstance * createNewObject < AbstractTestInstance * , QString > ( QString & & pStr )
{
class tmpCtor
: public AbstractTestInstance
{
private :
const QString mDummy ;
public :
tmpCtor ( const QString & pDummy ) : mDummy ( pDummy )
{
}
virtual QString dummy ( ) override
{
return mDummy ;
}
} ;
return new tmpCtor ( pStr ) ;
}
template < > AbstractTestPodInstance * createNewObject < AbstractTestPodInstance * > ( )
{
return nullptr ;
}
template < > AbstractTestPodInstance * createNewObject < AbstractTestPodInstance * , QString > ( QString & & )
{
return nullptr ;
}
2019-01-03 15:06:22 +01:00
} // namespace governikus
2017-12-20 14:54:05 +01:00
class test_Env
: public QObject
{
Q_OBJECT
private Q_SLOTS :
2019-01-03 15:06:22 +01:00
void initTestCase ( )
{
Env : : getSingleton < LogHandler > ( ) - > init ( ) ;
}
2017-12-20 14:54:05 +01:00
void cleanup ( )
{
2019-01-03 15:06:22 +01:00
Env : : getSingleton < LogHandler > ( ) - > resetBacklog ( ) ;
2017-12-20 14:54:05 +01:00
Env : : clear ( ) ;
}
void checkOverrideMock ( )
{
QCOMPARE ( Env : : getInstance ( ) . mInstancesCreator . size ( ) , 0 ) ;
QScopedPointer < AbstractTestInstance > obj ( Env : : create < AbstractTestInstance * > ( ) ) ;
QVERIFY ( obj ) ;
const std : : function < AbstractTestInstance * ( ) > func = [ ] ( ) {
return nullptr ;
} ;
Env : : setCreator < AbstractTestInstance * > ( func ) ;
QCOMPARE ( Env : : getInstance ( ) . mInstancesCreator . size ( ) , 1 ) ;
Env : : setCreator < AbstractTestInstance * > ( func ) ;
Env : : setCreator < AbstractTestInstance * > ( func ) ;
Env : : setCreator < AbstractTestInstance * > ( func ) ;
QCOMPARE ( Env : : getInstance ( ) . mInstancesCreator . size ( ) , 1 ) ;
QCOMPARE ( Env : : create < AbstractTestInstance * > ( ) , nullptr ) ;
}
void mockGet ( )
{
auto orig = Env : : getSingleton < TestInstance > ( ) ;
QVERIFY ( orig ) ;
QCOMPARE ( orig - > something ( ) , QLatin1String ( " orig " ) ) ;
TestMockedInstance mock ;
Env : : set ( TestInstance : : staticMetaObject , & mock ) ;
auto mocked = Env : : getSingleton < TestInstance > ( ) ;
QVERIFY ( mocked ) ;
QCOMPARE ( mocked - > something ( ) , QLatin1String ( " mocked " ) ) ;
Env : : set ( TestInstance : : staticMetaObject ) ;
auto orig_again = Env : : getSingleton < TestInstance > ( ) ;
QVERIFY ( orig_again ) ;
QCOMPARE ( orig_again - > something ( ) , QLatin1String ( " orig " ) ) ;
}
void mockAbstract ( )
{
auto impl = Env : : getSingleton < AbstractTestInstance > ( ) ;
QVERIFY ( impl ) ;
QCOMPARE ( impl - > dummy ( ) , QLatin1String ( " impl " ) ) ;
auto impl2 = Env : : getSingleton < AbstractTestInstance > ( ) ;
QVERIFY ( impl = = impl2 ) ;
MockedAbstractTestInstance m ;
Env : : set ( AbstractTestInstance : : staticMetaObject , & m ) ;
auto mocked = Env : : getSingleton < AbstractTestInstance > ( ) ;
QVERIFY ( mocked ) ;
QVERIFY ( mocked ! = impl ) ;
QCOMPARE ( mocked - > dummy ( ) , QLatin1String ( " mocked " ) ) ;
}
void mockShared ( )
{
auto orig = Env : : getShared < TestSharedInstance > ( ) ;
QVERIFY ( orig ) ;
QCOMPARE ( orig - > something ( ) , QLatin1String ( " orig " ) ) ;
auto orig2 = Env : : getShared < TestSharedInstance > ( ) ;
QVERIFY ( orig = = orig2 ) ;
auto mock = QSharedPointer < TestMockedSharedInstance > : : create ( ) ;
Env : : setShared ( TestSharedInstance : : staticMetaObject , mock ) ;
auto mocked = Env : : getShared < TestSharedInstance > ( ) ;
QVERIFY ( mocked ) ;
QCOMPARE ( mocked - > something ( ) , QLatin1String ( " mocked " ) ) ;
QVERIFY ( mock = = mocked ) ;
QVERIFY ( orig ! = mocked ) ;
Env : : setShared ( TestSharedInstance : : staticMetaObject ) ;
auto orig3 = Env : : getShared < TestSharedInstance > ( ) ;
QVERIFY ( orig ! = orig3 ) ;
QCOMPARE ( orig3 - > something ( ) , QLatin1String ( " orig " ) ) ;
}
void getUnmanagedSingleton ( )
{
auto first = Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
QVERIFY ( first ) ;
QCOMPARE ( first - > something ( ) , QLatin1String ( " TestUnmanagedInstance " ) ) ;
auto second = Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
QVERIFY ( second ) ;
QVERIFY ( first = = second ) ;
}
void mockCreateNewInstance ( )
{
QScopedPointer < AbstractTestInstance > implOrig ( Env : : create < AbstractTestInstance * > ( ) ) ;
QVERIFY ( implOrig ) ;
QCOMPARE ( implOrig - > dummy ( ) , QLatin1String ( " impl " ) ) ;
implOrig . reset ( ) ;
std : : function < AbstractTestInstance * ( ) > func = [ ] ( ) {
class tmp
: public AbstractTestInstanceImpl
{
virtual QString dummy ( ) override
{
return QStringLiteral ( " lambda " ) ;
}
} ;
return new tmp ;
} ;
Env : : setCreator < AbstractTestInstance * > ( func ) ;
QScopedPointer < AbstractTestInstance > impl ( Env : : create < AbstractTestInstance * > ( ) ) ;
QVERIFY ( impl ) ;
QCOMPARE ( impl - > dummy ( ) , QLatin1String ( " lambda " ) ) ;
QScopedPointer < AbstractTestInstance > impl2 ( Env : : create < AbstractTestInstance * > ( ) ) ;
QVERIFY ( impl2 ) ;
QCOMPARE ( impl2 - > dummy ( ) , QLatin1String ( " lambda " ) ) ;
QVERIFY ( impl ! = impl2 ) ;
}
void mockArgs ( )
{
QScopedPointer < AbstractTestInstance > impl ( Env : : create < AbstractTestInstance * > ( QString ( " flupp " ) ) ) ;
QVERIFY ( impl ) ;
QCOMPARE ( impl - > dummy ( ) , QLatin1String ( " flupp " ) ) ;
impl . reset ( ) ;
std : : function < AbstractTestPodInstance * ( QString ) > funcString = [ ] ( const QString & pStr ) {
class tmp
: public AbstractTestPodInstance
{
const QString mDummy ;
public :
tmp ( const QString & pDummy )
: mDummy ( QStringLiteral ( " YEAH: " ) + pDummy )
{
}
virtual QString dummy ( ) override
{
return mDummy ;
}
} ;
return new tmp ( pStr ) ;
} ;
std : : function < AbstractTestPodInstance * ( ) > funcDefault = [ ] ( ) {
class tmp
: public AbstractTestPodInstance
{
public :
virtual QString dummy ( ) override
{
return QStringLiteral ( " default " ) ;
}
} ;
return new tmp ( ) ;
} ;
Env : : setCreator < AbstractTestPodInstance * , QString > ( funcString ) ;
Env : : setCreator < AbstractTestPodInstance * > ( funcDefault ) ;
QScopedPointer < AbstractTestPodInstance > catcher ( Env : : create < AbstractTestPodInstance * > ( QString ( " bla " ) ) ) ;
QVERIFY ( catcher ) ;
QCOMPARE ( catcher - > dummy ( ) , QLatin1String ( " YEAH: bla " ) ) ;
catcher . reset ( Env : : create < AbstractTestPodInstance * > ( ) ) ;
QVERIFY ( catcher ) ;
QCOMPARE ( catcher - > dummy ( ) , QLatin1String ( " default " ) ) ;
}
void mockCopy ( )
{
class TestCopyCtor
{
private :
int mData ;
public :
TestCopyCtor ( int pData )
: mData ( pData )
{
}
int data ( )
{
return mData ;
}
} ;
auto obj = Env : : create < TestCopyCtor > ( 666 ) ;
QCOMPARE ( obj . data ( ) , 666 ) ;
obj = Env : : create < TestCopyCtor > ( 1904 ) ;
QCOMPARE ( obj . data ( ) , 1904 ) ;
std : : function < TestCopyCtor ( int ) > func = [ ] ( int ) {
return TestCopyCtor ( 1 ) ;
} ;
Env : : setCreator < TestCopyCtor , int > ( func ) ;
obj = Env : : create < TestCopyCtor > ( 1982 ) ;
QCOMPARE ( obj . data ( ) , 1 ) ;
}
void mockMove ( )
{
auto obj = Env : : create < TestMoveCtorAssign > ( QString ( " huhu " ) ) ;
QCOMPARE ( obj . data ( ) , QLatin1String ( " huhu " ) ) ;
std : : function < TestMoveCtorAssign ( QString ) > func = [ ] ( const QString & ) {
return TestMoveCtorAssign ( QStringLiteral ( " mocked " ) ) ;
} ;
Env : : setCreator < TestMoveCtorAssign , QString > ( func ) ;
auto mock = Env : : create < TestMoveCtorAssign > ( QString ( " huhu " ) ) ;
QCOMPARE ( mock . data ( ) , QLatin1String ( " mocked " ) ) ;
QCOMPARE ( obj . data ( ) , QLatin1String ( " huhu " ) ) ;
func = [ ] ( const QString & ) {
return TestMoveCtorAssign ( QStringLiteral ( " mocked2 " ) ) ;
} ;
Env : : setCreator < TestMoveCtorAssign , QString > ( func ) ;
mock = Env : : create < TestMoveCtorAssign > ( QString ( " huhu2 " ) ) ;
QCOMPARE ( mock . data ( ) , QLatin1String ( " mocked2 " ) ) ;
QCOMPARE ( obj . data ( ) , QLatin1String ( " huhu " ) ) ;
}
void mockCounter ( )
{
struct TestTmp
{
int mData ;
TestTmp ( int pData = 666 )
: mData ( pData )
{
}
} ;
auto obj = Env : : create < TestTmp > ( ) ;
QCOMPARE ( obj . mData , 666 ) ;
obj = Env : : create < TestTmp > ( 1904 ) ;
QCOMPARE ( obj . mData , 1904 ) ;
const std : : function < TestTmp ( ) > func = [ ] ( ) {
return TestTmp ( 999 ) ;
} ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , - 1 ) ;
Env : : setCreator < TestTmp > ( func ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 0 ) ;
obj = Env : : create < TestTmp > ( ) ;
QCOMPARE ( obj . mData , 999 ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 1 ) ;
obj = Env : : create < TestTmp > ( 123 ) ;
QCOMPARE ( obj . mData , 123 ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 1 ) ;
const std : : function < TestTmp ( int ) > func2 = [ ] ( int ) {
return TestTmp ( 777 ) ;
} ;
QCOMPARE ( ( Env : : getCounter < TestTmp , int > ( ) ) , - 1 ) ;
Env : : setCreator < TestTmp > ( func2 ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 1 ) ;
QCOMPARE ( ( Env : : getCounter < TestTmp , int > ( ) ) , 0 ) ;
obj = Env : : create < TestTmp > ( ) ;
QCOMPARE ( obj . mData , 999 ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 2 ) ;
QCOMPARE ( ( Env : : getCounter < TestTmp , int > ( ) ) , 0 ) ;
obj = Env : : create < TestTmp > ( 123 ) ;
QCOMPARE ( obj . mData , 777 ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 2 ) ;
QCOMPARE ( ( Env : : getCounter < TestTmp , int > ( ) ) , 1 ) ;
Env : : resetCounter ( ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , 0 ) ;
QCOMPARE ( ( Env : : getCounter < TestTmp , int > ( ) ) , 0 ) ;
Env : : clear ( ) ;
QCOMPARE ( Env : : getCounter < TestTmp > ( ) , - 1 ) ;
QCOMPARE ( ( Env : : getCounter < TestTmp , int > ( ) ) , - 1 ) ;
}
2019-01-03 15:06:22 +01:00
void threadSafeCheckSetCreator ( )
{
QVERIFY ( QThreadPool : : globalInstance ( ) - > maxThreadCount ( ) > 1 ) ;
const std : : function < TestEmptyTmp ( ) > func = [ ] ( ) {
return TestEmptyTmp ( ) ;
} ;
TestMockedInstance mock ;
QVector < QFuture < void > > threads ;
for ( int i = 0 ; i < 100 ; + + i )
{
threads < < QtConcurrent : : run ( [ func , & mock ] {
for ( int j = 0 ; j < 100 ; + + j )
{
Env : : setCreator < TestEmptyTmp > ( func ) ;
Env : : create < TestEmptyTmp > ( ) ;
Env : : getSingleton < TestSingleInCtor > ( ) ;
Env : : getShared < TestSharedInstance > ( ) ;
Env : : set ( TestInstance : : staticMetaObject , & mock ) ;
}
} ) ;
}
for ( auto future : qAsConst ( threads ) )
{
future . waitForFinished ( ) ;
}
}
void threadSafeDeadlockDuringCreate_data ( )
{
QTest : : addColumn < std : : function < TestEmptyTmp ( ) > > ( " creator " ) ;
QTest : : newRow ( " with mock " ) < < std : : function < TestEmptyTmp ( ) > ( [ ] ( ) {
return TestEmptyTmp ( ) ;
} ) ;
QTest : : newRow ( " without mock " ) < < std : : function < TestEmptyTmp ( ) > ( ) ;
}
void threadSafeDeadlockDuringCreate ( )
{
QFETCH ( std : : function < TestEmptyTmp ( ) > , creator ) ;
if ( creator )
{
Env : : setCreator < TestEmptyTmp > ( creator ) ;
}
class CreateTmp
{
public :
CreateTmp ( )
{
Env : : create < TestEmptyTmp > ( ) ;
Env : : getSingleton < TestSingleInCtor > ( ) ;
}
} ;
Env : : create < CreateTmp > ( ) ;
if ( creator )
{
QVERIFY ( Env : : getCounter < TestEmptyTmp > ( ) > 0 ) ;
}
}
void checkLogForSameThreadsGadget ( )
{
QThread : : currentThread ( ) - > setObjectName ( " Main " ) ;
QSignalSpy spy ( Env : : getSingleton < LogHandler > ( ) , & LogHandler : : fireLog ) ;
Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
QCOMPARE ( spy . count ( ) , 0 ) ;
Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
QCOMPARE ( spy . count ( ) , 0 ) ;
}
void checkLogForDifferentThreadsGadget ( )
{
QThread : : currentThread ( ) - > setObjectName ( " Main " ) ;
QSignalSpy spy ( Env : : getSingleton < LogHandler > ( ) , & LogHandler : : fireLog ) ;
Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
QCOMPARE ( spy . count ( ) , 0 ) ;
QThreadPool pool ; // do not use global one, otherwise the main thread is allowed, too
QtConcurrent : : run ( & pool , [ ] {
Env : : getSingleton < TestAbstractUnmanagedInstance > ( ) ;
} ) . waitForFinished ( ) ;
QCOMPARE ( spy . count ( ) , 0 ) ;
}
void checkLogForSameThreadsQObject ( )
{
QThread : : currentThread ( ) - > setObjectName ( " Main " ) ;
QSignalSpy spy ( Env : : getSingleton < LogHandler > ( ) , & LogHandler : : fireLog ) ;
Env : : getSingleton < LogHandler > ( ) ;
QCOMPARE ( spy . count ( ) , 0 ) ;
}
void checkLogForDifferentThreadsQObject ( )
{
QThread : : currentThread ( ) - > setObjectName ( " Main " ) ;
QSignalSpy spy ( Env : : getSingleton < LogHandler > ( ) , & LogHandler : : fireLog ) ;
Env : : getSingleton < LogHandler > ( ) ;
QCOMPARE ( spy . count ( ) , 0 ) ;
QThreadPool pool ; // do not use global one, otherwise the main thead is allowed, too
QtConcurrent : : run ( & pool , [ ] {
Env : : getSingleton < LogHandler > ( ) ;
} ) . waitForFinished ( ) ;
QCOMPARE ( spy . count ( ) , 1 ) ;
QVERIFY ( spy . takeLast ( ) . at ( 0 ) . toString ( ) . contains ( QLatin1String ( " governikus::LogHandler was created in \" Main \" but is requested by \" Thread (pooled) \" " ) ) ) ;
}
2017-12-20 14:54:05 +01:00
} ;
QTEST_GUILESS_MAIN ( test_Env )
# include "test_Env.moc"