2011-02-18 14:48:16 +01:00
/**************************************************************************
* *
* * This file is part of Qt Creator
* *
2012-01-26 18:33:46 +01:00
* * Copyright ( c ) 2012 Nokia Corporation and / or its subsidiary ( - ies ) .
2011-02-18 14:48:16 +01:00
* *
2011-11-02 15:59:12 +01:00
* * Contact : Nokia Corporation ( qt - info @ nokia . com )
2011-02-18 14:48:16 +01:00
* *
* *
* * GNU Lesser General Public License Usage
* *
2011-04-13 08:42:33 +02:00
* * This file may be used under the terms of the GNU Lesser General Public
* * License version 2.1 as published by the Free Software Foundation and
* * appearing in the file LICENSE . LGPL included in the packaging of this file .
* * Please review the following information to ensure the GNU Lesser General
* * Public License version 2.1 requirements will be met :
* * http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
2011-02-18 14:48:16 +01:00
* *
* * In addition , as a special exception , Nokia gives you certain additional
2011-04-13 08:42:33 +02:00
* * rights . These rights are described in the Nokia Qt LGPL Exception
2011-02-18 14:48:16 +01:00
* * version 1.1 , included in the file LGPL_EXCEPTION . txt in this package .
* *
2011-04-13 08:42:33 +02:00
* * Other Usage
* *
* * Alternatively , this file may be used in accordance with the terms and
* * conditions contained in a signed written agreement between you and Nokia .
* *
2011-02-18 14:48:16 +01:00
* * If you have questions regarding the use of this file , please contact
2011-11-02 15:59:12 +01:00
* * Nokia at qt - info @ nokia . com .
2011-02-18 14:48:16 +01:00
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-03-02 11:30:43 +01:00
# include <QtTest>
# include <pp.h>
2010-09-02 15:32:04 +10:00
//TESTED_COMPONENT=src/libs/cplusplus
2009-03-03 16:32:08 +01:00
using namespace CPlusPlus ;
2009-03-02 11:30:43 +01:00
2012-03-26 15:18:01 +02:00
# define DUMP_OUTPUT(x) {QByteArray b(x);qDebug("output: [[%s]]", b.replace("\n", "<<\n").constData());}
QByteArray loadSource ( const QString & fileName )
{
QFile inf ( fileName ) ;
if ( ! inf . open ( QIODevice : : ReadOnly | QIODevice : : Text ) ) {
qDebug ( " Cannot open \" %s \" " , fileName . toUtf8 ( ) . constData ( ) ) ;
return QByteArray ( ) ;
}
QTextStream ins ( & inf ) ;
QString source = ins . readAll ( ) ;
inf . close ( ) ;
return source . toUtf8 ( ) ;
}
void saveData ( const QByteArray & data , const QString & fileName )
{
QFile inf ( fileName ) ;
if ( ! inf . open ( QIODevice : : WriteOnly | QIODevice : : Text ) ) {
qDebug ( " Cannot open \" %s \" " , fileName . toUtf8 ( ) . constData ( ) ) ;
return ;
}
inf . write ( data ) ;
inf . close ( ) ;
}
2012-03-29 17:14:42 +02:00
struct Include
{
Include ( const QString & fileName , Client : : IncludeType type , unsigned line )
: fileName ( fileName ) , type ( type ) , line ( line )
{ }
QString fileName ;
Client : : IncludeType type ;
unsigned line ;
} ;
2012-04-24 18:29:09 +02:00
QDebug & operator < < ( QDebug & d , const Include & i )
{
2012-03-29 17:14:42 +02:00
d < < ' [ ' < < i . fileName
2012-04-24 18:29:09 +02:00
< < ' , ' < < ( i . type = = Client : : IncludeGlobal ? " Global "
: ( i . type = = Client : : IncludeLocal ? " Local " : " Unknown " ) )
2012-03-29 17:14:42 +02:00
< < ' , ' < < i . line
< < ' ] ' ;
return d ;
}
2012-04-24 18:29:09 +02:00
class MockClient : public Client
2012-03-26 15:18:01 +02:00
{
public :
struct Block {
2012-04-24 18:29:09 +02:00
Block ( ) : start ( 0 ) , end ( 0 ) { }
Block ( unsigned start ) : start ( start ) , end ( 0 ) { }
2012-03-26 15:18:01 +02:00
unsigned start ;
unsigned end ;
} ;
public :
MockClient ( Environment * env , QByteArray * output )
: m_env ( env )
, m_output ( output )
, m_pp ( this , env )
, m_includeDepth ( 0 )
{ }
virtual ~ MockClient ( ) { }
2012-04-02 14:57:13 +02:00
virtual void macroAdded ( const Macro & macro )
{
m_definedMacros . append ( macro . name ( ) ) ;
m_definedMacrosLine . append ( macro . line ( ) ) ;
}
2012-03-26 15:18:01 +02:00
virtual void passedMacroDefinitionCheck ( unsigned /*offset*/ , const Macro & /*macro*/ ) { }
2012-04-18 17:44:23 +02:00
virtual void failedMacroDefinitionCheck ( unsigned /*offset*/ , const ByteArrayRef & /*name*/ ) { }
2012-03-26 15:18:01 +02:00
2012-04-02 13:46:53 +02:00
virtual void startExpandingMacro ( unsigned offset ,
2012-03-26 15:18:01 +02:00
const Macro & /*macro*/ ,
2012-04-18 17:44:23 +02:00
const ByteArrayRef & originalText ,
2012-03-26 15:18:01 +02:00
const QVector < MacroArgumentReference > & /*actuals*/
2012-04-02 08:46:56 +02:00
= QVector < MacroArgumentReference > ( ) )
2012-04-02 13:46:53 +02:00
{
2012-04-18 17:44:23 +02:00
m_expandedMacros . append ( QByteArray ( originalText . start ( ) , originalText . length ( ) ) ) ;
2012-04-02 13:46:53 +02:00
m_expandedMacrosOffset . append ( offset ) ;
}
2012-03-26 15:18:01 +02:00
2012-04-24 18:29:09 +02:00
virtual void stopExpandingMacro ( unsigned /*offset*/ , const Macro & /*macro*/ ) { }
2012-03-26 15:18:01 +02:00
virtual void startSkippingBlocks ( unsigned offset )
{ m_skippedBlocks . append ( Block ( offset ) ) ; }
virtual void stopSkippingBlocks ( unsigned offset )
{ m_skippedBlocks . last ( ) . end = offset ; }
virtual void sourceNeeded ( QString & includedFileName , IncludeType mode ,
2012-03-29 17:14:42 +02:00
unsigned line )
2012-03-26 15:18:01 +02:00
{
2012-03-29 17:14:42 +02:00
# if 1
m_recordedIncludes . append ( Include ( includedFileName , mode , line ) ) ;
# else
Q_UNUSED ( line ) ;
2012-03-26 15:18:01 +02:00
QString resolvedFileName ;
if ( mode = = IncludeLocal )
resolvedFileName = resolveLocally ( m_env - > currentFile , includedFileName ) ;
else
resolvedFileName = resolveGlobally ( includedFileName ) ;
// qDebug("resolved [[%s]] to [[%s]] from [[%s]] (%s)\n",
// includedFileName.toUtf8().constData(),
// resolvedFileName.toUtf8().constData(),
// currentFileName.toUtf8().constData(),
// (mode == IncludeLocal) ? "locally" : "globally");
if ( resolvedFileName . isEmpty ( ) )
return ;
+ + m_includeDepth ;
// qDebug("%5d %s %s", m_includeDepth, QByteArray(m_includeDepth, '+').constData(), resolvedFileName.toUtf8().constData());
sourceNeeded ( resolvedFileName ) ;
- - m_includeDepth ;
2012-03-29 17:14:42 +02:00
# endif
2012-03-26 15:18:01 +02:00
}
QString resolveLocally ( const QString & currentFileName ,
2012-04-24 18:29:09 +02:00
const QString & includedFileName ) const
2012-03-26 15:18:01 +02:00
{
QDir dir ;
if ( currentFileName . isEmpty ( ) )
dir = QDir : : current ( ) ;
else
dir = QFileInfo ( currentFileName ) . dir ( ) ;
const QFileInfo inc ( dir , includedFileName ) ;
if ( inc . exists ( ) ) {
const QString resolved = inc . filePath ( ) ;
return resolved . toUtf8 ( ) . constData ( ) ;
} else {
// std::cerr<<"Cannot find " << inc.fileName().toUtf8().constData()<<std::endl;
return QString ( ) ;
}
}
QString resolveGlobally ( const QString & currentFileName ) const
{
foreach ( const QDir & dir , m_includePaths ) {
QFileInfo f ( dir , currentFileName ) ;
if ( f . exists ( ) )
return f . filePath ( ) ;
}
return QString ( ) ;
}
void setIncludePaths ( const QStringList & includePaths )
{
foreach ( const QString & path , includePaths ) {
QDir dir ( path ) ;
if ( dir . exists ( ) )
m_includePaths . append ( dir ) ;
}
}
2012-03-30 13:01:32 +02:00
void sourceNeeded ( const QString & fileName , bool nolines )
2012-03-26 15:18:01 +02:00
{
QByteArray src = loadSource ( fileName ) ;
QVERIFY ( ! src . isEmpty ( ) ) ;
2012-04-19 10:35:49 +02:00
* m_output = m_pp . run ( fileName , src , nolines , true ) ;
2012-03-26 15:18:01 +02:00
}
QList < Block > skippedBlocks ( ) const
{ return m_skippedBlocks ; }
2012-03-29 17:14:42 +02:00
QList < Include > recordedIncludes ( ) const
{ return m_recordedIncludes ; }
2012-04-02 08:46:56 +02:00
QList < QByteArray > expandedMacros ( ) const
{ return m_expandedMacros ; }
2012-04-02 13:46:53 +02:00
QList < unsigned > expandedMacrosOffset ( ) const
{ return m_expandedMacrosOffset ; }
2012-04-02 14:57:13 +02:00
QList < QByteArray > definedMacros ( ) const
{ return m_definedMacros ; }
QList < unsigned > definedMacrosLine ( ) const
{ return m_definedMacrosLine ; }
2012-03-26 15:18:01 +02:00
private :
Environment * m_env ;
QByteArray * m_output ;
Preprocessor m_pp ;
QList < QDir > m_includePaths ;
unsigned m_includeDepth ;
QList < Block > m_skippedBlocks ;
2012-03-29 17:14:42 +02:00
QList < Include > m_recordedIncludes ;
2012-04-02 08:46:56 +02:00
QList < QByteArray > m_expandedMacros ;
2012-04-02 13:46:53 +02:00
QList < unsigned > m_expandedMacrosOffset ;
2012-04-02 14:57:13 +02:00
QList < QByteArray > m_definedMacros ;
QList < unsigned > m_definedMacrosLine ;
2012-03-26 15:18:01 +02:00
} ;
2012-04-16 11:39:46 +02:00
QT_BEGIN_NAMESPACE
2012-04-02 13:46:53 +02:00
namespace QTest {
template < > char * toString ( const QList < unsigned > & list )
{
QByteArray ba = " QList<unsigned>( " ;
foreach ( const unsigned & item , list ) {
ba + = QTest : : toString ( item ) ;
ba + = ' , ' ;
}
if ( ! list . isEmpty ( ) )
ba [ ba . size ( ) - 1 ] = ' ) ' ;
return qstrdup ( ba . data ( ) ) ;
}
template < > char * toString ( const QList < QByteArray > & list )
{
QByteArray ba = " QList<QByteArray>( " ;
foreach ( const QByteArray & item , list ) {
ba + = QTest : : toString ( item ) ;
ba + = ' , ' ;
}
if ( ! list . isEmpty ( ) )
ba [ ba . size ( ) - 1 ] = ' ) ' ;
return qstrdup ( ba . data ( ) ) ;
}
}
2012-04-16 11:39:46 +02:00
QT_END_NAMESPACE
2012-04-02 13:46:53 +02:00
2012-04-24 18:29:09 +02:00
QDebug & operator < < ( QDebug & d , const MockClient : : Block & b )
{
d < < ' [ ' < < b . start < < ' , ' < < b . end < < ' ] ' ;
return d ;
}
2012-03-26 15:18:01 +02:00
2012-04-24 18:29:09 +02:00
class tst_Preprocessor : public QObject
2009-03-02 11:30:43 +01:00
{
2012-03-26 15:18:01 +02:00
Q_OBJECT
protected :
2012-03-30 13:01:32 +02:00
QByteArray preprocess ( const QString & fileName , QByteArray * /*errors*/ , bool nolines ) {
2012-03-26 15:18:01 +02:00
//### TODO: hook up errors
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
2012-03-30 13:01:32 +02:00
client . sourceNeeded ( " data/ " + fileName , nolines ) ;
2012-03-26 15:18:01 +02:00
return output ;
}
2012-04-02 13:46:53 +02:00
static QString simplified ( QByteArray buf ) ;
2009-03-02 11:30:43 +01:00
2012-03-26 15:18:01 +02:00
private /* not corrected yet */ :
void macro_definition_lineno ( ) ;
2012-04-02 13:46:53 +02:00
void param_expanding_as_multiple_params ( ) ;
void macro_argument_expansion ( ) ;
2012-03-26 15:18:01 +02:00
private slots :
2012-04-19 15:19:38 +02:00
void defined ( ) ;
void defined_data ( ) ;
2012-03-16 14:06:09 +01:00
void va_args ( ) ;
void named_va_args ( ) ;
2012-03-16 14:10:46 +01:00
void first_empty_macro_arg ( ) ;
2012-03-26 15:18:01 +02:00
void invalid_param_count ( ) ;
2012-04-02 08:46:56 +02:00
void objmacro_expanding_as_fnmacro_notification ( ) ;
2012-04-02 13:46:53 +02:00
void macro_uses ( ) ;
2012-04-02 08:46:56 +02:00
void macro_arguments_notificatin ( ) ;
2009-03-03 16:32:08 +01:00
void unfinished_function_like_macro_call ( ) ;
2010-02-10 11:04:31 +01:00
void nasty_macro_expansion ( ) ;
2012-02-24 13:06:21 +01:00
void tstst ( ) ;
2012-03-26 15:18:01 +02:00
void test_file_builtin ( ) ;
void blockSkipping ( ) ;
2012-03-29 17:14:42 +02:00
void includes_1 ( ) ;
2012-03-26 15:18:01 +02:00
void comparisons_data ( ) ;
void comparisons ( ) ;
2009-03-02 11:30:43 +01:00
} ;
2012-04-02 13:46:53 +02:00
// Remove all #... lines, and 'simplify' string, to allow easily comparing the result
// Also, remove all unneeded spaces: keep only to ensure identifiers are separated.
// NOTE: may not correctly handle underscore in identifiers
QString tst_Preprocessor : : simplified ( QByteArray buf )
{
QString out ;
QList < QByteArray > lines = buf . split ( ' \n ' ) ;
2012-04-24 18:29:09 +02:00
foreach ( const QByteArray & line , lines ) {
2012-04-02 13:46:53 +02:00
if ( ! line . startsWith ( ' # ' ) ) {
out . append ( ' ' ) ;
out . append ( line ) ;
}
2012-04-24 18:29:09 +02:00
}
2012-04-02 13:46:53 +02:00
out = out . simplified ( ) ;
2012-04-24 18:29:09 +02:00
for ( int i = 1 ; i < out . length ( ) - 1 ; ) {
if ( out . at ( i ) . isSpace ( )
& & ! ( out . at ( i - 1 ) . isLetterOrNumber ( )
& & out . at ( i + 1 ) . isLetterOrNumber ( ) ) )
2012-04-02 13:46:53 +02:00
out . remove ( i , 1 ) ;
else
i + + ;
}
return out ;
}
2012-03-16 14:06:09 +01:00
void tst_Preprocessor : : va_args ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
" #define foo(...) int f(__VA_ARGS__); \n "
" \n foo( ) \n "
" \n foo(int a) \n "
" \n foo(int a,int b) \n " ,
true , false ) ;
2012-03-16 14:06:09 +01:00
2012-03-26 15:18:01 +02:00
preprocessed = preprocessed . simplified ( ) ;
// DUMP_OUTPUT(preprocessed);
2012-04-02 13:46:53 +02:00
QCOMPARE ( simplified ( preprocessed ) , QString ( " int f();int f(int a);int f(int a,int b); " ) ) ;
2012-03-16 14:06:09 +01:00
}
void tst_Preprocessor : : named_va_args ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
" \n #define foo(ARGS...) int f(ARGS); "
" \n foo( ) \n "
" \n foo(int a) \n "
" \n foo(int a,int b) \n " ,
true , false ) ;
2012-03-16 14:06:09 +01:00
2012-03-26 15:18:01 +02:00
preprocessed = preprocessed . simplified ( ) ;
2012-04-02 13:46:53 +02:00
QCOMPARE ( simplified ( preprocessed ) , QString ( " int f();int f(int a);int f(int a,int b); " ) ) ;
2012-03-16 14:06:09 +01:00
}
2012-03-16 14:10:46 +01:00
void tst_Preprocessor : : first_empty_macro_arg ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
" \n #define foo(a,b) a int b; "
" \n foo(const,cVal) \n "
" \n foo(,Val) \n "
" \n foo( ,Val2) \n " ,
true , false ) ;
2012-03-16 14:10:46 +01:00
2012-03-26 15:18:01 +02:00
preprocessed = preprocessed . simplified ( ) ;
// DUMP_OUTPUT(preprocessed);
2012-04-02 13:46:53 +02:00
QCOMPARE ( simplified ( preprocessed ) , QString ( " const int cVal;int Val;int Val2; " ) ) ;
2012-03-16 14:10:46 +01:00
}
2012-03-26 15:18:01 +02:00
void tst_Preprocessor : : invalid_param_count ( )
2012-03-16 14:22:17 +01:00
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-03-26 15:18:01 +02:00
// The following is illegal, but shouldn't crash the preprocessor.
// GCC says: 3:14: error: macro "foo" requires 2 arguments, but only 1 given
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
" \n #define foo(a,b) int f(a,b); "
" \n #define ARGS(t) t a,t b "
" \n foo(ARGS(int)) " ,
true , false ) ;
2012-03-26 15:18:01 +02:00
// do not verify the output: it's illegal, so anything might be outputted.
2012-03-16 14:22:17 +01:00
}
2012-04-02 13:46:53 +02:00
void tst_Preprocessor : : param_expanding_as_multiple_params ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
" \n #define foo(a,b) int f(a,b); "
" \n #define ARGS(t) t a,t b "
" \n foo(ARGS(int)) " ,
false , true ) ;
2012-04-02 13:46:53 +02:00
QCOMPARE ( simplified ( preprocessed ) , QString ( " int f(int a,int b); " ) ) ;
}
void tst_Preprocessor : : macro_argument_expansion ( ) //QTCREATORBUG-7225
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
" \n #define BAR1 2,3,4 "
" \n #define FOO1(a,b,c) a+b+c "
" \n void test2(){ "
" \n int x=FOO1(BAR1); "
" \n } " ,
false , true ) ;
2012-04-02 13:46:53 +02:00
QCOMPARE ( simplified ( preprocessed ) , QString ( " void test2(){int x=2+3+4;} " ) ) ;
}
void tst_Preprocessor : : macro_uses ( )
{
QByteArray buffer = QByteArray ( " \n #define FOO 8 "
" \n #define BAR 9 "
" \n void test(){ "
" \n \t int x=FOO; "
" \n \t int y=BAR; "
" \n } " ) ;
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
Preprocessor preprocess ( & client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) , buffer ) ;
2012-04-02 13:46:53 +02:00
QCOMPARE ( simplified ( preprocessed ) , QString ( " void test(){int x=8;int y=9;} " ) ) ;
QCOMPARE ( client . expandedMacros ( ) , QList < QByteArray > ( ) < < QByteArray ( " FOO " ) < < QByteArray ( " BAR " ) ) ;
QCOMPARE ( client . expandedMacrosOffset ( ) , QList < unsigned > ( ) < < buffer . indexOf ( " FOO; " ) < < buffer . indexOf ( " BAR; " ) ) ;
2012-04-02 14:57:13 +02:00
QCOMPARE ( client . definedMacros ( ) , QList < QByteArray > ( ) < < QByteArray ( " FOO " ) < < QByteArray ( " BAR " ) ) ;
QCOMPARE ( client . definedMacrosLine ( ) , QList < unsigned > ( ) < < 2 < < 3 ) ;
2012-04-02 13:46:53 +02:00
}
2012-03-16 15:16:06 +01:00
void tst_Preprocessor : : macro_definition_lineno ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-03-16 15:16:06 +01:00
QByteArray ( " #define foo(ARGS) int f(ARGS) \n "
" foo(int a); \n " ) ) ;
2012-04-02 13:46:53 +02:00
QVERIFY ( preprocessed . contains ( " #gen true \n # 2 \" <stdin> \" \n int f " ) ) ;
2012-03-16 15:16:06 +01:00
2012-04-19 10:35:49 +02:00
preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-03-16 15:16:06 +01:00
QByteArray ( " #define foo(ARGS) int f(ARGS) \n "
" foo(int a) \n "
" ; \n " ) ) ;
2012-04-02 13:46:53 +02:00
QVERIFY ( preprocessed . contains ( " #gen true \n # 2 \" <stdin> \" \n int f " ) ) ;
2012-03-16 15:16:06 +01:00
2012-04-19 10:35:49 +02:00
preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-03-16 15:16:06 +01:00
QByteArray ( " #define foo(ARGS) int f(ARGS) \n "
" foo(int \n "
" a); \n " ) ) ;
2012-04-02 13:46:53 +02:00
QVERIFY ( preprocessed . contains ( " #gen true \n # 2 \" <stdin> \" \n int f " ) ) ;
2012-03-16 15:16:06 +01:00
2012-04-19 10:35:49 +02:00
preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-03-16 15:16:06 +01:00
QByteArray ( " #define foo int f \n "
" foo; \n " ) ) ;
2012-04-02 13:46:53 +02:00
QVERIFY ( preprocessed . contains ( " #gen true \n # 2 \" <stdin> \" \n int f " ) ) ;
2012-03-16 15:16:06 +01:00
2012-04-19 10:35:49 +02:00
preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-03-16 15:16:06 +01:00
QByteArray ( " #define foo int f \n "
" foo \n "
" ; \n " ) ) ;
2012-04-02 13:46:53 +02:00
QVERIFY ( preprocessed . contains ( " #gen true \n # 2 \" <stdin> \" \n int f " ) ) ;
2012-03-16 15:16:06 +01:00
}
2012-04-02 08:46:56 +02:00
void tst_Preprocessor : : objmacro_expanding_as_fnmacro_notification ( )
{
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
Preprocessor preprocess ( & client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-04-02 08:46:56 +02:00
QByteArray ( " \n #define bar(a,b) a + b "
" \n #define foo bar "
" \n foo(1, 2) \n " ) ) ;
QVERIFY ( client . expandedMacros ( ) = = ( QList < QByteArray > ( ) < < QByteArray ( " foo " ) ) ) ;
}
void tst_Preprocessor : : macro_arguments_notificatin ( )
{
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
Preprocessor preprocess ( & client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2012-04-02 08:46:56 +02:00
QByteArray ( " \n #define foo(a,b) a + b "
" \n #define arg(a) a "
" \n #define value 2 "
" \n foo(arg(1), value) \n " ) ) ;
QVERIFY ( client . expandedMacros ( ) = = ( QList < QByteArray > ( ) < < QByteArray ( " foo " )
< < QByteArray ( " arg " )
< < QByteArray ( " value " ) ) ) ;
}
2009-03-03 16:32:08 +01:00
void tst_Preprocessor : : unfinished_function_like_macro_call ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) ,
2009-06-19 11:19:41 +02:00
QByteArray ( " \n #define foo(a,b) a + b "
" \n foo(1, 2 \n " ) ) ;
2012-03-30 13:01:32 +02:00
QByteArray expected__ ( " # 1 \" <stdin> \" \n \n \n 1 \n #gen true \n # 2 \" <stdin> \" \n + \n #gen false \n # 3 \" <stdin> \" \n 2 \n " ) ;
// DUMP_OUTPUT(preprocessed);
2012-03-26 15:18:01 +02:00
QCOMPARE ( preprocessed , expected__ ) ;
2009-03-03 16:32:08 +01:00
}
2010-02-10 11:04:31 +01:00
void tst_Preprocessor : : nasty_macro_expansion ( )
{
QByteArray input ( " \n "
" #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) \n "
" #define is_power_of_two(x) ( !((x) & ((x)-1)) ) \n "
" #define low_bit_mask(x) ( ((x)-1) & ~(x) ) \n "
" #define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x)) \n "
" #define compile_ffs2(__x) \\ \n "
" __builtin_choose_expr(((__x) & 0x1), 0, 1) \n "
" #define compile_ffs4(__x) \\ \n "
" __builtin_choose_expr(((__x) & 0x3), \\ \n "
" (compile_ffs2((__x))), \\ \n "
" (compile_ffs2((__x) >> 2) + 2)) \n "
" #define compile_ffs8(__x) \\ \n "
" __builtin_choose_expr(((__x) & 0xf), \\ \n "
" (compile_ffs4((__x))), \\ \n "
" (compile_ffs4((__x) >> 4) + 4)) \n "
" #define compile_ffs16(__x) \\ \n "
" __builtin_choose_expr(((__x) & 0xff), \\ \n "
" (compile_ffs8((__x))), \\ \n "
" (compile_ffs8((__x) >> 8) + 8)) \n "
" #define compile_ffs32(__x) \\ \n "
" __builtin_choose_expr(((__x) & 0xffff), \\ \n "
" (compile_ffs16((__x))), \\ \n "
" (compile_ffs16((__x) >> 16) + 16)) \n "
" #define FIELD_CHECK(__mask, __type) \\ \n "
" BUILD_BUG_ON(!(__mask) || \\ \n "
" !is_valid_mask(__mask) || \\ \n "
" (__mask) != (__type)(__mask)) \\ \n "
" \n "
" #define FIELD32(__mask) \\ \n "
" ({ \\ \n "
" FIELD_CHECK(__mask, u32); \\ \n "
" (struct rt2x00_field32) { \\ \n "
" compile_ffs32(__mask), (__mask) \\ \n "
" }; \\ \n "
" }) \n "
" #define BBPCSR 0x00f0 \n "
" #define BBPCSR_BUSY FIELD32(0x00008000) \n "
" #define WAIT_FOR_BBP(__dev, __reg) \\ \n "
" rt2x00pci_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) \n "
" if (WAIT_FOR_BBP(rt2x00dev, ®)) {} \n "
) ;
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run ( QLatin1String ( " <stdin> " ) , input ) ;
2010-02-10 11:04:31 +01:00
QVERIFY ( ! preprocessed . contains ( " FIELD32 " ) ) ;
}
2012-02-24 13:06:21 +01:00
void tst_Preprocessor : : tstst ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run (
2012-02-24 13:06:21 +01:00
QLatin1String ( " <stdin> " ) ,
QByteArray ( " \n "
" # define _GLIBCXX_VISIBILITY(V) __attribute__ ((__visibility__ (#V))) \n "
" namespace std _GLIBCXX_VISIBILITY(default) { \n "
" } \n "
) ) ;
2012-03-30 13:01:32 +02:00
const QByteArray result____ = " # 1 \" <stdin> \" \n \n \n "
2012-03-26 15:18:01 +02:00
" namespace std \n "
2012-03-13 11:50:56 +01:00
" #gen true \n "
2012-03-26 15:18:01 +02:00
" # 2 \" <stdin> \" \n "
" __attribute__ ((__visibility__ ( \n "
" \" default \" \n "
" # 2 \" <stdin> \" \n "
" ))) \n "
2012-03-13 11:50:56 +01:00
" #gen false \n "
" # 3 \" <stdin> \" \n "
" { \n "
2012-03-26 15:18:01 +02:00
" } \n " ;
2012-03-30 13:01:32 +02:00
// DUMP_OUTPUT(preprocessed);
2012-03-26 15:18:01 +02:00
QCOMPARE ( preprocessed , result____ ) ;
}
void tst_Preprocessor : : test_file_builtin ( )
{
Client * client = 0 ; // no client.
Environment env ;
Preprocessor preprocess ( client , & env ) ;
2012-04-19 10:35:49 +02:00
QByteArray preprocessed = preprocess . run (
2012-03-26 15:18:01 +02:00
QLatin1String ( " some-file.c " ) ,
QByteArray ( " const char *f = __FILE__ \n "
) ) ;
const QByteArray result____ =
2012-03-30 13:01:32 +02:00
" # 1 \" some-file.c \" \n "
2012-03-26 15:18:01 +02:00
" const char *f = \n "
" #gen true \n "
" # 1 \" some-file.c \" \n "
" \" some-file.c \" \n "
" #gen false \n "
" # 2 \" some-file.c \" \n "
;
QCOMPARE ( preprocessed , result____ ) ;
}
void tst_Preprocessor : : comparisons_data ( )
{
QTest : : addColumn < QString > ( " infile " ) ;
QTest : : addColumn < QString > ( " outfile " ) ;
QTest : : addColumn < QString > ( " errorfile " ) ;
QTest : : newRow ( " do nothing " ) < < " noPP.1.cpp " < < " noPP.1.cpp " < < " " ;
2012-04-24 18:29:09 +02:00
QTest : : newRow ( " identifier-expansion 1 " )
< < " identifier-expansion.1.cpp " < < " identifier-expansion.1.out.cpp " < < " " ;
QTest : : newRow ( " identifier-expansion 2 " )
< < " identifier-expansion.2.cpp " < < " identifier-expansion.2.out.cpp " < < " " ;
QTest : : newRow ( " identifier-expansion 3 " )
< < " identifier-expansion.3.cpp " < < " identifier-expansion.3.out.cpp " < < " " ;
QTest : : newRow ( " identifier-expansion 4 " )
< < " identifier-expansion.4.cpp " < < " identifier-expansion.4.out.cpp " < < " " ;
QTest : : newRow ( " identifier-expansion 5 " )
< < " identifier-expansion.5.cpp " < < " identifier-expansion.5.out.cpp " < < " " ;
2012-03-26 15:18:01 +02:00
QTest : : newRow ( " reserved 1 " ) < < " reserved.1.cpp " < < " reserved.1.out.cpp " < < " " ;
QTest : : newRow ( " recursive 1 " ) < < " recursive.1.cpp " < < " recursive.1.out.cpp " < < " " ;
QTest : : newRow ( " macro_pounder_fn " ) < < " macro_pounder_fn.c " < < " " < < " " ;
QTest : : newRow ( " macro_expand " ) < < " macro_expand.c " < < " macro_expand.out.c " < < " " ;
QTest : : newRow ( " macro-test " ) < < " macro-test.cpp " < < " macro-test.out.cpp " < < " " ;
QTest : : newRow ( " empty-macro " ) < < " empty-macro.cpp " < < " empty-macro.out.cpp " < < " " ;
QTest : : newRow ( " empty-macro 2 " ) < < " empty-macro.2.cpp " < < " empty-macro.2.out.cpp " < < " " ;
}
void tst_Preprocessor : : comparisons ( )
{
QFETCH ( QString , infile ) ;
QFETCH ( QString , outfile ) ;
QFETCH ( QString , errorfile ) ;
QByteArray errors ;
2012-03-30 13:01:32 +02:00
QByteArray preprocessed = preprocess ( infile , & errors , infile = = outfile ) ;
2012-03-26 15:18:01 +02:00
// DUMP_OUTPUT(preprocessed);
if ( ! outfile . isEmpty ( ) ) {
QByteArray output____ = loadSource ( " data/ " + outfile ) ; // these weird underscores are here to make the name as long as "preprocessed", so the QCOMPARE error messages are nicely aligned.
// QCOMPARE(preprocessed, output____);
QCOMPARE ( QString : : fromUtf8 ( preprocessed . constData ( ) ) , QString : : fromUtf8 ( output____ . constData ( ) ) ) ;
}
if ( ! errorfile . isEmpty ( ) ) {
QByteArray errorFileContents = loadSource ( " data/ " + errorfile ) ;
QCOMPARE ( QString : : fromUtf8 ( errors . constData ( ) ) , QString : : fromUtf8 ( errorFileContents . constData ( ) ) ) ;
}
}
void tst_Preprocessor : : blockSkipping ( )
{
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
Preprocessor pp ( & client , & env ) ;
2012-04-19 10:35:49 +02:00
/*QByteArray preprocessed =*/ pp . run (
2012-03-26 15:18:01 +02:00
QLatin1String ( " <stdin> " ) ,
QByteArray ( " #if 0 \n "
" \n "
" int yes; \n "
" \n "
" #elif 0 \n "
" \n "
" int no; \n "
" \n "
" #else // foobar \n "
" \n "
" void also_not; \n "
" \n "
" #endif \n "
) ) ;
2012-03-13 11:50:56 +01:00
2012-03-26 15:18:01 +02:00
QList < MockClient : : Block > blocks = client . skippedBlocks ( ) ;
QCOMPARE ( blocks . size ( ) , 1 ) ;
MockClient : : Block b = blocks . at ( 0 ) ;
QCOMPARE ( b . start , 6U ) ;
QCOMPARE ( b . end , 34U ) ;
2012-02-24 13:06:21 +01:00
}
2012-03-29 17:14:42 +02:00
void tst_Preprocessor : : includes_1 ( )
{
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
Preprocessor pp ( & client , & env ) ;
2012-04-19 10:35:49 +02:00
/*QByteArray preprocessed =*/ pp . run (
2012-03-29 17:14:42 +02:00
QLatin1String ( " <stdin> " ) ,
QByteArray ( " #define FOO <foo.h> \n "
" #define BAR \" bar.h \" \n "
" \n "
" #include FOO \n "
" #include BAR \n "
" \n "
" #include <zoo.h> \n "
" #include \" mooze.h \" \n "
) ) ;
QList < Include > incs = client . recordedIncludes ( ) ;
// qDebug()<<incs;
QCOMPARE ( incs . size ( ) , 4 ) ;
QCOMPARE ( incs . at ( 0 ) . fileName , QLatin1String ( " foo.h " ) ) ;
QCOMPARE ( incs . at ( 0 ) . type , Client : : IncludeGlobal ) ;
QCOMPARE ( incs . at ( 0 ) . line , 4U ) ;
QCOMPARE ( incs . at ( 1 ) . fileName , QLatin1String ( " bar.h " ) ) ;
QCOMPARE ( incs . at ( 1 ) . type , Client : : IncludeLocal ) ;
QCOMPARE ( incs . at ( 1 ) . line , 5U ) ;
QCOMPARE ( incs . at ( 2 ) . fileName , QLatin1String ( " zoo.h " ) ) ;
QCOMPARE ( incs . at ( 2 ) . type , Client : : IncludeGlobal ) ;
QCOMPARE ( incs . at ( 2 ) . line , 7U ) ;
QCOMPARE ( incs . at ( 3 ) . fileName , QLatin1String ( " mooze.h " ) ) ;
QCOMPARE ( incs . at ( 3 ) . type , Client : : IncludeLocal ) ;
QCOMPARE ( incs . at ( 3 ) . line , 8U ) ;
}
2012-04-19 15:19:38 +02:00
void tst_Preprocessor : : defined ( )
{
QFETCH ( bool , xdefined ) ;
QFETCH ( bool , ydefined ) ;
QFETCH ( QString , input ) ;
QByteArray output ;
Environment env ;
MockClient client ( & env , & output ) ;
Preprocessor pp ( & client , & env ) ;
pp . run ( QLatin1String ( " <stdin> " ) , input . toLatin1 ( ) , false , true ) ;
QList < QByteArray > expected ;
if ( xdefined )
expected . append ( " X " ) ;
if ( ydefined )
expected . append ( " Y " ) ;
if ( client . definedMacros ( ) ! = expected )
qWarning ( ) < < " \n Source: " < < input . replace ( ' \n ' , " " ) ;
QCOMPARE ( client . definedMacros ( ) , expected ) ;
}
void tst_Preprocessor : : defined_data ( )
{
QTest : : addColumn < bool > ( " xdefined " ) ;
QTest : : addColumn < bool > ( " ydefined " ) ;
QTest : : addColumn < QString > ( " input " ) ;
QTest : : newRow ( " 1a " ) < < true < < true < <
" #define X \n #if defined(X) \n #define Y \n #endif " ;
QTest : : newRow ( " 1b " ) < < true < < true < <
" #define X \n #if defined X \n #define Y \n #endif " ;
QTest : : newRow ( " 1c " ) < < true < < true < <
" #define X \n #ifdef X \n #define Y \n #endif " ;
QTest : : newRow ( " 2a " ) < < false < < false < <
" #if defined(X) \n #define Y \n #endif " ;
QTest : : newRow ( " 2b " ) < < false < < false < <
" #if defined X \n #define Y \n #endif " ;
QTest : : newRow ( " 2c " ) < < false < < false < <
" #ifdef X \n #define Y \n #endif " ;
QTest : : newRow ( " 3a " ) < < true < < false < <
" #define X \n #if !defined(X) \n #define Y \n #endif " ;
QTest : : newRow ( " 3b " ) < < true < < false < <
" #define X \n #if !defined X \n #define Y \n #endif " ;
QTest : : newRow ( " 3c " ) < < true < < false < <
" #define X \n #ifndef X \n #define Y \n #endif " ;
QTest : : newRow ( " 4a " ) < < false < < true < <
" #if !defined(X) \n #define Y \n #endif " ;
QTest : : newRow ( " 4b " ) < < false < < true < <
" #if !defined X \n #define Y \n #endif " ;
QTest : : newRow ( " 4c " ) < < false < < true < <
" #ifndef X \n #define Y \n #endif " ;
2012-04-24 18:50:22 +02:00
QTest : : newRow ( " 5a " ) < < false < < false < <
" #if !defined(X) && (defined(Y)) \n "
" #define X \n "
" #endif \n " ;
QTest : : newRow ( " 5b " ) < < false < < false < <
" #if !defined(X) && defined(Y) \n "
" #define X \n "
" #endif \n " ;
QTest : : newRow ( " 5c " ) < < false < < false < <
" #if !defined(X) && 0 "
" #define X \n "
" #endif \n " ;
QTest : : newRow ( " 5d " ) < < false < < false < <
" #if (!defined(X)) && defined(Y) \n "
" #define X \n "
" #endif \n " ;
QTest : : newRow ( " 5d " ) < < false < < false < <
" #if (define(Y)) \n "
" #define X \n "
" #endif \n " ;
2012-04-19 15:19:38 +02:00
}
2009-03-02 11:30:43 +01:00
QTEST_APPLESS_MAIN ( tst_Preprocessor )
2012-04-19 15:19:38 +02:00
2009-03-02 11:30:43 +01:00
# include "tst_preprocessor.moc"