2016-02-06 02:52:06 +01:00
# include "./testutils.h"
# include "../application/failure.h"
2017-06-20 23:19:49 +02:00
# include "../conversion/stringbuilder.h"
2016-02-17 20:21:11 +01:00
# include "../conversion/stringconversion.h"
2017-10-24 01:02:07 +02:00
# include "../io/ansiescapecodes.h"
2016-06-14 22:53:19 +02:00
# include "../io/catchiofailure.h"
2017-10-30 23:01:07 +01:00
# include "../io/misc.h"
# include "../io/path.h"
2016-02-06 02:52:06 +01:00
# include <cstdlib>
# include <cstring>
# include <fstream>
2016-07-30 22:34:31 +02:00
# include <initializer_list>
2017-05-01 03:13:11 +02:00
# include <iostream>
2017-05-19 00:12:07 +02:00
# include <limits>
2016-02-06 02:52:06 +01:00
2016-07-30 22:34:31 +02:00
# ifdef PLATFORM_UNIX
2017-05-01 03:13:11 +02:00
# include <poll.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <unistd.h>
2016-07-30 22:34:31 +02:00
# endif
2016-02-09 02:21:42 +01:00
2016-02-06 02:52:06 +01:00
using namespace std ;
using namespace ApplicationUtilities ;
2016-02-17 20:21:11 +01:00
using namespace ConversionUtilities ;
2017-10-24 01:02:07 +02:00
using namespace EscapeCodes ;
2016-06-14 22:53:19 +02:00
using namespace IoUtilities ;
2016-02-06 02:52:06 +01:00
2016-06-10 22:59:22 +02:00
/*!
* \ brief Contains classes and functions utilizing creating of test applications .
*/
2016-02-06 02:52:06 +01:00
namespace TestUtilities {
TestApplication * TestApplication : : m_instance = nullptr ;
/*!
* \ class TestApplication
2016-02-09 02:21:42 +01:00
* \ brief The TestApplication class simplifies writing test applications that require opening test files .
2016-02-06 02:52:06 +01:00
* \ remarks Only one instance is allowed at a time ( singletone class ) .
*/
/*!
* \ brief Constructs a TestApplication instance .
* \ throws Throws std : : runtime_error if an instance has already been created .
*/
2017-05-01 03:13:11 +02:00
TestApplication : : TestApplication ( int argc , char * * argv )
: m_helpArg ( m_parser )
, m_testFilesPathArg ( " test-files-path " , ' p ' , " specifies the path of the directory with test files " )
, m_applicationPathArg ( " app-path " , ' a ' , " specifies the path of the application to be tested " )
, m_workingDirArg ( " working-dir " , ' w ' , " specifies the directory to store working copies of test files " )
, m_unitsArg ( " units " , ' u ' , " specifies the units to test; omit to test all units " )
2016-02-06 02:52:06 +01:00
{
2016-02-09 02:21:42 +01:00
// check whether there is already an instance
2017-05-01 03:13:11 +02:00
if ( m_instance ) {
2016-02-06 02:52:06 +01:00
throw runtime_error ( " only one TestApplication instance allowed at a time " ) ;
}
m_instance = this ;
2016-02-09 02:21:42 +01:00
2017-10-30 23:01:07 +01:00
// determine fallback path for testfiles which is used when --test-files-path/-p not present
// -> read TEST_FILE_PATH environment variable
2018-02-03 17:08:43 +01:00
m_fallbackTestFilesPath = readTestfilePathFromEnv ( ) ;
2017-10-30 23:01:07 +01:00
// -> find source directory if TEST_FILE_PATH not present
2018-02-03 17:08:43 +01:00
bool fallbackIsSourceDir = m_fallbackTestFilesPath . empty ( ) ;
2017-10-30 23:01:07 +01:00
if ( fallbackIsSourceDir ) {
2018-02-03 17:08:43 +01:00
m_fallbackTestFilesPath = readTestfilePathFromSrcRef ( ) ;
2016-02-06 02:52:06 +01:00
}
2016-02-09 02:21:42 +01:00
2017-11-12 16:17:08 +01:00
// handle specified arguments (if present)
if ( argc & & argv ) {
// setup argument parser
for ( Argument * arg : initializer_list < Argument * > { & m_testFilesPathArg , & m_applicationPathArg , & m_workingDirArg } ) {
arg - > setRequiredValueCount ( 1 ) ;
arg - > setValueNames ( { " path " } ) ;
arg - > setCombinable ( true ) ;
}
m_unitsArg . setRequiredValueCount ( Argument : : varValueCount ) ;
m_unitsArg . setValueNames ( { " unit1 " , " unit2 " , " unit3 " } ) ;
m_unitsArg . setCombinable ( true ) ;
m_parser . setMainArguments ( { & m_testFilesPathArg , & m_applicationPathArg , & m_workingDirArg , & m_unitsArg , & m_helpArg } ) ;
2016-02-09 02:21:42 +01:00
2017-11-12 16:17:08 +01:00
// parse arguments
try {
m_parser . parseArgs ( argc , argv ) ;
} catch ( const Failure & failure ) {
cerr < < failure ;
m_valid = false ;
return ;
}
2017-06-20 23:19:49 +02:00
2017-11-12 16:17:08 +01:00
// print help
if ( m_helpArg . isPresent ( ) ) {
exit ( 0 ) ;
}
2017-10-24 01:02:07 +02:00
}
2017-06-20 23:19:49 +02:00
2017-10-24 01:02:07 +02:00
// handle path for testfiles and working-copy
cerr < < " Directories used to search for testfiles: " < < endl ;
if ( m_testFilesPathArg . isPresent ( ) ) {
if ( * m_testFilesPathArg . values ( ) . front ( ) ) {
2017-10-30 23:01:07 +01:00
cerr < < ( ( m_testFilesPath = m_testFilesPathArg . values ( ) . front ( ) ) + = ' / ' ) < < endl ;
2017-10-24 01:02:07 +02:00
} else {
2017-10-30 23:01:07 +01:00
cerr < < ( m_testFilesPath = " ./ " ) < < endl ;
2016-02-06 02:52:06 +01:00
}
2018-02-03 17:08:43 +01:00
} else {
// use fallback path if --test-files-path/-p not present
m_testFilesPath . swap ( m_fallbackTestFilesPath ) ;
}
// if it wasn't already the case, use the source directory as fallback dir
if ( m_fallbackTestFilesPath . empty ( ) & & ! fallbackIsSourceDir ) {
m_fallbackTestFilesPath = readTestfilePathFromSrcRef ( ) ;
fallbackIsSourceDir = true ;
2017-10-24 01:02:07 +02:00
}
2017-10-30 23:01:07 +01:00
if ( ! m_fallbackTestFilesPath . empty ( ) & & m_testFilesPath ! = m_fallbackTestFilesPath ) {
cerr < < m_fallbackTestFilesPath < < endl ;
2017-10-24 01:02:07 +02:00
}
cerr < < " ./testfiles/ " < < endl < < endl ;
cerr < < " Directory used to store working copies: " < < endl ;
if ( m_workingDirArg . isPresent ( ) ) {
if ( * m_workingDirArg . values ( ) . front ( ) ) {
( m_workingDir = m_workingDirArg . values ( ) . front ( ) ) + = ' / ' ;
2016-02-09 02:21:42 +01:00
} else {
2017-10-24 01:02:07 +02:00
m_workingDir = " ./ " ;
2016-02-09 02:21:42 +01:00
}
2017-10-24 01:02:07 +02:00
} else if ( const char * workingDirEnv = getenv ( " WORKING_DIR " ) ) {
2018-02-03 17:08:43 +01:00
if ( * workingDirEnv ) {
m_workingDir = argsToString ( workingDirEnv , ' / ' ) ;
2017-06-20 23:19:49 +02:00
}
2017-10-24 01:02:07 +02:00
} else {
if ( m_testFilesPathArg . isPresent ( ) ) {
2017-10-30 23:01:07 +01:00
m_workingDir = m_testFilesPath + " workingdir/ " ;
} else if ( ! m_fallbackTestFilesPath . empty ( ) & & ! fallbackIsSourceDir ) {
m_workingDir = m_fallbackTestFilesPath + " workingdir/ " ;
2017-10-24 01:02:07 +02:00
} else {
m_workingDir = " ./testfiles/workingdir/ " ;
}
}
cerr < < m_workingDir < < endl < < endl ;
2017-06-20 23:19:49 +02:00
2017-10-24 01:02:07 +02:00
// clear list of all additional profiling files created when forking the test application
if ( const char * profrawListFile = getenv ( " LLVM_PROFILE_LIST_FILE " ) ) {
ofstream ( profrawListFile , ios_base : : trunc ) ;
2016-02-06 02:52:06 +01:00
}
2017-10-24 01:02:07 +02:00
m_valid = true ;
cerr < < TextAttribute : : Bold < < " Executing test cases ... " < < Phrases : : EndFlush ;
2016-02-06 02:52:06 +01:00
}
/*!
* \ brief Destroys the TestApplication .
*/
TestApplication : : ~ TestApplication ( )
{
m_instance = nullptr ;
}
/*!
2016-02-09 02:21:42 +01:00
* \ brief Returns the full path of the test file with the specified \ a name .
2017-11-12 16:58:53 +01:00
*
* The specified \ a name might be a relative path in the testfiles directory .
*
* The following directories are searched for the specified testfile :
* 1. The directory specified as CLI argument .
2018-02-03 17:08:43 +01:00
* 2. The fallback directory , which can be set by setting the environment
2017-11-12 16:58:53 +01:00
* variable ` TEST_FILE_PATH ` .
2018-02-03 17:08:43 +01:00
* 3. The source directory , if it could be determined via " srcref " - file
* unless both , the CLI argument and environment variable are present .
2016-02-06 02:52:06 +01:00
*/
string TestApplication : : testFilePath ( const string & name ) const
{
string path ;
2016-02-09 02:21:42 +01:00
fstream file ; // used to check whether the file is present
2018-02-03 17:08:43 +01:00
// check the path specified by command line argument or via environment variable
if ( ! m_testFilesPath . empty ( ) ) {
2017-10-30 23:01:07 +01:00
file . open ( path = m_testFilesPath + name , ios_base : : in ) ;
2017-05-01 03:13:11 +02:00
if ( file . good ( ) ) {
2016-02-06 02:52:06 +01:00
return path ;
}
}
2016-02-09 02:21:42 +01:00
2018-02-03 17:08:43 +01:00
// check the fallback path (value from environment variable or source directory)
2017-10-30 23:01:07 +01:00
if ( ! m_fallbackTestFilesPath . empty ( ) ) {
2016-02-06 02:52:06 +01:00
file . clear ( ) ;
2017-10-30 23:01:07 +01:00
file . open ( path = m_fallbackTestFilesPath + name , ios_base : : in ) ;
2017-05-01 03:13:11 +02:00
if ( file . good ( ) ) {
2016-02-06 02:52:06 +01:00
return path ;
}
}
2016-02-09 02:21:42 +01:00
// file still not found -> return default path
2017-03-12 20:25:06 +01:00
file . clear ( ) ;
2017-05-19 00:08:27 +02:00
file . open ( path = " ./testfiles/ " + name , ios_base : : in ) ;
2017-05-01 03:13:11 +02:00
if ( ! file . good ( ) ) {
2017-10-24 01:02:07 +02:00
cerr < < Phrases : : Warning < < " The testfile \" " < < path < < " \" can not be located. " < < Phrases : : EndFlush ;
2017-03-12 20:25:06 +01:00
}
return path ;
2016-02-06 02:52:06 +01:00
}
2016-02-27 01:18:54 +01:00
# ifdef PLATFORM_UNIX
2016-02-09 02:21:42 +01:00
/*!
* \ brief Returns the full path to a working copy of the test file with the specified \ a name .
2017-11-12 16:58:53 +01:00
*
* The specified \ a mode controls whether a working copy is actually created or whether just the path is returned .
* The test file is located using testFilePath ( ) .
*
2016-07-30 22:34:31 +02:00
* \ remarks Currently only available under UNIX .
2016-02-09 02:21:42 +01:00
*/
2017-02-04 20:16:50 +01:00
string TestApplication : : workingCopyPathMode ( const string & name , WorkingCopyMode mode ) const
2016-02-09 02:21:42 +01:00
{
// ensure working directory is present
struct stat currentStat ;
2017-05-01 03:13:11 +02:00
if ( stat ( m_workingDir . c_str ( ) , & currentStat ) | | ! S_ISDIR ( currentStat . st_mode ) ) {
if ( mkdir ( m_workingDir . c_str ( ) , S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) ) {
2017-10-24 01:02:07 +02:00
cerr < < Phrases : : Error < < " Unable to create working copy for \" " < < name < < " \" : can't create working directory. " < < Phrases : : EndFlush ;
2016-02-09 02:21:42 +01:00
return string ( ) ;
}
}
2016-02-17 20:21:11 +01:00
// ensure subdirectory exists
2017-05-04 22:44:00 +02:00
const auto parts = splitString < vector < string > > ( name , string ( " / " ) , EmptyPartsTreat : : Omit ) ;
2017-05-01 03:13:11 +02:00
if ( ! parts . empty ( ) ) {
2016-02-17 20:21:11 +01:00
string currentLevel = m_workingDir ;
2018-07-28 19:39:11 +02:00
// create subdirectory level by level
2017-05-01 03:13:11 +02:00
for ( auto i = parts . cbegin ( ) , end = parts . end ( ) - 1 ; i ! = end ; + + i ) {
if ( currentLevel . back ( ) ! = ' / ' ) {
2016-04-16 00:50:16 +02:00
currentLevel + = ' / ' ;
}
currentLevel + = * i ;
2018-07-28 19:39:11 +02:00
// continue if subdirectory level already exists
if ( ! stat ( currentLevel . c_str ( ) , & currentStat ) & & S_ISDIR ( currentStat . st_mode ) ) {
continue ;
2016-02-17 20:21:11 +01:00
}
2018-07-28 19:39:11 +02:00
// continue if we can successfully create the directory
if ( ! mkdir ( currentLevel . c_str ( ) , S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) ) {
continue ;
}
// fail otherwise
cerr < < Phrases : : Error < < " Unable to create working copy for \" " < < name < < " \" : can't create working directory. "
< < Phrases : : EndFlush ;
return string ( ) ;
2016-02-17 20:21:11 +01:00
}
}
2018-07-28 19:39:11 +02:00
// just return the path if we don't want to actually create a copy
2018-07-28 15:59:31 +02:00
if ( mode = = WorkingCopyMode : : NoCopy ) {
2017-02-04 20:16:50 +01:00
return m_workingDir + name ;
2016-02-09 02:21:42 +01:00
}
2018-07-28 15:59:31 +02:00
2018-07-28 19:39:11 +02:00
// copy the file
2018-07-28 15:59:31 +02:00
const auto origFilePath ( testFilePath ( name ) ) ;
const auto workingCopyPath ( m_workingDir + name ) ;
2018-07-28 19:39:11 +02:00
fstream origFile , workingCopy ;
origFile . open ( origFilePath , ios_base : : in | ios_base : : binary ) ;
if ( origFile . fail ( ) ) {
cerr < < Phrases : : Error < < " Unable to create working copy for \" " < < name < < " \" : an IO error occurred when opening original file \" " < < origFilePath < < " \" . " < < Phrases : : EndFlush ;
return string ( ) ;
}
workingCopy . open ( workingCopyPath , ios_base : : out | ios_base : : binary | ios_base : : trunc ) ;
if ( workingCopy . fail ( ) ) {
cerr < < Phrases : : Error < < " Unable to create working copy for \" " < < name < < " \" : an IO error occurred when opening target file \" " < < workingCopyPath < < " \" . " < < Phrases : : EndFlush ;
return string ( ) ;
}
workingCopy < < origFile . rdbuf ( ) ;
if ( ! origFile . fail ( ) & & ! workingCopy . fail ( ) ) {
2018-07-28 15:59:31 +02:00
return workingCopyPath ;
2018-07-28 19:39:11 +02:00
}
cerr < < Phrases : : Error < < " Unable to create working copy for \" " < < name < < " \" : " ;
if ( origFile . fail ( ) ) {
cerr < < " an IO error occurred when reading original file \" " < < origFilePath < < " \" " ;
return string ( ) ;
}
if ( workingCopy . fail ( ) ) {
if ( origFile . fail ( ) ) {
cerr < < " and " ;
}
cerr < < " an IO error occurred when writing to target file \" " < < workingCopyPath < < " \" . " ;
2018-07-28 15:59:31 +02:00
}
2016-02-09 02:21:42 +01:00
return string ( ) ;
}
2016-07-30 22:34:31 +02:00
2017-11-12 16:58:53 +01:00
/*!
* \ brief Creates a working copy of the test file with the specified \ a name and returns the full path of the created file .
*
* The test file is located using testFilePath ( ) .
*
* \ remarks Currently only available under UNIX .
*/
2017-02-04 20:16:50 +01:00
string TestApplication : : workingCopyPath ( const string & name ) const
{
return workingCopyPathMode ( name , WorkingCopyMode : : CreateCopy ) ;
}
2016-07-30 22:34:31 +02:00
/*!
2017-06-20 23:19:49 +02:00
* \ brief Executes an application with the specified \ a args .
* \ remarks Provides internal implementation of execApp ( ) and execHelperApp ( ) .
2016-07-30 22:34:31 +02:00
*/
2017-06-20 23:19:49 +02:00
int execAppInternal ( const char * appPath , const char * const * args , std : : string & output , std : : string & errors , bool suppressLogging , int timeout ,
const std : : string & newProfilingPath )
2016-07-30 22:34:31 +02:00
{
2016-08-05 01:43:46 +02:00
// print log message
2017-05-01 03:13:11 +02:00
if ( ! suppressLogging ) {
2016-08-05 01:43:46 +02:00
cout < < ' - ' ;
2017-05-01 03:13:11 +02:00
for ( const char * const * i = args ; * i ; + + i ) {
2016-08-05 01:43:46 +02:00
cout < < ' ' < < * i ;
}
cout < < endl ;
}
2017-06-20 23:19:49 +02:00
2016-07-30 22:34:31 +02:00
// create pipes
int coutPipes [ 2 ] , cerrPipes [ 2 ] ;
pipe ( coutPipes ) , pipe ( cerrPipes ) ;
int readCoutPipe = coutPipes [ 0 ] , writeCoutPipe = coutPipes [ 1 ] ;
int readCerrPipe = cerrPipes [ 0 ] , writeCerrPipe = cerrPipes [ 1 ] ;
2017-06-20 23:19:49 +02:00
2016-07-30 22:34:31 +02:00
// create child process
2017-05-01 03:13:11 +02:00
if ( int child = fork ( ) ) {
2016-07-30 22:34:31 +02:00
// parent process: read stdout and stderr from child
close ( writeCoutPipe ) , close ( writeCerrPipe ) ;
2016-08-15 22:35:37 +02:00
try {
2017-05-01 03:13:11 +02:00
if ( child = = - 1 ) {
2016-08-15 22:35:37 +02:00
throw runtime_error ( " Unable to create fork " ) ;
2016-07-30 22:34:31 +02:00
}
2016-08-15 22:35:37 +02:00
// init file descriptor set for poll
struct pollfd fileDescriptorSet [ 2 ] ;
fileDescriptorSet [ 0 ] . fd = readCoutPipe ;
fileDescriptorSet [ 1 ] . fd = readCerrPipe ;
fileDescriptorSet [ 0 ] . events = fileDescriptorSet [ 1 ] . events = POLLIN ;
// init variables for reading
2016-07-30 22:34:31 +02:00
char buffer [ 512 ] ;
ssize_t count ;
2017-05-19 00:07:38 +02:00
output . clear ( ) , errors . clear ( ) ;
2016-08-15 22:35:37 +02:00
// poll as long as at least one pipe is open
do {
int retpoll = poll ( fileDescriptorSet , 2 , timeout ) ;
2017-05-01 03:13:11 +02:00
if ( retpoll > 0 ) {
2016-08-15 22:35:37 +02:00
// poll succeeds
2017-05-01 03:13:11 +02:00
if ( fileDescriptorSet [ 0 ] . revents & POLLIN ) {
if ( ( count = read ( readCoutPipe , buffer , sizeof ( buffer ) ) ) > 0 ) {
2017-10-24 01:02:07 +02:00
output . append ( buffer , static_cast < size_t > ( count ) ) ;
2016-08-15 22:35:37 +02:00
}
2017-05-01 03:13:11 +02:00
} else if ( fileDescriptorSet [ 0 ] . revents & POLLHUP ) {
2016-08-15 22:35:37 +02:00
close ( readCoutPipe ) ;
fileDescriptorSet [ 0 ] . fd = - 1 ;
}
2017-05-01 03:13:11 +02:00
if ( fileDescriptorSet [ 1 ] . revents & POLLIN ) {
if ( ( count = read ( readCerrPipe , buffer , sizeof ( buffer ) ) ) > 0 ) {
2017-10-24 01:02:07 +02:00
errors . append ( buffer , static_cast < size_t > ( count ) ) ;
2016-08-15 22:35:37 +02:00
}
2017-05-01 03:13:11 +02:00
} else if ( fileDescriptorSet [ 1 ] . revents & POLLHUP ) {
2016-08-15 22:35:37 +02:00
close ( readCerrPipe ) ;
fileDescriptorSet [ 1 ] . fd = - 1 ;
}
2017-05-01 03:13:11 +02:00
} else if ( retpoll = = 0 ) {
2016-08-15 22:35:37 +02:00
// timeout
throw runtime_error ( " Poll time-out " ) ;
} else {
// fail
throw runtime_error ( " Poll failed " ) ;
}
2017-05-01 03:13:11 +02:00
} while ( fileDescriptorSet [ 0 ] . fd > = 0 | | fileDescriptorSet [ 1 ] . fd > = 0 ) ;
} catch ( . . . ) {
2016-08-15 22:35:37 +02:00
// ensure all pipes are close in the error case
close ( readCoutPipe ) , close ( readCerrPipe ) ;
throw ;
}
// get return code
2016-07-30 22:34:31 +02:00
int childReturnCode ;
waitpid ( child , & childReturnCode , 0 ) ;
return childReturnCode ;
} else {
2017-06-20 23:19:49 +02:00
// child process
// -> set pipes to be used for stdout/stderr
2016-07-30 22:34:31 +02:00
dup2 ( writeCoutPipe , STDOUT_FILENO ) , dup2 ( writeCerrPipe , STDERR_FILENO ) ;
close ( readCoutPipe ) , close ( writeCoutPipe ) , close ( readCerrPipe ) , close ( writeCerrPipe ) ;
2017-06-20 23:19:49 +02:00
// -> modify environment variable LLVM_PROFILE_FILE to apply new path for profiling output
if ( ! newProfilingPath . empty ( ) ) {
setenv ( " LLVM_PROFILE_FILE " , newProfilingPath . data ( ) , true ) ;
}
// -> execute application
2016-07-30 22:34:31 +02:00
execv ( appPath , const_cast < char * const * > ( args ) ) ;
2017-10-24 01:02:07 +02:00
cerr < < Phrases : : Error < < " Unable to execute \" " < < appPath < < " \" : execv() failed " < < Phrases : : EndFlush ;
2016-07-30 22:34:31 +02:00
exit ( - 101 ) ;
}
}
2017-06-20 23:19:49 +02:00
/*!
* \ brief Executes the application to be tested with the specified \ a args and stores the standard output and
* errors in \ a stdout and \ a stderr .
* \ throws Throws std : : runtime_error when the application can not be executed .
* \ remarks
* - The specified \ a args must be 0 terminated . The first argument is the application name .
* - Currently only supported under UNIX .
* - \ a stdout and \ a stderr are cleared before .
*/
int TestApplication : : execApp ( const char * const * args , string & output , string & errors , bool suppressLogging , int timeout ) const
{
// increase counter used for giving profiling files unique names
static unsigned int invocationCount = 0 ;
+ + invocationCount ;
2017-10-30 23:03:43 +01:00
// determine the path of the application to be tested
const char * appPath = m_applicationPathArg . firstValue ( ) ;
string fallbackAppPath ;
2017-06-20 23:19:49 +02:00
if ( ! appPath | | ! * appPath ) {
2017-10-30 23:03:43 +01:00
// try to find the path by removing "_tests"-suffix from own executable path
// (the own executable path is the path of the test application and its name is usually the name of the application
// to be tested with "_tests"-suffix)
const char * const testAppPath = m_parser . executable ( ) ;
const size_t testAppPathLength = strlen ( testAppPath ) ;
if ( testAppPathLength > 6 & & ! strcmp ( testAppPath + testAppPathLength - 6 , " _tests " ) ) {
fallbackAppPath . assign ( testAppPath , testAppPathLength - 6 ) ;
appPath = fallbackAppPath . data ( ) ;
// TODO: it would not hurt to verify whether "fallbackAppPath" actually exists and is executalbe
} else {
throw runtime_error ( " Unable to execute application to be tested: no application path specified " ) ;
}
2017-06-20 23:19:49 +02:00
}
// determine new path for profiling output (to not override profiling output of parent and previous invocations)
string newProfilingPath ;
if ( const char * llvmProfileFile = getenv ( " LLVM_PROFILE_FILE " ) ) {
// replace eg. "/some/path/tageditor_tests.profraw" with "/some/path/tageditor0.profraw"
if ( const char * llvmProfileFileEnd = strstr ( llvmProfileFile , " .profraw " ) ) {
const string llvmProfileFileWithoutExtension ( llvmProfileFile , llvmProfileFileEnd ) ;
// extract application name from path
const char * appName = strrchr ( appPath , ' / ' ) ;
appName = appName ? appName + 1 : appPath ;
// concat new path
newProfilingPath = argsToString ( llvmProfileFileWithoutExtension , ' _ ' , appName , invocationCount , " .profraw " ) ;
// append path to profiling list file
if ( const char * profrawListFile = getenv ( " LLVM_PROFILE_LIST_FILE " ) ) {
ofstream ( profrawListFile , ios_base : : app ) < < newProfilingPath < < endl ;
}
}
}
return execAppInternal ( appPath , args , output , errors , suppressLogging , timeout , newProfilingPath ) ;
}
2017-11-01 19:30:55 +01:00
/*!
* \ brief Executes an application with the specified \ a args .
* \ remarks
* - Intended to invoke helper applications ( eg . to setup test files ) . Use execApp ( ) and TestApplication : : execApp ( ) to
* invoke the application to be tested itself .
* - Currently only supported under UNIX .
*/
int execHelperApp ( const char * appPath , const char * const * args , std : : string & output , std : : string & errors , bool suppressLogging , int timeout )
{
return execAppInternal ( appPath , args , output , errors , suppressLogging , timeout , string ( ) ) ;
}
# endif // PLATFORM_UNIX
2018-02-03 17:08:43 +01:00
string TestApplication : : readTestfilePathFromEnv ( )
2017-10-30 23:01:07 +01:00
{
2018-02-03 17:08:43 +01:00
const char * const testFilesPathEnv = getenv ( " TEST_FILE_PATH " ) ;
if ( ! testFilesPathEnv | | ! * testFilesPathEnv ) {
return string ( ) ;
2017-10-30 23:01:07 +01:00
}
2018-02-03 17:08:43 +01:00
return argsToString ( testFilesPathEnv , ' / ' ) ;
2017-10-30 23:01:07 +01:00
}
2018-02-03 17:08:43 +01:00
string TestApplication : : readTestfilePathFromSrcRef ( )
2017-10-30 23:01:07 +01:00
{
try {
// read "srcdirref" file which should contain the path of the source directory; this file should have been
// create by the CMake module "TestTarget.cmake"
2018-02-03 17:08:43 +01:00
string srcDirContent ( readFile ( " srcdirref " , 2 * 1024 ) ) ;
2017-10-30 23:01:07 +01:00
if ( srcDirContent . empty ( ) ) {
cerr < < Phrases : : Warning < < " The file \" srcdirref \" is empty. " < < Phrases : : EndFlush ;
2018-02-03 17:08:43 +01:00
return string ( ) ;
2017-10-30 23:01:07 +01:00
}
2018-03-24 17:00:30 +01:00
// check whether the referenced source directory contains a "testfiles" directory
2017-10-30 23:01:07 +01:00
# ifdef PLATFORM_UNIX // directoryEntries() is not implemented under Windows so we can only to the check under UNIX
bool hasTestfilesDir = false ;
2017-11-01 19:17:57 +01:00
for ( const string & dir : directoryEntries ( srcDirContent . data ( ) , DirectoryEntryType : : Directory ) ) {
2017-10-30 23:01:07 +01:00
if ( dir = = " testfiles " ) {
hasTestfilesDir = true ;
break ;
}
}
if ( ! hasTestfilesDir ) {
cerr < < Phrases : : Warning
< < " The source directory referenced by the file \" srcdirref \" does not contain a \" testfiles \" directory or does not exist. "
< < Phrases : : End < < " Referenced source directory: " < < srcDirContent < < endl ;
2018-02-03 17:08:43 +01:00
return string ( ) ;
2017-10-30 23:01:07 +01:00
}
2017-11-01 19:30:55 +01:00
# endif // PLATFORM_UNIX
2017-10-30 23:01:07 +01:00
2018-02-03 17:08:43 +01:00
return srcDirContent + = " /testfiles/ " ;
2017-10-30 23:01:07 +01:00
} catch ( . . . ) {
2018-05-01 23:59:28 +02:00
cerr < < Phrases : : Warning < < " The file \" srcdirref \" can not be opened. It likely just doesn't exist in the working directory. "
< < Phrases : : EndFlush ;
2017-10-30 23:01:07 +01:00
catchIoFailure ( ) ;
}
2018-02-03 17:08:43 +01:00
return string ( ) ;
2017-10-30 23:01:07 +01:00
}
2017-09-17 21:45:23 +02:00
} // namespace TestUtilities