Skip to content

Commit 7390def

Browse files
committed
[isoltest] Add support for call-effects.
1 parent f1d58c5 commit 7390def

9 files changed

+156
-10
lines changed

test/libsolidity/SemanticTest.cpp

+37-3
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ void SemanticTest::initializeBuiltins()
123123
solAssert(m_builtins.count("smokeTest") == 0, "");
124124
m_builtins["smokeTest"] = [](FunctionCall const&) -> std::optional<bytes>
125125
{
126-
return util::toBigEndian(u256(0x1234));
126+
return util::toBigEndian(u256(0x1234));
127+
};
128+
solAssert(m_builtins.count("effectsTest") == 0, "");
129+
m_builtins["effectsTest"] = [](FunctionCall const&) -> std::optional<bytes>
130+
{
131+
return util::toBigEndian(u256(0x2345));
127132
};
128133
soltestAssert(m_builtins.count("balance") == 0, "");
129134
m_builtins["balance"] = [this](FunctionCall const& _call) -> std::optional<bytes>
@@ -142,6 +147,31 @@ void SemanticTest::initializeBuiltins()
142147
soltestAssert(_call.arguments.parameters.empty(), "No arguments expected.");
143148
return toBigEndian(u256(storageEmpty(m_contractAddress) ? 1 : 0));
144149
};
150+
m_hooks.emplace_back(
151+
[](FunctionCall const& _call) -> std::vector<std::string>
152+
{
153+
if (_call.signature.find("effectsTest") != string::npos)
154+
return {_call.signature + "0"};
155+
return {};
156+
}
157+
);
158+
m_hooks.emplace_back(
159+
[](FunctionCall const& _call) -> std::vector<std::string>
160+
{
161+
if (_call.signature.find("effectsTest") != string::npos)
162+
return {_call.signature + "1", _call.signature + "2"};
163+
return {};
164+
}
165+
);
166+
}
167+
168+
vector<string> SemanticTest::effectsOfCall(FunctionCall const& _call) const
169+
{
170+
vector<string> effects;
171+
for (auto const& hook: m_hooks)
172+
for (auto const& effect: hook(_call))
173+
effects.emplace_back(effect);
174+
return effects;
145175
}
146176

147177
TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
@@ -299,6 +329,8 @@ TestCase::TestResult SemanticTest::runTest(
299329
test.setRawBytes(std::move(output));
300330
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName(m_sources.mainSourceFile)));
301331
}
332+
333+
success &= test.call().effects == effectsOfCall(test.call());
302334
}
303335

304336
if (!m_testCaseWantsYulRun && _isYulRun)
@@ -361,7 +393,8 @@ TestCase::TestResult SemanticTest::runTest(
361393
_linePrefix,
362394
m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
363395
_formatted,
364-
/* _interactivePrint */ true
396+
/* _interactivePrint */ true,
397+
effectsOfCall(test.call())
365398
) << endl;
366399
_stream << errorReporter.format(_linePrefix, _formatted);
367400
}
@@ -483,7 +516,8 @@ void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) con
483516
_stream << test.format(
484517
"",
485518
m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
486-
/* _highlight = */ false
519+
/* _highlight = */ false,
520+
effectsOfCall(test.call())
487521
) << endl;
488522
}
489523

test/libsolidity/SemanticTest.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
8181
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _isYulRun, bool _isEwasmRun);
8282
bool checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const;
8383
void initializeBuiltins();
84+
std::vector<std::string> effectsOfCall(FunctionCall const& _call) const;
8485
SourceMap m_sources;
8586
std::size_t m_lineOffset;
8687
std::vector<TestFunctionCall> m_tests;
@@ -94,7 +95,7 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
9495
bool m_canEnableYulRun = false;
9596
bool m_canEnableEwasmRun = false;
9697
std::map<std::string, Builtin> m_builtins{};
97-
98+
std::vector<Hook> m_hooks{};
9899
bool m_gasCostFailure = false;
99100
bool m_enforceGasCost = false;
100101
u256 m_enforceGasCostMinValue;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
contract SmokeTest {
2+
}
3+
// ====
4+
// compileViaYul: also
5+
// ----
6+
// constructor()
7+
// effectsTest -> 0x2345
8+
// - effectsTest0
9+
// - effectsTest1
10+
// - effectsTest2
11+
// effectsTest -> 0x2345
12+
// - effectsTest0
13+
// - effectsTest1
14+
// - effectsTest2

test/libsolidity/util/SoltestTypes.h

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ namespace solidity::frontend::test
4242
T(Comma, ",", 0) \
4343
T(Period, ".", 0) \
4444
T(Arrow, "->", 0) \
45+
T(Effect, "- ", 0) \
4546
T(Newline, "//", 0) \
4647
/* Literals & identifier */ \
4748
T(Comment, "#", 0) \
@@ -302,8 +303,11 @@ struct FunctionCall
302303
/// Marks this function call as "short-handed", meaning
303304
/// no `->` declared.
304305
bool omitsArrow = true;
306+
/// A textual representation of the effect of the function call.
307+
std::vector<std::string> effects{};
305308
};
306309

307310
using Builtin = std::function<std::optional<bytes>(FunctionCall const&)>;
311+
using Hook = std::function<std::vector<std::string>(FunctionCall const&)>;
308312

309313
}

test/libsolidity/util/TestFileParser.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
154154
call.kind = FunctionCall::Kind::Constructor;
155155
}
156156

157+
call.effects = parseFunctionCallEffects();
158+
157159
calls.emplace_back(std::move(call));
158160
}
159161
}
@@ -169,6 +171,25 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
169171
return calls;
170172
}
171173

174+
vector<string> TestFileParser::parseFunctionCallEffects()
175+
{
176+
vector<string> result;
177+
while (accept(Token::Effect, false))
178+
{
179+
string effect = m_scanner.currentLiteral();
180+
result.emplace_back(effect);
181+
182+
if (m_scanner.currentToken() == Token::Effect)
183+
m_scanner.scanNextToken();
184+
if (m_scanner.currentToken() == Token::Newline)
185+
m_scanner.scanNextToken();
186+
if (m_scanner.currentToken() == Token::EOS)
187+
break;
188+
}
189+
190+
return result;
191+
}
192+
172193
bool TestFileParser::accept(Token _token, bool const _expect)
173194
{
174195
if (m_scanner.currentToken() != _token)
@@ -542,6 +563,11 @@ void TestFileParser::Scanner::scanNextToken()
542563
advance();
543564
selectToken(Token::Arrow);
544565
}
566+
else if (peek() == ' ')
567+
{
568+
advance();
569+
selectToken(Token::Effect, scanEffectString());
570+
}
545571
else
546572
selectToken(Token::Sub);
547573
break;
@@ -601,6 +627,17 @@ void TestFileParser::Scanner::scanNextToken()
601627
while (m_currentToken == Token::Whitespace);
602628
}
603629

630+
string TestFileParser::Scanner::scanEffectString()
631+
{
632+
string effect;
633+
while (peek() != '/' && peek() != '\0')
634+
{
635+
advance();
636+
effect += current();
637+
}
638+
return effect;
639+
}
640+
604641
string TestFileParser::Scanner::scanComment()
605642
{
606643
string comment;

test/libsolidity/util/TestFileParser.h

+4
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class TestFileParser
8787
std::string scanDecimalNumber();
8888
std::string scanHexNumber();
8989
std::string scanString();
90+
std::string scanEffectString();
9091
char scanHexPart();
9192

9293
private:
@@ -177,6 +178,9 @@ class TestFileParser
177178
/// Parses the current string literal.
178179
std::string parseString();
179180

181+
/// Parses the expected effects of a function call execution.
182+
std::vector<std::string> parseFunctionCallEffects();
183+
180184
/// Checks whether a builtin function with the given signature exist.
181185
/// @returns true, if builtin found, false otherwise
182186
bool isBuiltinFunction(std::string const& signature);

test/libsolidity/util/TestFileParserTests.cpp

+40-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <string>
2424
#include <tuple>
2525
#include <boost/test/unit_test.hpp>
26+
#include <boost/algorithm/string/trim.hpp>
2627
#include <liblangutil/Exceptions.h>
2728
#include <test/ExecutionFramework.h>
2829

@@ -42,10 +43,10 @@ using Mode = FunctionCall::DisplayMode;
4243
namespace
4344
{
4445

45-
vector<FunctionCall> parse(string const& _source)
46+
vector<FunctionCall> parse(string const& _source, std::map<std::string, Builtin> const& _builtins = {})
4647
{
4748
istringstream stream{_source, ios_base::out};
48-
TestFileParser parser{stream, {}};
49+
TestFileParser parser{stream, _builtins};
4950
return parser.parseFunctionCalls(0);
5051
}
5152

@@ -958,6 +959,43 @@ BOOST_AUTO_TEST_CASE(library)
958959
);
959960
}
960961

962+
BOOST_AUTO_TEST_CASE(call_effects)
963+
{
964+
std::map<std::string, Builtin> builtins;
965+
builtins["builtin_returning_call_effect"] = [](FunctionCall const&) -> std::optional<bytes>
966+
{
967+
return util::toBigEndian(u256(0x1234));
968+
};
969+
char const* source = R"(
970+
// builtin_returning_call_effect -> 1
971+
// - bla
972+
// - bla bla
973+
// - bla bla bla
974+
)";
975+
vector<FunctionCall> calls = parse(source, builtins);
976+
BOOST_REQUIRE_EQUAL(calls.size(), 1);
977+
BOOST_REQUIRE_EQUAL(calls[0].effects.size(), 3);
978+
BOOST_REQUIRE_EQUAL(boost::trim_copy(calls[0].effects[0]), "bla");
979+
BOOST_REQUIRE_EQUAL(boost::trim_copy(calls[0].effects[1]), "bla bla");
980+
BOOST_REQUIRE_EQUAL(boost::trim_copy(calls[0].effects[2]), "bla bla bla");
981+
source = R"(
982+
// builtin_returning_call_effect -> 1
983+
// - bla
984+
// - bla bla
985+
// builtin_returning_call_effect -> 2
986+
// - bla bla bla
987+
// builtin_returning_call_effect -> 3
988+
)";
989+
calls = parse(source, builtins);
990+
BOOST_REQUIRE_EQUAL(calls.size(), 3);
991+
BOOST_REQUIRE_EQUAL(calls[0].effects.size(), 2);
992+
BOOST_REQUIRE_EQUAL(calls[1].effects.size(), 1);
993+
BOOST_REQUIRE_EQUAL(calls[2].effects.size(), 0);
994+
BOOST_REQUIRE_EQUAL(boost::trim_copy(calls[0].effects[0]), "bla");
995+
BOOST_REQUIRE_EQUAL(boost::trim_copy(calls[0].effects[1]), "bla bla");
996+
BOOST_REQUIRE_EQUAL(boost::trim_copy(calls[1].effects[0]), "bla bla bla");
997+
}
998+
961999
BOOST_AUTO_TEST_SUITE_END()
9621000

9631001
}

test/libsolidity/util/TestFunctionCall.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ string TestFunctionCall::format(
3737
string const& _linePrefix,
3838
RenderMode _renderMode,
3939
bool const _highlight,
40-
bool const _interactivePrint
40+
bool const _interactivePrint,
41+
std::vector<std::string> const & _callEffects
4142
) const
4243
{
4344
stringstream stream;
@@ -197,6 +198,17 @@ string TestFunctionCall::format(
197198
}
198199

199200
stream << formatGasExpectations(_linePrefix, _renderMode == RenderMode::ExpectedValuesActualGas, _interactivePrint);
201+
202+
if (!_callEffects.empty())
203+
{
204+
stream << std::endl;
205+
for (string const& effect: _callEffects)
206+
{
207+
stream << _linePrefix << "// - " << effect;
208+
if (effect != *_callEffects.rbegin())
209+
stream << std::endl;
210+
}
211+
}
200212
};
201213

202214
formatOutput(m_call.displayMode == FunctionCall::DisplayMode::SingleLine);

test/libsolidity/util/TestFunctionCall.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,21 @@ class TestFunctionCall
6868
std::string const& _linePrefix = "",
6969
RenderMode _renderMode = RenderMode::ExpectedValuesExpectedGas,
7070
bool const _highlight = false,
71-
bool const _interactivePrint = false
71+
bool const _interactivePrint = false,
72+
std::vector<std::string> const & _callEffects = {}
7273
) const;
7374

7475
/// Overloaded version that passes an error reporter which is never used outside
7576
/// of this function.
7677
std::string format(
7778
std::string const& _linePrefix = "",
7879
RenderMode const _renderMode = RenderMode::ExpectedValuesExpectedGas,
79-
bool const _highlight = false
80+
bool const _highlight = false,
81+
std::vector<std::string> const & _callEffects = {}
8082
) const
8183
{
8284
ErrorReporter reporter;
83-
return format(reporter, _linePrefix, _renderMode, _highlight);
85+
return format(reporter, _linePrefix, _renderMode, _highlight, false, _callEffects);
8486
}
8587

8688
/// Resets current results in case the function was called and the result

0 commit comments

Comments
 (0)