2018-10-10 14:12:18 +00:00
/*
This file is part of solidity .
solidity is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
solidity is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with solidity . If not , see < http : //www.gnu.org/licenses/>.
*/
2020-07-17 14:54:12 +00:00
// SPDX-License-Identifier: GPL-3.0
2018-10-10 14:12:18 +00:00
2020-01-14 16:48:17 +00:00
# include <stdexcept>
2020-06-05 23:10:48 +00:00
# include <iostream>
2018-10-10 14:12:18 +00:00
# include <test/Common.h>
2021-05-10 15:23:23 +00:00
# include <test/EVMHost.h>
2022-05-27 17:55:41 +00:00
# include <test/libsolidity/util/SoltestErrors.h>
2018-10-10 14:12:18 +00:00
2020-01-06 10:52:23 +00:00
# include <libsolutil/Assertions.h>
2022-05-27 17:55:41 +00:00
# include <libsolutil/StringUtils.h>
2021-03-15 22:52:24 +00:00
# include <boost/algorithm/string.hpp>
2018-10-10 14:12:18 +00:00
# include <boost/filesystem.hpp>
2019-02-19 16:34:59 +00:00
# include <boost/program_options.hpp>
2022-05-27 17:55:41 +00:00
# include <range/v3/all.hpp>
2018-10-10 14:12:18 +00:00
namespace fs = boost : : filesystem ;
2019-02-19 16:34:59 +00:00
namespace po = boost : : program_options ;
2018-10-10 14:12:18 +00:00
2022-05-27 17:55:41 +00:00
using namespace std ;
2019-12-23 15:50:30 +00:00
namespace solidity : : test
2018-10-10 14:12:18 +00:00
{
2020-12-08 20:06:10 +00:00
namespace
{
2019-02-19 16:34:59 +00:00
/// If non-empty returns the value of the env. variable ETH_TEST_PATH, otherwise
/// it tries to find a path that contains the directories "libsolidity/syntaxTests"
/// and returns it if found.
/// The routine searches in the current directory, and inside the "test" directory
/// starting from the current directory and up to three levels up.
/// @returns the path of the first match or an empty path if not found.
boost : : filesystem : : path testPath ( )
2018-10-10 14:12:18 +00:00
{
2019-02-19 12:24:04 +00:00
if ( auto path = getenv ( " ETH_TEST_PATH " ) )
return path ;
2018-10-10 14:12:18 +00:00
auto const searchPath =
{
fs : : current_path ( ) / " .. " / " .. " / " .. " / " test " ,
fs : : current_path ( ) / " .. " / " .. " / " test " ,
fs : : current_path ( ) / " .. " / " test " ,
fs : : current_path ( ) / " test " ,
fs : : current_path ( )
} ;
for ( auto const & basePath : searchPath )
{
fs : : path syntaxTestPath = basePath / " libsolidity " / " syntaxTests " ;
if ( fs : : exists ( syntaxTestPath ) & & fs : : is_directory ( syntaxTestPath ) )
return basePath ;
}
return { } ;
}
2021-05-10 15:23:23 +00:00
std : : optional < fs : : path > findInDefaultPath ( std : : string const & lib_name )
2019-02-19 16:34:59 +00:00
{
2019-07-17 06:22:14 +00:00
auto const searchPath =
{
fs : : current_path ( ) / " deps " ,
fs : : current_path ( ) / " deps " / " lib " ,
fs : : current_path ( ) / " .. " / " deps " ,
fs : : current_path ( ) / " .. " / " deps " / " lib " ,
fs : : current_path ( ) / " .. " / " .. " / " deps " ,
fs : : current_path ( ) / " .. " / " .. " / " deps " / " lib " ,
fs : : current_path ( )
} ;
for ( auto const & basePath : searchPath )
{
2020-06-05 23:10:48 +00:00
fs : : path p = basePath / lib_name ;
2019-07-17 06:22:14 +00:00
if ( fs : : exists ( p ) )
2021-05-10 15:23:23 +00:00
return p ;
2019-07-17 06:22:14 +00:00
}
2021-05-10 15:23:23 +00:00
return std : : nullopt ;
2019-02-19 16:34:59 +00:00
}
2020-12-08 20:06:10 +00:00
}
2019-02-19 16:34:59 +00:00
CommonOptions : : CommonOptions ( std : : string _caption ) :
options ( _caption ,
po : : options_description : : m_default_line_length ,
po : : options_description : : m_default_line_length - 23
)
2021-09-18 09:55:50 +00:00
{
}
2021-09-18 12:13:04 +00:00
void CommonOptions : : addOptions ( )
2019-02-19 16:34:59 +00:00
{
options . add_options ( )
2022-11-22 12:05:20 +00:00
( " evm-version " , po : : value ( & evmVersionString ) , " which EVM version to use " )
// "eof-version" is declared as uint64_t, since uint8_t will be parsed as character by boost.
( " eof-version " , po : : value < uint64_t > ( ) - > implicit_value ( 1u ) , " which EOF version to use " )
2019-12-23 15:50:30 +00:00
( " testpath " , po : : value < fs : : path > ( & this - > testPath ) - > default_value ( solidity : : test : : testPath ( ) ) , " path to test files " )
2020-06-05 23:10:48 +00:00
( " vm " , po : : value < std : : vector < fs : : path > > ( & vmPaths ) , " path to evmc library, can be supplied multiple times. " )
2021-09-18 12:13:04 +00:00
( " ewasm " , po : : bool_switch ( & ewasm ) - > default_value ( ewasm ) , " tries to automatically find an ewasm vm and enable ewasm test-execution. " )
2021-12-20 18:03:48 +00:00
( " batches " , po : : value < size_t > ( & this - > batches ) - > default_value ( 1 ) , " set number of batches to split the tests into " )
( " selected-batch " , po : : value < size_t > ( & this - > selectedBatch ) - > default_value ( 0 ) , " zero-based number of batch to execute " )
2021-09-18 12:13:04 +00:00
( " no-semantic-tests " , po : : bool_switch ( & disableSemanticTests ) - > default_value ( disableSemanticTests ) , " disable semantic tests " )
( " no-smt " , po : : bool_switch ( & disableSMT ) - > default_value ( disableSMT ) , " disable SMT checker " )
( " optimize " , po : : bool_switch ( & optimize ) - > default_value ( optimize ) , " enables optimization " )
( " enforce-compile-to-ewasm " , po : : bool_switch ( & enforceCompileToEwasm ) - > default_value ( enforceCompileToEwasm ) , " Enforce compiling all tests to Ewasm to see if additional tests can be activated. " )
2021-09-20 18:17:35 +00:00
( " enforce-gas-cost " , po : : value < bool > ( & enforceGasTest ) - > default_value ( enforceGasTest ) - > implicit_value ( true ) , " Enforce checking gas cost in semantic tests. " )
2021-09-18 12:13:04 +00:00
( " enforce-gas-cost-min-value " , po : : value ( & enforceGasTestMinValue ) - > default_value ( enforceGasTestMinValue ) , " Threshold value to enforce adding gas checks to a test. " )
( " abiencoderv1 " , po : : bool_switch ( & useABIEncoderV1 ) - > default_value ( useABIEncoderV1 ) , " enables abi encoder v1 " )
( " show-messages " , po : : bool_switch ( & showMessages ) - > default_value ( showMessages ) , " enables message output " )
( " show-metadata " , po : : bool_switch ( & showMetadata ) - > default_value ( showMetadata ) , " enables metadata output " ) ;
2019-02-19 16:34:59 +00:00
}
void CommonOptions : : validate ( ) const
{
assertThrow (
! testPath . empty ( ) ,
ConfigException ,
" No test path specified. The --testpath argument must not be empty when given. "
) ;
assertThrow (
fs : : exists ( testPath ) ,
ConfigException ,
" Invalid test path specified. "
) ;
2021-12-20 18:03:48 +00:00
assertThrow (
batches > 0 ,
ConfigException ,
" Batches needs to be at least 1. "
) ;
assertThrow (
selectedBatch < batches ,
ConfigException ,
" Selected batch has to be less than number of batches. "
) ;
2021-02-12 11:55:07 +00:00
if ( enforceGasTest )
{
assertThrow (
evmVersion ( ) = = langutil : : EVMVersion { } ,
ConfigException ,
" Gas costs can only be enforced on latest evm version. "
) ;
assertThrow (
useABIEncoderV1 = = false ,
ConfigException ,
" Gas costs can only be enforced on abi encoder v2. "
) ;
}
2019-02-19 16:34:59 +00:00
}
bool CommonOptions : : parse ( int argc , char const * const * argv )
{
po : : variables_map arguments ;
2021-09-18 10:43:09 +00:00
addOptions ( ) ;
2019-02-19 16:34:59 +00:00
2021-11-15 16:51:18 +00:00
try
{
po : : command_line_parser cmdLineParser ( argc , argv ) ;
cmdLineParser . options ( options ) ;
auto parsedOptions = cmdLineParser . run ( ) ;
po : : store ( parsedOptions , arguments ) ;
po : : notify ( arguments ) ;
2022-11-22 12:05:20 +00:00
if ( arguments . count ( " eof-version " ) )
{
// Request as uint64_t, since uint8_t will be parsed as character by boost.
uint64_t eofVersion = arguments [ " eof-version " ] . as < uint64_t > ( ) ;
if ( eofVersion ! = 1 )
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Invalid EOF version: " + to_string ( eofVersion ) ) ) ;
m_eofVersion = 1 ;
}
2021-11-15 16:51:18 +00:00
for ( auto const & parsedOption : parsedOptions . options )
if ( parsedOption . position_key > = 0 )
{
if (
parsedOption . original_tokens . empty ( ) | |
( parsedOption . original_tokens . size ( ) = = 1 & & parsedOption . original_tokens . front ( ) . empty ( ) )
)
continue ; // ignore empty options
std : : stringstream errorMessage ;
errorMessage < < " Unrecognized option: " ;
for ( auto const & token : parsedOption . original_tokens )
errorMessage < < token ;
BOOST_THROW_EXCEPTION ( std : : runtime_error ( errorMessage . str ( ) ) ) ;
}
}
catch ( po : : error const & exception )
{
solThrow ( ConfigException , exception . what ( ) ) ;
}
2020-01-06 14:28:20 +00:00
2020-06-05 23:10:48 +00:00
if ( vmPaths . empty ( ) )
{
2021-05-10 15:23:23 +00:00
if ( auto envPath = getenv ( " ETH_EVMONE " ) )
vmPaths . emplace_back ( envPath ) ;
else if ( auto repoPath = findInDefaultPath ( evmoneFilename ) )
vmPaths . emplace_back ( * repoPath ) ;
2020-06-05 23:10:48 +00:00
else
2021-05-10 15:23:23 +00:00
vmPaths . emplace_back ( evmoneFilename ) ;
if ( ewasm ) {
if ( auto envPath = getenv ( " ETH_HERA " ) )
vmPaths . emplace_back ( envPath ) ;
else if ( auto repoPath = findInDefaultPath ( heraFilename ) )
vmPaths . emplace_back ( * repoPath ) ;
else
vmPaths . emplace_back ( heraFilename ) ;
2020-06-05 23:10:48 +00:00
}
}
2019-02-19 16:34:59 +00:00
return true ;
2018-10-10 14:12:18 +00:00
}
2019-02-19 16:34:59 +00:00
2022-05-27 17:55:41 +00:00
string CommonOptions : : toString ( vector < string > const & _selectedOptions ) const
{
if ( _selectedOptions . empty ( ) )
return " " ;
auto boolToString = [ ] ( bool _value ) - > string { return _value ? " true " : " false " ; } ;
// Using std::map to avoid if-else/switch-case block
map < string , string > optionValueMap = {
{ " evmVersion " , evmVersion ( ) . name ( ) } ,
{ " optimize " , boolToString ( optimize ) } ,
{ " useABIEncoderV1 " , boolToString ( useABIEncoderV1 ) } ,
{ " batch " , to_string ( selectedBatch + 1 ) + " / " + to_string ( batches ) } ,
{ " ewasm " , boolToString ( ewasm ) } ,
{ " enforceCompileToEwasm " , boolToString ( enforceCompileToEwasm ) } ,
{ " enforceGasTest " , boolToString ( enforceGasTest ) } ,
{ " enforceGasTestMinValue " , enforceGasTestMinValue . str ( ) } ,
{ " disableSemanticTests " , boolToString ( disableSemanticTests ) } ,
{ " disableSMT " , boolToString ( disableSMT ) } ,
{ " showMessages " , boolToString ( showMessages ) } ,
{ " showMetadata " , boolToString ( showMetadata ) }
} ;
soltestAssert ( ranges : : all_of ( _selectedOptions , [ & optionValueMap ] ( string const & _option ) { return optionValueMap . count ( _option ) > 0 ; } ) ) ;
vector < string > optionsWithValues = _selectedOptions |
ranges : : views : : transform ( [ & optionValueMap ] ( string const & _option ) { return _option + " = " + optionValueMap . at ( _option ) ; } ) |
ranges : : to < vector > ( ) ;
return solidity : : util : : joinHumanReadable ( optionsWithValues ) ;
}
void CommonOptions : : printSelectedOptions ( ostream & _stream , string const & _linePrefix , vector < string > const & _selectedOptions ) const
{
_stream < < _linePrefix < < " Run Settings: " < < toString ( _selectedOptions ) < < endl ;
}
2019-03-15 16:22:04 +00:00
langutil : : EVMVersion CommonOptions : : evmVersion ( ) const
{
if ( ! evmVersionString . empty ( ) )
{
auto version = langutil : : EVMVersion : : fromString ( evmVersionString ) ;
if ( ! version )
2021-02-18 23:49:34 +00:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Invalid EVM version: " + evmVersionString ) ) ;
2019-03-15 16:22:04 +00:00
return * version ;
}
else
return langutil : : EVMVersion ( ) ;
}
2020-01-14 16:48:17 +00:00
CommonOptions const & CommonOptions : : get ( )
{
if ( ! m_singleton )
2021-02-18 23:49:34 +00:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Options not yet constructed! " ) ) ;
2020-01-14 16:48:17 +00:00
return * m_singleton ;
}
void CommonOptions : : setSingleton ( std : : unique_ptr < CommonOptions const > & & _instance )
{
m_singleton = std : : move ( _instance ) ;
}
std : : unique_ptr < CommonOptions const > CommonOptions : : m_singleton = nullptr ;
2021-03-15 22:52:24 +00:00
bool isValidSemanticTestPath ( boost : : filesystem : : path const & _testPath )
{
bool insideSemanticTests = false ;
fs : : path testPathPrefix ;
for ( auto const & element : _testPath )
{
testPathPrefix / = element ;
if ( boost : : ends_with ( canonical ( testPathPrefix ) . generic_string ( ) , " /test/libsolidity/semanticTests " ) )
insideSemanticTests = true ;
if ( insideSemanticTests & & boost : : starts_with ( element . string ( ) , " _ " ) )
return false ;
}
return true ;
}
2021-05-10 15:23:23 +00:00
bool loadVMs ( CommonOptions const & _options )
{
if ( _options . disableSemanticTests & & ! _options . ewasm )
return true ;
auto [ evmSupported , ewasmSupported ] = solidity : : test : : EVMHost : : checkVmPaths ( _options . vmPaths ) ;
if ( ! _options . disableSemanticTests & & ! evmSupported )
{
std : : cerr < < " Unable to find " < < solidity : : test : : evmoneFilename ;
std : : cerr < < " . Please disable semantics tests with --no-semantic-tests or provide a path using --vm <path>. " < < std : : endl ;
std : : cerr < < " You can download it at " < < std : : endl ;
std : : cerr < < solidity : : test : : evmoneDownloadLink < < std : : endl ;
return false ;
}
if ( _options . ewasm & & ! ewasmSupported )
{
std : : cerr < < " Unable to find " < < solidity : : test : : heraFilename ;
std : : cerr < < " . To be able to enable ewasm tests, please provide the path using --vm <path>. " < < std : : endl ;
std : : cerr < < " You can download it at " < < std : : endl ;
std : : cerr < < solidity : : test : : heraDownloadLink < < std : : endl ;
return false ;
}
return true ;
}
2019-02-19 16:34:59 +00:00
}