mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			476 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			476 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
	This file is part of cpp-ethereum.
 | 
						|
 | 
						|
	cpp-ethereum 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.
 | 
						|
 | 
						|
	cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
/** @file TestHelper.cpp
 | 
						|
 * @author Marko Simovic <markobarko@gmail.com>
 | 
						|
 * @date 2014
 | 
						|
 */
 | 
						|
 | 
						|
#include "TestHelper.h"
 | 
						|
 | 
						|
#include <thread>
 | 
						|
#include <chrono>
 | 
						|
#include <boost/filesystem/path.hpp>
 | 
						|
#include <libethereum/Client.h>
 | 
						|
#include <liblll/Compiler.h>
 | 
						|
 | 
						|
using namespace std;
 | 
						|
using namespace dev::eth;
 | 
						|
 | 
						|
namespace dev
 | 
						|
{
 | 
						|
namespace eth
 | 
						|
{
 | 
						|
 | 
						|
void mine(Client& c, int numBlocks)
 | 
						|
{
 | 
						|
	auto startBlock = c.blockChain().details().number;
 | 
						|
 | 
						|
	c.startMining();
 | 
						|
	while(c.blockChain().details().number < startBlock + numBlocks)
 | 
						|
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
						|
	c.stopMining();
 | 
						|
}
 | 
						|
 | 
						|
void connectClients(Client& c1, Client& c2)
 | 
						|
{
 | 
						|
	(void)c1;
 | 
						|
	(void)c2;
 | 
						|
	// TODO: Move to WebThree. eth::Client no longer handles networking.
 | 
						|
#if 0
 | 
						|
	short c1Port = 20000;
 | 
						|
	short c2Port = 21000;
 | 
						|
	c1.startNetwork(c1Port);
 | 
						|
	c2.startNetwork(c2Port);
 | 
						|
	c2.connect("127.0.0.1", c1Port);
 | 
						|
#endif
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
namespace test
 | 
						|
{
 | 
						|
 | 
						|
ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o)
 | 
						|
{
 | 
						|
	importEnv(_o["env"].get_obj());
 | 
						|
	importState(_o["pre"].get_obj(), m_statePre);
 | 
						|
	importTransaction(_o["transaction"].get_obj());
 | 
						|
 | 
						|
	if (!isFiller)
 | 
						|
	{
 | 
						|
		importState(_o["post"].get_obj(), m_statePost);
 | 
						|
		m_environment.sub.logs = importLog(_o["logs"].get_array());
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ImportTest::importEnv(json_spirit::mObject& _o)
 | 
						|
{
 | 
						|
	BOOST_REQUIRE(_o.count("previousHash") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("currentGasLimit") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("currentDifficulty") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("currentTimestamp") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("currentCoinbase") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("currentNumber") > 0);
 | 
						|
 | 
						|
	m_environment.previousBlock.hash = h256(_o["previousHash"].get_str());
 | 
						|
	m_environment.currentBlock.number = toInt(_o["currentNumber"]);
 | 
						|
	m_environment.currentBlock.gasLimit = toInt(_o["currentGasLimit"]);
 | 
						|
	m_environment.currentBlock.difficulty = toInt(_o["currentDifficulty"]);
 | 
						|
	m_environment.currentBlock.timestamp = toInt(_o["currentTimestamp"]);
 | 
						|
	m_environment.currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str());
 | 
						|
 | 
						|
	m_statePre.m_previousBlock = m_environment.previousBlock;
 | 
						|
	m_statePre.m_currentBlock = m_environment.currentBlock;
 | 
						|
}
 | 
						|
 | 
						|
void ImportTest::importState(json_spirit::mObject& _o, State& _state)
 | 
						|
{
 | 
						|
	for (auto& i: _o)
 | 
						|
	{
 | 
						|
		json_spirit::mObject o = i.second.get_obj();
 | 
						|
 | 
						|
		BOOST_REQUIRE(o.count("balance") > 0);
 | 
						|
		BOOST_REQUIRE(o.count("nonce") > 0);
 | 
						|
		BOOST_REQUIRE(o.count("storage") > 0);
 | 
						|
		BOOST_REQUIRE(o.count("code") > 0);
 | 
						|
 | 
						|
		Address address = Address(i.first);
 | 
						|
 | 
						|
		bytes code = importCode(o);
 | 
						|
 | 
						|
		if (code.size())
 | 
						|
		{
 | 
						|
			_state.m_cache[address] = Account(toInt(o["balance"]), Account::ContractConception);
 | 
						|
			_state.m_cache[address].setCode(bytesConstRef(&code));
 | 
						|
		}
 | 
						|
		else
 | 
						|
			_state.m_cache[address] = Account(toInt(o["balance"]), Account::NormalCreation);
 | 
						|
 | 
						|
		for (auto const& j: o["storage"].get_obj())
 | 
						|
			_state.setStorage(address, toInt(j.first), toInt(j.second));
 | 
						|
 | 
						|
		for(int i=0; i<toInt(o["nonce"]); ++i)
 | 
						|
			_state.noteSending(address);
 | 
						|
 | 
						|
		_state.ensureCached(address, false, false);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ImportTest::importTransaction(json_spirit::mObject& _o)
 | 
						|
{
 | 
						|
	BOOST_REQUIRE(_o.count("nonce")> 0);
 | 
						|
	BOOST_REQUIRE(_o.count("gasPrice") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("gasLimit") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("to") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("value") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("secretKey") > 0);
 | 
						|
	BOOST_REQUIRE(_o.count("data") > 0);
 | 
						|
 | 
						|
	m_transaction = _o["to"].get_str().empty() ?
 | 
						|
		Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), importData(_o), toInt(_o["nonce"]), Secret(_o["secretKey"].get_str())) :
 | 
						|
		Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), Address(_o["to"].get_str()), importData(_o), toInt(_o["nonce"]), Secret(_o["secretKey"].get_str()));
 | 
						|
}
 | 
						|
 | 
						|
void ImportTest::exportTest(bytes _output, State& _statePost)
 | 
						|
{
 | 
						|
	// export output
 | 
						|
	m_TestObject["out"] = "0x" + toHex(_output);
 | 
						|
 | 
						|
	// export logs
 | 
						|
	m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
 | 
						|
 | 
						|
	// export post state
 | 
						|
	json_spirit::mObject postState;
 | 
						|
 | 
						|
	std::map<Address, Account> genesis = genesisState();
 | 
						|
 | 
						|
	for (auto const& a: _statePost.addresses())
 | 
						|
	{
 | 
						|
		if (genesis.count(a.first))
 | 
						|
			continue;
 | 
						|
 | 
						|
		json_spirit::mObject o;
 | 
						|
		o["balance"] = toString(_statePost.balance(a.first));
 | 
						|
		o["nonce"] = toString(_statePost.transactionsFrom(a.first));
 | 
						|
		{
 | 
						|
			json_spirit::mObject store;
 | 
						|
			for (auto const& s: _statePost.storage(a.first))
 | 
						|
				store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second));
 | 
						|
			o["storage"] = store;
 | 
						|
		}
 | 
						|
		o["code"] = "0x" + toHex(_statePost.code(a.first));
 | 
						|
 | 
						|
		postState[toString(a.first)] = o;
 | 
						|
	}
 | 
						|
	m_TestObject["post"] = json_spirit::mValue(postState);
 | 
						|
 | 
						|
	// export pre state
 | 
						|
	json_spirit::mObject preState;
 | 
						|
 | 
						|
	for (auto const& a: m_statePre.addresses())
 | 
						|
	{
 | 
						|
		if (genesis.count(a.first))
 | 
						|
			continue;
 | 
						|
 | 
						|
		json_spirit::mObject o;
 | 
						|
		o["balance"] = toString(m_statePre.balance(a.first));
 | 
						|
		o["nonce"] = toString(m_statePre.transactionsFrom(a.first));
 | 
						|
		{
 | 
						|
			json_spirit::mObject store;
 | 
						|
			for (auto const& s: m_statePre.storage(a.first))
 | 
						|
				store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second));
 | 
						|
			o["storage"] = store;
 | 
						|
		}
 | 
						|
		o["code"] = "0x" + toHex(m_statePre.code(a.first));
 | 
						|
 | 
						|
		preState[toString(a.first)] = o;
 | 
						|
	}
 | 
						|
	m_TestObject["pre"] = json_spirit::mValue(preState);
 | 
						|
}
 | 
						|
 | 
						|
u256 toInt(json_spirit::mValue const& _v)
 | 
						|
{
 | 
						|
	switch (_v.type())
 | 
						|
	{
 | 
						|
	case json_spirit::str_type: return u256(_v.get_str());
 | 
						|
	case json_spirit::int_type: return (u256)_v.get_uint64();
 | 
						|
	case json_spirit::bool_type: return (u256)(uint64_t)_v.get_bool();
 | 
						|
	case json_spirit::real_type: return (u256)(uint64_t)_v.get_real();
 | 
						|
	default: cwarn << "Bad type for scalar: " << _v.type();
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
byte toByte(json_spirit::mValue const& _v)
 | 
						|
{
 | 
						|
	switch (_v.type())
 | 
						|
	{
 | 
						|
	case json_spirit::str_type: return (byte)stoi(_v.get_str());
 | 
						|
	case json_spirit::int_type: return (byte)_v.get_uint64();
 | 
						|
	case json_spirit::bool_type: return (byte)_v.get_bool();
 | 
						|
	case json_spirit::real_type: return (byte)_v.get_real();
 | 
						|
	default: cwarn << "Bad type for scalar: " << _v.type();
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
bytes importData(json_spirit::mObject& _o)
 | 
						|
{
 | 
						|
	bytes data;
 | 
						|
	if (_o["data"].type() == json_spirit::str_type)
 | 
						|
		if (_o["data"].get_str().find_first_of("0x") == 0)
 | 
						|
			data = fromHex(_o["data"].get_str().substr(2));
 | 
						|
		else
 | 
						|
			data = fromHex(_o["data"].get_str());
 | 
						|
	else
 | 
						|
		for (auto const& j: _o["data"].get_array())
 | 
						|
			data.push_back(toByte(j));
 | 
						|
 | 
						|
	return data;
 | 
						|
}
 | 
						|
 | 
						|
bytes importCode(json_spirit::mObject& _o)
 | 
						|
{
 | 
						|
	bytes code;
 | 
						|
	if (_o["code"].type() == json_spirit::str_type)
 | 
						|
		if (_o["code"].get_str().find_first_of("0x") != 0)
 | 
						|
			code = compileLLL(_o["code"].get_str(), false);
 | 
						|
		else
 | 
						|
			code = fromHex(_o["code"].get_str().substr(2));
 | 
						|
	else if (_o["code"].type() == json_spirit::array_type)
 | 
						|
	{
 | 
						|
		code.clear();
 | 
						|
		for (auto const& j: _o["code"].get_array())
 | 
						|
			code.push_back(toByte(j));
 | 
						|
	}
 | 
						|
	return code;
 | 
						|
}
 | 
						|
 | 
						|
LogEntries importLog(json_spirit::mArray& _a)
 | 
						|
{
 | 
						|
	LogEntries logEntries;
 | 
						|
	for (auto const& l: _a)
 | 
						|
	{
 | 
						|
		json_spirit::mObject o = l.get_obj();
 | 
						|
		// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
 | 
						|
		assert(o.count("address") > 0);
 | 
						|
		assert(o.count("topics") > 0);
 | 
						|
		assert(o.count("data") > 0);
 | 
						|
		assert(o.count("bloom") > 0);
 | 
						|
		LogEntry log;
 | 
						|
		log.address = Address(o["address"].get_str());
 | 
						|
		for (auto const& t: o["topics"].get_array())
 | 
						|
			log.topics.push_back(h256(t.get_str()));
 | 
						|
		log.data = importData(o);
 | 
						|
		logEntries.push_back(log);
 | 
						|
	}
 | 
						|
	return logEntries;
 | 
						|
}
 | 
						|
 | 
						|
json_spirit::mArray exportLog(eth::LogEntries _logs)
 | 
						|
{
 | 
						|
	json_spirit::mArray ret;
 | 
						|
	if (_logs.size() == 0) return ret;
 | 
						|
	for (LogEntry const& l: _logs)
 | 
						|
	{
 | 
						|
		json_spirit::mObject o;
 | 
						|
		o["address"] = toString(l.address);
 | 
						|
		json_spirit::mArray topics;
 | 
						|
		for (auto const& t: l.topics)
 | 
						|
			topics.push_back(toString(t));
 | 
						|
		o["topics"] = topics;
 | 
						|
		o["data"] = "0x" + toHex(l.data);
 | 
						|
		o["bloom"] = toString(l.bloom());
 | 
						|
		ret.push_back(o);
 | 
						|
	}
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void checkOutput(bytes const& _output, json_spirit::mObject& _o)
 | 
						|
{
 | 
						|
	int j = 0;
 | 
						|
	if (_o["out"].type() == json_spirit::array_type)
 | 
						|
		for (auto const& d: _o["out"].get_array())
 | 
						|
		{
 | 
						|
			BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!");
 | 
						|
			++j;
 | 
						|
		}
 | 
						|
	else if (_o["out"].get_str().find("0x") == 0)
 | 
						|
		BOOST_CHECK(_output == fromHex(_o["out"].get_str().substr(2)));
 | 
						|
	else
 | 
						|
		BOOST_CHECK(_output == fromHex(_o["out"].get_str()));
 | 
						|
}
 | 
						|
 | 
						|
void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore, Address _expectedAddr)
 | 
						|
{
 | 
						|
	for (auto&& expectedStorePair : _expectedStore)
 | 
						|
	{
 | 
						|
		auto& expectedStoreKey = expectedStorePair.first;
 | 
						|
		auto resultStoreIt = _resultStore.find(expectedStoreKey);
 | 
						|
		if (resultStoreIt == _resultStore.end())
 | 
						|
			BOOST_ERROR(_expectedAddr << ": missing store key " << expectedStoreKey);
 | 
						|
		else
 | 
						|
		{
 | 
						|
			auto& expectedStoreValue = expectedStorePair.second;
 | 
						|
			auto& resultStoreValue = resultStoreIt->second;
 | 
						|
			BOOST_CHECK_MESSAGE(expectedStoreValue == resultStoreValue, _expectedAddr << ": store[" << expectedStoreKey << "] = " << resultStoreValue << ", expected " << expectedStoreValue);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	BOOST_CHECK_EQUAL(_resultStore.size(), _expectedStore.size());
 | 
						|
	for (auto&& resultStorePair : _resultStore)
 | 
						|
	{
 | 
						|
		if (!_expectedStore.count(resultStorePair.first))
 | 
						|
			BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs)
 | 
						|
{
 | 
						|
	BOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size());
 | 
						|
 | 
						|
	for (size_t i = 0; i < _resultLogs.size(); ++i)
 | 
						|
	{
 | 
						|
		BOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address);
 | 
						|
		BOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics);
 | 
						|
		BOOST_CHECK(_resultLogs[i].data == _expectedLogs[i].data);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
std::string getTestPath()
 | 
						|
{
 | 
						|
	string testPath;
 | 
						|
	const char* ptestPath = getenv("ETHEREUM_TEST_PATH");
 | 
						|
 | 
						|
	if (ptestPath == NULL)
 | 
						|
	{
 | 
						|
		cnote << " could not find environment variable ETHEREUM_TEST_PATH \n";
 | 
						|
		testPath = "../../../tests";
 | 
						|
	}
 | 
						|
	else
 | 
						|
		testPath = ptestPath;
 | 
						|
 | 
						|
	return testPath;
 | 
						|
}
 | 
						|
 | 
						|
void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests)
 | 
						|
{
 | 
						|
	for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
 | 
						|
	{
 | 
						|
		string arg = boost::unit_test::framework::master_test_suite().argv[i];
 | 
						|
		if (arg == testTypeFlag)
 | 
						|
		{
 | 
						|
			if (boost::unit_test::framework::master_test_suite().argc <= i + 2)
 | 
						|
			{
 | 
						|
				cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " <filename> <testname>\n";
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			string filename = boost::unit_test::framework::master_test_suite().argv[i + 1];
 | 
						|
			string testname = boost::unit_test::framework::master_test_suite().argv[i + 2];
 | 
						|
			int currentVerbosity = g_logVerbosity;
 | 
						|
			g_logVerbosity = 12;
 | 
						|
			try
 | 
						|
			{
 | 
						|
				cnote << "Testing user defined test: " << filename;
 | 
						|
				json_spirit::mValue v;
 | 
						|
				string s = asString(contents(filename));
 | 
						|
				BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
 | 
						|
				json_spirit::read_string(s, v);
 | 
						|
				json_spirit::mObject oSingleTest;
 | 
						|
 | 
						|
				json_spirit::mObject::const_iterator pos = v.get_obj().find(testname);
 | 
						|
				if (pos == v.get_obj().end())
 | 
						|
				{
 | 
						|
					cnote << "Could not find test: " << testname << " in " << filename << "\n";
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				else
 | 
						|
					oSingleTest[pos->first] = pos->second;
 | 
						|
 | 
						|
				json_spirit::mValue v_singleTest(oSingleTest);
 | 
						|
				doTests(v_singleTest, false);
 | 
						|
			}
 | 
						|
			catch (Exception const& _e)
 | 
						|
			{
 | 
						|
				BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e));
 | 
						|
				g_logVerbosity = currentVerbosity;
 | 
						|
			}
 | 
						|
			catch (std::exception const& _e)
 | 
						|
			{
 | 
						|
				BOOST_ERROR("Failed Test with Exception: " << _e.what());
 | 
						|
				g_logVerbosity = currentVerbosity;
 | 
						|
			}
 | 
						|
			g_logVerbosity = currentVerbosity;
 | 
						|
		}
 | 
						|
		else
 | 
						|
			continue;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void executeTests(const string& _name, const string& _testPathAppendix, std::function<void(json_spirit::mValue&, bool)> doTests)
 | 
						|
{
 | 
						|
	string testPath = getTestPath();
 | 
						|
	testPath += _testPathAppendix;
 | 
						|
 | 
						|
	for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
 | 
						|
	{
 | 
						|
		string arg = boost::unit_test::framework::master_test_suite().argv[i];
 | 
						|
		if (arg == "--filltests")
 | 
						|
		{
 | 
						|
			try
 | 
						|
			{
 | 
						|
				cnote << "Populating tests...";
 | 
						|
				json_spirit::mValue v;
 | 
						|
				boost::filesystem::path p(__FILE__);
 | 
						|
				boost::filesystem::path dir = p.parent_path();
 | 
						|
				string s = asString(dev::contents(dir.string() + "/" + _name + "Filler.json"));
 | 
						|
				BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + dir.string() + "/" + _name + "Filler.json is empty.");
 | 
						|
				json_spirit::read_string(s, v);
 | 
						|
				doTests(v, true);
 | 
						|
				writeFile(testPath + "/" + _name + ".json", asBytes(json_spirit::write_string(v, true)));
 | 
						|
			}
 | 
						|
			catch (Exception const& _e)
 | 
						|
			{
 | 
						|
				BOOST_ERROR("Failed test with Exception: " << diagnostic_information(_e));
 | 
						|
			}
 | 
						|
			catch (std::exception const& _e)
 | 
						|
			{
 | 
						|
				BOOST_ERROR("Failed test with Exception: " << _e.what());
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	try
 | 
						|
	{
 | 
						|
		cnote << "Testing ..." << _name;
 | 
						|
		json_spirit::mValue v;
 | 
						|
		string s = asString(dev::contents(testPath + "/" + _name + ".json"));
 | 
						|
		BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + testPath + "/" + _name + ".json is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?");
 | 
						|
		json_spirit::read_string(s, v);
 | 
						|
		doTests(v, false);
 | 
						|
	}
 | 
						|
	catch (Exception const& _e)
 | 
						|
	{
 | 
						|
		BOOST_ERROR("Failed test with Exception: " << diagnostic_information(_e));
 | 
						|
	}
 | 
						|
	catch (std::exception const& _e)
 | 
						|
	{
 | 
						|
		BOOST_ERROR("Failed test with Exception: " << _e.what());
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
} } // namespaces
 |