2017-04-19 14:54:14 +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/>.
*/
/**
* @ date 2017
* Unit tests for interface / StandardCompiler . h .
*/
# include <string>
# include <boost/test/unit_test.hpp>
# include <libsolidity/interface/StandardCompiler.h>
2017-04-19 17:39:37 +00:00
# include <libdevcore/JSON.h>
2017-04-19 14:54:14 +00:00
2017-05-02 22:34:12 +00:00
# include "../Metadata.h"
2017-04-19 14:54:14 +00:00
using namespace std ;
using namespace dev : : eth ;
namespace dev
{
namespace solidity
{
namespace test
{
namespace
{
/// Helper to match a specific error type and message
bool containsError ( Json : : Value const & _compilerResult , string const & _type , string const & _message )
{
if ( ! _compilerResult . isMember ( " errors " ) )
return false ;
for ( auto const & error : _compilerResult [ " errors " ] )
{
BOOST_REQUIRE ( error . isObject ( ) ) ;
BOOST_REQUIRE ( error [ " type " ] . isString ( ) ) ;
BOOST_REQUIRE ( error [ " message " ] . isString ( ) ) ;
if ( ( error [ " type " ] . asString ( ) = = _type ) & & ( error [ " message " ] . asString ( ) = = _message ) )
return true ;
}
return false ;
}
bool containsAtMostWarnings ( Json : : Value const & _compilerResult )
{
if ( ! _compilerResult . isMember ( " errors " ) )
return true ;
for ( auto const & error : _compilerResult [ " errors " ] )
{
BOOST_REQUIRE ( error . isObject ( ) ) ;
BOOST_REQUIRE ( error [ " severity " ] . isString ( ) ) ;
if ( error [ " severity " ] . asString ( ) ! = " warning " )
return false ;
}
return true ;
}
2017-04-19 17:39:37 +00:00
Json : : Value getContractResult ( Json : : Value const & _compilerResult , string const & _file , string const & _name )
{
if (
! _compilerResult [ " contracts " ] . isObject ( ) | |
! _compilerResult [ " contracts " ] [ _file ] . isObject ( ) | |
! _compilerResult [ " contracts " ] [ _file ] [ _name ] . isObject ( )
)
return Json : : Value ( ) ;
return _compilerResult [ " contracts " ] [ _file ] [ _name ] ;
}
2017-04-19 14:54:14 +00:00
Json : : Value compile ( string const & _input )
{
StandardCompiler compiler ;
string output = compiler . compile ( _input ) ;
Json : : Value ret ;
2018-02-07 01:05:20 +00:00
BOOST_REQUIRE ( jsonParseStrict ( output , ret ) ) ;
2017-04-19 14:54:14 +00:00
return ret ;
}
} // end anonymous namespace
BOOST_AUTO_TEST_SUITE ( StandardCompiler )
BOOST_AUTO_TEST_CASE ( assume_object_input )
{
2017-04-19 16:41:55 +00:00
Json : : Value result ;
/// Use the native JSON interface of StandardCompiler to trigger these
solidity : : StandardCompiler compiler ;
result = compiler . compile ( Json : : Value ( ) ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " Input is not a JSON object. " ) ) ;
result = compiler . compile ( Json : : Value ( " INVALID " ) ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " Input is not a JSON object. " ) ) ;
/// Use the string interface of StandardCompiler to trigger these
result = compile ( " " ) ;
2018-02-07 01:05:20 +00:00
BOOST_CHECK ( containsError ( result , " JSONError " , " * Line 1, Column 1 \n Syntax error: value, object or array expected. \n * Line 1, Column 1 \n A valid JSON document must be either an array or an object value. \n " ) ) ;
2017-04-19 14:54:14 +00:00
result = compile ( " invalid " ) ;
2018-02-07 01:05:20 +00:00
BOOST_CHECK ( containsError ( result , " JSONError " , " * Line 1, Column 1 \n Syntax error: value, object or array expected. \n * Line 1, Column 2 \n Extra non-whitespace after JSON value. \n " ) ) ;
2017-04-19 14:54:14 +00:00
result = compile ( " \" invalid \" " ) ;
2018-02-07 01:05:20 +00:00
BOOST_CHECK ( containsError ( result , " JSONError " , " * Line 1, Column 1 \n A valid JSON document must be either an array or an object value. \n " ) ) ;
2017-04-19 14:54:14 +00:00
BOOST_CHECK ( ! containsError ( result , " JSONError " , " * Line 1, Column 1 \n Syntax error: value, object or array expected. \n " ) ) ;
result = compile ( " {} " ) ;
BOOST_CHECK ( ! containsError ( result , " JSONError " , " * Line 1, Column 1 \n Syntax error: value, object or array expected. \n " ) ) ;
BOOST_CHECK ( ! containsAtMostWarnings ( result ) ) ;
}
BOOST_AUTO_TEST_CASE ( invalid_language )
{
char const * input = R " (
{
" language " : " INVALID "
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " Only \" Solidity \" is supported as a language. " ) ) ;
}
BOOST_AUTO_TEST_CASE ( valid_language )
{
char const * input = R " (
{
" language " : " Solidity "
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( ! containsError ( result , " JSONError " , " Only \" Solidity \" is supported as a language. " ) ) ;
}
BOOST_AUTO_TEST_CASE ( no_sources )
{
char const * input = R " (
{
" language " : " Solidity "
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " No input sources specified. " ) ) ;
}
2018-02-13 20:40:16 +00:00
BOOST_AUTO_TEST_CASE ( no_sources_empty_object )
{
char const * input = R " (
{
" language " : " Solidity " ,
" sources " : { }
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " No input sources specified. " ) ) ;
}
BOOST_AUTO_TEST_CASE ( no_sources_empty_array )
{
char const * input = R " (
{
" language " : " Solidity " ,
" sources " : [ ]
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " \" sources \" is not a JSON object. " ) ) ;
}
BOOST_AUTO_TEST_CASE ( sources_is_array )
{
char const * input = R " (
{
" language " : " Solidity " ,
" sources " : [ " aa " , " bb " ]
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " \" sources \" is not a JSON object. " ) ) ;
}
2018-02-07 01:05:20 +00:00
BOOST_AUTO_TEST_CASE ( unexpected_trailing_test )
{
char const * input = R " (
{
" language " : " Solidity " ,
" sources " : {
" A " : {
" content " : " contract A { function f() {} } "
}
}
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsError ( result , " JSONError " , " * Line 10, Column 2 \n Extra non-whitespace after JSON value. \n " ) ) ;
}
2017-04-19 14:54:14 +00:00
BOOST_AUTO_TEST_CASE ( smoke_test )
{
char const * input = R " (
{
" language " : " Solidity " ,
" sources " : {
" empty " : {
" content " : " "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
}
2017-04-19 17:39:37 +00:00
BOOST_AUTO_TEST_CASE ( basic_compilation )
{
char const * input = R " (
{
" language " : " Solidity " ,
" sources " : {
" fileA " : {
" content " : " contract A { } "
}
2017-04-20 22:34:12 +00:00
} ,
" settings " : {
" outputSelection " : {
" fileA " : {
" A " : [ " abi " , " devdoc " , " userdoc " , " evm.bytecode " , " evm.assembly " , " evm.gasEstimates " , " metadata " ] ,
" " : [ " legacyAST " ]
}
}
2017-04-19 17:39:37 +00:00
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
2017-05-02 14:20:27 +00:00
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
2017-06-14 17:36:05 +00:00
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [] " ) ;
2017-05-02 14:20:27 +00:00
BOOST_CHECK ( contract [ " devdoc " ] . isObject ( ) ) ;
2017-06-14 17:36:05 +00:00
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " devdoc " ] ) , " { \" methods \" :{}} " ) ;
2017-05-02 14:20:27 +00:00
BOOST_CHECK ( contract [ " userdoc " ] . isObject ( ) ) ;
2017-06-14 17:36:05 +00:00
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " userdoc " ] ) , " { \" methods \" :{}} " ) ;
2017-04-19 17:39:37 +00:00
BOOST_CHECK ( contract [ " evm " ] . isObject ( ) ) ;
/// @TODO check evm.methodIdentifiers, legacyAssembly, bytecode, deployedBytecode
2017-04-20 09:29:42 +00:00
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] . isObject ( ) ) ;
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] [ " object " ] . isString ( ) ) ;
2017-06-14 17:36:05 +00:00
BOOST_CHECK_EQUAL (
dev : : test : : bytecodeSansMetadata ( contract [ " evm " ] [ " bytecode " ] [ " object " ] . asString ( ) ) ,
2017-08-15 13:24:57 +00:00
" 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00 "
2017-06-14 17:36:05 +00:00
) ;
2017-04-19 17:39:37 +00:00
BOOST_CHECK ( contract [ " evm " ] [ " assembly " ] . isString ( ) ) ;
2017-06-14 19:31:20 +00:00
BOOST_CHECK ( contract [ " evm " ] [ " assembly " ] . asString ( ) . find (
2017-04-19 17:39:37 +00:00
" /* \" fileA \" :0:14 contract A { } */ \n mstore(0x40, 0x60) \n jumpi(tag_1, iszero(callvalue)) \n "
2017-08-15 13:24:57 +00:00
" 0x0 \n dup1 \n revert \n tag_1: \n dataSize(sub_0) \n dup1 \n dataOffset(sub_0) \n 0x0 \n codecopy \n 0x0 \n "
2017-04-19 17:39:37 +00:00
" return \n stop \n \n sub_0: assembly { \n /* \" fileA \" :0:14 contract A { } */ \n "
2017-08-15 13:24:57 +00:00
" mstore(0x40, 0x60) \n 0x0 \n dup1 \n revert \n \n "
2017-06-14 17:36:05 +00:00
" auxdata: 0xa165627a7a7230582 " ) = = 0 ) ;
2017-04-19 17:39:37 +00:00
BOOST_CHECK ( contract [ " evm " ] [ " gasEstimates " ] . isObject ( ) ) ;
2017-06-14 17:36:05 +00:00
BOOST_CHECK_EQUAL (
dev : : jsonCompactPrint ( contract [ " evm " ] [ " gasEstimates " ] ) ,
2017-08-15 13:24:57 +00:00
" { \" creation \" :{ \" codeDepositCost \" : \" 10600 \" , \" executionCost \" : \" 61 \" , \" totalCost \" : \" 10661 \" }} "
2017-06-14 17:36:05 +00:00
) ;
2017-04-19 17:39:37 +00:00
BOOST_CHECK ( contract [ " metadata " ] . isString ( ) ) ;
2017-05-02 22:34:12 +00:00
BOOST_CHECK ( dev : : test : : isValidMetadata ( contract [ " metadata " ] . asString ( ) ) ) ;
2017-04-20 09:29:53 +00:00
BOOST_CHECK ( result [ " sources " ] . isObject ( ) ) ;
BOOST_CHECK ( result [ " sources " ] [ " fileA " ] . isObject ( ) ) ;
BOOST_CHECK ( result [ " sources " ] [ " fileA " ] [ " legacyAST " ] . isObject ( ) ) ;
2017-06-14 17:36:05 +00:00
BOOST_CHECK_EQUAL (
dev : : jsonCompactPrint ( result [ " sources " ] [ " fileA " ] [ " legacyAST " ] ) ,
2017-03-20 18:06:17 +00:00
" { \" attributes \" :{ \" absolutePath \" : \" fileA \" , \" exportedSymbols \" :{ \" A \" :[1]}}, \" children \" : "
2017-05-17 16:22:39 +00:00
" [{ \" attributes \" :{ \" baseContracts \" :[null], \" contractDependencies \" :[null], \" contractKind \" : \" contract \" , "
2017-05-30 17:25:54 +00:00
" \" documentation \" :null, \" fullyImplemented \" :true, \" linearizedBaseContracts \" :[1], \" name \" : \" A \" , \" nodes \" :[null], \" scope \" :2}, "
2017-06-14 17:36:05 +00:00
" \" id \" :1, \" name \" : \" ContractDefinition \" , \" src \" : \" 0:14:0 \" }], \" id \" :2, \" name \" : \" SourceUnit \" , \" src \" : \" 0:14:0 \" } "
) ;
2017-04-19 17:39:37 +00:00
}
2017-12-18 11:40:06 +00:00
BOOST_AUTO_TEST_CASE ( compilation_error )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" fileA " : {
" A " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " contract A { function } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( result . isMember ( " errors " ) ) ;
BOOST_CHECK ( result [ " errors " ] . size ( ) > = 1 ) ;
for ( auto const & error : result [ " errors " ] )
{
BOOST_REQUIRE ( error . isObject ( ) ) ;
BOOST_REQUIRE ( error [ " message " ] . isString ( ) ) ;
if ( error [ " message " ] . asString ( ) . find ( " pre-release compiler " ) = = string : : npos )
{
BOOST_CHECK_EQUAL (
dev : : jsonCompactPrint ( error ) ,
" { \" component \" : \" general \" , \" formattedMessage \" : \" fileA:1:23: ParserError: Expected identifier, got 'RBrace' \\ n "
" contract A { function } \\ n ^ \\ n \" , \" message \" : \" Expected identifier, got 'RBrace' \" , "
" \" severity \" : \" error \" , \" sourceLocation \" :{ \" end \" :22, \" file \" : \" fileA \" , \" start \" :22}, \" type \" : \" ParserError \" } "
) ;
}
}
}
2017-09-29 18:05:39 +00:00
BOOST_AUTO_TEST_CASE ( output_selection_explicit )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" fileA " : {
" A " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " contract A { } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [] " ) ;
}
BOOST_AUTO_TEST_CASE ( output_selection_all_contracts )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" fileA " : {
" * " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " contract A { } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [] " ) ;
}
BOOST_AUTO_TEST_CASE ( output_selection_all_files_single_contract )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" * " : {
" A " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " contract A { } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [] " ) ;
}
BOOST_AUTO_TEST_CASE ( output_selection_all_files_all_contracts )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" * " : {
" * " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " contract A { } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [] " ) ;
}
2017-10-05 08:53:43 +00:00
BOOST_AUTO_TEST_CASE ( output_selection_dependent_contract )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" * " : {
" A " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " contract B { } contract A { function f() { new B(); } } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [{ \" constant \" :false, \" inputs \" :[], \" name \" : \" f \" , \" outputs \" :[], \" payable \" :false, \" stateMutability \" : \" nonpayable \" , \" type \" : \" function \" }] " ) ;
}
BOOST_AUTO_TEST_CASE ( output_selection_dependent_contract_with_import )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" * " : {
" A " : [
" abi "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " import \" fileB \" ; contract A { function f() { new B(); } } "
} ,
" fileB " : {
" content " : " contract B { } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [{ \" constant \" :false, \" inputs \" :[], \" name \" : \" f \" , \" outputs \" :[], \" payable \" :false, \" stateMutability \" : \" nonpayable \" , \" type \" : \" function \" }] " ) ;
}
2018-01-03 11:34:48 +00:00
BOOST_AUTO_TEST_CASE ( filename_with_colon )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" http://github.com/ethereum/solidity/std/StandardToken.sol " : {
" A " : [
" abi "
]
}
}
} ,
" sources " : {
" http://github.com/ethereum/solidity/std/StandardToken.sol " : {
" content " : " contract A { } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " http://github.com/ethereum/solidity/std/StandardToken.sol " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " abi " ] . isArray ( ) ) ;
BOOST_CHECK_EQUAL ( dev : : jsonCompactPrint ( contract [ " abi " ] ) , " [] " ) ;
}
2018-01-05 13:24:07 +00:00
BOOST_AUTO_TEST_CASE ( library_filename_with_colon )
{
char const * input = R " (
{
" language " : " Solidity " ,
" settings " : {
" outputSelection " : {
" fileA " : {
" A " : [
" evm.bytecode "
]
}
}
} ,
" sources " : {
" fileA " : {
" content " : " import \" git:library.sol \" ; contract A { function f() returns (uint) { return L.g(); } } "
} ,
" git:library.sol " : {
" content " : " library L { function g() returns (uint) { return 1; } } "
}
}
}
) " ;
Json : : Value result = compile ( input ) ;
BOOST_CHECK ( containsAtMostWarnings ( result ) ) ;
Json : : Value contract = getContractResult ( result , " fileA " , " A " ) ;
BOOST_CHECK ( contract . isObject ( ) ) ;
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] . isObject ( ) ) ;
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] [ " linkReferences " ] . isObject ( ) ) ;
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] [ " linkReferences " ] [ " git:library.sol " ] . isObject ( ) ) ;
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] [ " linkReferences " ] [ " git:library.sol " ] [ " L " ] . isArray ( ) ) ;
BOOST_CHECK ( contract [ " evm " ] [ " bytecode " ] [ " linkReferences " ] [ " git:library.sol " ] [ " L " ] [ 0 ] . isObject ( ) ) ;
}
2018-01-03 11:34:48 +00:00
2017-04-19 14:54:14 +00:00
BOOST_AUTO_TEST_SUITE_END ( )
}
}
} // end namespaces