Skip to content

Commit dacad62

Browse files
committed
Adds semantic tests to test framework and isoltest.
1 parent 190634e commit dacad62

12 files changed

+291
-85
lines changed

test/RPCSession.cpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,16 @@ string IPCSocket::sendRequest(string const& _req)
139139

140140
RPCSession& RPCSession::instance(const string& _path)
141141
{
142-
static RPCSession session(_path);
143-
BOOST_REQUIRE_EQUAL(session.m_ipcSocket.path(), _path);
144-
return session;
142+
try
143+
{
144+
static RPCSession session(_path);
145+
BOOST_REQUIRE_EQUAL(session.m_ipcSocket.path(), _path);
146+
return session;
147+
}
148+
catch (std::exception const&)
149+
{
150+
BOOST_THROW_EXCEPTION(std::runtime_error("Error creating RPC session for socket: " + _path));
151+
}
145152
}
146153

147154
string RPCSession::eth_getCode(string const& _address, string const& _blockNumber)

test/TestCase.h

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ namespace solidity
3030
namespace test
3131
{
3232

33+
#define soltestAssert(CONDITION, DESCRIPTION) \
34+
do \
35+
{ \
36+
if (!(CONDITION)) \
37+
BOOST_THROW_EXCEPTION(runtime_error(DESCRIPTION)); \
38+
} \
39+
while (false)
40+
3341
/** Common superclass of SyntaxTest and SemanticsTest. */
3442
class TestCase
3543
{

test/boostTest.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,13 @@ int registerTests(
7474
boost::unit_test::test_suite& _suite,
7575
boost::filesystem::path const& _basepath,
7676
boost::filesystem::path const& _path,
77+
std::string const& _ipcPath,
7778
TestCase::TestCaseCreator _testCaseCreator
7879
)
7980
{
8081
int numTestsAdded = 0;
8182
fs::path fullpath = _basepath / _path;
83+
TestCase::Config config{fullpath.string(), _ipcPath};
8284
if (fs::is_directory(fullpath))
8385
{
8486
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
@@ -87,7 +89,7 @@ int registerTests(
8789
fs::directory_iterator()
8890
))
8991
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
90-
numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _testCaseCreator);
92+
numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _ipcPath, _testCaseCreator);
9193
_suite.add(sub_suite);
9294
}
9395
else
@@ -96,13 +98,13 @@ int registerTests(
9698

9799
filenames.emplace_back(new string(_path.string()));
98100
_suite.add(make_test_case(
99-
[fullpath, _testCaseCreator]
101+
[config, _testCaseCreator]
100102
{
101103
BOOST_REQUIRE_NO_THROW({
102104
try
103105
{
104106
stringstream errorStream;
105-
if (!_testCaseCreator(fullpath.string())->run(errorStream))
107+
if (!_testCaseCreator(config)->run(errorStream))
106108
BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str());
107109
}
108110
catch (boost::exception const& _e)
@@ -142,6 +144,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
142144
master,
143145
options.testPath / ts.path,
144146
ts.subpath,
147+
options.ipcPath,
145148
ts.testCaseCreator
146149
) > 0, std::string("no ") + ts.title + " tests found");
147150
}

test/libsolidity/SemanticTest.cpp

+106-53
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,33 @@
2828
using namespace dev;
2929
using namespace solidity;
3030
using namespace dev::solidity::test;
31-
using namespace dev::solidity::test::formatting;
31+
using namespace dev::formatting;
3232
using namespace std;
33-
namespace fs = boost::filesystem;
3433
using namespace boost;
3534
using namespace boost::algorithm;
3635
using namespace boost::unit_test;
36+
namespace fs = boost::filesystem;
3737

3838
namespace
3939
{
40-
using ParamList = dev::solidity::test::ParameterList;
41-
using FunctionCallTest = dev::solidity::test::SemanticTest::FunctionCallTest;
40+
using FunctionCallTest = SemanticTest::FunctionCallTest;
4241
using FunctionCall = dev::solidity::test::FunctionCall;
42+
using ParamList = dev::solidity::test::ParameterList;
43+
4344

44-
string formatBytes(bytes const& _bytes, ParamList const& _params, bool const _formatInvalid = false)
45+
string formatBytes(bytes const& _bytes, ParamList const& _params)
4546
{
4647
stringstream resultStream;
4748
if (_bytes.empty())
48-
resultStream.str();
49+
return {};
4950
auto it = _bytes.begin();
5051
for (auto const& param: _params)
5152
{
52-
bytes byteRange{it, it + param.abiType.size};
53-
// FIXME Check range
54-
// TODO Check range
53+
long offset = static_cast<long>(param.abiType.size);
54+
auto offsetIter = it + offset;
55+
soltestAssert(offsetIter <= _bytes.end(), "Byte range can not be extended past the end of given bytes.");
56+
57+
bytes byteRange{it, offsetIter};
5558
switch (param.abiType.type)
5659
{
5760
case ABIType::SignedDec:
@@ -71,64 +74,116 @@ namespace
7174
resultStream << fromBigEndian<u256>(byteRange);
7275
break;
7376
case ABIType::Failure:
74-
// If expectations are empty, the encoding type is invalid.
75-
// In order to still print the actual result even if
76-
// empty expectations were detected, it must be forced.
77-
if (_formatInvalid)
78-
resultStream << fromBigEndian<u256>(byteRange);
7977
break;
8078
case ABIType::None:
81-
// If expectations are empty, the encoding type is NONE.
82-
if (_formatInvalid)
83-
resultStream << fromBigEndian<u256>(byteRange);
8479
break;
8580
}
86-
it += param.abiType.size;
81+
it += offset;
8782
if (it != _bytes.end() && !(param.abiType.type == ABIType::None))
8883
resultStream << ", ";
8984
}
85+
soltestAssert(it == _bytes.end(), "Parameter encoding too short for the given byte range.");
86+
return resultStream.str();
87+
}
88+
89+
string formatRawArguments(ParamList const& _params, string const& _linePrefix = "")
90+
{
91+
stringstream resultStream;
92+
for (auto const& param: _params)
93+
{
94+
if (param.format.newline)
95+
resultStream << endl << _linePrefix << "//";
96+
resultStream << " " << param.rawString;
97+
if (&param != &_params.back())
98+
resultStream << ",";
99+
}
90100
return resultStream.str();
91101
}
92102

93103
string formatFunctionCallTest(
94104
FunctionCallTest const& _test,
95105
string const& _linePrefix = "",
96106
bool const _renderResult = false,
97-
bool const _higlight = false
107+
bool const _highlight = false
98108
)
99109
{
110+
using namespace soltest;
111+
using Token = soltest::Token;
112+
100113
stringstream _stream;
101114
FunctionCall call = _test.call;
102-
bool hightlight = !_test.matchesExpectation() && _higlight;
115+
bool highlight = !_test.matchesExpectation() && _highlight;
103116

104117
auto formatOutput = [&](bool const _singleLine)
105118
{
106-
_stream << _linePrefix << "// " << call.signature;
119+
string ws = " ";
120+
string arrow = formatToken(Token::Arrow);
121+
string colon = formatToken(Token::Colon);
122+
string comma = formatToken(Token::Comma);
123+
string comment = formatToken(Token::Comment);
124+
string ether = formatToken(Token::Ether);
125+
string newline = formatToken(Token::Newline);
126+
string failure = formatToken(Token::Failure);
127+
128+
/// Prints the function signature. This is the same independent from the display-mode.
129+
_stream << _linePrefix << newline << ws << call.signature;
107130
if (call.value > u256(0))
108-
_stream << TestFileParser::formatToken(SoltToken::Comma)
109-
<< call.value << " "
110-
<< TestFileParser::formatToken(SoltToken::Ether);
131+
_stream << comma << ws << call.value << ws << ether;
111132
if (!call.arguments.rawBytes().empty())
112-
_stream << ": "
113-
<< formatBytes(call.arguments.rawBytes(), call.arguments.parameters);
114-
if (!_singleLine)
115-
_stream << endl << _linePrefix << "// ";
133+
{
134+
string output = formatRawArguments(call.arguments.parameters, _linePrefix);
135+
_stream << colon << output;
136+
}
137+
138+
/// Prints comments on the function parameters and the arrow taking
139+
/// the display-mode into account.
116140
if (_singleLine)
117-
_stream << " ";
118-
_stream << "-> ";
119-
if (!_singleLine)
120-
_stream << endl << _linePrefix << "// ";
121-
if (hightlight)
122-
_stream << formatting::RED_BACKGROUND;
123-
bytes output;
124-
if (_renderResult)
125-
output = call.expectations.rawBytes();
141+
{
142+
if (!call.arguments.comment.empty())
143+
_stream << ws << comment << call.arguments.comment << comment;
144+
_stream << ws << arrow << ws;
145+
}
146+
else
147+
{
148+
_stream << endl << _linePrefix << newline << ws;
149+
if (!call.arguments.comment.empty())
150+
{
151+
_stream << comment << call.arguments.comment << comment;
152+
_stream << endl << _linePrefix << newline << ws;
153+
}
154+
_stream << arrow << ws;
155+
}
156+
157+
/// Print either the expected output or the actual result output
158+
string result;
159+
if (!_renderResult)
160+
{
161+
bytes output = call.expectations.rawBytes();
162+
bool const isFailure = call.expectations.failure;
163+
result = isFailure ? failure : formatBytes(output, call.expectations.result);
164+
}
126165
else
127-
output = _test.rawBytes;
128-
if (!output.empty())
129-
_stream << formatBytes(output, call.expectations.result);
130-
if (hightlight)
131-
_stream << formatting::RESET;
166+
{
167+
bytes output = _test.rawBytes;
168+
bool const isFailure = _test.failure;
169+
result = isFailure ? failure : formatBytes(output, call.expectations.result);
170+
}
171+
AnsiColorized(_stream, highlight, {RED_BACKGROUND}) << result;
172+
173+
/// Print comments on expectations taking the display-mode into account.
174+
if (_singleLine)
175+
{
176+
if (!call.expectations.comment.empty())
177+
_stream << ws << comment << call.expectations.comment << comment;
178+
}
179+
else
180+
{
181+
if (!call.expectations.comment.empty())
182+
{
183+
_stream << endl << _linePrefix << newline << ws;
184+
_stream << comment << call.expectations.comment << comment;
185+
}
186+
}
132187
};
133188

134189
if (call.displayMode == FunctionCall::DisplayMode::SingleLine)
@@ -145,8 +200,7 @@ SemanticTest::SemanticTest(string const& _filename, string const& _ipcPath):
145200
SolidityExecutionFramework(_ipcPath)
146201
{
147202
ifstream file(_filename);
148-
if (!file)
149-
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
203+
soltestAssert(file, "Cannot open test contract: \"" + _filename + "\".");
150204
file.exceptions(ios::badbit);
151205

152206
m_source = parseSource(file);
@@ -155,8 +209,7 @@ SemanticTest::SemanticTest(string const& _filename, string const& _ipcPath):
155209

156210
bool SemanticTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
157211
{
158-
if (!deploy("", 0, bytes()))
159-
BOOST_THROW_EXCEPTION(runtime_error("Failed to deploy contract."));
212+
soltestAssert(deploy("", 0, bytes()), "Failed to deploy contract.");
160213

161214
bool success = true;
162215
for (auto& test: m_tests)
@@ -179,15 +232,15 @@ bool SemanticTest::run(ostream& _stream, string const& _linePrefix, bool const _
179232

180233
if (!success)
181234
{
182-
FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
235+
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
183236
for (auto const& test: m_tests)
184-
_stream << formatFunctionCallTest(test, _linePrefix, false, true);
237+
_stream << formatFunctionCallTest(test, _linePrefix, false, true & _formatted);
185238

186-
FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
239+
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
187240
for (auto const& test: m_tests)
188-
_stream << formatFunctionCallTest(test, _linePrefix, true, true);
241+
_stream << formatFunctionCallTest(test, _linePrefix, true, true & _formatted);
189242

190-
FormattedScope(_stream, _formatted, {BOLD, RED}) << _linePrefix
243+
AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix
191244
<< "Attention: Updates on the test will apply the detected format displayed." << endl;
192245
return false;
193246
}
@@ -202,10 +255,10 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
202255
_stream << _linePrefix << line << endl;
203256
}
204257

205-
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const
258+
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
206259
{
207260
for (auto const& test: m_tests)
208-
_stream << formatFunctionCallTest(test, _linePrefix, false, false);
261+
_stream << formatFunctionCallTest(test, "", true, false);
209262
}
210263

211264
void SemanticTest::parseExpectations(istream& _stream)

test/libsolidity/SemanticTest.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
#pragma once
1616

1717
#include <test/libsolidity/util/TestFileParser.h>
18-
#include <test/libsolidity/FormattedScope.h>
1918
#include <test/libsolidity/SolidityExecutionFramework.h>
2019
#include <test/libsolidity/AnalysisFramework.h>
2120
#include <test/TestCase.h>
2221
#include <liblangutil/Exceptions.h>
22+
#include <libdevcore/AnsiColorized.h>
2323

2424
#include <iosfwd>
2525
#include <string>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
contract C {
2+
function f() public returns (uint) {
3+
return 1;
4+
}
5+
function g(uint x, uint y) public returns (uint) {
6+
return x - y;
7+
}
8+
function h() public payable returns (uint) {
9+
return f();
10+
}
11+
}
12+
// ----
13+
// f() -> 1
14+
// g(uint256,uint256): 1, -2 -> 3
15+
// h(), 1 ether -> 1
16+
// j() -> FAILURE
17+
// i() # Does not exist. # -> FAILURE # Reverts. #
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
contract C {
2+
function f(uint a, uint b, uint c, uint d, uint e) public returns (uint) {
3+
return a + b + c + d + e;
4+
}
5+
}
6+
// ----
7+
// f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1
8+
// -> 5
9+
// g()
10+
// # g() does not exist #
11+
// -> FAILURE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
contract C {
2+
function f(uint a, uint b, uint c, uint d, uint e) public returns (uint) {
3+
return a + b + c + d + e;
4+
}
5+
}
6+
// ----
7+
// f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1
8+
// # A comment on the function parameters. #
9+
// -> 5
10+
// f(uint256,uint256,uint256,uint256,uint256):
11+
// 1,
12+
// 1,
13+
// 1,
14+
// 1,
15+
// 1
16+
// -> 5
17+
// # Should return sum of all parameters. #

0 commit comments

Comments
 (0)