1818
1919#include < test/TestCaseReader.h>
2020
21+ #include < libsolidity/parsing/Parser.h>
2122#include < libsolutil/StringUtils.h>
23+ #include < libsolutil/CommonIO.h>
2224
2325#include < boost/algorithm/string.hpp>
2426#include < boost/throw_exception.hpp>
27+ #include < boost/filesystem.hpp>
2528
2629#include < range/v3/view/map.hpp>
2730
2831using namespace std ;
32+ using namespace solidity ::langutil;
2933using namespace solidity ::frontend::test;
3034
31- TestCaseReader::TestCaseReader (string const & _filename):
32- m_file(_filename)
35+ namespace fs = boost::filesystem;
36+
37+ TestCaseReader::TestCaseReader (string const & _filename): m_fileStream(_filename), m_fileName(_filename)
3338{
34- if (!m_file )
39+ if (!m_fileStream )
3540 BOOST_THROW_EXCEPTION (runtime_error (" Cannot open file: \" " + _filename + " \" ." ));
36- m_file .exceptions (ios::badbit);
41+ m_fileStream .exceptions (ios::badbit);
3742
38- tie (m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber (m_file );
43+ tie (m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber (m_fileStream );
3944 m_unreadSettings = m_settings;
4045}
4146
@@ -55,7 +60,7 @@ string const& TestCaseReader::source() const
5560
5661string TestCaseReader::simpleExpectations ()
5762{
58- return parseSimpleExpectations (m_file );
63+ return parseSimpleExpectations (m_fileStream );
5964}
6065
6166bool TestCaseReader::boolSetting (std::string const & _name, bool _defaultValue)
@@ -105,10 +110,12 @@ void TestCaseReader::ensureAllSettingsRead() const
105110pair<SourceMap, size_t > TestCaseReader::parseSourcesAndSettingsWithLineNumber (istream& _stream)
106111{
107112 map<string, string> sources;
113+ map<string, boost::filesystem::path> externalSources;
108114 string currentSourceName;
109115 string currentSource;
110116 string line;
111117 size_t lineNumber = 1 ;
118+ static string const externalSourceDelimiterStart (" ==== ExternalSource:" );
112119 static string const sourceDelimiterStart (" ==== Source:" );
113120 static string const sourceDelimiterEnd (" ====" );
114121 static string const comment (" // " );
@@ -137,6 +144,41 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
137144 if (sources.count (currentSourceName))
138145 BOOST_THROW_EXCEPTION (runtime_error (" Multiple definitions of test source \" " + currentSourceName + " \" ." ));
139146 }
147+ else if (boost::algorithm::starts_with (line, externalSourceDelimiterStart) && boost::algorithm::ends_with (line, sourceDelimiterEnd))
148+ {
149+ string externalSourceString = boost::trim_copy (line.substr (
150+ externalSourceDelimiterStart.size (),
151+ line.size () - sourceDelimiterEnd.size () - externalSourceDelimiterStart.size ()
152+ ));
153+
154+ string externalSourceName;
155+ size_t remappingPos = externalSourceString.find (' =' );
156+ // Does the external source define a remapping?
157+ if (remappingPos != string::npos)
158+ {
159+ externalSourceName = boost::trim_copy (externalSourceString.substr (0 , remappingPos));
160+ externalSourceString = boost::trim_copy (externalSourceString.substr (remappingPos + 1 ));
161+ }
162+ else
163+ externalSourceName = externalSourceString;
164+
165+ solAssert (!externalSourceName.empty (), " " );
166+ fs::path externalSourceTarget (externalSourceString);
167+ fs::path testCaseParentDir = m_fileName.parent_path ();
168+ if (!externalSourceTarget.is_relative ())
169+ BOOST_THROW_EXCEPTION (runtime_error (" External Source paths need to be relative to the location of the test case." ));
170+ fs::path externalSourceFullPath = testCaseParentDir / externalSourceTarget;
171+ string externalSourceContent;
172+ if (!fs::exists (externalSourceFullPath))
173+ BOOST_THROW_EXCEPTION (runtime_error (" External Source '" + externalSourceTarget.string () + " ' not found." ));
174+ else
175+ externalSourceContent = util::readFileAsString (externalSourceFullPath.string ());
176+
177+ if (sources.count (externalSourceName))
178+ BOOST_THROW_EXCEPTION (runtime_error (" Multiple definitions of test source \" " + externalSourceName + " \" ." ));
179+ sources[externalSourceName] = externalSourceContent;
180+ externalSources[externalSourceName] = externalSourceTarget;
181+ }
140182 else
141183 currentSource += line + " \n " ;
142184 }
@@ -156,7 +198,7 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
156198 }
157199 // Register the last source as the main one
158200 sources[currentSourceName] = currentSource;
159- return {{move (sources), move (currentSourceName)}, lineNumber};
201+ return {{move (sources), move (externalSources), move ( currentSourceName)}, lineNumber};
160202}
161203
162204string TestCaseReader::parseSimpleExpectations (istream& _file)
0 commit comments