diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 99f2f6fc..3fb8fd31 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -58,7 +58,7 @@ jobs: run: ./scripts/auto.sh -s bdk exec 'rm coverage.xml || true' - name: Collect coverage into one XML report run: ./scripts/auto.sh -s bdk exec \ - 'gcovr -d --gcov-ignore-parse-errors --sonarqube -o coverage.xml' + 'gcovr -d --gcov-ignore-parse-errors --exclude-throw-branches --sonarqube -o coverage.xml' - name: Run SonarQube Scanner run: ./scripts/auto.sh -s bdk exec \ 'env SONAR_TOKEN=${{ env.SONAR_TOKEN }} diff --git a/src/bins/bdkd-discovery/main.cpp b/src/bins/bdkd-discovery/main.cpp index 37ed6ec3..e52fc28a 100644 --- a/src/bins/bdkd-discovery/main.cpp +++ b/src/bins/bdkd-discovery/main.cpp @@ -7,11 +7,12 @@ See the LICENSE.txt file in the project root for more information. #include #include +#include + +#include "src/net/p2p/managerdiscovery.h" -#include "net/p2p/managerdiscovery.h" -#include "utils/options.h" -#include "iostream" #include "src/utils/clargs.h" +#include "src/utils/options.h" std::condition_variable cv; std::mutex cv_m; diff --git a/src/contract/contract.h b/src/contract/contract.h index ff127d03..6c87a431 100644 --- a/src/contract/contract.h +++ b/src/contract/contract.h @@ -106,19 +106,19 @@ class BaseContract : public ContractLocals, public Dumpable { BaseContract(const Address &address, const DB& db) : contractAddress_(address), dbPrefix_([&]() { - Bytes prefix = DBPrefix::contracts; - prefix.reserve(prefix.size() + address.size()); - prefix.insert(prefix.end(), address.cbegin(), address.cend()); - return prefix; + Bytes prefix = DBPrefix::contracts; + prefix.reserve(prefix.size() + address.size()); + prefix.insert(prefix.end(), address.cbegin(), address.cend()); + return prefix; }()), contractName_([&]() { - return StrConv::bytesToString(db.get(std::string("contractName_"), dbPrefix_)); + return StrConv::bytesToString(db.get(std::string("contractName_"), dbPrefix_)); }()), contractCreator_([&]() { - return Address(db.get(std::string("contractCreator_"), dbPrefix_)); + return Address(db.get(std::string("contractCreator_"), dbPrefix_)); }()), contractChainId_([&]() { - return UintConv::bytesToUint64(db.get(std::string("contractChainId_"), dbPrefix_)); + return UintConv::bytesToUint64(db.get(std::string("contractChainId_"), dbPrefix_)); }()) {} diff --git a/src/contract/templates/erc20wrapper.h b/src/contract/templates/erc20wrapper.h index cf0dcb0b..ada65a9f 100644 --- a/src/contract/templates/erc20wrapper.h +++ b/src/contract/templates/erc20wrapper.h @@ -32,10 +32,9 @@ class ERC20Wrapper : public DynamicContract { void registerContractFunctions() override; public: - /** - * ConstructorArguments is a tuple of the contract constructor arguments in the order they appear in the constructor. - */ + * ConstructorArguments is a tuple of the contract constructor arguments in the order they appear in the constructor. + */ using ConstructorArguments = std::tuple<>; /** diff --git a/src/contract/templates/erc721.cpp b/src/contract/templates/erc721.cpp index 66c8a98a..026bc41d 100644 --- a/src/contract/templates/erc721.cpp +++ b/src/contract/templates/erc721.cpp @@ -163,7 +163,7 @@ void ERC721::mint_(const Address& to, const uint256_t& tokenId) { } void ERC721::burn_(const uint256_t& tokenId) { - Address prevOwner = this->update_(Address(), tokenId, Address()); + Address prevOwner = this->update_(Address(), tokenId, this->getCaller()); if (prevOwner == Address()) { throw DynamicException("ERC721::burn_: inexistent token"); } @@ -174,7 +174,7 @@ void ERC721::transfer_(const Address& from, const Address& to, const uint256_t& throw DynamicException("ERC721::transfer_: transfer to the zero address"); } - Address prevOwner = this->update_(to, tokenId, Address()); + Address prevOwner = this->update_(to, tokenId, this->getCaller()); if (prevOwner == Address()) { throw DynamicException("ERC721::transfer_: inexistent token"); } else if (prevOwner != from) { @@ -257,15 +257,7 @@ bool ERC721::isApprovedForAll(const Address& owner, const Address& operatorAddre } void ERC721::transferFrom(const Address& from, const Address& to, const uint256_t& tokenId) { - if (to == Address()) { - throw DynamicException("ERC721::transferFrom: transfer to the zero address"); - } - Address prevOwner = this->update_(to, tokenId, this->getCaller()); - if (prevOwner == Address()) { - throw DynamicException("ERC721::transferFrom: inexistent token"); - } else if (prevOwner != from) { - throw DynamicException("ERC721::transferFrom: incorrect owner"); - } + this->transfer_(from, to, tokenId); } DBBatch ERC721::dump() const { diff --git a/src/contract/templates/erc721test.cpp b/src/contract/templates/erc721test.cpp index 7a0d8c85..727009d0 100644 --- a/src/contract/templates/erc721test.cpp +++ b/src/contract/templates/erc721test.cpp @@ -73,7 +73,7 @@ void ERC721Test::mint(const Address& to) { } void ERC721Test::burn(const uint256_t& tokenId) { - this->update_(Address(), tokenId, this->getCaller()); + this->burn_(tokenId); --totalSupply_; } diff --git a/src/contract/templates/erc721test.h b/src/contract/templates/erc721test.h index 4070e2a6..f68172d0 100644 --- a/src/contract/templates/erc721test.h +++ b/src/contract/templates/erc721test.h @@ -35,8 +35,7 @@ class ERC721Test : public ERC721 { * ConstructorArguments is a tuple of the contract constructor arguments in * the order they appear in the constructor. */ - using ConstructorArguments = - std::tuple; + using ConstructorArguments = std::tuple; /** * Constructor for loading contract from DB. @@ -77,19 +76,13 @@ class ERC721Test : public ERC721 { void burn(const uint256_t& tokenId); /// Getter for the tokenIdCounter_ - uint64_t tokenIdCounter() const { - return tokenIdCounter_.get(); - } + uint64_t tokenIdCounter() const { return tokenIdCounter_.get(); } /// Getter for the maxTokens_ - uint64_t maxTokens() const { - return maxTokens_.get(); - } + uint64_t maxTokens() const { return maxTokens_.get(); } /// Getter for the totalSupply_ - uint64_t totalSupply() const { - return totalSupply_.get(); - } + uint64_t totalSupply() const { return totalSupply_.get(); } /// Register contract class via ContractReflectionInterface. static void registerContract() { @@ -111,9 +104,4 @@ class ERC721Test : public ERC721 { DBBatch dump() const override; }; - - - - - #endif // ERC721_TEST diff --git a/src/contract/templates/ownable.cpp b/src/contract/templates/ownable.cpp index 92c7446e..5e8657ee 100644 --- a/src/contract/templates/ownable.cpp +++ b/src/contract/templates/ownable.cpp @@ -61,17 +61,15 @@ void Ownable::checkOwner_() const { } void Ownable::transferOwnership_(const Address& newOwner) { + Address prevOwner = this->owner_.get(); this->owner_ = newOwner; + this->ownershipTransferred(prevOwner, newOwner); } -void Ownable::onlyOwner() const { - this->checkOwner_(); -} +void Ownable::onlyOwner() const { this->checkOwner_(); } -Address Ownable::owner() const { - return this->owner_.get(); -} +Address Ownable::owner() const { return this->owner_.get(); } void Ownable::renounceOwnership() { this->onlyOwner(); @@ -80,8 +78,7 @@ void Ownable::renounceOwnership() { void Ownable::transferOwnership(const Address& newOwner) { this->onlyOwner(); - if (newOwner == Address()) { - throw DynamicException("Ownable: new owner is the zero address"); - } + if (newOwner == Address()) throw DynamicException("Ownable: new owner is the zero address"); this->transferOwnership_(newOwner); } + diff --git a/src/contract/templates/pebble.cpp b/src/contract/templates/pebble.cpp index 5f5555d4..afee63fe 100644 --- a/src/contract/templates/pebble.cpp +++ b/src/contract/templates/pebble.cpp @@ -254,7 +254,7 @@ std::string Pebble::rarityToString(const Rarity& rarity) const { return ret; } -void Pebble::onlyAuthorizer() const { +void Pebble::onlyAuthorizer() { if (this->getCaller() != this->authorizer_) { throw DynamicException("Pebble: caller is not the authorizer"); } diff --git a/src/contract/templates/pebble.h b/src/contract/templates/pebble.h index ea118401..34d3bdac 100644 --- a/src/contract/templates/pebble.h +++ b/src/contract/templates/pebble.h @@ -121,7 +121,7 @@ class Pebble : public virtual ERC721URIStorage, public virtual Ownable { * Check if contract caller is the authorizer. * @throw DynamicException if caller is not the authorizer. */ - void onlyAuthorizer() const; + void onlyAuthorizer(); /** * Change the authorizer address. diff --git a/src/contract/templates/simplecontract.cpp b/src/contract/templates/simplecontract.cpp index 364dbabc..35180061 100644 --- a/src/contract/templates/simplecontract.cpp +++ b/src/contract/templates/simplecontract.cpp @@ -86,6 +86,9 @@ void SimpleContract::setNumber(const uint256_t& argNumber) { } void SimpleContract::setNumbers(const std::vector& argNumber) { + if (this->getCaller() != this->getContractCreator()) { + throw DynamicException("Only contract creator can call this function."); + } this->number_ = 0; for (const auto& number : argNumber) this->number_ += number; this->numberChanged(this->number_.get()); @@ -222,10 +225,11 @@ void SimpleContract::registerContractFunctions() { } DBBatch SimpleContract::dump() const { - DBBatch dbBatch; + DBBatch dbBatch = BaseContract::dump(); dbBatch.push_back(StrConv::stringToBytes("name_"), StrConv::stringToBytes(this->name_.get()), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("number_"), UintConv::uint256ToBytes(this->number_.get()), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("tuple_name"), StrConv::stringToBytes(get<0>(this->tuple_)), this->getDBPrefix()); dbBatch.push_back(StrConv::stringToBytes("tuple_number"), UintConv::uint256ToBytes(get<1>(this->tuple_)), this->getDBPrefix()); return dbBatch; } + diff --git a/src/contract/variables/reentrancyguard.h b/src/contract/variables/reentrancyguard.h index 40e58089..3537e0a3 100644 --- a/src/contract/variables/reentrancyguard.h +++ b/src/contract/variables/reentrancyguard.h @@ -8,6 +8,8 @@ See the LICENSE.txt file in the project root for more information. #ifndef REENTRANCY_GUARD_H #define REENTRANCY_GUARD_H +#include "../../utils/dynamicexception.h" + /** * RAII object used to prevent reentrancy attacks, similar to std::unique_lock or std::shared_lock. * It is meant to be used within the first line of the function you want to protect against reentrancy attacks. diff --git a/src/net/p2p/encoding.h b/src/net/p2p/encoding.h index 14405bf9..c43ae3ee 100644 --- a/src/net/p2p/encoding.h +++ b/src/net/p2p/encoding.h @@ -556,9 +556,10 @@ namespace P2P { */ class Message { private: - /// The internal message data to be read/written, stored as bytes. - /// Sessions has directly access to it - /// As it can use the vector for its buffer. + /** + * The internal message data to be read/written, stored as bytes. + * Sessions has directly access to it as it can use the vector for its buffer. + */ Bytes rawMessage_; /// Assignment operator. diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index cabccc60..2c3c9774 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,4 +1,5 @@ set(UTILS_HEADERS + ${CMAKE_SOURCE_DIR}/src/utils/clargs.h ${CMAKE_SOURCE_DIR}/src/utils/db.h ${CMAKE_SOURCE_DIR}/src/utils/utils.h ${CMAKE_SOURCE_DIR}/src/utils/strings.h @@ -21,6 +22,7 @@ set(UTILS_HEADERS ) set(UTILS_SOURCES + ${CMAKE_SOURCE_DIR}/src/utils/clargs.cpp ${CMAKE_SOURCE_DIR}/src/utils/db.cpp ${CMAKE_SOURCE_DIR}/src/utils/utils.cpp ${CMAKE_SOURCE_DIR}/src/utils/strings.cpp diff --git a/src/utils/clargs.cpp b/src/utils/clargs.cpp new file mode 100644 index 00000000..b3b850ea --- /dev/null +++ b/src/utils/clargs.cpp @@ -0,0 +1,127 @@ +/* +Copyright (c) [2023-2024] [AppLayer Developers] + +This software is distributed under the MIT License. +See the LICENSE.txt file in the project root for more information. +*/ + +#include "clargs.h" + +ProcessOptions parseCommandLineArgs(int argc, char* argv[], [[maybe_unused]] BDKTool tool) { + ProcessOptions opt; + try { + boost::program_options::options_description desc("Allowed options"); + desc.add_options() + ("help,h", + "Print help message and exit") + ("loglevel,l", boost::program_options::value(), + "Set the log level ([X]TRACE, [T]RACE, [D]EBUG, [I]NFO, [W]ARNING, [E]RROR, [F]ATAL, [N]ONE)") + ("loglinelimit", boost::program_options::value(), + "Set the log line limit (# of lines per file); 0 = no limit") + ("logfilelimit", boost::program_options::value(), + "Set the log file limit (# of files); 0 = no limit") + ("netthreads", boost::program_options::value(), + "Set ManagerBase::netThreads_ (main IO thread count)") + ; + + boost::program_options::variables_map vm; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::notify(vm); + + if (vm.count("help")) { + std::cout << desc << "\n"; + exit(0); + } + + if (vm.count("loglevel")) { + opt.logLevel = vm["loglevel"].as(); + } + + if (vm.count("loglinelimit")) { + opt.logLineLimit = vm["loglinelimit"].as(); + if (opt.logLineLimit < 0) { + std::cerr << "ERROR: --loglinelimit must be >= 0\n"; + return {}; + } + } + + if (vm.count("logfilelimit")) { + opt.logFileLimit = vm["logfilelimit"].as(); + if (opt.logFileLimit < 0) { + std::cerr << "ERROR: --logfilelimit must be >= 0\n"; + return {}; + } + } + + if (vm.count("netthreads")) { + opt.netThreads = vm["netthreads"].as(); + if (opt.netThreads < 1) { + std::cerr << "ERROR: --netthreads must be >= 1\n"; + return {}; + } + } + } catch (std::exception& e) { + std::cout << "ERROR: parseCommandLineArgs(): " << e.what() << "\n"; + return {}; + } catch (...) { + std::cout << "ERROR: parseCommandLineArgs(): Unknown exception\n"; + return {}; + } + + opt.valid = true; + return opt; +} + +bool applyProcessOptions(ProcessOptions& opt) { + if (!opt.valid) { + std::cout << "ERROR: Invalid command-line arguments" << std::endl; + return false; + } + + std::transform(opt.logLevel.begin(), opt.logLevel.end(), opt.logLevel.begin(), ::toupper); + + if (opt.logLevel == "X") { opt.logLevel = "XTRACE"; } + else if (opt.logLevel == "T") { opt.logLevel = "TRACE"; } + else if (opt.logLevel == "D") { opt.logLevel = "DEBUG"; } + else if (opt.logLevel == "I") { opt.logLevel = "INFO"; } + else if (opt.logLevel == "W") { opt.logLevel = "WARNING"; } + else if (opt.logLevel == "E") { opt.logLevel = "ERROR"; } + else if (opt.logLevel == "F") { opt.logLevel = "FATAL"; } + else if (opt.logLevel == "N") { opt.logLevel = "NONE"; } + + if (opt.logLevel == "") { /* Do nothing */ } + else if (opt.logLevel == "XTRACE") { Logger::setLogLevel(LogType::XTRACE); } + else if (opt.logLevel == "TRACE") { Logger::setLogLevel(LogType::TRACE); } + else if (opt.logLevel == "DEBUG") { Logger::setLogLevel(LogType::DEBUG); } + else if (opt.logLevel == "INFO") { Logger::setLogLevel(LogType::INFO); } + else if (opt.logLevel == "WARNING") { Logger::setLogLevel(LogType::WARNING); } + else if (opt.logLevel == "ERROR") { Logger::setLogLevel(LogType::ERROR); } + else if (opt.logLevel == "FATAL") { Logger::setLogLevel(LogType::FATAL); } + else if (opt.logLevel == "NONE") { Logger::setLogLevel(LogType::NONE); } + else { + std::cout << "ERROR: Invalid log level requested: " << opt.logLevel << std::endl; + return false; + } + + if (opt.logLevel != "") { + std::cout << "Log level set to " << opt.logLevel << std::endl; + } + + if (opt.logLineLimit >= 0) { + Logger::setLogLineLimit(opt.logLineLimit); + std::cout << "Log line limit set to " << opt.logLineLimit << std::endl; + } + + if (opt.logFileLimit >= 0) { + Logger::setLogFileLimit(opt.logFileLimit); + std::cout << "Log file limit set to " << opt.logFileLimit << std::endl; + } + + if (opt.netThreads >= 0) { // negative number signals unset; 0 is invalid, but somehow it was set to that value + P2P::ManagerBase::setNetThreads(opt.netThreads); + std::cout << "ManagerBase::netThreads_ set to " << opt.netThreads << std::endl; + } + + return true; +} + diff --git a/src/utils/clargs.h b/src/utils/clargs.h index 79bd4ef4..64bf2294 100644 --- a/src/utils/clargs.h +++ b/src/utils/clargs.h @@ -37,128 +37,13 @@ struct ProcessOptions { * @param tool Which tool is taking args; can be used to determine which args are available. * @return A ProcessOptions struct with the result of argument parsing. */ -ProcessOptions parseCommandLineArgs(int argc, char* argv[], [[maybe_unused]] BDKTool tool) { - ProcessOptions opt; - try { - boost::program_options::options_description desc("Allowed options"); - desc.add_options() - ("help,h", - "Print help message and exit") - ("loglevel,l", boost::program_options::value(), - "Set the log level ([T]RACE, [D]EBUG, [I]NFO, [W]ARNING, [E]RROR, [N]ONE)") - ("loglinelimit", boost::program_options::value(), - "Set the log line limit (# of lines per file); 0 = no limit") - ("logfilelimit", boost::program_options::value(), - "Set the log file limit (# of files); 0 = no limit") - ("netthreads", boost::program_options::value(), - "Set ManagerBase::netThreads_ (main IO thread count)") - ; - - boost::program_options::variables_map vm; - boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); - boost::program_options::notify(vm); - - if (vm.count("help")) { - std::cout << desc << "\n"; - exit(0); - } - - if (vm.count("loglevel")) { - opt.logLevel = vm["loglevel"].as(); - } - - if (vm.count("loglinelimit")) { - opt.logLineLimit = vm["loglinelimit"].as(); - if (opt.logLineLimit < 0) { - std::cerr << "ERROR: --loglinelimit must be >= 0\n"; - return {}; - } - } - - if (vm.count("logfilelimit")) { - opt.logFileLimit = vm["logfilelimit"].as(); - if (opt.logFileLimit < 0) { - std::cerr << "ERROR: --logfilelimit must be >= 0\n"; - return {}; - } - } - - if (vm.count("netthreads")) { - opt.netThreads = vm["netthreads"].as(); - if (opt.netThreads < 1) { - std::cerr << "ERROR: --netthreads must be >= 1\n"; - return {}; - } - } - - } catch (std::exception& e) { - std::cout << "ERROR: parseCommandLineArgs(): " << e.what() << "\n"; - return {}; - } catch (...) { - std::cout << "ERROR: parseCommandLineArgs(): Unknown exception\n"; - return {}; - } - - opt.valid = true; - return opt; -} +ProcessOptions parseCommandLineArgs(int argc, char* argv[], [[maybe_unused]] BDKTool tool); /** * This function provides a default way to apply a ProcessOptions object. * @param opt Process Options object to apply. * @return `true` if no error, `false` otherwise. */ -bool applyProcessOptions(ProcessOptions& opt) { - if (!opt.valid) { - std::cout << "ERROR: Invalid command-line arguments" << std::endl; - return false; - } - - std::transform(opt.logLevel.begin(), opt.logLevel.end(), opt.logLevel.begin(), ::toupper); - - if (opt.logLevel == "X") { opt.logLevel = "XTRACE"; } - else if (opt.logLevel == "T") { opt.logLevel = "TRACE"; } - else if (opt.logLevel == "D") { opt.logLevel = "DEBUG"; } - else if (opt.logLevel == "I") { opt.logLevel = "INFO"; } - else if (opt.logLevel == "W") { opt.logLevel = "WARNING"; } - else if (opt.logLevel == "E") { opt.logLevel = "ERROR"; } - else if (opt.logLevel == "F") { opt.logLevel = "FATAL"; } - else if (opt.logLevel == "N") { opt.logLevel = "NONE"; } - - if (opt.logLevel == "") { /* Do nothing */ } - else if (opt.logLevel == "XTRACE") { Logger::setLogLevel(LogType::XTRACE); } - else if (opt.logLevel == "TRACE") { Logger::setLogLevel(LogType::TRACE); } - else if (opt.logLevel == "DEBUG") { Logger::setLogLevel(LogType::DEBUG); } - else if (opt.logLevel == "INFO") { Logger::setLogLevel(LogType::INFO); } - else if (opt.logLevel == "WARNING") { Logger::setLogLevel(LogType::WARNING); } - else if (opt.logLevel == "ERROR") { Logger::setLogLevel(LogType::ERROR); } - else if (opt.logLevel == "FATAL") { Logger::setLogLevel(LogType::FATAL); } - else if (opt.logLevel == "NONE") { Logger::setLogLevel(LogType::NONE); } - else { - std::cout << "ERROR: Invalid log level requested: " << opt.logLevel << std::endl; - return false; - } - - if (opt.logLevel != "") { - std::cout << "Log level set to " << opt.logLevel << std::endl; - } - - if (opt.logLineLimit >= 0) { - Logger::setLogLineLimit(opt.logLineLimit); - std::cout << "Log line limit set to " << opt.logLineLimit << std::endl; - } - - if (opt.logFileLimit >= 0) { - Logger::setLogFileLimit(opt.logFileLimit); - std::cout << "Log file limit set to " << opt.logFileLimit << std::endl; - } - - if (opt.netThreads >= 0) { // negative number signals unset; 0 is invalid, but somehow it was set to that value - P2P::ManagerBase::setNetThreads(opt.netThreads); - std::cout << "ManagerBase::netThreads_ set to " << opt.netThreads << std::endl; - } - - return true; -} +bool applyProcessOptions(ProcessOptions& opt); #endif // CLARGS_H diff --git a/src/utils/hex.cpp b/src/utils/hex.cpp index fb5e71ed..263b7240 100644 --- a/src/utils/hex.cpp +++ b/src/utils/hex.cpp @@ -16,6 +16,7 @@ Hex::Hex(const std::string_view value, bool strict) : strict_(strict) { } else { if (ret[0] == '0' && (ret[1] == 'x' || ret[1] == 'X')) ret.erase(0, 2); } + if (ret[1] == 'X') ret[1] = 'x'; // Always fix "0X" back to "0x" if (!Hex::isValid(ret, strict)) throw DynamicException("Invalid Hex string at constructor"); this->hex_ = std::move(ret); } @@ -30,6 +31,7 @@ Hex::Hex(std::string&& value, bool strict) : hex_(std::move(value)), strict_(str this->hex_.erase(0, 2); } } + if (this->hex_[1] == 'X') this->hex_[1] = 'x'; // Always fix "0X" back to "0x" if (!Hex::isValid(this->hex_, strict)) throw DynamicException("Invalid Hex string at constructor"); } @@ -75,6 +77,7 @@ Hex Hex::fromUTF8(std::string_view str, bool strict) { std::string Hex::forRPC() const { std::string retHex = this->hex_; + if (retHex[1] == 'X') retHex[1] = 'x'; // Always fix "0X" back to "0x" if (retHex[0] != '0' && retHex[1] != 'x') retHex.insert(0, "0x"); if (retHex == "0x") { retHex ="0x0"; return retHex; } // Check for leading zeroes! @@ -85,20 +88,22 @@ std::string Hex::forRPC() const { Bytes Hex::toBytes(const std::string_view hex) { Bytes ret; - size_t i = (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) ? 2 : 0; + std::string hexStr(hex); + if (hexStr[1] == 'X') hexStr[1] = 'x'; // Always fix "0X" back to "0x" + size_t i = (hexStr.starts_with("0x")) ? 2 : 0; const static std::string_view filter("0123456789abcdefABCDEF"); - if (auto pos = hex.find_first_not_of(filter, i); pos != std::string::npos) { + if (auto pos = hexStr.find_first_not_of(filter, i); pos != std::string::npos) { throw DynamicException(std::string(__func__) + ": Invalid hex string: " - + std::string(hex) + " filter: " + std::string(filter) + " at pos: " + std::to_string(pos)); + + hexStr + " filter: " + std::string(filter) + " at pos: " + std::to_string(pos)); } - if (hex.size() % 2) { - int h = Hex::toInt(hex[i]); + if (hexStr.size() % 2) { + int h = Hex::toInt(hexStr[i]); i++; ret.emplace_back(uint8_t(h)); } - for (; i < hex.size(); i += 2) { - int h = Hex::toInt(hex[i]); - int l = Hex::toInt(hex[i + 1]); + for (; i < hexStr.size(); i += 2) { + int h = Hex::toInt(hexStr[i]); + int l = Hex::toInt(hexStr[i + 1]); ret.emplace_back(uint8_t(h * 16 + l)); } return ret; @@ -122,8 +127,10 @@ Bytes Hex::bytes() const { } Hex& Hex::operator+=(const std::string& hex) { - if (Hex::isValid(hex, (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')))) { - this->hex_ += (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) ? hex.substr(2) : hex; + bool strict = (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')); + if (Hex::isValid(hex, strict)) { + this->hex_ += strict ? hex.substr(2) : hex; + if (this->hex_[1] == 'X') this->hex_[1] = 'x'; // Always fix "0X" back to "0x" } else { throw DynamicException("Invalid Hex concat operation"); } diff --git a/src/utils/options.h.in b/src/utils/options.h.in index daf0cc74..1e53cae1 100644 --- a/src/utils/options.h.in +++ b/src/utils/options.h.in @@ -38,7 +38,7 @@ See the LICENSE.txt file in the project root for more information. * "0xbec7b74f70c151707a0bfb20fe3767c6e65499e0" * ], * "timestamp" : 1656356646000000, - * "signer" : "0x4d48bdf34d65ef2bed2e4ee9020a7d3162b494ac31d3088153425f286f3d3c8c" + * "signer" : "0x4d48bdf34d65ef2bed2e4ee9020a7d3162b494ac31d3088153425f286f3d3c8c", * "balances": [ * { "address": "0x00dead00665771855a34155f5e7405489df2c3c6", "balance": "1000000000000000000000" } * ] diff --git a/src/utils/strings.h b/src/utils/strings.h index c8557dea..598bb6ee 100644 --- a/src/utils/strings.h +++ b/src/utils/strings.h @@ -118,6 +118,7 @@ template class FixedBytes { * @param pos (optional) Index to start getting chars from. Defaults to the start of the string. * @param len (optional) Number of chars to get. Defaults to the whole string. * @return A string view of the data, in bytes. + * @throws std::out_of_range if pos goes beyond the length. */ inline bytes::View view(size_t pos = 0, size_t len = N) const { auto real_len = std::min(len, N - pos); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 036dd100..d72fba75 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,7 @@ set (TESTS_SOURCES "" ${CMAKE_SOURCE_DIR}/tests/utils/block.cpp ${CMAKE_SOURCE_DIR}/tests/utils/block_throw.cpp + ${CMAKE_SOURCE_DIR}/tests/utils/clargs.cpp ${CMAKE_SOURCE_DIR}/tests/utils/db.cpp ${CMAKE_SOURCE_DIR}/tests/utils/ecdsa.cpp ${CMAKE_SOURCE_DIR}/tests/utils/evmcconv.cpp @@ -61,6 +62,7 @@ set (TESTS_SOURCES # TODO: change this when we take out boost multiprecision from the project for C23 BigInt if (BUILD_VARIABLES_TESTS) list(APPEND TESTS_SOURCES + ${CMAKE_SOURCE_DIR}/tests/contract/variables/reentrancyguard.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safeaddress.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safearray.cpp ${CMAKE_SOURCE_DIR}/tests/contract/variables/safebool.cpp diff --git a/tests/contract/abi.cpp b/tests/contract/abi.cpp index 812d697c..82e4ea3f 100644 --- a/tests/contract/abi.cpp +++ b/tests/contract/abi.cpp @@ -16,8 +16,7 @@ TEST_CASE("ABI Namespace", "[contract][abi]") { // This should cover all types for functor, including nested. SECTION("Encode Functor for FunnsiesFunc(std::tuple>, std::string, uint256_t, std::vector, std::string>>> arg)") { - struct Test - { + struct Test { void FunnsiesFunc(const std::tuple< std::vector< std::tuple>, @@ -1314,25 +1313,42 @@ TEST_CASE("ABI Namespace", "[contract][abi]") { "00000000000000000000000000000000000000000000000000016201a9fce5dd" ); - auto decodedData = ABI::Decoder::decodeData>(ABI); - std::vector decodedVector = std::get<0>(decodedData); + auto decodedData = ABI::Decoder::decodeData>(ABI); + std::vector decodedVector = std::get<0>(decodedData); - REQUIRE(decodedVector[0] == uint256_t(2312415123141231511)); - REQUIRE(decodedVector[1] == uint256_t(2734526262645)); - REQUIRE(decodedVector[2] == uint256_t(389234263123421)); - } + REQUIRE(decodedVector[0] == uint256_t(2312415123141231511)); + REQUIRE(decodedVector[1] == uint256_t(2734526262645)); + REQUIRE(decodedVector[2] == uint256_t(389234263123421)); + + // For coverage + Bytes ABIWrongSize = Hex::toBytes("0x" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000003" + "0000000000000000000000000000000000000000000000002017594d84130397" + "0000000000000000000000000000000000000000000000000000027cae776d75" + "00000000000000000000000000000000000000000000000000016201a9fce5" // missing last "dd" + ); + + REQUIRE_THROWS(ABI::Decoder::decodeData>(ABIWrongSize)); + } SECTION("Decode Int256") { Bytes ABI = Hex::toBytes("0x" "fffffffffffffffffdaf1854f62f44391b682b86c9106cac85300e6931c0f52e" ); - auto decodedData = ABI::Decoder::decodeData(ABI); - int256_t decodedInt = std::get<0>(decodedData); + auto decodedData = ABI::Decoder::decodeData(ABI); + int256_t decodedInt = std::get<0>(decodedData); - REQUIRE(decodedInt == int256_t("-56789012345678901234567890123456789012345678901234567890")); + REQUIRE(decodedInt == int256_t("-56789012345678901234567890123456789012345678901234567890")); - } + // For coverage + Bytes ABIWrongSize = Hex::toBytes("0x" + "fffffffffffffffffdaf1854f62f44391b682b86c9106cac85300e6931c0f5" // missing last "2e" + ); + + REQUIRE_THROWS(ABI::Decoder::decodeData(ABIWrongSize)); + } SECTION("Decode Int256 (Array)") { Bytes ABI = Hex::toBytes("0x" diff --git a/tests/contract/calltracer.cpp b/tests/contract/calltracer.cpp index ea0465d3..4ac9b47d 100644 --- a/tests/contract/calltracer.cpp +++ b/tests/contract/calltracer.cpp @@ -130,7 +130,7 @@ namespace TCallTracer { REQUIRE(callJson["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6"); REQUIRE(callJson["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); REQUIRE(callJson["value"] == "0x0"); - REQUIRE(callJson["gas"] == "0x8727"); + REQUIRE(callJson["gas"] == "0x9582"); REQUIRE(callJson["gasUsed"] == "0x6017"); REQUIRE(callJson["input"] == "0x1003e2d20000000000000000000000000000000000000000000000000000000000000021"); @@ -157,7 +157,7 @@ namespace TCallTracer { REQUIRE(callJson2["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6"); REQUIRE(callJson2["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); REQUIRE(callJson2["value"] == "0x0"); - REQUIRE(callJson2["gas"] == "0x88a5"); + REQUIRE(callJson2["gas"] == "0x973c"); REQUIRE(callJson2["gasUsed"] == "0x6195"); REQUIRE(callJson2["input"] == "0x4fa522db0000000000000000000000000000000000000000000000000000000000000042"); REQUIRE(callJson2["output"] == "0x0000000000000000000000000000000000000000000000000000000000000063"); @@ -313,7 +313,7 @@ namespace TCallTracer { REQUIRE(errorJson["from"] == "0x00dead00665771855a34155f5e7405489df2c3c6"); REQUIRE(errorJson["to"] == "0x6d48fdfe009e309dd5c4e69dec87365bfa0c8119"); REQUIRE(errorJson["value"] == "0x0"); - REQUIRE(errorJson["gas"] == "0x958b"); + REQUIRE(errorJson["gas"] == "0xa611"); REQUIRE(errorJson["gasUsed"] == "0x6e7b"); REQUIRE(errorJson["input"] == "0x7f3358bc0000000000000000000000005b41cef7f46a4a147e31150c3c5ffd077e54d0e100000000000000000000000000000000000000000000000000000000000001f5"); REQUIRE(errorJson.contains("calls")); @@ -323,7 +323,7 @@ namespace TCallTracer { REQUIRE(errorJsonCall["from"] == "0x6d48fdfe009e309dd5c4e69dec87365bfa0c8119"); REQUIRE(errorJsonCall["to"] == "0x5b41cef7f46a4a147e31150c3c5ffd077e54d0e1"); REQUIRE(errorJsonCall["value"] == "0x0"); - REQUIRE(errorJsonCall["gas"] == "0x8f18"); + REQUIRE(errorJsonCall["gas"] == "0x9f5c"); REQUIRE(errorJsonCall["gasUsed"] == "0x16bb"); REQUIRE(errorJsonCall["input"] == "0x2e1a7d4d00000000000000000000000000000000000000000000000000000000000001f5"); REQUIRE(errorJsonCall["output"] == "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012496e73756666696369656e742066756e64730000000000000000000000000000"); diff --git a/tests/contract/createcontract.cpp b/tests/contract/createcontract.cpp index 49bd6ffd..b80ff692 100644 --- a/tests/contract/createcontract.cpp +++ b/tests/contract/createcontract.cpp @@ -81,7 +81,6 @@ class SolCreateContract { } }; - namespace TContractRandomness { TEST_CASE("Contract Create Another Contract", "[contract][contractcreate]") { auto contractCreateAnotherContractBytecode = Hex::toBytes("6080604052348015600e575f80fd5b5061079d8061001c5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c80630246c1bd1461004357806393d5252614610073578063fc858c24146100a3575b5f80fd5b61005d60048036038101906100589190610302565b6100d3565b60405161006a919061036c565b60405180910390f35b61008d600480360381019061008891906103b8565b61014d565b60405161009a919061036c565b60405180910390f35b6100bd60048036038101906100b891906103f6565b61020e565b6040516100ca919061036c565b60405180910390f35b5f80826040516100e2906102be565b6100ec9190610443565b604051809103905ff080158015610105573d5f803e3d5ffd5b5090505f8190507f8ffcdc15a283d706d38281f500270d8b5a656918f555de0913d7455e3e6bc1bf8160405161013b919061036c565b60405180910390a18092505050919050565b5f806040518060200161015f906102be565b6020820181038252601f19601f82011660405250846040516020016101849190610443565b6040516020818303038152906040526040516020016101a49291906104c8565b60405160208183030381529060405290505f838251602084015ff59050806101ca575f80fd5b7f83bd07281c54a9ab5bdb03b29af123d7033d997e633859dc80c70022d763771881856040516101fb9291906104fa565b60405180910390a1809250505092915050565b5f8060405180602001610220906102be565b6020820181038252601f19601f82011660405250836040516020016102459190610443565b6040516020818303038152906040526040516020016102659291906104c8565b60405160208183030381529060405290505f8180519060200120905060ff60f81b30868360405160200161029c94939291906105d1565b604051602081830303815290604052805190602001205f1c9250505092915050565b6101498061061f83390190565b5f80fd5b5f819050919050565b6102e1816102cf565b81146102eb575f80fd5b50565b5f813590506102fc816102d8565b92915050565b5f60208284031215610317576103166102cb565b5b5f610324848285016102ee565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6103568261032d565b9050919050565b6103668161034c565b82525050565b5f60208201905061037f5f83018461035d565b92915050565b5f819050919050565b61039781610385565b81146103a1575f80fd5b50565b5f813590506103b28161038e565b92915050565b5f80604083850312156103ce576103cd6102cb565b5b5f6103db858286016102ee565b92505060206103ec858286016103a4565b9150509250929050565b5f806040838503121561040c5761040b6102cb565b5b5f610419858286016103a4565b925050602061042a858286016102ee565b9150509250929050565b61043d816102cf565b82525050565b5f6020820190506104565f830184610434565b92915050565b5f81519050919050565b5f81905092915050565b5f5b8381101561048d578082015181840152602081019050610472565b5f8484015250505050565b5f6104a28261045c565b6104ac8185610466565b93506104bc818560208601610470565b80840191505092915050565b5f6104d38285610498565b91506104df8284610498565b91508190509392505050565b6104f481610385565b82525050565b5f60408201905061050d5f83018561035d565b61051a60208301846104eb565b9392505050565b5f7fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b5f819050919050565b61056661056182610521565b61054c565b82525050565b5f8160601b9050919050565b5f6105828261056c565b9050919050565b5f61059382610578565b9050919050565b6105ab6105a68261034c565b610589565b82525050565b5f819050919050565b6105cb6105c682610385565b6105b1565b82525050565b5f6105dc8287610555565b6001820191506105ec828661059a565b6014820191506105fc82856105ba565b60208201915061060c82846105ba565b6020820191508190509594505050505056fe6080604052348015600e575f80fd5b506040516101493803806101498339818101604052810190602e9190606b565b805f81905550506091565b5f80fd5b5f819050919050565b604d81603d565b81146056575f80fd5b50565b5f815190506065816046565b92915050565b5f60208284031215607d57607c6039565b5b5f6088848285016059565b91505092915050565b60ac8061009d5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80633fa4f24514602a575b5f80fd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea2646970667358221220800668e87144b8625a7e59ac82528e013d51d6ed08562ba8b641f0a2e66c0f3764736f6c634300081a0033a264697066735822122057c5861494d2efb5d5f4f9c3be1a01d9bb2473e1b53e00c25866e51d52795c8c64736f6c634300081a0033"); @@ -114,6 +113,13 @@ namespace TContractRandomness { REQUIRE(newContractAddress == ContractHost::deriveContractAddress(1, createContractAddress)); Bytes init_code = Hex::toBytes("6080604052348015600e575f80fd5b506040516101493803806101498339818101604052810190602e9190606b565b805f81905550506091565b5f80fd5b5f819050919050565b604d81603d565b81146056575f80fd5b50565b5f815190506065816046565b92915050565b5f60208284031215607d57607c6039565b5b5f6088848285016059565b91505092915050565b60ac8061009d5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80633fa4f24514602a575b5f80fd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea2646970667358221220800668e87144b8625a7e59ac82528e013d51d6ed08562ba8b641f0a2e66c0f3764736f6c634300081a00330000000000000000000000000000000000000000000000000000000000000064"); REQUIRE(newContractAddressCreate2 == ContractHost::deriveContractAddress(createContractAddress, salt, init_code)); + + // For coverage + std::string contractCode = "6080604052348015600e575f80fd5b50600436106026575f3560e01c80633fa4f24514602a575b5f80fd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea2646970667358221220800668e87144b8625a7e59ac82528e013d51d6ed08562ba8b641f0a2e66c0f3764736f6c634300081a0033"; + REQUIRE(Hex::fromBytes(sdk.getState().getContractCode(newContractAddressCreate2)).get() == contractCode); + Address randAdd("0x1234567890123456789012345678901234567890", false); + REQUIRE(Hex::fromBytes(sdk.getState().getContractCode(randAdd)).get() == ""); } } } + diff --git a/tests/contract/dexv2.cpp b/tests/contract/dexv2.cpp index a4e0ff79..261afc3b 100644 --- a/tests/contract/dexv2.cpp +++ b/tests/contract/dexv2.cpp @@ -85,16 +85,25 @@ namespace TDEXV2 { TEST_CASE("DEXV2 Router Test", "[contract][dexv2][dexv2router]") { SECTION("Deploy + Dump DEXV2Router/Factory with a single pair") { + Address tokenA; + Address tokenB; Address wrapped; Address factory; Address router; + Address pair; std::unique_ptr options; { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testDEXV2RouterSinglePair"); + tokenA = sdk.deployContract(std::string("TokenA"), std::string("TKNA"), uint8_t(18), uint256_t("10000000000000000000000")); + tokenB = sdk.deployContract(std::string("TokenB"), std::string("TKNB"), uint8_t(18), uint256_t("10000000000000000000000")); wrapped = sdk.deployContract(std::string("WSPARQ"), std::string("WSPARQ"), uint8_t(18)); factory = sdk.deployContract(Address()); router = sdk.deployContract(factory, wrapped); + sdk.callFunction(factory, &DEXV2Factory::createPair, tokenA, tokenB); + pair = sdk.callViewFunction(factory, &DEXV2Factory::getPairByIndex, uint64_t(0)); for (const auto& contract : sdk.getState().getCppContracts()) { + if (contract.first == "TokenA") REQUIRE(contract.second == tokenA); + if (contract.first == "TokenB") REQUIRE(contract.second == tokenB); if (contract.first == "NativeWrapper") REQUIRE(contract.second == wrapped); if (contract.first == "DEXV2Factory") REQUIRE(contract.second == factory); if (contract.first == "DEXV2Router02") REQUIRE(contract.second == router); @@ -108,6 +117,8 @@ namespace TDEXV2 { // (The createNewEnvironment DELETES the DB if any is found) SDKTestSuite sdk(*options); for (const auto& contract : sdk.getState().getCppContracts()) { + if (contract.first == "TokenA") REQUIRE(contract.second == tokenA); + if (contract.first == "TokenB") REQUIRE(contract.second == tokenB); if (contract.first == "NativeWrapper") REQUIRE(contract.second == wrapped); if (contract.first == "DEXV2Factory") REQUIRE(contract.second == factory); if (contract.first == "DEXV2Router02") REQUIRE(contract.second == router); @@ -116,6 +127,23 @@ namespace TDEXV2 { // For coverage REQUIRE(sdk.callViewFunction(router, &DEXV2Router02::factory) == factory); REQUIRE(sdk.callViewFunction(router, &DEXV2Router02::wrappedNative) == wrapped); + REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::feeTo) == Address()); + REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::feeToSetter) == Address()); + REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::allPairsLength) == 1); + std::vector
allPairs = sdk.callViewFunction(factory, &DEXV2Factory::allPairs); + REQUIRE(allPairs.size() == 1); + REQUIRE(allPairs[0] == pair); + Address add("0x1234567890123456789012345678901234567890", false); + sdk.callFunction(factory, &DEXV2Factory::setFeeTo, add); + sdk.callFunction(factory, &DEXV2Factory::setFeeToSetter, add); + REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::feeTo) == add); + REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::feeToSetter) == add); + REQUIRE(sdk.callViewFunction(factory, &DEXV2Factory::getPair, tokenA, factory) == Address()); + + // For coverage (createPair) + REQUIRE_THROWS(sdk.callFunction(factory, &DEXV2Factory::createPair, pair, pair)); // Identical addresses + REQUIRE_THROWS(sdk.callFunction(factory, &DEXV2Factory::createPair, Address(), pair)); // Zero address + REQUIRE_THROWS(sdk.callFunction(factory, &DEXV2Factory::createPair, tokenA, tokenB)); // Pair exists } SECTION("Deploy DEXV2 and add/remove liquidity to token/token pair") { @@ -201,6 +229,9 @@ namespace TDEXV2 { tokenA, tokenB, uint256_t("5000000000000000000"), uint256_t(0), uint256_t("500000000000000000000"), owner, deadline // insufficient amountB (500) )); + // For coverage (sync and skim) + sdk.callFunction(pair, &DEXV2Pair::sync); + sdk.callFunction(pair, &DEXV2Pair::skim, owner); } SECTION("Deploy DEXV2 and add/remove liquidity to token/native pair") { diff --git a/tests/contract/erc20.cpp b/tests/contract/erc20.cpp index 5d031b8b..77c917a4 100644 --- a/tests/contract/erc20.cpp +++ b/tests/contract/erc20.cpp @@ -72,11 +72,16 @@ namespace TERC20 { Hash approveTx = sdk.callFunction(erc20, &ERC20::approve, to, uint256_t("500000000000000000")); allowance = sdk.callViewFunction(erc20, &ERC20::allowance, owner, to); REQUIRE(allowance == uint256_t("500000000000000000")); // "to" can now spend 0.5 TST + auto approveEvents = sdk.getEventsEmittedByTx(approveTx, &ERC20::Approval); REQUIRE(approveEvents.size() == 1); REQUIRE(std::get<0>(ABI::Decoder::decodeData
(approveEvents[0].getTopics()[1].asBytes())) == owner); REQUIRE(std::get<0>(ABI::Decoder::decodeData
(approveEvents[0].getTopics()[2].asBytes())) == to); REQUIRE(std::get<0>(ABI::Decoder::decodeData(approveEvents[0].getData())) == uint256_t("500000000000000000")); + + // Search for a non-existing spender (for coverage) + Address ghost("0x1234567890123456789012345678901234567890", false); + REQUIRE(sdk.callViewFunction(erc20, &ERC20::allowance, owner, ghost) == uint256_t(0)); } SECTION("ERC20 transferFrom()") { diff --git a/tests/contract/erc20wrapper.cpp b/tests/contract/erc20wrapper.cpp index ec967c47..fa5d0e85 100644 --- a/tests/contract/erc20wrapper.cpp +++ b/tests/contract/erc20wrapper.cpp @@ -16,7 +16,7 @@ See the LICENSE.txt file in the project root for more information. namespace TERC20Wrapper { TEST_CASE("ERC20Wrapper Class", "[contract][erc20wrapper]") { SECTION("ERC20Wrapper creation") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC20Creation"); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC20WrapperCreation"); Address erc20 = sdk.deployContract( std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000") ); @@ -29,7 +29,8 @@ namespace TERC20Wrapper { } SECTION("ERC20Wrapper deposit() and withdraw()") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC20DepositAndWithdraw"); + TestAccount randomAcc = TestAccount::newRandomAccount(); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC20WrapperDepositAndWithdraw", {randomAcc}); Address erc20 = sdk.deployContract( std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000") ); @@ -70,10 +71,22 @@ namespace TERC20Wrapper { REQUIRE(contractBal == uint256_t("250000000000000000")); REQUIRE(erc20Bal == uint256_t("750000000000000000")); REQUIRE(wrapperBal == uint256_t("250000000000000000")); + + // For coverage + Address randomToken(Utils::randBytes(20)); + Address randomUser(Utils::randBytes(20)); + REQUIRE(sdk.callViewFunction(erc20Wrapper, &ERC20Wrapper::getUserBalance, erc20, randomUser) == 0); + // Token not found + REQUIRE_THROWS(sdk.callFunction(erc20Wrapper, &ERC20Wrapper::withdraw, randomToken, uint256_t("250000000000000000"))); + // User not found + REQUIRE_THROWS(sdk.callFunction(erc20Wrapper, randomAcc, &ERC20Wrapper::withdraw, erc20, uint256_t("250000000000000000"))); + // Not enough balance + REQUIRE_THROWS(sdk.callFunction(erc20Wrapper, &ERC20Wrapper::withdraw, erc20, uint256_t("250000000000000000"))); } SECTION("ERC20Wrapper transferTo()") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC20TransferTo"); + TestAccount randomAcc = TestAccount::newRandomAccount(); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC20WrapperTransferTo", {randomAcc}); Address erc20 = sdk.deployContract( std::string("TestToken"), std::string("TST"), uint8_t(18), uint256_t("1000000000000000000") ); @@ -119,6 +132,17 @@ namespace TERC20Wrapper { REQUIRE(erc20Bal == uint256_t("500000000000000000")); REQUIRE(wrapperBal == uint256_t("250000000000000000")); REQUIRE(destBal == uint256_t("250000000000000000")); + + // For coverage + Address randomToken(Utils::randBytes(20)); + Address randomUser(Utils::randBytes(20)); + REQUIRE(sdk.callViewFunction(erc20Wrapper, &ERC20Wrapper::getUserBalance, erc20, randomUser) == 0); + // Token not found + REQUIRE_THROWS(sdk.callFunction(erc20Wrapper, &ERC20Wrapper::transferTo, randomToken, dest, uint256_t("250000000000000000"))); + // User not found + REQUIRE_THROWS(sdk.callFunction(erc20Wrapper, randomAcc, &ERC20Wrapper::transferTo, erc20, dest, uint256_t("250000000000000000"))); + // Not enough balance + REQUIRE_THROWS(sdk.callFunction(erc20Wrapper, &ERC20Wrapper::transferTo, erc20, dest, uint256_t("250000000000000000"))); } } } diff --git a/tests/contract/erc721test.cpp b/tests/contract/erc721test.cpp index 7dfb6cc8..5c58491e 100644 --- a/tests/contract/erc721test.cpp +++ b/tests/contract/erc721test.cpp @@ -36,6 +36,51 @@ namespace TERC721Test { REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::tokenIdCounter) == 0); } + SECTION("ERC721Test 1 Token (Mint + Dump + Burn + Transfer)") { + Address ERC721Address; + std::unique_ptr options; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestOneToken"); + ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); + // Mint exactly one token for the chain owner + auto mintTx = sdk.callFunction(ERC721Address, &ERC721Test::mint, sdk.getChainOwnerAccount().address); + auto mintEvents = sdk.getEventsEmittedByTx(mintTx, &ERC721Test::Transfer); + REQUIRE(mintEvents.size() == 1); + REQUIRE(std::get<0>(ABI::Decoder::decodeData
(mintEvents[0].getTopics()[1].asBytes())) == Address()); + REQUIRE(std::get<0>(ABI::Decoder::decodeData
(mintEvents[0].getTopics()[2].asBytes())) == sdk.getChainOwnerAccount().address); + REQUIRE(std::get<0>(ABI::Decoder::decodeData(mintEvents[0].getTopics()[3].asBytes())) == uint256_t(0)); + // Confirm that token is minted and owned by the chain owner + auto owner = sdk.callViewFunction(ERC721Address, &ERC721Test::ownerOf, uint256_t(0)); + REQUIRE(owner == sdk.getChainOwnerAccount().address); + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); + auto owner = sdk.callViewFunction(ERC721Address, &ERC721Test::ownerOf, uint256_t(0)); + REQUIRE(owner == sdk.getChainOwnerAccount().address); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::balanceOf, sdk.getChainOwnerAccount().address) == 1); + REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 1); + + // For coverage + // Try minting to zero address + REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::mint, Address())); + + // Try transferring to zero address and from wrong owner + Address add1("0x1234567890123456789012345678901234567890", false); + Address add2("0x0987654321098765432109876543210987654321", false); + REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::transferFrom, sdk.getChainOwnerAccount().address, Address(), uint256_t(0))); + REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::transferFrom, add1, add2, uint256_t(0))); + + // Burn the token and try to burn it again then transfer it + REQUIRE_NOTHROW(sdk.callFunction(ERC721Address, &ERC721Test::burn, uint256_t(0))); + REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::burn, uint256_t(0))); // Already burnt + REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::transferFrom, sdk.getChainOwnerAccount().address, add1, uint256_t(0))); + } + SECTION("ERC721Test Mint 100 Token Same Address") { SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testERC721TestMint100TokenSameAddress"); auto ERC721Address = sdk.deployContract(std::string("My Test NFT!"), std::string("NFT"), uint64_t(100)); @@ -54,6 +99,9 @@ namespace TERC721Test { } REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::balanceOf, sdk.getChainOwnerAccount().address) == 100); REQUIRE(sdk.callViewFunction(ERC721Address, &ERC721Test::totalSupply) == 100); + + // Try minting a 101th time (for coverage) + REQUIRE_THROWS(sdk.callFunction(ERC721Address, &ERC721Test::mint, sdk.getChainOwnerAccount().address)); } SECTION("ERC721Test Mint 100 Different Addresses") { diff --git a/tests/contract/pebble.cpp b/tests/contract/pebble.cpp index 7d18afdf..d9d03a4c 100644 --- a/tests/contract/pebble.cpp +++ b/tests/contract/pebble.cpp @@ -11,6 +11,8 @@ See the LICENSE.txt file in the project root for more information. #include "../sdktestsuite.hpp" +using Catch::Matchers::Equals; + namespace TPEBBLE { TEST_CASE("Pebble Class", "[contract][pebble]") { SECTION("Pebble creation + dump") { @@ -82,6 +84,16 @@ namespace TPEBBLE { REQUIRE_NOTHROW(sdk.callFunction(pebbleAddr, &Pebble::changeAuthorizer, authorizerAccount.address)); REQUIRE_NOTHROW(sdk.callFunction(pebbleAddr, 0, authorizerAccount, &Pebble::addMinter, minterAccount.address)); + // Check only authorizer can add minters + REQUIRE_THROWS(sdk.callFunction(pebbleAddr, 0, anotherAccount, &Pebble::addMinter, anotherAccount.address)); + // Add and remove another minter (for coverage) + REQUIRE_NOTHROW(sdk.callFunction(pebbleAddr, 0, authorizerAccount, &Pebble::addMinter, anotherAccount.address)); + REQUIRE_THROWS(sdk.callFunction(pebbleAddr, 0, anotherAccount, &Pebble::removeMinter, anotherAccount.address)); + REQUIRE_NOTHROW(sdk.callFunction(pebbleAddr, 0, authorizerAccount, &Pebble::removeMinter, anotherAccount.address)); + // Check minter account can actually mint and others don't + REQUIRE_NOTHROW(sdk.callViewFunction(pebbleAddr, &Pebble::canMint, minterAccount.address)); + REQUIRE_THROWS(sdk.callViewFunction(pebbleAddr, &Pebble::canMint, anotherAccount.address)); + auto mintTx = sdk.callFunction(pebbleAddr, 0, minterAccount, &Pebble::mintNFT, minterAccount.address, uint64_t(1)); auto events = sdk.getEventsEmittedByTxTup(mintTx, &Pebble::MintedNFT); @@ -144,7 +156,14 @@ namespace TPEBBLE { REQUIRE_THROWS(sdk.callFunction(pebbleAddr, 0, anotherAccount, &Pebble::setDiamondRarity, uint256_t(1))); // Check throw against non authorized mint REQUIRE_THROWS(sdk.callFunction(pebbleAddr, 0, anotherAccount, &Pebble::mintNFT, anotherAccount.address, uint64_t(1))); + // Check throw against excessive minting (> 25 tokens at once) + REQUIRE_THROWS(sdk.callFunction(pebbleAddr, 0, minterAccount, &Pebble::mintNFT, minterAccount.address, uint64_t(30))); opts = std::make_unique(sdk.getOptions()); + // Check unknown token rarity + REQUIRE(sdk.callViewFunction(pebbleAddr, &Pebble::getTokenRarity, uint256_t(99999999)) == "Unknown"); + // Check token URIs + REQUIRE_THAT(sdk.callViewFunction(pebbleAddr, &Pebble::tokenURI, uint256_t(1)), Equals("https://s3.amazonaws.com/com.applayer.pebble/Diamond.json")); + REQUIRE_THAT(sdk.callViewFunction(pebbleAddr, &Pebble::tokenURI, uint256_t(99999999)), Equals("")); } auto sdk = SDKTestSuite(*opts); diff --git a/tests/contract/simplecontract.cpp b/tests/contract/simplecontract.cpp index fd34d7d3..2aa475f1 100644 --- a/tests/contract/simplecontract.cpp +++ b/tests/contract/simplecontract.cpp @@ -13,17 +13,31 @@ See the LICENSE.txt file in the project root for more information. namespace TSimpleContract { TEST_CASE("SimpleContract class", "[contract][simplecontract]") { - SECTION("SimpleContract creation") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractCreation"); - Address simpleContract = sdk.deployContract( - std::string("TestName"), uint256_t(19283187581), - std::make_tuple(std::string("TupleName"), uint256_t(987654321)) - ); - std::string name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); - uint256_t number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); - std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); - REQUIRE(name == "TestName"); - REQUIRE(number == 19283187581); + SECTION("SimpleContract creation and dump") { + Address simpleContract; + std::unique_ptr options; + std::tuple tuple; + { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractCreation"); + simpleContract = sdk.deployContract( + std::string("TestName"), uint256_t(19283187581), + std::make_tuple(std::string("TupleName"), uint256_t(987654321)) + ); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); + tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); + REQUIRE(std::get<0>(tuple) == "TupleName"); + REQUIRE(std::get<1>(tuple) == 987654321); + // Dump to database + options = std::make_unique(sdk.getOptions()); + sdk.getState().saveToDB(); + } + // SDKTestSuite should automatically load the state from the DB if we construct it with an Options object + // (The createNewEnvironment DELETES the DB if any is found) + SDKTestSuite sdk(*options); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); + tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); REQUIRE(std::get<0>(tuple) == "TupleName"); REQUIRE(std::get<1>(tuple) == 987654321); } @@ -35,22 +49,19 @@ namespace TSimpleContract { std::make_tuple(std::string("TupleName"), uint256_t(987654321)) ); - std::string name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); - uint256_t number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); + REQUIRE(sdk.callViewFunction(simpleContract, static_cast(&SimpleContract::getNumber), uint256_t(1)) == 19283187582); std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); - REQUIRE(name == "TestName"); - REQUIRE(number == 19283187581); REQUIRE(std::get<0>(tuple) == "TupleName"); REQUIRE(std::get<1>(tuple) == 987654321); Hash nameTx = sdk.callFunction(simpleContract, &SimpleContract::setName, std::string("TryThisName")); Hash numberTx = sdk.callFunction(simpleContract, &SimpleContract::setNumber, uint256_t("918258172319061203818967178162134821351")); Hash tupleTx = sdk.callFunction(simpleContract, &SimpleContract::setTuple, std::make_tuple(std::string("AnotherName"), uint256_t(999999999))); - name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); - number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TryThisName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == uint256_t("918258172319061203818967178162134821351")); tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); - REQUIRE(name == "TryThisName"); - REQUIRE(number == uint256_t("918258172319061203818967178162134821351")); REQUIRE(std::get<0>(tuple) == "AnotherName"); REQUIRE(std::get<1>(tuple) == 999999999); @@ -68,37 +79,145 @@ namespace TSimpleContract { REQUIRE(tupleEvent.size() == 1); } - SECTION("SimpleContract setNamesAndNumbers and setNamesAndNumbersInTuple") { - SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndNumbersInTuple"); + SECTION("SimpleContract setNames and setNumbers") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndSetNumbers"); Address simpleContract = sdk.deployContract( std::string("TestName"), uint256_t(19283187581), std::make_tuple(std::string("TupleName"), uint256_t(987654321)) ); - std::string name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); - uint256_t number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); + std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); + REQUIRE(std::get<0>(tuple) == "TupleName"); + REQUIRE(std::get<1>(tuple) == 987654321); + + Hash namesTx = sdk.callFunction(simpleContract, &SimpleContract::setNames, {std::string("Try"), std::string("This"), std::string("Name")}); + Hash numbersTx = sdk.callFunction(simpleContract, &SimpleContract::setNumbers, {uint256_t(111000000), uint256_t(222000), uint256_t(333)}); + std::vector names = sdk.callViewFunction(simpleContract, &SimpleContract::getNames, uint256_t(3)); + std::vector numbers = sdk.callViewFunction(simpleContract, &SimpleContract::getNumbers, uint256_t(3)); + REQUIRE(names.size() == 3); + REQUIRE(numbers.size() == 3); + for (int i = 0; i < 3; i++) { + REQUIRE(names[i] == "TryThisName"); + REQUIRE(numbers[i] == uint256_t(111222333)); + } + + auto nameEvent = sdk.getEventsEmittedByTx(namesTx, &SimpleContract::nameChanged, + std::make_tuple(EventParam("TryThisName")) + ); + auto numberEvent = sdk.getEventsEmittedByTx(numbersTx, &SimpleContract::numberChanged, + std::make_tuple(EventParam(uint256_t(111222333))) + ); + REQUIRE(nameEvent.size() == 1); + REQUIRE(numberEvent.size() == 1); + } + + SECTION("SimpleContract setNamesAndNumbers") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndNumbers"); + Address simpleContract = sdk.deployContract( + std::string("TestName"), uint256_t(19283187581), + std::make_tuple(std::string("TupleName"), uint256_t(987654321)) + ); + + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); - REQUIRE(name == "TestName"); - REQUIRE(number == 19283187581); REQUIRE(std::get<0>(tuple) == "TupleName"); REQUIRE(std::get<1>(tuple) == 987654321); std::vector names = {"111", "222", "333"}; std::vector numbers = {400, 300, 200, 100}; Hash namesAndNumbersTx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbers, names, numbers); - name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); - number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); - REQUIRE(name == "111222333"); - REQUIRE(number == 1000); + std::tuple nameAndNumber = sdk.callViewFunction(simpleContract, &SimpleContract::getNameAndNumber); + std::tuple, std::vector> namesAndNumbers = sdk.callViewFunction(simpleContract, &SimpleContract::getNamesAndNumbers, uint256_t(3)); + REQUIRE(std::get<0>(nameAndNumber) == "111222333"); + REQUIRE(std::get<1>(nameAndNumber) == 1000); + REQUIRE(std::get<0>(namesAndNumbers).size() == 3); + REQUIRE(std::get<1>(namesAndNumbers).size() == 3); + for (int i = 0; i < 3; i++) { + REQUIRE(std::get<0>(namesAndNumbers)[i] == "111222333"); + REQUIRE(std::get<1>(namesAndNumbers)[i] == 1000); + } + } + + SECTION("SimpleContract setNamesAndNumbersInTuple") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndNumbersInTuple"); + Address simpleContract = sdk.deployContract( + std::string("TestName"), uint256_t(19283187581), + std::make_tuple(std::string("TupleName"), uint256_t(987654321)) + ); + + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); + std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); + REQUIRE(std::get<0>(tuple) == "TupleName"); + REQUIRE(std::get<1>(tuple) == 987654321); std::vector> list = { {"555", 1000}, {"444", 500}, {"333", 250}, {"222", 150}, {"111", 100} }; Hash namesAndNumbersInTupleTx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbersInTuple, list); - name = sdk.callViewFunction(simpleContract, &SimpleContract::getName); - number = sdk.callViewFunction(simpleContract, &SimpleContract::getNumber); - REQUIRE(name == "555444333222111"); - REQUIRE(number == 2000); + std::vector> namesAndNumbers = sdk.callViewFunction(simpleContract, &SimpleContract::getNamesAndNumbersInTuple, uint256_t(3)); + REQUIRE(namesAndNumbers.size() == 3); + for (int i = 0; i < 3; i++) { + REQUIRE(std::get<0>(namesAndNumbers[i]) == "555444333222111"); + REQUIRE(std::get<1>(namesAndNumbers[i]) == 2000); + } + } + + SECTION("SimpleContract setNamesAndNumbersInArrayOfArrays") { + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractSetNamesAndNumbersInArrayOfArrays"); + Address simpleContract = sdk.deployContract( + std::string("TestName"), uint256_t(19283187581), + std::make_tuple(std::string("TupleName"), uint256_t(987654321)) + ); + + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getName) == "TestName"); + REQUIRE(sdk.callViewFunction(simpleContract, &SimpleContract::getNumber) == 19283187581); + std::tuple tuple = sdk.callViewFunction(simpleContract, &SimpleContract::getTuple); + REQUIRE(std::get<0>(tuple) == "TupleName"); + REQUIRE(std::get<1>(tuple) == 987654321); + + std::vector>> namesAndNumbers = { + {std::make_tuple("a", uint256_t(1)), std::make_tuple("b", uint256_t(2)), std::make_tuple("c", uint256_t(3))}, + {std::make_tuple("d", uint256_t(4)), std::make_tuple("e", uint256_t(5)), std::make_tuple("f", uint256_t(6))} + }; + Hash tx = sdk.callFunction(simpleContract, &SimpleContract::setNamesAndNumbersInArrayOfArrays, namesAndNumbers); + std::vector>> arr = sdk.callViewFunction(simpleContract, &SimpleContract::getNamesAndNumbersInArrayOfArrays, uint256_t(3)); + REQUIRE(arr.size() == 3); + for (int i = 0; i < 3; i++) { + REQUIRE(arr[i].size() == 3); + for (int j = 0; j < 3; j++) { + REQUIRE(std::get<0>(arr[i][j]) == "abcdef"); + REQUIRE(std::get<1>(arr[i][j]) == 21); + } + } + } + + SECTION("SimpleContract wrong caller (coverage)") { + TestAccount acc = TestAccount::newRandomAccount(); + SDKTestSuite sdk = SDKTestSuite::createNewEnvironment("testSimpleContractWrongCaller", {acc}); + Address simpleContract = sdk.deployContract( + std::string("TestName"), uint256_t(19283187581), + std::make_tuple(std::string("TupleName"), uint256_t(987654321)) + ); + + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setName, std::string("aaa"))); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setNumber, uint256_t(123))); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setTuple, std::make_tuple(std::string("bbb"), uint256_t(456)))); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setNames, {std::string("ccc"), std::string("ddd")})); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setNumbers, {uint256_t(789), uint256_t(987)})); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setNamesAndNumbers, + {std::string("eee"), std::string("fff")}, {uint256_t(654), uint256_t(321)} + )); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setNamesAndNumbersInTuple, { + std::make_tuple(std::string("gggg"), uint256_t(1234)), std::make_tuple(std::string("hhhh"), uint256_t(5678)) + })); + REQUIRE_THROWS(sdk.callFunction(simpleContract, acc, &SimpleContract::setNamesAndNumbersInArrayOfArrays, { + {std::make_tuple(std::string("hhhh"), uint256_t(9999)), std::make_tuple(std::string("iiii"), uint256_t(1111))}, + {std::make_tuple(std::string("jjjj"), uint256_t(1000)), std::make_tuple(std::string("kkkk"), uint256_t(9000))} + })); } } } diff --git a/tests/contract/variables/reentrancyguard.cpp b/tests/contract/variables/reentrancyguard.cpp new file mode 100644 index 00000000..6f7e8e37 --- /dev/null +++ b/tests/contract/variables/reentrancyguard.cpp @@ -0,0 +1,23 @@ +/* +Copyright (c) [2023-2024] [AppLayer Developers] + +This software is distributed under the MIT License. +See the LICENSE.txt file in the project root for more information. +*/ + +#include "../../src/libs/catch2/catch_amalgamated.hpp" +#include "../../src/contract/variables/reentrancyguard.h" + +namespace TReentrancyGuard { + TEST_CASE("ReentrancyGuard class", "[contract][variables][reentrancyguard]") { + SECTION("ReentrancyGuard constructor") { + bool lock = false; + ReentrancyGuard guard(lock); + REQUIRE(lock == true); + REQUIRE_THROWS(ReentrancyGuard(lock)); + guard.~ReentrancyGuard(); + REQUIRE(lock == false); + } + } +} + diff --git a/tests/contract/variables/safearray.cpp b/tests/contract/variables/safearray.cpp index 100530a0..e94d49ba 100644 --- a/tests/contract/variables/safearray.cpp +++ b/tests/contract/variables/safearray.cpp @@ -116,6 +116,21 @@ namespace TSafeArray { arrFill.commit(); for (std::size_t i = 0; i < arrFill.size(); i++) REQUIRE(arrFill[i] == 100); } + + SECTION("SafeArray Coverage") { + SafeArray arrFill({1,2,3,4,5}); + // copy_ != nullptr + arrFill.fill(200); + for (std::size_t i = 0; i < arrFill.size(); i++) REQUIRE(arrFill.at(i) == 200); + for (std::size_t i = 0; i < arrFill.size(); i++) REQUIRE(arrFill[i] == 200); + REQUIRE(arrFill.front() == 200); + REQUIRE(arrFill.back() == 200); + arrFill.fill(300); + for (std::size_t i = 0; i < arrFill.size(); i++) REQUIRE(arrFill.at(i) == 300); + for (std::size_t i = 0; i < arrFill.size(); i++) REQUIRE(arrFill[i] == 300); + REQUIRE(arrFill.front() == 300); + REQUIRE(arrFill.back() == 300); + } } } diff --git a/tests/contract/variables/safebytes.cpp b/tests/contract/variables/safebytes.cpp index d4bffc48..780f4ba1 100644 --- a/tests/contract/variables/safebytes.cpp +++ b/tests/contract/variables/safebytes.cpp @@ -69,6 +69,7 @@ namespace TSafeBytes { for (std::size_t i = 0; i < vec.size(); i++) REQUIRE(vec[i] == 0xFF); // assign with iterators vec2.assign(vec.cbegin(), vec.cend() - 2); + vec2.assign(vec.cbegin(), vec.cend() - 2); // extra call for coverage (copy != nullptr) vec2.revert(); REQUIRE(vec2.empty()); vec2.assign(vec.cbegin(), vec.cend() - 2); @@ -77,6 +78,7 @@ namespace TSafeBytes { for (std::size_t i = 0; i < vec2.size(); i++) REQUIRE(vec2[i] == 0xFF); // assign with ilist vec3.assign(ilist); + vec3.assign(ilist); // extra call for coverage (copy != nullptr) vec3.revert(); REQUIRE(vec3.empty()); vec3.assign(ilist); @@ -102,6 +104,13 @@ namespace TSafeBytes { REQUIRE(vec.at(2) == 0xFF); REQUIRE(vec.at(3) == 0xFF); REQUIRE(vec.at(4) == 0xFF); + // For coverage (copy != nullptr) + vec.assign(5, 0xFF); + REQUIRE(vec.at(0) == 0xFF); + REQUIRE(vec.at(1) == 0xFF); + REQUIRE(vec.at(2) == 0xFF); + REQUIRE(vec.at(3) == 0xFF); + REQUIRE(vec.at(4) == 0xFF); } SECTION("SafeBytes operator[]") { @@ -120,6 +129,13 @@ namespace TSafeBytes { REQUIRE(vec[2] == 0xFF); REQUIRE(vec[3] == 0xFF); REQUIRE(vec[4] == 0xFF); + // For coverage (copy != nullptr) + vec.assign(5, 0xFF); + REQUIRE(vec[0] == 0xFF); + REQUIRE(vec[1] == 0xFF); + REQUIRE(vec[2] == 0xFF); + REQUIRE(vec[3] == 0xFF); + REQUIRE(vec[4] == 0xFF); } SECTION("SafeBytes front and back") { @@ -136,6 +152,10 @@ namespace TSafeBytes { vec.back() = 0xFF; vec.commit(); REQUIRE(vec.back() == 0xFF); + // For coverage (copy != nullptr) + vec.assign(5, 0xAA); + REQUIRE(vec.front() == 0xAA); + REQUIRE(vec.back() == 0xAA); } // TODO: missing tests for cbegin, cend, crbegin and crend - check SafeUnorderedMap for more info @@ -153,6 +173,11 @@ namespace TSafeBytes { vec.commit(); REQUIRE(vec.empty()); REQUIRE(vec.size() == 0); + // For coverage (copy != nullptr) + vec.assign(5, 0xFF); + vec.clear(); + REQUIRE(vec.empty()); + REQUIRE(vec.size() == 0); } SECTION("SafeBytes insert") { @@ -215,6 +240,30 @@ namespace TSafeBytes { REQUIRE(*(vec.cend() - 3) == 0xA0); REQUIRE(*(vec.cend() - 2) == 0xB0); REQUIRE(*(vec.cend() - 1) == 0xC0); + // For coverage (undo != nullptr) + vec.clear(); + vec.assign(1, 0x02); + vec.commit(); + vec.insert(vec.cbegin(), 0x01); + uint8_t mv = 0x00; + vec.insert(vec.cbegin(), std::move(mv)); + vec.insert(vec.cend(), 1, 0x03); + std::vector vecIt({0x04,0x05,0x06}); + vec.insert(vec.cend(), vecIt.cbegin(), vecIt.cend()); + std::initializer_list ilistCov {0x07, 0x08, 0x09}; + vec.insert(vec.cend(), ilistCov); + // For coverage (copy != nullptr) + vec.clear(); + vec.commit(); + vec.assign(1, 0x02); + vec.insert(vec.cbegin(), 0x01); + uint8_t mv2 = 0x00; + vec.insert(vec.cbegin(), std::move(mv2)); + vec.insert(vec.cend(), 1, 0x03); + std::vector vecIt2({0x04,0x05,0x06}); + vec.insert(vec.cend(), vecIt2.cbegin(), vecIt2.cend()); + std::initializer_list ilistCov2 {0x07, 0x08, 0x09}; + vec.insert(vec.cend(), ilistCov2); } SECTION("SafeBytes emplace") { @@ -228,6 +277,13 @@ namespace TSafeBytes { vec.commit(); REQUIRE(vec.size() == 6); REQUIRE(*(vec.cbegin()) == 0x00); + // For coverage (undo != nullptr) + SafeBytes vec2({0x01,0x02,0x03,0x04,0x05}); + vec2.emplace(vec2.cbegin(), 0xFF); + vec2.emplace(vec2.cend(), 0xFF); + // For coverage (copy != nullptr) + vec2.clear(); + vec2.emplace(vec2.cbegin(), 0xFF); } SECTION("SafeBytes erase") { @@ -255,6 +311,16 @@ namespace TSafeBytes { REQUIRE(vec.size() == 2); REQUIRE(vec[0] == 0x01); REQUIRE(vec[1] == 0x05); + // For coverage (undo != nullptr) + SafeBytes vec2({0x00,0x01,0x02,0x03,0x04,0x05}); + vec2.erase(vec2.cbegin()); + vec2.erase(vec2.cbegin()); + vec2.erase(vec2.cbegin(), vec2.cend()); + // For coverage (copy != nullptr) + vec2.commit(); + vec2.assign(5, 0xFF); + vec2.erase(vec2.cbegin()); + vec2.erase(vec2.cbegin(), vec2.cend()); } SECTION("SafeBytes push_back, emplace_back and pop_back") { @@ -289,6 +355,22 @@ namespace TSafeBytes { for (int i = 0; i < 5; i++) vec.pop_back(); vec.commit(); REQUIRE((vec.size() == 1 && vec.back() == 0x01)); // vec = {0x01} + // For coverage (undo != nullptr) + SafeBytes vec2({0x01, 0x02, 0x03}); + vec2.push_back(0x00); + vec2.push_back(0xAA); + uint8_t mv = 0xBB; + vec2.push_back(std::move(mv)); + vec2.emplace_back(0xCC); + vec2.pop_back(); + // For coverage (copy != nullptr) + vec2.clear(); + vec2.push_back(0x00); + vec2.push_back(0xAA); + uint8_t mv3 = 0xBB; + vec2.push_back(std::move(mv2)); + vec2.emplace_back(0xCC); + vec2.pop_back(); } SECTION("SafeBytes resize") { @@ -383,6 +465,21 @@ namespace TSafeBytes { vec.resize(0, 0xFF); vec.commit(); REQUIRE(vec.empty()); + // For coverage (undo != nullptr) + vec.resize(2); + vec.resize(5); + vec.resize(1); + // For coverage (copy != nullptr) + vec.clear(); + vec.resize(10); + // Same but it's the other overload + vec.clear(); + vec.commit(); + vec.resize(2, 0x00); + vec.resize(5, 0x01); + vec.resize(1, 0x02); + vec.clear(); + vec.resize(10, 0x03); } } } diff --git a/tests/contract/variables/safeint_t.cpp b/tests/contract/variables/safeint_t.cpp index 5df3f0c3..902d93cd 100644 --- a/tests/contract/variables/safeint_t.cpp +++ b/tests/contract/variables/safeint_t.cpp @@ -67,7 +67,8 @@ template struct SafeIntTester { } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator+") { - SafeInt val(UnderlyingType(-42)); + SafeInt valPos(UnderlyingType(42)); + SafeInt valMin(UnderlyingType(-42)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); bool hadOver1 = false; @@ -75,15 +76,16 @@ template struct SafeIntTester { bool hadUnder1 = false; bool hadUnder2 = false; // catch over/underflow - try { valOver = valOver + UnderlyingType(1); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver = valOver + valOver; } catch (std::overflow_error& e) { hadOver2 = true; } - try { valUnder = valUnder + UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder1 = true; } - try { valUnder = valUnder + valUnder; } catch (std::underflow_error& e) { hadUnder2 = true; } + try { valOver = valOver + valPos; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver + UnderlyingType(1); } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder = valUnder + valMin; } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder = valUnder + UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder2 = true; } REQUIRE(hadOver1); REQUIRE(hadOver2); REQUIRE(hadUnder1); REQUIRE(hadUnder2); // operate with int + SafeInt val(UnderlyingType(-42)); val = val + UnderlyingType(5); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -98,28 +100,31 @@ template struct SafeIntTester { val = val + sum; val.commit(); REQUIRE(val == UnderlyingType(-27)); + // For coverage (operate with 0) + REQUIRE(val + UnderlyingType(0) == val); + REQUIRE(val + SafeInt(UnderlyingType(0)) == val); } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator-") { - SafeInt val(UnderlyingType(-42)); + SafeInt valPos(UnderlyingType(1)); + SafeInt valMin(UnderlyingType(-1)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); - SafeInt valOverMinus(UnderlyingType(-1)); - SafeInt valUnderMinus(UnderlyingType(1)); bool hadOver1 = false; bool hadOver2 = false; bool hadUnder1 = false; bool hadUnder2 = false; // catch over/underflow - try { valOver = valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver = valOver - valOverMinus; } catch (std::overflow_error& e) { hadOver2 = true; } - try { valUnder = valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder1 = true; } - try { valUnder = valUnder - valUnderMinus; } catch (std::underflow_error& e) { hadUnder2 = true; } + try { valOver = valOver - valMin; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder = valUnder - valPos; } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder = valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder2 = true; } REQUIRE(hadOver1); REQUIRE(hadOver2); REQUIRE(hadUnder1); REQUIRE(hadUnder2); // operate with int + SafeInt val(UnderlyingType(-42)); val = val - UnderlyingType(5); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -134,31 +139,43 @@ template struct SafeIntTester { val = val - sub; val.commit(); REQUIRE(val == UnderlyingType(-57)); + // For coverage (operate with 0) + REQUIRE(val - UnderlyingType(0) == val); + REQUIRE(val - SafeInt(UnderlyingType(0)) == val); } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator*") { - SafeInt val(UnderlyingType(-42)); - SafeInt valZero1(UnderlyingType(-42)); - SafeInt valZero2(UnderlyingType(0)); + SafeInt valPos(UnderlyingType(42)); + SafeInt valZero(UnderlyingType(0)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); bool hadZero1 = false; bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; bool hadOver1 = false; bool hadOver2 = false; - bool hadUnder = false; + bool hadUnder1 = false; + bool hadUnder2 = false; // catch over/underflow and mul by zero - try { valZero1 = valZero1 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } - try { valZero2 = valZero2 * UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } - try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver = valOver * valOver; } catch (std::overflow_error& e) { hadOver2 = true; } - try { valUnder = valUnder * UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder = true; } + try { valPos = valPos * valZero; } catch (std::domain_error& e) { hadZero1 = true; } + try { valPos = valPos * UnderlyingType(0); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero = valZero * valPos; } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero = valZero * UnderlyingType(42); } catch (std::domain_error& e) { hadZero4 = true; } + try { valOver = valOver * valPos; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder = valUnder * valPos; } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder = valUnder * UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder2 = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); REQUIRE(hadOver1); REQUIRE(hadOver2); - REQUIRE(hadUnder); + REQUIRE(hadUnder1); + REQUIRE(hadUnder2); // operate with int + SafeInt val(UnderlyingType(-42)); val = val * UnderlyingType(2); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -177,21 +194,25 @@ template struct SafeIntTester { } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator/") { - SafeInt val(UnderlyingType(-42)); - SafeInt valZero(UnderlyingType(-42)); - SafeInt valMinusOne(UnderlyingType(-1)); + SafeInt valPos(UnderlyingType(42)); + SafeInt valMin(UnderlyingType(-1)); + SafeInt valZero(UnderlyingType(0)); SafeInt valOver(std::numeric_limits::min()); - bool hadZero = false; + bool hadZero1 = false; + bool hadZero2 = false; bool hadOver1 = false; bool hadOver2 = false; // catch overflow and div by zero - try { valZero = valZero / UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - try { valOver = valOver / UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver = valOver / valMinusOne; } catch (std::overflow_error& e) { hadOver2 = true; } - REQUIRE(hadZero); + try { valPos = valPos / valZero; } catch (std::domain_error& e) { hadZero1 = true; } + try { valPos = valPos / UnderlyingType(0); } catch (std::domain_error& e) { hadZero2 = true; } + try { valOver = valOver / valMin; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver = valOver / UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver2 = true; } + REQUIRE(hadZero1); + REQUIRE(hadZero2); REQUIRE(hadOver1); REQUIRE(hadOver2); // operate with int + SafeInt val(UnderlyingType(-42)); val = val / UnderlyingType(2); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -206,15 +227,22 @@ template struct SafeIntTester { val = val / div; val.commit(); REQUIRE(val == UnderlyingType(-7)); + // For coverage + SafeInt valOne(UnderlyingType(1)); + REQUIRE(valOver / valOne == valOver); + REQUIRE(valOver / UnderlyingType(1) == valOver); } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator%") { SafeInt val(UnderlyingType(-42)); - SafeInt valZero(UnderlyingType(-42)); - bool hadZero = false; + SafeInt valZero(UnderlyingType(0)); + bool hadZero1 = false; + bool hadZero2 = false; // catch mod by zero - try { valZero = valZero % UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - REQUIRE(hadZero); + try { val = val % valZero; } catch (std::domain_error& e) { hadZero1 = true; } + try { val = val % UnderlyingType(0); } catch (std::domain_error& e) { hadZero2 = true; } + REQUIRE(hadZero1); + REQUIRE(hadZero2); // operate with int val = val % UnderlyingType(9); val.revert(); @@ -441,7 +469,8 @@ template struct SafeIntTester { } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator+=") { - SafeInt val(UnderlyingType(-42)); + SafeInt valPos(UnderlyingType(42)); + SafeInt valMin(UnderlyingType(-42)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); bool hadOver1 = false; @@ -449,16 +478,17 @@ template struct SafeIntTester { bool hadUnder1 = false; bool hadUnder2 = false; // catch over/underflow - try { valOver += UnderlyingType(1); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver += valOver; } catch (std::overflow_error& e) { hadOver2 = true; } - try { valUnder += UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder1 = true; } - try { valUnder += valUnder; } catch (std::underflow_error& e) { hadUnder2 = true; } + try { valOver += valPos; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver += UnderlyingType(1); } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder += valMin; } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder += UnderlyingType(-1); } catch (std::underflow_error& e) { hadUnder2 = true; } REQUIRE(hadOver1); REQUIRE(hadOver2); REQUIRE(hadUnder1); REQUIRE(hadUnder2); // operate with int - val = val += UnderlyingType(5); + SafeInt val(UnderlyingType(-42)); + val += UnderlyingType(5); val.revert(); REQUIRE(val == UnderlyingType(-42)); val += UnderlyingType(5); @@ -472,28 +502,33 @@ template struct SafeIntTester { val += sum; val.commit(); REQUIRE(val == UnderlyingType(-27)); + // For coverage (operate with 0) + val += UnderlyingType(0); + REQUIRE(val == UnderlyingType(-27)); + val += SafeInt(UnderlyingType(0)); + REQUIRE(val == UnderlyingType(-27)); } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator-=") { - SafeInt val(UnderlyingType(-42)); + SafeInt valPos(UnderlyingType(1)); + SafeInt valMin(UnderlyingType(-1)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); - SafeInt valOverMinus(UnderlyingType(-1)); - SafeInt valUnderMinus(UnderlyingType(1)); bool hadOver1 = false; bool hadOver2 = false; bool hadUnder1 = false; bool hadUnder2 = false; // catch over/underflow - try { valOver -= valOver - UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver -= valOver - valOverMinus; } catch (std::overflow_error& e) { hadOver2 = true; } - try { valUnder -= valUnder - UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder1 = true; } - try { valUnder -= valUnder - valUnderMinus; } catch (std::underflow_error& e) { hadUnder2 = true; } + try { valOver -= valMin; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver -= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder -= valPos; } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder -= UnderlyingType(1); } catch (std::underflow_error& e) { hadUnder2 = true; } REQUIRE(hadOver1); REQUIRE(hadOver2); REQUIRE(hadUnder1); REQUIRE(hadUnder2); // operate with int + SafeInt val(UnderlyingType(-42)); val -= UnderlyingType(5); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -508,31 +543,47 @@ template struct SafeIntTester { val -= sub; val.commit(); REQUIRE(val == UnderlyingType(-57)); + // For coverage (operate with 0) + REQUIRE(val - UnderlyingType(0) == val); + REQUIRE(val - SafeInt(UnderlyingType(0)) == val); + val -= UnderlyingType(0); + REQUIRE(val == UnderlyingType(-57)); + val -= SafeInt(UnderlyingType(0)); + REQUIRE(val == UnderlyingType(-57)); } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator*=") { - SafeInt val(UnderlyingType(-42)); - SafeInt valZero1(UnderlyingType(-42)); - SafeInt valZero2(UnderlyingType(0)); + SafeInt valPos(UnderlyingType(42)); + SafeInt valZero(UnderlyingType(0)); SafeInt valOver(std::numeric_limits::max()); SafeInt valUnder(std::numeric_limits::min()); bool hadZero1 = false; bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; bool hadOver1 = false; bool hadOver2 = false; - bool hadUnder = false; + bool hadUnder1 = false; + bool hadUnder2 = false; // catch over/underflow and mul by zero - try { valZero1 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } - try { valZero2 *= UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } - try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver *= valOver; } catch (std::overflow_error& e) { hadOver2 = true; } - try { valUnder *= UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder = true; } + try { valPos *= valZero; } catch (std::domain_error& e) { hadZero1 = true; } + try { valPos *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero *= valPos; } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero *= UnderlyingType(42); } catch (std::domain_error& e) { hadZero4 = true; } + try { valOver *= valPos; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver2 = true; } + try { valUnder *= valPos; } catch (std::underflow_error& e) { hadUnder1 = true; } + try { valUnder *= UnderlyingType(2); } catch (std::underflow_error& e) { hadUnder2 = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); REQUIRE(hadOver1); REQUIRE(hadOver2); - REQUIRE(hadUnder); + REQUIRE(hadUnder1); + REQUIRE(hadUnder2); // operate with int + SafeInt val(UnderlyingType(-42)); val *= UnderlyingType(2); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -551,21 +602,25 @@ template struct SafeIntTester { } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator/=") { - SafeInt val(UnderlyingType(-42)); - SafeInt valZero(UnderlyingType(-42)); - SafeInt valMinusOne(UnderlyingType(-1)); + SafeInt valPos(UnderlyingType(42)); + SafeInt valMin(UnderlyingType(-1)); + SafeInt valZero(UnderlyingType(0)); SafeInt valOver(std::numeric_limits::min()); - bool hadZero = false; + bool hadZero1 = false; + bool hadZero2 = false; bool hadOver1 = false; bool hadOver2 = false; // catch overflow and div by zero - try { valZero /= UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - try { valOver /= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver1 = true; } - try { valOver /= valMinusOne; } catch (std::overflow_error& e) { hadOver2 = true; } - REQUIRE(hadZero); + try { valPos /= valZero; } catch (std::domain_error& e) { hadZero1 = true; } + try { valPos /= UnderlyingType(0); } catch (std::domain_error& e) { hadZero2 = true; } + try { valOver /= valMin; } catch (std::overflow_error& e) { hadOver1 = true; } + try { valOver /= UnderlyingType(-1); } catch (std::overflow_error& e) { hadOver2 = true; } + REQUIRE(hadZero1); + REQUIRE(hadZero2); REQUIRE(hadOver1); REQUIRE(hadOver2); // operate with int + SafeInt val(UnderlyingType(-42)); val /= UnderlyingType(2); val.revert(); REQUIRE(val == UnderlyingType(-42)); @@ -580,15 +635,24 @@ template struct SafeIntTester { val /= div; val.commit(); REQUIRE(val == UnderlyingType(-7)); + // For coverage + SafeInt valOne(UnderlyingType(1)); + val /= valOne; + REQUIRE(val == UnderlyingType(-7)); + val /= UnderlyingType(1); + REQUIRE(val == UnderlyingType(-7)); } SECTION(std::string("SafeInt_t<") + std::to_string(Size) + "> operator%=") { SafeInt val(UnderlyingType(-42)); - SafeInt valZero(UnderlyingType(-42)); - bool hadZero = false; + SafeInt valZero(UnderlyingType(0)); + bool hadZero1 = false; + bool hadZero2 = false; // catch mod by zero - try { valZero %= UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - REQUIRE(hadZero); + try { val %= valZero; } catch (std::domain_error& e) { hadZero1 = true; } + try { val %= UnderlyingType(0); } catch (std::domain_error& e) { hadZero2 = true; } + REQUIRE(hadZero1); + REQUIRE(hadZero2); // operate with int val %= UnderlyingType(9); val.revert(); diff --git a/tests/contract/variables/safestring.cpp b/tests/contract/variables/safestring.cpp index e2d6c3eb..75ccf5e4 100644 --- a/tests/contract/variables/safestring.cpp +++ b/tests/contract/variables/safestring.cpp @@ -45,7 +45,9 @@ namespace TSafeString { // assign std::string move std::string mov1 = "222"; std::string mov2 = "222"; + std::string movA = "222"; str.assign(std::move(mov1)); + str.assign(std::move(movA)); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "111"); str.assign(std::move(mov2)); @@ -54,6 +56,7 @@ namespace TSafeString { // assign SafeString copy SafeString cpy("333"); str.assign(cpy); + str.assign(cpy); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "222"); str.assign(cpy); @@ -62,6 +65,7 @@ namespace TSafeString { // assign std::string substring (str, pos, count) std::string sub = "aa444aa"; str.assign(sub, 2, 3); + str.assign(sub, 2, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "333"); str.assign(sub, 2, 3); @@ -70,6 +74,7 @@ namespace TSafeString { // assign SafeString substring (str, pos, count) SafeString sub2("bbbbb555bbbbb"); str.assign(sub2, 5, 3); + str.assign(sub2, 5, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "444"); str.assign(sub2, 5, 3); @@ -77,6 +82,7 @@ namespace TSafeString { REQUIRE(str == "555"); // assign number of chars (count, ch) str.assign(3, '6'); + str.assign(3, '9'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "555"); str.assign(3, '6'); @@ -85,6 +91,7 @@ namespace TSafeString { // assign non-NULL-terminated C-style string (char*, count) char c[3] = {'7', '7', '7'}; str.assign(c, 3); + str.assign(c, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "666"); str.assign(c, 3); @@ -93,6 +100,7 @@ namespace TSafeString { // assign NULL-terminated C-style string (char*) char c2[4] = {'8', '8', '8', '\0'}; str.assign(c2); + str.assign(c2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "777"); str.assign(c2); @@ -101,6 +109,7 @@ namespace TSafeString { // assign iterators std::string iter("cccccccccc999cccccccccc"); str.assign(iter.cbegin() + 10, iter.cend() - 10); + str.assign(iter.cbegin() + 10, iter.cend() - 10); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "888"); str.assign(iter.cbegin() + 10, iter.cend() - 10); @@ -109,6 +118,7 @@ namespace TSafeString { // assign ilist std::initializer_list ilist {'!','!','!'}; str.assign(ilist); + str.assign(ilist); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "999"); str.assign(ilist); @@ -161,6 +171,10 @@ namespace TSafeString { str.back() = '!'; str.commit(); REQUIRE(std::as_const(str).back() == '!'); + // For coverage (copy != nullptr) + REQUIRE(str.at(0) == 'H'); + REQUIRE(str.front() == 'H'); + REQUIRE(str.back() == '!'); } SECTION("SafeString begin, end, rbegin, rend") { @@ -203,6 +217,7 @@ namespace TSafeString { std::size_t oriCap = str.capacity(); // reserve str.reserve(100); + str.reserve(200); // For coverage (copy != nullptr) str.revert(); REQUIRE(str.capacity() <= oriCap); str.reserve(100); @@ -211,6 +226,7 @@ namespace TSafeString { REQUIRE(str.capacity() <= 100); // shrink_to_fit str.shrink_to_fit(); + str.shrink_to_fit(); // For coverage (copy != nullptr) str.revert(); REQUIRE(str.capacity() > oriCap); REQUIRE(str.capacity() <= 100); @@ -222,6 +238,7 @@ namespace TSafeString { SECTION("SafeString clear") { SafeString str("Hello World"); str.clear(); + str.clear(); // For coverage (copy != nullptr) str.revert(); REQUIRE((!str.empty() && str == "Hello World")); str.clear(); @@ -233,6 +250,7 @@ namespace TSafeString { SafeString str("Hello"); // insert repeat chars (count, ch) str.insert(0, 5, 'a'); + str.insert(0, 5, 'b'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, 5, 'a'); @@ -242,6 +260,7 @@ namespace TSafeString { // insert NULL-terminated C-style string (char*) char c[4] = {'b', 'b', 'b', '\0'}; str.insert(0, c); + str.insert(0, c); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, c); @@ -251,6 +270,7 @@ namespace TSafeString { // insert non-NULL-terminated C-style string (char*, count) char c2[3] = {'c', 'c', 'c'}; str.insert(0, c2, 3); + str.insert(0, c2, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, c2, 3); @@ -260,6 +280,7 @@ namespace TSafeString { // insert SafeString SafeString str2("World"); str.insert(0, str2); + str.insert(0, str2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, str2); @@ -269,6 +290,7 @@ namespace TSafeString { // insert std::string std::string str3("World"); str.insert(0, str3); + str.insert(0, str3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, str3); @@ -278,6 +300,7 @@ namespace TSafeString { // insert SafeString substring (str, idx, count) SafeString str4("dddddWorldddddd"); str.insert(0, str4, 5, 5); + str.insert(0, str4, 5, 5); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, str4, 5, 5); @@ -287,6 +310,7 @@ namespace TSafeString { // insert std::string substring (str, idx, count) std::string str5("eeeeeWorldeeeee"); str.insert(0, str5, 5, 5); + str.insert(0, str5, 5, 5); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(0, str5, 5, 5); @@ -295,6 +319,7 @@ namespace TSafeString { str = "Hello"; str.commit(); // insert char with iterator (pos, ch) str.insert(str.cend(), '!'); + str.insert(str.cend(), '#'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(str.cend(), '!'); @@ -303,6 +328,7 @@ namespace TSafeString { str = "Hello"; str.commit(); // insert repeat chars with iterator (pos, count, ch) str.insert(str.cend(), 3, '!'); + str.insert(str.cend(), 3, '#'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(str.cend(), 3, '!'); @@ -312,6 +338,7 @@ namespace TSafeString { // insert with iterators std::string iter("ffffffffffWorldffffffffff"); str.insert(str.cend(), iter.cbegin() + 10, iter.cend() - 10); + str.insert(str.cend(), iter.cbegin() + 10, iter.cend() - 10); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(str.cend(), iter.cbegin() + 10, iter.cend() - 10); @@ -321,6 +348,7 @@ namespace TSafeString { // insert ilist std::initializer_list ilist { 'D', 'a', 'r', 'k', 'n', 'e', 's', 's' }; str.insert(str.cend(), ilist); + str.insert(str.cend(), ilist); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello"); str.insert(str.cend(), ilist); @@ -332,6 +360,7 @@ namespace TSafeString { SafeString str("Hello World"); // erase a number of chars str.erase(2, 6); // "llo Wo" + str.erase(0, 1); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello World"); str.erase(2, 6); @@ -340,6 +369,7 @@ namespace TSafeString { str = "Hello World"; str.commit(); // always reset str for next test // erase one char str.erase(str.cbegin() + 4); // "o" + str.erase(str.cbegin() + 1); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello World"); str.erase(str.cbegin() + 4); @@ -348,6 +378,7 @@ namespace TSafeString { str = "Hello World"; str.commit(); // erase a range of chars str.erase(str.cbegin() + 5, str.cend()); // " World" + str.erase(str.cbegin(), str.cend()); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Hello World"); str.erase(str.cbegin() + 5, str.cend()); // " World" @@ -369,12 +400,18 @@ namespace TSafeString { str.pop_back(); str.commit(); REQUIRE(str == "Goodbye"); + // For coverage (copy != nullptr) + str.push_back('!'); + str.pop_back(); + str.push_back('!'); + REQUIRE(str == "Goodbye!"); } SECTION("SafeString append") { SafeString str("Howdy"); // append number of chars str.append(3, '.'); + str.append(3, '.'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(3, '.'); @@ -384,6 +421,7 @@ namespace TSafeString { // append SafeString SafeString str2("Pardner"); str.append(str2); + str.append(str2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(str2); @@ -393,6 +431,7 @@ namespace TSafeString { // append std::string std::string str3("Miss"); str.append(str3); + str.append(str3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(str3); @@ -402,6 +441,7 @@ namespace TSafeString { // append Safestring substring SafeString str4("Dat's Mah Horse"); str.append(str4, 10, 2); // "Ho" + str.append(str4, 10, 2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(str4, 10, 2); @@ -411,6 +451,7 @@ namespace TSafeString { // append std::string substring std::string str5("It's a Champion Breed"); str.append(str5, 7, 5); // "Champ" + str.append(str5, 7, 5); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(str5, 7, 5); @@ -420,6 +461,7 @@ namespace TSafeString { // append non-NULL-terminated C-style string char c[6] = {'F','a','m','i','l','y'}; str.append(c, 3); + str.append(c, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(c, 3); @@ -429,6 +471,7 @@ namespace TSafeString { // append NULL-terminated C-style string char c2[4] = {'B','r','o','\0'}; str.append(c2); + str.append(c2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(c2); @@ -438,6 +481,7 @@ namespace TSafeString { // append range of chars (iterator) std::string iter("The Pizza Planet Oneiric Experience"); // I'm hungry and sleepy and running out of ideas str.append(iter.cbegin() + 10, iter.cbegin() + 16); // "Planet" + str.append(iter.cbegin() + 10, iter.cbegin() + 16); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(iter.cbegin() + 10, iter.cbegin() + 16); @@ -447,6 +491,7 @@ namespace TSafeString { // append ilist std::initializer_list ilist {'S','e','e','y','a'}; str.append(ilist); + str.append(ilist); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Howdy"); str.append(ilist); @@ -539,6 +584,7 @@ namespace TSafeString { // replace SafeString (pos + count) SafeString str1("ost"); str.replace(5, 3, str1); + str.replace(5, 3, str1); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(5, 3, str1); @@ -548,6 +594,7 @@ namespace TSafeString { // replace std::string (pos + count) std::string str2("urr"); str.replace(5, 3, str2); + str.replace(5, 3, str2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(5, 3, str2); @@ -557,6 +604,7 @@ namespace TSafeString { // replace SafeString (iterators) SafeString str3("anan"); str.replace(str.cbegin() + 5, str.cend() - 2, str3); + str.replace(str.cbegin() + 5, str.cend() - 2, str3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 5, str.cend() - 2, str3); @@ -566,6 +614,7 @@ namespace TSafeString { // replace std::string (iterators) std::string str4("eston"); str.replace(str.cbegin() + 5, str.cend() - 2, str4); + str.replace(str.cbegin() + 5, str.cend() - 2, str4); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 5, str.cend() - 2, str4); @@ -575,6 +624,7 @@ namespace TSafeString { // replace SafeString substring (pos + count) SafeString str5("fundo do poço"); str.replace(5, 3, str5, 1, 3); + str.replace(5, 3, str5, 1, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(5, 3, str5, 1, 3); @@ -584,6 +634,7 @@ namespace TSafeString { // replace std::string substring (pos + count) std::string str6("viva o aldeão da taverna"); str.replace(5, 3, str6, 7, 3); + str.replace(5, 3, str6, 7, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(5, 3, str6, 7, 3); @@ -593,6 +644,7 @@ namespace TSafeString { // replace std::string substring (iterators) std::string str7("todo mundo sabe que latrocinio significa roubo seguido de morte"); str.replace(str.cbegin() + 4, str.cend() - 2, str7.cbegin() + 20, str7.cend() - 39); + str.replace(str.cbegin() + 4, str.cend() - 2, str7.cbegin() + 20, str7.cend() - 39); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 4, str.cend() - 2, str7.cbegin() + 20, str7.cend() - 39); @@ -602,6 +654,7 @@ namespace TSafeString { // replace C-style substring (pos + count) const char* c = "inutil, a gente somos inutil"; str.replace(4, 4, c, 4); + str.replace(4, 4, c, 4); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(4, 4, c, 4); @@ -611,6 +664,7 @@ namespace TSafeString { // replace C-style substring (iterators) const char* c2 = "establishment"; str.replace(str.cbegin() + 5, str.cend() - 2, c2, 3); + str.replace(str.cbegin() + 5, str.cend() - 2, c2, 3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 5, str.cend() - 2, c2, 3); @@ -620,6 +674,7 @@ namespace TSafeString { // replace C-style string (pos) const char* c3 = "Huelandia"; str.replace(4, 6, c3); + str.replace(4, 6, c3); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(4, 6, c3); @@ -629,6 +684,7 @@ namespace TSafeString { // replace C-style string (iterators) const char* c4 = "infern"; str.replace(str.cbegin() + 4, str.cend() - 2, c4); + str.replace(str.cbegin() + 4, str.cend() - 2, c4); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 4, str.cend() - 2, c4); @@ -637,6 +693,7 @@ namespace TSafeString { str = "Alo Brasil"; str.commit(); // replace repeat chars (pos + count) str.replace(6, 4, 10, 'r'); + str.replace(6, 4, 10, 'r'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(6, 4, 10, 'r'); @@ -645,6 +702,7 @@ namespace TSafeString { str = "Alo Brasil"; str.commit(); // replace repeat chars (iterators) str.replace(str.cbegin() + 3, str.cend(), 10, 'o'); + str.replace(str.cbegin() + 3, str.cend(), 10, 'o'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 3, str.cend(), 10, 'o'); @@ -654,6 +712,7 @@ namespace TSafeString { // replace ilist (iterators) std::initializer_list ilist { 'A', 'd', 'e', 'u', 's' }; str.replace(str.cbegin(), str.cbegin() + 3, ilist); + str.replace(str.cbegin(), str.cbegin() + 3, ilist); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin(), str.cbegin() + 3, ilist); @@ -663,6 +722,7 @@ namespace TSafeString { // replace std::string_view (pos + count) std::string_view sv1("orr"); str.replace(5, 3, sv1); + str.replace(5, 3, sv1); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(5, 3, sv1); @@ -672,6 +732,7 @@ namespace TSafeString { // replace std::string_view (iterators) std::string_view sv2("arr"); str.replace(str.cbegin() + 5, str.cend() - 2, sv2); + str.replace(str.cbegin() + 5, str.cend() - 2, sv2); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(str.cbegin() + 5, str.cend() - 2, sv2); @@ -681,6 +742,7 @@ namespace TSafeString { // replace std::string_view substring (pos + count) std::string_view sv3("Baronesa da Pisadinha"); str.replace(4, 4, sv3, 0, 5); + str.replace(4, 4, sv3, 0, 5); // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Alo Brasil"); str.replace(4, 4, sv3, 0, 5); @@ -713,6 +775,7 @@ namespace TSafeString { SafeString str("aaa"); // resize bigger, default char ('\0') str.resize(5); + str.resize(10); // For coverage (copy != nullptr) str.revert(); REQUIRE(str.size() == 3); REQUIRE(str == "aaa"); @@ -739,6 +802,7 @@ namespace TSafeString { REQUIRE(str == "a"); // resize bigger, custom char str.resize(10, 'a'); + str.resize(20, 'a'); // For coverage (copy != nullptr) str.revert(); REQUIRE(str.size() == 1); REQUIRE(str == "a"); @@ -764,6 +828,7 @@ namespace TSafeString { std::string strRaw2("string4"); // swap std::string str1.swap(strRaw1); + str1.swap(strRaw1); // For coverage (copy != nullptr) str1.revert(); REQUIRE(str1 == "string1"); str1.swap(strRaw2); @@ -772,6 +837,7 @@ namespace TSafeString { str1 = "string1"; str1.commit(); // reset str1 for next test // swap SafeString str1.swap(str2); + str1.swap(str2); // For coverage (copy != nullptr) str1.revert(); str2.revert(); REQUIRE(str1 == "string1"); @@ -926,6 +992,7 @@ namespace TSafeString { // assign SafeString SafeString str1("Test1"); str = str1; + str = str1; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test0"); str = str1; @@ -934,6 +1001,7 @@ namespace TSafeString { // assign std::string std::string str2("Test2"); str = str2; + str = str2; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test1"); str = str2; @@ -942,6 +1010,7 @@ namespace TSafeString { // assign C-style string const char* str3 = "Test3"; str = str3; + str = str3; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test2"); str = str3; @@ -950,6 +1019,7 @@ namespace TSafeString { // assign char char ch = '4'; str = ch; + str = ch; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test3"); str = ch; @@ -958,6 +1028,7 @@ namespace TSafeString { // assign ilist std::initializer_list ilist { 'T', 'e', 's', 't', '5' }; str = ilist; + str = ilist; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "4"); str = ilist; @@ -970,6 +1041,7 @@ namespace TSafeString { // assign SafeString SafeString str1("111"); str += str1; + str += str1; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test"); str += str1; @@ -978,6 +1050,7 @@ namespace TSafeString { // assign std::string std::string str2("222"); str += str2; + str += str2; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test111"); str += str2; @@ -986,6 +1059,7 @@ namespace TSafeString { // assign C-style string const char* str3 = "333"; str += str3; + str += str3; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test111222"); str += str3; @@ -994,6 +1068,7 @@ namespace TSafeString { // assign char char ch = '4'; str += ch; + str += ch; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test111222333"); str += ch; @@ -1002,6 +1077,7 @@ namespace TSafeString { // assign ilist std::initializer_list ilist { '5', '6', '7', '8', '9' }; str += ilist; + str += ilist; // For coverage (copy != nullptr) str.revert(); REQUIRE(str == "Test1112223334"); str += ilist; @@ -1108,6 +1184,7 @@ namespace TSafeString { std::stringstream ss; ss << str; REQUIRE_THAT(ss.str(), Equals("ABCDE")); + str.revert(); // For coverage (copy != nullptr) } } } diff --git a/tests/contract/variables/safeuint_t.cpp b/tests/contract/variables/safeuint_t.cpp index 382ce087..b9b3e2f4 100644 --- a/tests/contract/variables/safeuint_t.cpp +++ b/tests/contract/variables/safeuint_t.cpp @@ -156,17 +156,23 @@ template struct SafeUintTester { SafeUint valUnder(1); bool hadZero1 = false; bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; bool hadOver1 = false; bool hadOver2 = false; bool hadUnder = false; // catch over/underflow and mul by zero try { valZero1 = valZero1 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } try { valZero2 = valZero2 * UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero2 = valZero2 * UnderlyingType(0); } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero2 = valZero2 * int(0); } catch (std::domain_error& e) { hadZero4 = true; } try { valOver = valOver * UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } try { valOver = valOver * valOver; } catch (std::overflow_error& e) { hadOver2 = true; } try { valUnder = valUnder * int(-1); } catch (std::underflow_error& e) { hadUnder = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); REQUIRE(hadOver1); REQUIRE(hadOver2); REQUIRE(hadUnder); @@ -198,14 +204,27 @@ template struct SafeUintTester { SECTION(std::string("SafeUint_t<") + std::to_string(Size) + "> operator/") { SafeUint val(UnderlyingType(42)); - SafeUint valZero(UnderlyingType(42)); + SafeUint valZero1(UnderlyingType(42)); + SafeUint valZero2(UnderlyingType(0)); SafeUint valUnder(1); - bool hadZero = false; + bool hadZero1 = false; + bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; + bool hadZero5 = false; bool hadUnder = false; // catch underflow and div by zero - try { valZero = valZero / UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } + try { valZero1 = valZero1 / UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } + try { valZero2 = valZero2 / UnderlyingType(2); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero1 = valZero1 / valZero2; } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero2 = valZero2 / valZero1; } catch (std::domain_error& e) { hadZero4 = true; } + try { valZero1 = valZero1 / int(0); } catch (std::domain_error& e) { hadZero5 = true; } try { valUnder = valUnder / int(-1); } catch (std::domain_error& e) { hadUnder = true; } - REQUIRE(hadZero); + REQUIRE(hadZero1); + REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); + REQUIRE(hadZero5); REQUIRE(hadUnder); // operate with uint val = val / UnderlyingType(2); @@ -233,11 +252,27 @@ template struct SafeUintTester { SECTION(std::string("SafeUint_t<") + std::to_string(Size) + "> operator%") { SafeUint val(UnderlyingType(42)); - SafeUint valZero(UnderlyingType(42)); - bool hadZero = false; + SafeUint valZero1(UnderlyingType(42)); + SafeUint valZero2(UnderlyingType(0)); + bool hadZero1 = false; + bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; + bool hadZero5 = false; + bool hadZero6 = false; // catch mod by zero - try { valZero = valZero % UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - REQUIRE(hadZero); + try { valZero1 = valZero1 % UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } + try { valZero2 = valZero2 % UnderlyingType(2); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero1 = valZero1 % valZero2; } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero2 = valZero2 % valZero1; } catch (std::domain_error& e) { hadZero4 = true; } + try { valZero1 = valZero1 % int(0); } catch (std::domain_error& e) { hadZero5 = true; } + try { valZero2 = valZero2 % int(2); } catch (std::domain_error& e) { hadZero6 = true; } + REQUIRE(hadZero1); + REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); + REQUIRE(hadZero5); + REQUIRE(hadZero6); // operate with uint val = val % UnderlyingType(15); val.revert(); @@ -415,6 +450,25 @@ template struct SafeUintTester { val = val || SafeUint(UnderlyingType(1)); val.commit(); REQUIRE(val == UnderlyingType(1)); + // For coverage + SafeUint val0(UnderlyingType(0)); + SafeUint val1(UnderlyingType(1)); + REQUIRE_FALSE((val0 && val0)); + REQUIRE_FALSE((val0 && val1)); + REQUIRE_FALSE((val1 && val0)); + REQUIRE((val1 && val1)); + REQUIRE_FALSE((val0 && UnderlyingType(0))); + REQUIRE_FALSE((val0 && UnderlyingType(1))); + REQUIRE_FALSE((val1 && UnderlyingType(0))); + REQUIRE((val1 && UnderlyingType(1))); + REQUIRE_FALSE((val0 || val0)); + REQUIRE((val0 || val1)); + REQUIRE((val1 || val0)); + REQUIRE((val1 || val1)); + REQUIRE_FALSE((val0 || UnderlyingType(0))); + REQUIRE((val0 || UnderlyingType(1))); + REQUIRE((val1 || UnderlyingType(0))); + REQUIRE((val1 || UnderlyingType(1))); } SECTION(std::string("SafeUint_t<") + std::to_string(Size) + "> operator== and !=") { @@ -654,17 +708,23 @@ template struct SafeUintTester { SafeUint valUnder(1); bool hadZero1 = false; bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; bool hadOver1 = false; bool hadOver2 = false; bool hadUnder = false; // catch over/underflow and mul by zero try { valZero1 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } try { valZero2 *= UnderlyingType(10); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero2 *= UnderlyingType(0); } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero2 *= int(0); } catch (std::domain_error& e) { hadZero4 = true; } try { valOver *= UnderlyingType(2); } catch (std::overflow_error& e) { hadOver1 = true; } try { valOver *= valOver; } catch (std::overflow_error& e) { hadOver2 = true; } try { valUnder *= int(-1); } catch (std::underflow_error& e) { hadUnder = true; } REQUIRE(hadZero1); REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); REQUIRE(hadOver1); REQUIRE(hadOver2); REQUIRE(hadUnder); @@ -696,14 +756,27 @@ template struct SafeUintTester { SECTION(std::string("SafeUint_t<") + std::to_string(Size) + "> operator/=") { SafeUint val(UnderlyingType(42)); - SafeUint valZero(UnderlyingType(42)); + SafeUint valZero1(UnderlyingType(42)); + SafeUint valZero2(UnderlyingType(0)); SafeUint valUnder(1); - bool hadZero = false; + bool hadZero1 = false; + bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; + bool hadZero5 = false; bool hadUnder = false; // catch underflow and div by zero - try { valZero /= UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } + try { valZero1 /= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } + try { valZero2 /= UnderlyingType(2); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero1 /= valZero2; } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero2 /= valZero1; } catch (std::domain_error& e) { hadZero4 = true; } + try { valZero1 /= int(0); } catch (std::domain_error& e) { hadZero5 = true; } try { valUnder /= int(-1); } catch (std::domain_error& e) { hadUnder = true; } - REQUIRE(hadZero); + REQUIRE(hadZero1); + REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); + REQUIRE(hadZero5); REQUIRE(hadUnder); // operate with uint val /= UnderlyingType(2); @@ -731,11 +804,27 @@ template struct SafeUintTester { SECTION(std::string("SafeUint_t<") + std::to_string(Size) + "> operator%=") { SafeUint val(UnderlyingType(42)); - SafeUint valZero(UnderlyingType(42)); - bool hadZero = false; + SafeUint valZero1(UnderlyingType(42)); + SafeUint valZero2(UnderlyingType(0)); + bool hadZero1 = false; + bool hadZero2 = false; + bool hadZero3 = false; + bool hadZero4 = false; + bool hadZero5 = false; + bool hadZero6 = false; // catch mod by zero - try { valZero %= UnderlyingType(0); } catch (std::domain_error& e) { hadZero = true; } - REQUIRE(hadZero); + try { valZero1 %= UnderlyingType(0); } catch (std::domain_error& e) { hadZero1 = true; } + try { valZero2 %= UnderlyingType(2); } catch (std::domain_error& e) { hadZero2 = true; } + try { valZero1 %= valZero2; } catch (std::domain_error& e) { hadZero3 = true; } + try { valZero2 %= valZero1; } catch (std::domain_error& e) { hadZero4 = true; } + try { valZero1 %= int(0); } catch (std::domain_error& e) { hadZero5 = true; } + try { valZero2 %= int(2); } catch (std::domain_error& e) { hadZero6 = true; } + REQUIRE(hadZero1); + REQUIRE(hadZero2); + REQUIRE(hadZero3); + REQUIRE(hadZero4); + REQUIRE(hadZero5); + REQUIRE(hadZero6); // operate with uint val %= UnderlyingType(15); val.revert(); diff --git a/tests/contract/variables/safeunorderedmap.cpp b/tests/contract/variables/safeunorderedmap.cpp index 65d3b791..964ef76f 100644 --- a/tests/contract/variables/safeunorderedmap.cpp +++ b/tests/contract/variables/safeunorderedmap.cpp @@ -210,6 +210,9 @@ namespace TSafeUnorderedMap { map.commit(); REQUIRE(map.size() == 101); for (std::pair val : values) REQUIRE(map.at(val.first) == val.second); + + // Force failure on insert (for coverage) + REQUIRE_FALSE(map.insert(valCopy2[0]).second); } SECTION("SafeUnorderedMap insert (hint)") { diff --git a/tests/contract/variables/safevector.cpp b/tests/contract/variables/safevector.cpp index dceb25d2..53fbedf0 100644 --- a/tests/contract/variables/safevector.cpp +++ b/tests/contract/variables/safevector.cpp @@ -45,6 +45,7 @@ namespace TSafeVector { std::initializer_list ilist { "AAAAA", "AAAAA", "AAAAA", "AAAAA" }; // assign with repeating value vec.assign(5, "AAAAA"); + vec.assign(5, "BBBBB"); // For coverage (copy != nullptr) vec.revert(); REQUIRE(vec.empty()); vec.assign(5, "AAAAA"); @@ -53,6 +54,7 @@ namespace TSafeVector { for (std::size_t i = 0; i < vec.size(); i++) REQUIRE(vec[i] == "AAAAA"); // assign with iterators vec2.assign(vec.cbegin(), vec.cend() - 2); + vec2.assign(vec.cbegin(), vec.cend() - 2); // For coverage (copy != nullptr) vec2.revert(); REQUIRE(vec2.empty()); vec2.assign(vec.cbegin(), vec.cend() - 2); @@ -61,6 +63,7 @@ namespace TSafeVector { for (std::size_t i = 0; i < vec2.size(); i++) REQUIRE(vec2[i] == "AAAAA"); // assign with ilist vec3.assign(ilist); + vec3.assign(ilist); // For coverage (copy != nullptr) vec3.revert(); REQUIRE(vec3.empty()); vec3.assign(ilist); @@ -86,6 +89,11 @@ namespace TSafeVector { REQUIRE(vec.at(2) == "x"); REQUIRE(vec.at(3) == "x"); REQUIRE(vec.at(4) == "x"); + // For coverage + const std::string str = std::as_const(vec)[0]; + REQUIRE(str == "x"); + vec.assign(5, "y"); // copy != nullptr + REQUIRE(vec.at(0) == "y"); } SECTION("SafeVector operator[]") { @@ -105,8 +113,10 @@ namespace TSafeVector { REQUIRE(vec[3] == "x"); REQUIRE(vec[4] == "x"); // For coverage - const std::string str = vec[0]; + const std::string str = std::as_const(vec)[0]; REQUIRE(str == "x"); + vec.assign(5, "y"); // copy != nullptr + REQUIRE(vec[0] == "y"); } SECTION("SafeVector front and back") { @@ -123,6 +133,13 @@ namespace TSafeVector { vec.back() = "y"; vec.commit(); REQUIRE(vec.back() == "y"); + // For coverage + vec.front(); // undo != nullptr + vec.back(); + vec.front(); + vec.assign(5, "y"); // copy != nullptr + REQUIRE(vec.front() == "y"); + REQUIRE(vec.back() == "y"); } // TODO: missing tests for cbegin, cend, crbegin and crend - check SafeUnorderedMap for more info @@ -140,12 +157,16 @@ namespace TSafeVector { vec.commit(); REQUIRE(vec.empty()); REQUIRE(vec.size() == 0); + // For coverage (copy != nullptr) + vec.clear(); + vec.clear(); } SECTION("SafeVector insert") { SafeVector vec({1,2,3,4,5}); // insert by copy (pos and value) vec.insert(vec.cbegin(), 0); + vec.insert(vec.cbegin(), 0); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 5); REQUIRE(*(vec.cbegin()) == 1); @@ -156,7 +177,9 @@ namespace TSafeVector { // insert by move (pos and value) int n = 6; int n2 = 6; + int nC = 6; vec.insert(vec.cend(), std::move(n)); + vec.insert(vec.cend(), std::move(nC)); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 6); REQUIRE(*(vec.cend() - 1) == 5); @@ -166,6 +189,7 @@ namespace TSafeVector { REQUIRE(*(vec.cend() - 1) == 6); // vec = {0,1,2,3,4,5,6} // insert with repeat (pos count and value) vec.insert(vec.cbegin() + 2, 3, 7); + vec.insert(vec.cbegin() + 2, 3, 7); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 7); REQUIRE(*(vec.cbegin() + 2) == 2); @@ -179,6 +203,7 @@ namespace TSafeVector { // insert with iterators std::vector vec2({10,20,30}); vec.insert(vec.cbegin(), vec2.cbegin(), vec2.cend()); + vec.insert(vec.cbegin(), vec2.cbegin(), vec2.cend()); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 10); REQUIRE(*(vec.cbegin()) == 0); @@ -192,6 +217,7 @@ namespace TSafeVector { // insert with pos and ilist std::initializer_list ilist {1000, 2000, 3000}; vec.insert(vec.cend(), ilist); + vec.insert(vec.cend(), ilist); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 13); REQUIRE(*(vec.cend() - 1) == 6); @@ -202,12 +228,21 @@ namespace TSafeVector { REQUIRE(*(vec.cend() - 3) == 1000); REQUIRE(*(vec.cend() - 2) == 2000); REQUIRE(*(vec.cend() - 1) == 3000); + // For coverage (copy != nullptr) + vec.clear(); + int nC2 = 6; + vec.insert(vec.cbegin(), 0); + vec.insert(vec.cend(), std::move(nC2)); + vec.insert(vec.cbegin() + 2, 3, 7); + vec.insert(vec.cbegin(), vec2.cbegin(), vec2.cend()); + vec.insert(vec.cend(), ilist); } SECTION("SafeVector emplace") { // Same as insert, but there's only one overload to care about SafeVector vec({1,2,3,4,5}); vec.emplace(vec.cbegin(), 0); + vec.emplace(vec.cbegin(), 0); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 5); REQUIRE(*(vec.cbegin()) == 1); @@ -215,12 +250,17 @@ namespace TSafeVector { vec.commit(); REQUIRE(vec.size() == 6); REQUIRE(*(vec.cbegin()) == 0); + // For coverage (copy != nullptr) + vec.clear(); + vec.emplace(vec.cbegin(), 0); + REQUIRE(vec[0] == 0); } SECTION("SafeVector erase") { SafeVector vec({0,1,2,3,4,5}); // erase a single value (pos) vec.erase(vec.cbegin()); + vec.erase(vec.cbegin()); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 6); REQUIRE(*(vec.cbegin()) == 0); @@ -230,6 +270,7 @@ namespace TSafeVector { REQUIRE(*(vec.cbegin()) == 1); // erase a range of values (iterator) vec.erase(vec.cbegin() + 1, vec.cend() - 1); // {2,3,4} + vec.erase(vec.cbegin(), vec.cend()); // For coverage (undo != nullptr) vec.revert(); REQUIRE(vec.size() == 5); REQUIRE(vec[0] == 1); @@ -242,12 +283,18 @@ namespace TSafeVector { REQUIRE(vec.size() == 2); REQUIRE(vec[0] == 1); REQUIRE(vec[1] == 5); + // For coverage (copy != nullptr) + vec.assign(5, 10); + vec.erase(vec.cbegin()); + vec.erase(vec.cbegin(), vec.cend() - 1); + REQUIRE(vec[0] == 10); } SECTION("SafeVector push_back, emplace_back and pop_back") { SafeVector vec({"a", "b", "c"}); // push back by copy vec.push_back("d"); + vec.push_back("d"); // For coverage (undo != nullptr) vec.revert(); REQUIRE((vec.size() == 3 && vec.back() == "c")); vec.push_back("d"); @@ -256,7 +303,9 @@ namespace TSafeVector { // push back by move std::string mv1 = "e"; std::string mv2 = "e"; + std::string mvC = "e"; vec.push_back(std::move(mv1)); + vec.push_back(std::move(mvC)); // For coverage (undo != nullptr) vec.revert(); REQUIRE((vec.size() == 4 && vec.back() == "d")); vec.push_back(std::move(mv2)); @@ -264,6 +313,7 @@ namespace TSafeVector { REQUIRE((vec.size() == 5 && vec.back() == "e")); // vec = {a,b,c,d,e} // emplace back vec.emplace_back("f"); + vec.emplace_back("f"); // For coverage (undo != nullptr) vec.revert(); REQUIRE((vec.size() == 5 && vec.back() == "e")); vec.emplace_back("f"); @@ -271,17 +321,27 @@ namespace TSafeVector { REQUIRE((vec.size() == 6 && vec.back() == "f")); // vec = {a,b,c,d,e,f} // pop back for (int i = 0; i < 5; i++) vec.pop_back(); + vec.pop_back(); // For coverage (undo != nullptr) vec.revert(); REQUIRE((vec.size() == 6 && vec.back() == "f")); for (int i = 0; i < 5; i++) vec.pop_back(); vec.commit(); REQUIRE((vec.size() == 1 && vec.back() == "a")); // vec = {a} + // For coverage (copy != nullptr) + vec.assign(5, "x"); + vec.push_back("y"); + std::string mvC2 = "z"; + vec.push_back(std::move(mvC2)); + vec.emplace_back("w"); + vec.pop_back(); } SECTION("SafeVector resize") { SafeVector vec({1,2,3,4,5}); // resize to a bigger size, with default elements vec.resize(10); + vec.resize(20); // For coverage (undo != nullptr) + vec.resize(5); vec.revert(); REQUIRE(vec.size() == 5); for (std::size_t i = 0; i < 5; i++) REQUIRE(vec[i] == i + 1); @@ -302,6 +362,8 @@ namespace TSafeVector { for (std::size_t i = 0; i < 3; i++) REQUIRE(vec[i] == i + 1); // resize to a bigger size, with repeated elements vec.resize(6, 100); + vec.resize(10, 100); // For coverage (undo != nullptr) + vec.resize(2, 100); vec.revert(); REQUIRE(vec.size() == 3); for (std::size_t i = 0; i < 3; i++) REQUIRE(vec[i] == i + 1); @@ -342,6 +404,10 @@ namespace TSafeVector { vec.resize(0, 100); vec.commit(); REQUIRE(vec.empty()); + // For coverage (copy != nullptr) + vec.assign(5, 100); + vec.resize(10); + vec.resize(20, 200); } SECTION("SafeVector operator=") { @@ -349,6 +415,7 @@ namespace TSafeVector { SafeVector vec2({"1", "2", "3"}); SafeVector vec3({"X", "Y", "Z"}); vec = vec2; + vec = vec2; // For coverage (copy != nullptr) vec.revert(); REQUIRE(vec[0] == "a"); REQUIRE(vec[1] == "b"); @@ -359,6 +426,7 @@ namespace TSafeVector { REQUIRE(vec[1] == "2"); REQUIRE(vec[2] == "3"); vec = vec3.get(); + vec = vec3.get(); // For coverage (copy != nullptr) vec.revert(); REQUIRE(vec[0] == "1"); REQUIRE(vec[1] == "2"); diff --git a/tests/core/blockchain.cpp b/tests/core/blockchain.cpp index 3760fbe9..34b9186a 100644 --- a/tests/core/blockchain.cpp +++ b/tests/core/blockchain.cpp @@ -26,7 +26,7 @@ void initialize( const std::string& web3clientVersion, const uint64_t& version, const uint64_t& chainId, - const uint64_t& wsPort, + const uint64_t& p2pPort, const uint64_t& httpPort, const PrivKey& privKey, const std::pair& discoveryNodes, @@ -34,23 +34,41 @@ void initialize( bool clearFolder = true ) { if (clearFolder) { - if (std::filesystem::exists(blockchainPath)) { - std::filesystem::remove_all(blockchainPath); - } + if (std::filesystem::exists(blockchainPath)) std::filesystem::remove_all(blockchainPath); std::filesystem::create_directories(blockchainPath); - json optionsJson; optionsJson["rootPath"] = blockchainPath; optionsJson["web3clientVersion"] = web3clientVersion; optionsJson["version"] = version; optionsJson["chainID"] = chainId; - optionsJson["wsPort"] = wsPort; + optionsJson["chainOwner"] = "0x00dead00665771855a34155f5e7405489df2c3c6"; + optionsJson["p2pIp"] = "127.0.0.1"; + optionsJson["p2pPort"] = p2pPort; optionsJson["httpPort"] = httpPort; - if (privKey) { - optionsJson["privKey"] = privKey.hex(); - } + optionsJson["minDiscoveryConns"] = 11; + optionsJson["minNormalConns"] = 11; + optionsJson["maxDiscoveryConns"] = 200; + optionsJson["maxNormalConns"] = 50; + optionsJson["eventBlockCap"] = 2000; + optionsJson["eventLogCap"] = 10000; + optionsJson["stateDumpTrigger"] = 1000; + optionsJson["minValidators"] = 4; + optionsJson["genesis"] = json::object(); + optionsJson["genesis"]["validators"] = { + "0x7588b0f553d1910266089c58822e1120db47e572", + "0xcabf34a268847a610287709d841e5cd590cc5c00", + "0x5fb516dc2cfc1288e689ed377a9eebe2216cf1e3", + "0x795083c42583842774febc21abb6df09e784fce5", + "0xbec7b74f70c151707a0bfb20fe3767c6e65499e0" + }; + optionsJson["genesis"]["timestamp"] = 1656356646000000; + optionsJson["genesis"]["signer"] = "0x4d48bdf34d65ef2bed2e4ee9020a7d3162b494ac31d3088153425f286f3d3c8c"; + optionsJson["genesis"]["balances"][0]["address"] = "0x00dead00665771855a34155f5e7405489df2c3c6"; + optionsJson["genesis"]["balances"][0]["balance"] = "1000000000000000000000"; optionsJson["discoveryNodes"][0]["address"] = discoveryNodes.first; optionsJson["discoveryNodes"][0]["port"] = discoveryNodes.second; + optionsJson["indexingMode"] = IndexingMode::RPC.toString(); + if (privKey) optionsJson["privKey"] = privKey.hex(); std::ofstream optionsFile(blockchainPath + "/options.json"); optionsFile << optionsJson.dump(2); optionsFile.close(); @@ -62,37 +80,28 @@ namespace TBlockchain { TEST_CASE("Blockchain Class", "[core][blockchain]") { std::string testDumpPath = Utils::getTestDumpPath(); SECTION("Initialize Blockchain Multiple Nodes") { - std::shared_ptr bestBlock; + std::shared_ptr bestBlock; { // Initialize ManagerDiscovery std::vector> discoveryNodes; - std::unique_ptr discoveryOptions = std::make_unique( - testDumpPath + "/statedDiscoveryNodeNetworkCapabilitiesWithTxBlockBroadcast", - "BDK/cpp/linux_x86-64/0.2.0", - 1, - 8080, - 8100, - 9999, - discoveryNodes - ); - std::unique_ptr p2pDiscovery = std::make_unique( - LOCALHOST, discoveryOptions); + Options discoveryOptions = Options::fromFile(testDumpPath + "/statedDiscoveryNodeNetworkCapabilitiesWithTxBlockBroadcast"); + std::unique_ptr p2pDiscovery = std::make_unique(LOCALHOST, discoveryOptions); // Initialize multiple blockchain nodes. std::unique_ptr blockchain1; - initialize("blockchainInitializeTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101, + initialize("blockchainInitializeTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8080, 8101, PrivKey(), {"127.0.0.1", 8100}, blockchain1); std::unique_ptr blockchain2; - initialize("blockchainInitializeTestNode2", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8081, 8102, + initialize("blockchainInitializeTestNode2", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8081, 8102, PrivKey(), {"127.0.0.1", 8100}, blockchain2); std::unique_ptr blockchain3; - initialize("blockchainInitializeTestNode3", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8082, 8103, + initialize("blockchainInitializeTestNode3", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8082, 8103, PrivKey(), {"127.0.0.1", 8100}, blockchain3); std::unique_ptr blockchain4; - initialize("blockchainInitializeTestNode4", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8083, 8104, + initialize("blockchainInitializeTestNode4", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8083, 8104, PrivKey(), {"127.0.0.1", 8100}, blockchain4); // Start the blockchain nodes. @@ -103,32 +112,32 @@ namespace TBlockchain { blockchain3->start(); blockchain4->start(); - REQUIRE(!blockchain1->getOptions()->getIsValidator()); - REQUIRE(!blockchain2->getOptions()->getIsValidator()); - REQUIRE(!blockchain3->getOptions()->getIsValidator()); - REQUIRE(!blockchain4->getOptions()->getIsValidator()); + REQUIRE(!blockchain1->getOptions().getIsValidator()); + REQUIRE(!blockchain2->getOptions().getIsValidator()); + REQUIRE(!blockchain3->getOptions().getIsValidator()); + REQUIRE(!blockchain4->getOptions().getIsValidator()); // Wait everyone to connect. auto connectFuture = std::async(std::launch::async, [&]() { - while (blockchain1->getP2P()->getSessionsIDs().size() != 4 || - blockchain2->getP2P()->getSessionsIDs().size() != 4 || - blockchain3->getP2P()->getSessionsIDs().size() != 4 || - blockchain4->getP2P()->getSessionsIDs().size() != 4) { + while (blockchain1->getP2P().getSessionsIDs().size() != 4 || + blockchain2->getP2P().getSessionsIDs().size() != 4 || + blockchain3->getP2P().getSessionsIDs().size() != 4 || + blockchain4->getP2P().getSessionsIDs().size() != 4) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }); REQUIRE(connectFuture.wait_for(std::chrono::seconds(5)) != std::future_status::timeout); - REQUIRE(blockchain1->getP2P()->getSessionsIDs().size() == 4); - REQUIRE(blockchain2->getP2P()->getSessionsIDs().size() == 4); - REQUIRE(blockchain3->getP2P()->getSessionsIDs().size() == 4); - REQUIRE(blockchain4->getP2P()->getSessionsIDs().size() == 4); + REQUIRE(blockchain1->getP2P().getSessionsIDs().size() == 4); + REQUIRE(blockchain2->getP2P().getSessionsIDs().size() == 4); + REQUIRE(blockchain3->getP2P().getSessionsIDs().size() == 4); + REQUIRE(blockchain4->getP2P().getSessionsIDs().size() == 4); - bestBlock = blockchain1->getStorage()->latest(); - REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain2->getStorage()->latest()->hash()); - REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain3->getStorage()->latest()->hash()); - REQUIRE(blockchain1->getStorage()->latest()->hash() == blockchain4->getStorage()->latest()->hash()); + bestBlock = blockchain1->getStorage().latest(); + REQUIRE(blockchain1->getStorage().latest()->getHash() == blockchain2->getStorage().latest()->getHash()); + REQUIRE(blockchain1->getStorage().latest()->getHash() == blockchain3->getStorage().latest()->getHash()); + REQUIRE(blockchain1->getStorage().latest()->getHash() == blockchain4->getStorage().latest()->getHash()); // Stop the blockchain nodes. p2pDiscovery->stop(); @@ -138,10 +147,10 @@ namespace TBlockchain { blockchain4->stop(); } std::unique_ptr blockchain1; - initialize("blockchainInitializeTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101, + initialize("blockchainInitializeTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8080, 8101, PrivKey(), {"127.0.0.1", 8100}, blockchain1, false); - REQUIRE(blockchain1->getStorage()->latest()->hash() == bestBlock->hash()); + REQUIRE(blockchain1->getStorage().latest()->getHash() == bestBlock->getHash()); } SECTION("Blockchain multiple nodes, move 10 blocks forward 1 tx per block.") { @@ -150,72 +159,63 @@ namespace TBlockchain { Address targetOfTransactions = Address(Utils::randBytes(20)); uint256_t targetBalance = 0; uint256_t myBalance("1000000000000000000000"); - std::shared_ptr bestBlock; + std::shared_ptr bestBlock; // Create the discovery node. { std::vector> discoveryNodes; - std::unique_ptr discoveryOptions = std::make_unique( - testDumpPath + "/statedDiscoveryNodeNetworkCapabilitiesWithTxBlockBroadcast", - "BDK/cpp/linux_x86-64/0.2.0", - 1, - 8080, - 8100, - 9999, - discoveryNodes - ); - std::unique_ptr p2pDiscovery = std::make_unique( - LOCALHOST, discoveryOptions); + Options discoveryOptions = Options::fromFile(testDumpPath + "/statedDiscoveryNodeNetworkCapabilitiesWithTxBlockBroadcast"); + std::unique_ptr p2pDiscovery = std::make_unique(LOCALHOST, discoveryOptions); // Create the validator nodes (5 in total) std::unique_ptr blockchainValidator1; - initialize("blockchainMove10BlocksTestValidator1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8080, 8101, + initialize("blockchainMove10BlocksTestValidator1", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8080, 8101, PrivKey(Hex::toBytes("0xba5e6e9dd9cbd263969b94ee385d885c2d303dfc181db2a09f6bf19a7ba26759")), {"127.0.0.1", 8100}, blockchainValidator1); std::unique_ptr blockchainValidator2; - initialize("blockchainMove10BlocksTestValidator2", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8081, 8102, + initialize("blockchainMove10BlocksTestValidator2", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8081, 8102, PrivKey(Hex::toBytes("0xfd84d99aa18b474bf383e10925d82194f1b0ca268e7a339032679d6e3a201ad4")), {"127.0.0.1", 8100}, blockchainValidator2); std::unique_ptr blockchainValidator3; - initialize("blockchainMove10BlocksTestValidator3", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8082, 8103, + initialize("blockchainMove10BlocksTestValidator3", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8082, 8103, PrivKey(Hex::toBytes("0x66ce71abe0b8acd92cfd3965d6f9d80122aed9b0e9bdd3dbe018230bafde5751")), {"127.0.0.1", 8100}, blockchainValidator3); std::unique_ptr blockchainValidator4; - initialize("blockchainMove10BlocksTestValidator4", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8083, 8104, + initialize("blockchainMove10BlocksTestValidator4", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8083, 8104, PrivKey(Hex::toBytes("0x856aeb3b9c20a80d1520a2406875f405d336e09475f43c478eb4f0dafb765fe7")), {"127.0.0.1", 8100}, blockchainValidator4); std::unique_ptr blockchainValidator5; - initialize("blockchainMove10BlocksTestValidator5", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8084, 8105, + initialize("blockchainMove10BlocksTestValidator5", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8084, 8105, PrivKey(Hex::toBytes("0x81f288dd776f4edfe256d34af1f7d719f511559f19115af3e3d692e741faadc6")), {"127.0.0.1", 8100}, blockchainValidator5); // Create the normal nodes (6 in total) std::unique_ptr blockchainNode1; - initialize("blockchainMove10BlocksTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8085, 8106, + initialize("blockchainMove10BlocksTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8085, 8106, PrivKey(), {"127.0.0.1", 8100}, blockchainNode1); std::unique_ptr blockchainNode2; - initialize("blockchainMove10BlocksTestNode2", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8086, 8107, + initialize("blockchainMove10BlocksTestNode2", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8086, 8107, PrivKey(), {"127.0.0.1", 8100}, blockchainNode2); std::unique_ptr blockchainNode3; - initialize("blockchainMove10BlocksTestNode3", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8087, 8108, + initialize("blockchainMove10BlocksTestNode3", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8087, 8108, PrivKey(), {"127.0.0.1", 8100}, blockchainNode3); std::unique_ptr blockchainNode4; - initialize("blockchainMove10BlocksTestNode4", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8088, 8109, + initialize("blockchainMove10BlocksTestNode4", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8088, 8109, PrivKey(), {"127.0.0.1", 8100}, blockchainNode4); std::unique_ptr blockchainNode5; - initialize("blockchainMove10BlocksTestNode5", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8089, 8110, + initialize("blockchainMove10BlocksTestNode5", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8089, 8110, PrivKey(), {"127.0.0.1", 8100}, blockchainNode5); std::unique_ptr blockchainNode6; - initialize("blockchainMove10BlocksTestNode6", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8090, 8111, + initialize("blockchainMove10BlocksTestNode6", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8090, 8111, PrivKey(), {"127.0.0.1", 8100}, blockchainNode6); // Start the discovery node. @@ -265,7 +265,7 @@ namespace TBlockchain { me, Bytes(), 8080, - blockchainValidator1->getState()->getNativeNonce(me), + blockchainValidator1->getState().getNativeNonce(me), 1000000000000000000, 21000, 1000000000, @@ -316,91 +316,91 @@ namespace TBlockchain { // Wait for the new best block to be broadcasted and accepted by all nodes ++blocks; auto blockFuture = std::async(std::launch::async, [&]() { - while (blocks != blockchainNode1->getStorage()->latest()->getNHeight() || - blocks != blockchainNode2->getStorage()->latest()->getNHeight() || - blocks != blockchainNode3->getStorage()->latest()->getNHeight() || - blocks != blockchainNode4->getStorage()->latest()->getNHeight() || - blocks != blockchainNode5->getStorage()->latest()->getNHeight() || - blocks != blockchainNode6->getStorage()->latest()->getNHeight() || - blocks != blockchainValidator1->getStorage()->latest()->getNHeight() || - blocks != blockchainValidator2->getStorage()->latest()->getNHeight() || - blocks != blockchainValidator3->getStorage()->latest()->getNHeight() || - blocks != blockchainValidator4->getStorage()->latest()->getNHeight() || - blocks != blockchainValidator5->getStorage()->latest()->getNHeight()) { + while (blocks != blockchainNode1->getStorage().latest()->getNHeight() || + blocks != blockchainNode2->getStorage().latest()->getNHeight() || + blocks != blockchainNode3->getStorage().latest()->getNHeight() || + blocks != blockchainNode4->getStorage().latest()->getNHeight() || + blocks != blockchainNode5->getStorage().latest()->getNHeight() || + blocks != blockchainNode6->getStorage().latest()->getNHeight() || + blocks != blockchainValidator1->getStorage().latest()->getNHeight() || + blocks != blockchainValidator2->getStorage().latest()->getNHeight() || + blocks != blockchainValidator3->getStorage().latest()->getNHeight() || + blocks != blockchainValidator4->getStorage().latest()->getNHeight() || + blocks != blockchainValidator5->getStorage().latest()->getNHeight()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }); std::cout << "Before: " << std::endl; - std::cout << blockchainNode1->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode2->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode3->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode4->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode5->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode6->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator1->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator2->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator3->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator4->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator5->getStorage()->latest()->getNHeight() << std::endl; + std::cout << blockchainNode1->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode2->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode3->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode4->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode5->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode6->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator1->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator2->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator3->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator4->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator5->getStorage().latest()->getNHeight() << std::endl; std::cout << "P2P Connections Count: " << std::endl; - std::cout << blockchainNode1->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode2->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode3->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode4->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode5->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode6->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator1->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator2->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator3->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator4->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator5->getP2P()->getSessionsIDs().size() << std::endl; + std::cout << blockchainNode1->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode2->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode3->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode4->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode5->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode6->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator1->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator2->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator3->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator4->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator5->getP2P().getSessionsIDs().size() << std::endl; auto wait = blockFuture.wait_for(std::chrono::seconds(10)); std::cout << "After: " << std::endl; - std::cout << blockchainNode1->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode2->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode3->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode4->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode5->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainNode6->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator1->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator2->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator3->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator4->getStorage()->latest()->getNHeight() << std::endl; - std::cout << blockchainValidator5->getStorage()->latest()->getNHeight() << std::endl; + std::cout << blockchainNode1->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode2->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode3->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode4->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode5->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainNode6->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator1->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator2->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator3->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator4->getStorage().latest()->getNHeight() << std::endl; + std::cout << blockchainValidator5->getStorage().latest()->getNHeight() << std::endl; std::cout << "P2P Connections Count: " << std::endl; - std::cout << blockchainNode1->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode2->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode3->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode4->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode5->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainNode6->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator1->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator2->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator3->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator4->getP2P()->getSessionsIDs().size() << std::endl; - std::cout << blockchainValidator5->getP2P()->getSessionsIDs().size() << std::endl; + std::cout << blockchainNode1->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode2->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode3->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode4->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode5->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainNode6->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator1->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator2->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator3->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator4->getP2P().getSessionsIDs().size() << std::endl; + std::cout << blockchainValidator5->getP2P().getSessionsIDs().size() << std::endl; REQUIRE(wait != std::future_status::timeout); // Everyone should be on the same block - REQUIRE(blockchainValidator1->getStorage()->latest()->getNHeight() == blocks); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator2->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator3->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator4->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainValidator5->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode1->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode2->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode3->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode4->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode5->getStorage()->latest()->hash()); - REQUIRE(blockchainValidator1->getStorage()->latest()->hash() == blockchainNode6->getStorage()->latest()->hash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getNHeight() == blocks); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainValidator2->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainValidator3->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainValidator4->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainValidator5->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainNode1->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainNode2->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainNode3->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainNode4->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainNode5->getStorage().latest()->getHash()); + REQUIRE(blockchainValidator1->getStorage().latest()->getHash() == blockchainNode6->getStorage().latest()->getHash()); } - bestBlock = blockchainValidator1->getStorage()->latest(); + bestBlock = blockchainValidator1->getStorage().latest(); // Stop the nodes p2pDiscovery->stop(); blockchainValidator1->stop(); @@ -417,10 +417,10 @@ namespace TBlockchain { } std::unique_ptr blockchainNode1; - initialize("blockchainMove10BlocksTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 8080, 8080, 8085, 8106, + initialize("blockchainMove10BlocksTestNode1", "BDK/cpp/linux_x86-64/0.2.0", 1, 808080, 8085, 8106, PrivKey(), {"127.0.0.1", 8100}, blockchainNode1, false); - REQUIRE(blockchainNode1->getStorage()->latest()->hash() == bestBlock->hash()); + REQUIRE(blockchainNode1->getStorage().latest()->getHash() == bestBlock->getHash()); } } } diff --git a/tests/core/dumpmanager.cpp b/tests/core/dumpmanager.cpp index cf36d6bb..78ed8f2e 100644 --- a/tests/core/dumpmanager.cpp +++ b/tests/core/dumpmanager.cpp @@ -26,19 +26,23 @@ namespace TDumpManager { SECTION("DumpManager Test With DumpWorker") { Hash bestBlockHash = Hash(); { - auto blockchainWrapper = initialize(validatorPrivKeysState, - validatorPrivKeysState[0], - 8080, - true, - testDumpPath + "/dumpManagerSimpleTests"); + auto blockchainWrapper = initialize( + validatorPrivKeysState, + validatorPrivKeysState[0], + 8080, + true, + testDumpPath + "/dumpManagerSimpleTests" + ); // start the dump worker blockchainWrapper.state.dumpStartWorker(); // create 1001 blocks for (uint64_t i = 0; i < 1001; ++i) { GLOGTRACE("Creating block: " + std::to_string(i)); - auto block = createValidBlock(validatorPrivKeysState, - blockchainWrapper.state, - blockchainWrapper.storage); + auto block = createValidBlock( + validatorPrivKeysState, + blockchainWrapper.state, + blockchainWrapper.storage + ); REQUIRE(blockchainWrapper.state.tryProcessNextBlock(std::move(block)) == BlockValidationStatus::valid); } // stop the dump worker @@ -47,15 +51,15 @@ namespace TDumpManager { REQUIRE(std::filesystem::exists(testDumpPath + "/dumpManagerSimpleTests" + "/stateDb")); bestBlockHash = blockchainWrapper.storage.latest()->getHash(); } - auto blockchainWrapper = initialize(validatorPrivKeysState, - validatorPrivKeysState[0], - 8080, - false, - testDumpPath + "/dumpManagerSimpleTests"); - - + auto blockchainWrapper = initialize( + validatorPrivKeysState, + validatorPrivKeysState[0], + 8080, + false, + testDumpPath + "/dumpManagerSimpleTests" + ); REQUIRE(bestBlockHash == blockchainWrapper.storage.latest()->getHash()); - } } } + diff --git a/tests/core/rdpos.cpp b/tests/core/rdpos.cpp index 29c2a4e8..36bed141 100644 --- a/tests/core/rdpos.cpp +++ b/tests/core/rdpos.cpp @@ -121,6 +121,17 @@ namespace TRdPoS { REQUIRE(blockchainWrapper.state.rdposGetBestRandomSeed() == expectedRandomnessFromBestBlock); REQUIRE(blockchainWrapper.state.rdposGetRandomList() == expectedRandomList); } + + SECTION("rdPoS validateBlock Errors (Coverage)") { + PrivKey validatorKey = PrivKey(); + auto blockchainWrapper = initialize(validatorPrivKeysRdpos, validatorKey, SDKTestSuite::getTestPort(), true, testDumpPath + "/rdPoSValidateBlockCoverage"); + + // Wrong signature (not randomList_[0]) + FinalizedBlock b1(Signature(), UPubKey(), Hash::random(), Hash::random(), Hash::random(), Hash::random(), 0, 0, {}, {}, Hash::random(), 1); + REQUIRE_FALSE(blockchainWrapper.state.rdposValidateBlock(b1)); + + // TODO: this should be covered further, but faking block contents is too much hassle as it is (same goes for addValidatorTx and maybe getTxValidatorFunction which is not exposed by State) + } } TEST_CASE("rdPoS Class With Network Functionality", "[core][rdpos][net]") { diff --git a/tests/core/storage.cpp b/tests/core/storage.cpp index 74e98227..6ac623f4 100644 --- a/tests/core/storage.cpp +++ b/tests/core/storage.cpp @@ -115,6 +115,54 @@ namespace TStorage { REQUIRE(Secp256k1::toAddress(genesis->getValidatorPubKey()) == Address(Hex::toBytes("0x00dead00665771855a34155f5e7405489df2c3c6"))); } + SECTION("Storage topicsMatch") { + Hash txHash = Hash::random(); + Hash blockHash = Hash::random(); + std::vector topics = {Hash::random(), Hash::random(), Hash::random(), Hash::random(), Hash::random()}; + Address add("0x1234567890123456789012345678901234567890", false); + Bytes data{0xDE, 0xAD, 0xBE, 0xEF}; + Event e("myEvent", 0, txHash, 1, blockHash, 2, add, data, topics, false); + REQUIRE(Storage::topicsMatch(e, topics)); + + // For coverage + REQUIRE(Storage::topicsMatch(e, {})); // Empty topics + topics.push_back(Hash::random()); + REQUIRE_FALSE(Storage::topicsMatch(e, topics)); // Event has fewer topics than required + topics.pop_back(); + topics[0] = Hash::random(); + REQUIRE_FALSE(Storage::topicsMatch(e, topics)); // Event has wrong topics + } + + SECTION("Storage getEvents") { + auto blockchainWrapper = initialize(validatorPrivKeysStorage, PrivKey(), 8080, true, "StorageGetEvents"); + Address add("0x1234567890123456789012345678901234567890", false); + Bytes data{0xDE, 0xAD, 0xBE, 0xEF}; + std::vector> topics; + std::vector events; + for (int i = 0; i < 5; i++) { + topics.push_back({Hash::random(), Hash::random(), Hash::random()}); + events.push_back(Event( + "event" + std::to_string(i), 0, Hash::random(), 0, Hash::random(), i, add, data, topics[i], false + )); + blockchainWrapper.storage.putEvent(events[i]); + } + + REQUIRE_THROWS(blockchainWrapper.storage.getEvents(1000000, 0, add, {})); // Querying too many blocks + std::vector got = blockchainWrapper.storage.getEvents(0, 3, add, {}); + for (int i = 0; i < 3; i++) { + REQUIRE(got[i].getName() == events[i].getName()); + REQUIRE(got[i].getLogIndex() == events[i].getLogIndex()); + REQUIRE(got[i].getTxHash() == events[i].getTxHash()); + REQUIRE(got[i].getTxIndex() == events[i].getTxIndex()); + REQUIRE(got[i].getBlockHash() == events[i].getBlockHash()); + REQUIRE(got[i].getBlockIndex() == events[i].getBlockIndex()); + REQUIRE(got[i].getAddress() == events[i].getAddress()); + REQUIRE(got[i].getData() == events[i].getData()); + REQUIRE(got[i].getTopics() == topics[i]); + REQUIRE(got[i].isAnonymous() == events[i].isAnonymous()); + } + } + SECTION("10 Blocks forward with destructor test") { // Create 10 Blocks, each with 100 dynamic transactions and 16 validator transactions std::vector blocks; diff --git a/tests/net/http/httpjsonrpc.cpp b/tests/net/http/httpjsonrpc.cpp index 5f7f1e78..16d2402c 100644 --- a/tests/net/http/httpjsonrpc.cpp +++ b/tests/net/http/httpjsonrpc.cpp @@ -106,7 +106,7 @@ template json requestMethod(std::string method, T params) { ); } -namespace THTTPJsonRPC{ +namespace THTTPJsonRPC { TEST_CASE("HTTPJsonRPC Tests", "[net][http][jsonrpc]") { SECTION("checkJsonRPCSpec") { json ok = {{"jsonrpc", "2.0"}, {"method", "myMethod"}, {"params", json::array()}}; @@ -177,6 +177,7 @@ namespace THTTPJsonRPC{ blockchainWrapper.http.start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); + blockchainWrapper.http.start(); // Attempt to start again, for coverage (runFuture is valid) json web3_clientVersionResponse = requestMethod("web3_clientVersion", json::array()); @@ -303,7 +304,7 @@ namespace THTTPJsonRPC{ {"data", "0x1"}, }), "latest"})); - REQUIRE(eth_estimateGasResponse["result"] == "0x5208"); + REQUIRE(eth_estimateGasResponse["result"] == "0x5e56"); json eth_gasPriceResponse = requestMethod("eth_gasPrice", json::array()); REQUIRE(eth_gasPriceResponse["result"] == "0x9502f900"); diff --git a/tests/net/http/parser.cpp b/tests/net/http/parser.cpp index 9b48acd7..48395e7c 100644 --- a/tests/net/http/parser.cpp +++ b/tests/net/http/parser.cpp @@ -14,7 +14,7 @@ namespace THTTPJSONRPCParser { SECTION("Parser operator()") { Hash h = Hash::random(); std::vector v = {10, 20, 30, 40, 50}; - + // Parser regex REQUIRES hex prefix (0x) json jsonHash = h.hex(true).get(); json jsonAdd = Address("0x0000111122223333444455556666777788889999", false).hex(true).get(); @@ -23,6 +23,8 @@ namespace THTTPJSONRPCParser { json jsonFloat = 13.37f; json jsonUint = uint64_t(3926591489); json jsonUintStr = "0xea0b0801"; + json jsonOptional = nullptr; + json jsonVariant = uint64_t(12345); json jsonBlockTagLatest = "latest"; json jsonBlockTagEarliest = "earliest"; json jsonBlockTagPending = "pending"; @@ -37,6 +39,8 @@ namespace THTTPJSONRPCParser { float resFloat = jsonrpc::parse(jsonFloat); uint64_t resUint = jsonrpc::parse(jsonUint); uint64_t resUintStr = jsonrpc::parse(jsonUintStr); + std::optional resOptional = jsonrpc::parse>(jsonOptional); + std::variant resVariant = jsonrpc::parse>(jsonVariant); jsonrpc::BlockTag resBlockTagLatest = jsonrpc::parse(jsonBlockTagLatest); jsonrpc::BlockTag resBlockTagEarliest = jsonrpc::parse(jsonBlockTagEarliest); jsonrpc::BlockTag resBlockTagPending = jsonrpc::parse(jsonBlockTagPending); @@ -50,6 +54,8 @@ namespace THTTPJSONRPCParser { REQUIRE(resFloat == 13.37f); REQUIRE(resUint == uint64_t(3926591489)); REQUIRE(resUintStr == uint64_t(3926591489)); + REQUIRE(resOptional == std::nullopt); + REQUIRE(std::get(resVariant) == 12345); REQUIRE(resBlockTagLatest == jsonrpc::BlockTag::LATEST); REQUIRE(resBlockTagEarliest == jsonrpc::BlockTag::EARLIEST); REQUIRE(resBlockTagPending == jsonrpc::BlockTag::PENDING); diff --git a/tests/net/p2p/encoding.cpp b/tests/net/p2p/encoding.cpp index 2d874fdf..6fccbf0e 100644 --- a/tests/net/p2p/encoding.cpp +++ b/tests/net/p2p/encoding.cpp @@ -31,8 +31,8 @@ namespace TP2PEncoding { // Throws for coverage Bytes msgSmall{0x00}; Bytes msgSmallV4{0x00, 0x00, 0x7f, 0x00}; // missing 0001 - Bytes msgSmallV6{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // missing 0000000000000001 - Bytes msgInvalidIp{0x02}; // only 00 (v4) or 01 (v6) + Bytes msgSmallV6{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // missing 0000000000000001 + Bytes msgInvalidIp{0x00, 0x02, 0x7f, 0x00, 0x00, 0x01}; // 02 is not 00 (v4) or 01 (v6) Bytes msgNoPort{0x00, 0x00, 0x7f, 0x00, 0x00, 0x01}; // missing 14f0 (8000) REQUIRE_THROWS(P2P::nodesFromMessage(msgSmall)); REQUIRE_THROWS(P2P::nodesFromMessage(msgSmallV4)); @@ -57,7 +57,7 @@ namespace TP2PEncoding { uint64_t nHeight = 92137812; FinalizedBlock newBlock = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); std::shared_ptr latestBlock = std::make_shared(newBlock); - + P2P::nodeInfoToMessage(msg, latestBlock, nodes, opts); Hex msgNodeVersion = Hex::fromBytes(Utils::create_view_span(msg).subspan(0, 8)); Hex hexNodeVersion = Hex::fromBytes(UintConv::uint64ToBytes(opts.getVersion())); @@ -160,7 +160,7 @@ namespace TP2PEncoding { REQUIRE_THROWS(P2P::txsFromMessage(Utils::create_view_span(txSmall), uint64_t(1))); } } - + TEST_CASE("P2P Encoding (Miscellaneous)", "[p2p][encoding]") { SECTION("RequestID Conversions") { P2P::RequestID id(uint64_t(793228721938748925)); @@ -197,5 +197,390 @@ namespace TP2PEncoding { REQUIRE_FALSE(node3 < node4); } } + + TEST_CASE("P2P Encoding (Requests)", "[p2p][encoding]") { + SECTION("Encode/Decode ping Request") { + P2P::Message msg = P2P::RequestEncoder::ping(); + bool req = P2P::RequestDecoder::ping(msg); + REQUIRE(req == true); + P2P::Message msg2 = P2P::AnswerEncoder::ping(msg); + bool req2 = P2P::AnswerDecoder::ping(msg2); + REQUIRE(req2 == true); + + // For coverage + P2P::Message msgWrongSize(Utils::randBytes(12)); // msg is 11 bytes + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Info) + ))); + P2P::Message msg2WrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + P2P::Message msg2WrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Answering), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Info) + ))); + REQUIRE_FALSE(P2P::RequestDecoder::ping(msgWrongSize)); + REQUIRE_FALSE(P2P::RequestDecoder::ping(msgWrongCmd)); + REQUIRE_FALSE(P2P::AnswerDecoder::ping(msgWrongSize)); + REQUIRE_FALSE(P2P::AnswerDecoder::ping(msg2WrongType)); + REQUIRE_FALSE(P2P::AnswerDecoder::ping(msg2WrongCmd)); + } + + SECTION("Encode/Decode info Request") { + PrivKey genesisSigner(Hex::toBytes("0x4d48bdf34d65ef2bed2e4ee9020a7d3162b494ac31d3088153425f286f3d3c8c")); + FinalizedBlock genesis = FinalizedBlock::createNewValidBlock({},{}, Hash(), 1656356646000000, 0, genesisSigner); + P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000}; + P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001}; + const boost::unordered_flat_map nodes = { + {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE} + }; + Options opts = Options::binaryDefaultOptions("options.json"); + + P2P::Message msg = P2P::RequestEncoder::info( + std::make_shared(genesis), nodes, opts + ); + P2P::NodeInfo req = P2P::RequestDecoder::info(msg); + REQUIRE(req.latestBlockHash() == genesis.getHash()); + std::vector peers = req.peers(); + for (P2P::NodeID peer : peers) REQUIRE(nodes.contains(peer)); + + P2P::Message msg2 = P2P::AnswerEncoder::info( + msg, std::make_shared(genesis), nodes, opts + ); + P2P::NodeInfo req2 = P2P::AnswerDecoder::info(msg2); + REQUIRE(req2.latestBlockHash() == genesis.getHash()); + std::vector peers2 = req2.peers(); + for (P2P::NodeID peer : peers2) REQUIRE(nodes.contains(peer)); + + // For coverage + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + P2P::Message msg2WrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Info) + ))); + P2P::Message msg2WrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Answering), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + REQUIRE_THROWS(P2P::RequestDecoder::info(msgWrongCmd)); + REQUIRE_THROWS(P2P::AnswerDecoder::info(msg2WrongType)); + REQUIRE_THROWS(P2P::AnswerDecoder::info(msg2WrongCmd)); + } + + SECTION("Encode/Decode requestNodes Request") { + P2P::Message msg = P2P::RequestEncoder::requestNodes(); + bool req = P2P::RequestDecoder::requestNodes(msg); + REQUIRE(req == true); + + P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000}; + P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001}; + const boost::unordered_flat_map nodes = { + {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE} + }; + P2P::Message msg2 = P2P::AnswerEncoder::requestNodes(msg, nodes); + boost::unordered_flat_map req2 = P2P::AnswerDecoder::requestNodes(msg2); + for (auto peer : req2) REQUIRE(nodes.contains(peer.first)); + + // For coverage + P2P::Message msgWrongSize(Utils::randBytes(12)); // msg is 11 bytes + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + P2P::Message msg2WrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::RequestNodes) + ))); + P2P::Message msg2WrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Answering), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + REQUIRE_FALSE(P2P::RequestDecoder::requestNodes(msgWrongSize)); + REQUIRE_FALSE(P2P::RequestDecoder::requestNodes(msgWrongCmd)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestNodes(msg2WrongType)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestNodes(msg2WrongCmd)); + } + + SECTION("Encode/Decode requestValidatorTxs Request") { + P2P::Message msg = P2P::RequestEncoder::requestValidatorTxs(); + bool req = P2P::RequestDecoder::requestValidatorTxs(msg); + REQUIRE(req == true); + + TxValidator tx1(Hex::toBytes("f845808026a08a4591f48d6307bb4cb8a0b0088b544d923d00bc1f264c3fdf16f946fdee0b34a077a6f6e8b3e78b45478827604f070d03060f413d823eae7fab9b139be7a41d81"), 1); + TxValidator tx2(Hex::toBytes("f86aa03051b7f769aaabd4ebb8ff991888c2891ef1d7b84cee2b44bb8274e8ed3687ff83139705820fa1a0609914de300a418c4ec1ef176efc1fbb64d73a0de0b7eee4fd31f11627e36412a01ed62c45f76f1ff05b4856ab7a6730c02bddfc891dd1fdd6b82469d41a07aaf9"), 1983); + TxValidator tx3(Hex::toBytes("f86599bf2ac52475bb963dcc43d6f10349a55e1f30899461e358e0ad850248e27503824e22a0b7c4f669b54cb0fb656f5e0c26b27405ca20a90e391222b8a8277d760273b0ffa01a27ef8cb8824267dae1884df1e31d440dacd995b7da027012798e2bd14c2ae9"), 9983); + const boost::unordered_flat_map txs = { + {tx1.hash(), tx1}, {tx2.hash(), tx2}, {tx3.hash(), tx3} + }; + P2P::Message msg2 = P2P::AnswerEncoder::requestValidatorTxs(msg, txs); + std::vector req2 = P2P::AnswerDecoder::requestValidatorTxs(msg2, 1); + for (TxValidator tx : req2) REQUIRE((txs.contains(tx.hash()) && txs.at(tx.hash()) == tx)); + + // For coverage + P2P::Message msgWrongSize(Utils::randBytes(12)); // msg is 11 bytes + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + P2P::Message msg2WrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::RequestValidatorTxs) + ))); + P2P::Message msg2WrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Answering), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + REQUIRE_FALSE(P2P::RequestDecoder::requestValidatorTxs(msgWrongSize)); + REQUIRE_FALSE(P2P::RequestDecoder::requestValidatorTxs(msgWrongCmd)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestValidatorTxs(msg2WrongType, 1)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestValidatorTxs(msg2WrongCmd, 1)); + } + + SECTION("Encode/Decode requestTxs Request") { + P2P::Message msg = P2P::RequestEncoder::requestTxs(); + bool req = P2P::RequestDecoder::requestTxs(msg); + REQUIRE(req == true); + + TxBlock tx1(Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328"), 1); + TxBlock tx2(Hex::toBytes("0x02f87301808405f5e100850b5b977f998252089495944f9d42e181d76bb2c7e428410533aa3fed4a88012386f1806fe51080c080a0102fc0316ef07a9be233a270cdeb692e1666710bbdb8be67bf7d896fa96c6bafa038b6cbfdeb433911da6958a9dd3ac24d4ff39f11d1b985efca6d6d79a96a62ce"), 1); + TxBlock tx3(Hex::toBytes("0x02f87501820cfd8405f5e100850b67b98b6b8252089480c67432656d59144ceff962e8faf8926599bcf8880de27d72f9c7632e80c001a057180e5af9ecbac905b17a45b56f1d93626190f1ec8df6a4a8cbbf7c0b8704a9a0166f15ae0e192835e1bf405b82c9faaaf9c8918a9d702441daa5168514377a17"), 1); + const std::vector txs = {tx1, tx2, tx3}; + P2P::Message msg2 = P2P::AnswerEncoder::requestTxs(msg, txs); + std::vector req2 = P2P::AnswerDecoder::requestTxs(msg2, 1); + REQUIRE(req2 == txs); + + // For coverage + P2P::Message msgWrongSize(Utils::randBytes(12)); // msg is 11 bytes + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + P2P::Message msg2WrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::RequestTxs) + ))); + P2P::Message msg2WrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Answering), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + REQUIRE_FALSE(P2P::RequestDecoder::requestTxs(msgWrongSize)); + REQUIRE_FALSE(P2P::RequestDecoder::requestTxs(msgWrongCmd)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestTxs(msg2WrongType, 1)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestTxs(msg2WrongCmd, 1)); + } + + SECTION("Encode/Decode requestBlock Request") { + P2P::Message msg = P2P::RequestEncoder::requestBlock(10, 100, 1000); + uint64_t height = 0; + uint64_t heightEnd = 0; + uint64_t bytesLimit = 0; + P2P::RequestDecoder::requestBlock(msg, height, heightEnd, bytesLimit); + REQUIRE(height == 10); + REQUIRE(heightEnd == 100); + REQUIRE(bytesLimit == 1000); + + PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d")); + Hash nPrevBlockHash(Hex::toBytes("97a5ebd9bbb5e330b0b3c74b9816d595ffb7a04d4a29fb117ea93f8a333b43be")); + uint64_t timestamp = 1678400843316; + uint64_t nHeight = 100; + TxBlock tx(Hex::toBytes("0x02f874821f9080849502f900849502f900825208942e951aa58c8b9b504a97f597bbb2765c011a8802880de0b6b3a764000080c001a0f56fe87778b4420d3b0f8eba91d28093abfdbea281a188b8516dd8411dc223d7a05c2d2d71ad3473571ff637907d72e6ac399fe4804641dbd9e2d863586c57717d"), 1); + std::vector txs; + for (uint64_t i = 0; i < 10; i++) txs.emplace_back(tx); + FinalizedBlock finalizedNewBlock = FinalizedBlock::createNewValidBlock(std::move(txs), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + const std::vector> blocks = { + std::make_shared(finalizedNewBlock) + }; + P2P::Message msg2 = P2P::AnswerEncoder::requestBlock(msg, blocks); + std::vector req2 = P2P::AnswerDecoder::requestBlock(msg2, 1); + REQUIRE(req2[0] == finalizedNewBlock); + + // For coverage + P2P::Message msgWrongSize(Utils::randBytes(40)); // msg is 35 bytes + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping), + UintConv::uint64ToBytes(height), + UintConv::uint64ToBytes(heightEnd), + UintConv::uint64ToBytes(bytesLimit) + ))); + P2P::Message msg2WrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::RequestBlock), + UintConv::uint64ToBytes(height), + UintConv::uint64ToBytes(heightEnd), + UintConv::uint64ToBytes(bytesLimit) + ))); + P2P::Message msg2WrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Answering), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping), + UintConv::uint64ToBytes(height), + UintConv::uint64ToBytes(heightEnd), + UintConv::uint64ToBytes(bytesLimit) + ))); + REQUIRE_THROWS(P2P::RequestDecoder::requestBlock(msgWrongSize, height, heightEnd, bytesLimit)); + REQUIRE_THROWS(P2P::RequestDecoder::requestBlock(msgWrongCmd, height, heightEnd, bytesLimit)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestBlock(msg2WrongType, 1)); + REQUIRE_THROWS(P2P::AnswerDecoder::requestBlock(msg2WrongCmd, 1)); + } + + SECTION("Encode/Decode broadcastValidatorTx Request") { + TxValidator tx(Hex::toBytes("f845808026a08a4591f48d6307bb4cb8a0b0088b544d923d00bc1f264c3fdf16f946fdee0b34a077a6f6e8b3e78b45478827604f070d03060f413d823eae7fab9b139be7a41d81"), 1); + P2P::Message msg = P2P::BroadcastEncoder::broadcastValidatorTx(tx); + TxValidator req = P2P::BroadcastDecoder::broadcastValidatorTx(msg, 1); + REQUIRE(req == tx); + + // For coverage + const Bytes& txSer = tx.rlpSerialize(); + P2P::Message msgWrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + UintConv::uint64ToBytes(FNVHash()(txSer)), + P2P::getCommandPrefix(P2P::CommandType::BroadcastValidatorTx), + txSer + ))); + P2P::Message msgWrongId(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Broadcasting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::BroadcastValidatorTx), + txSer + ))); + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Broadcasting), + UintConv::uint64ToBytes(FNVHash()(txSer)), + P2P::getCommandPrefix(P2P::CommandType::Ping), + txSer + ))); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastValidatorTx(msgWrongType, 1)); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastValidatorTx(msgWrongId, 1)); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastValidatorTx(msgWrongCmd, 1)); + } + + SECTION("Encode/Decode broadcastTx Request") { + TxBlock tx(Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328"), 1); + P2P::Message msg = P2P::BroadcastEncoder::broadcastTx(tx); + TxBlock req = P2P::BroadcastDecoder::broadcastTx(msg, 1); + REQUIRE(req == tx); + + // For coverage + const Bytes& txSer = tx.rlpSerialize(); + P2P::Message msgWrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + UintConv::uint64ToBytes(FNVHash()(txSer)), + P2P::getCommandPrefix(P2P::CommandType::BroadcastTx), + txSer + ))); + P2P::Message msgWrongId(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Broadcasting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::BroadcastTx), + txSer + ))); + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Broadcasting), + UintConv::uint64ToBytes(FNVHash()(txSer)), + P2P::getCommandPrefix(P2P::CommandType::Ping), + txSer + ))); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastTx(msgWrongType, 1)); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastTx(msgWrongId, 1)); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastTx(msgWrongCmd, 1)); + } + + SECTION("Encode/Decode broadcastBlock Request") { + PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d")); + Hash nPrevBlockHash(Hex::toBytes("97a5ebd9bbb5e330b0b3c74b9816d595ffb7a04d4a29fb117ea93f8a333b43be")); + uint64_t timestamp = 1678400843316; + uint64_t nHeight = 100; + TxBlock tx(Hex::toBytes("0x02f874821f9080849502f900849502f900825208942e951aa58c8b9b504a97f597bbb2765c011a8802880de0b6b3a764000080c001a0f56fe87778b4420d3b0f8eba91d28093abfdbea281a188b8516dd8411dc223d7a05c2d2d71ad3473571ff637907d72e6ac399fe4804641dbd9e2d863586c57717d"), 1); + std::vector txs; + for (uint64_t i = 0; i < 10; i++) txs.emplace_back(tx); + FinalizedBlock finalizedNewBlock = FinalizedBlock::createNewValidBlock(std::move(txs), {}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + + P2P::Message msg = P2P::BroadcastEncoder::broadcastBlock( + std::make_shared(finalizedNewBlock) + ); + FinalizedBlock req = P2P::BroadcastDecoder::broadcastBlock(msg, 1); + REQUIRE(req == finalizedNewBlock); + + // For coverage + const Bytes& blockSer = finalizedNewBlock.serializeBlock(); + P2P::Message msgWrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + UintConv::uint64ToBytes(FNVHash()(blockSer)), + P2P::getCommandPrefix(P2P::CommandType::BroadcastBlock), + blockSer + ))); + P2P::Message msgWrongId(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Broadcasting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::BroadcastBlock), + blockSer + ))); + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Broadcasting), + UintConv::uint64ToBytes(FNVHash()(blockSer)), + P2P::getCommandPrefix(P2P::CommandType::Ping), + blockSer + ))); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastBlock(msgWrongType, 1)); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastBlock(msgWrongId, 1)); + REQUIRE_THROWS(P2P::BroadcastDecoder::broadcastBlock(msgWrongCmd, 1)); + } + + SECTION("Encode/Decode notifyInfo Request") { + PrivKey genesisSigner(Hex::toBytes("0x4d48bdf34d65ef2bed2e4ee9020a7d3162b494ac31d3088153425f286f3d3c8c")); + FinalizedBlock genesis = FinalizedBlock::createNewValidBlock({},{}, Hash(), 1656356646000000, 0, genesisSigner); + P2P::NodeID node1{boost::asio::ip::address_v4::from_string("127.0.0.1"), 8000}; + P2P::NodeID node2{boost::asio::ip::address_v6::from_string("::1"), 8001}; + const boost::unordered_flat_map nodes = { + {node1, P2P::NodeType::NORMAL_NODE}, {node2, P2P::NodeType::DISCOVERY_NODE} + }; + Options opts = Options::binaryDefaultOptions("options.json"); + + P2P::Message msg = P2P::NotificationEncoder::notifyInfo( + std::make_shared(genesis), nodes, opts + ); + P2P::NodeInfo req = P2P::NotificationDecoder::notifyInfo(msg); + REQUIRE(req.latestBlockHash() == genesis.getHash()); + std::vector peers = req.peers(); + for (P2P::NodeID peer : peers) REQUIRE(nodes.contains(peer)); + + // For coverage + P2P::Message msgWrongType(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Requesting), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::NotifyInfo) + ))); + P2P::Message msgWrongCmd(Utils::makeBytes(bytes::join( + P2P::getRequestTypePrefix(P2P::RequestType::Notifying), + Utils::randBytes(8), + P2P::getCommandPrefix(P2P::CommandType::Ping) + ))); + REQUIRE_THROWS(P2P::NotificationDecoder::notifyInfo(msgWrongType)); + REQUIRE_THROWS(P2P::NotificationDecoder::notifyInfo(msgWrongCmd)); + } + } } diff --git a/tests/sdktestsuite.hpp b/tests/sdktestsuite.hpp index fe027cb0..6f3fae23 100644 --- a/tests/sdktestsuite.hpp +++ b/tests/sdktestsuite.hpp @@ -110,7 +110,7 @@ class SDKTestSuite { explicit SDKTestSuite(const Options& options) : options_(options), db_(std::get<0>(DumpManager::getBestStateDBPath(this->options_))), - storage_(p2p_.getLogicalLocation(),options_), + storage_(p2p_.getLogicalLocation(), options_), state_(db_, storage_, p2p_, std::get<1>(DumpManager::getBestStateDBPath(this->options_)), options_), p2p_(LOCALHOST, options_, storage_, state_), http_(state_, storage_, p2p_, options_) @@ -783,7 +783,14 @@ class SDKTestSuite { callValue = EVMCConv::uint256ToEvmcUint256(0); callCreate2Salt = {}; callCodeAddress = contractAddress.toEvmcAddress(); - return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); + + // If function is void do not return any data + if constexpr (std::is_same_v) { + this->state_.ethCall(callData); + return; + } else { + return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); + } } /** @@ -832,7 +839,13 @@ class SDKTestSuite { callCreate2Salt = {}; callCodeAddress = {}; - return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); + // If function is void do not return any data + if constexpr (std::is_same_v) { + this->state_.ethCall(callData); + return; + } else { + return std::get<0>(ABI::Decoder::decodeData(this->state_.ethCall(callData))); + } } diff --git a/tests/utils/block.cpp b/tests/utils/block.cpp index e9b9d9bf..3f99d80d 100644 --- a/tests/utils/block.cpp +++ b/tests/utils/block.cpp @@ -36,6 +36,20 @@ namespace TBlock { REQUIRE(finalizedNewBlock.getValidatorPubKey() == UPubKey(Hex::toBytes("046ab1f056c30ae181f92e97d0cbb73f4a8778e926c35f10f0c4d1626d8dfd51672366413809a48589aa103e1865e08bd6ddfd0559e095841eb1bd3021d9cc5e62"))); } + SECTION("Block operator== (coverage)") { + PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d")); + PrivKey validatorPrivKey2(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750e")); + Hash nPrevBlockHash(Hex::toBytes("22143e16db549af9ccfd3b746ea4a74421847fa0fe7e0e278626a4e7307ac0f6")); + uint64_t timestamp = 1678400201859; + uint64_t nHeight = 92137812; + FinalizedBlock blockA = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey); + FinalizedBlock blockB = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp, nHeight, validatorPrivKey2); + FinalizedBlock blockC = FinalizedBlock::createNewValidBlock({},{}, nPrevBlockHash, timestamp + 1, nHeight, validatorPrivKey); + REQUIRE(blockA == blockA); + REQUIRE(blockA != blockB); + REQUIRE(blockA != blockC); + } + SECTION("Block creation with 10 transactions") { PrivKey validatorPrivKey(Hex::toBytes("0x4d5db4107d237df6a3d58ee5f70ae63d73d765d8a1214214d8a13340d0f2750d")); Hash nPrevBlockHash(Hex::toBytes("97a5ebd9bbb5e330b0b3c74b9816d595ffb7a04d4a29fb117ea93f8a333b43be")); diff --git a/tests/utils/clargs.cpp b/tests/utils/clargs.cpp new file mode 100644 index 00000000..094d4187 --- /dev/null +++ b/tests/utils/clargs.cpp @@ -0,0 +1,88 @@ +/* +Copyright (c) [2023-2024] [AppLayer Developers] + +This software is distributed under the MIT License. +See the LICENSE.txt file in the project root for more information. +*/ + +#include "../../src/libs/catch2/catch_amalgamated.hpp" + +#include "../../src/utils/clargs.h" + +namespace TClargs { + TEST_CASE("Command-Line Arguments", "[utils][clargs]") { + SECTION("parseCommandLineArgs") { + ProcessOptions opt; + REQUIRE_FALSE(opt.valid); + + // argv[0] needs to exist so the others args can be parsed correctly. + // "--help" exits prematurely with exit(0) so we can't test it unless that changes. + // Casting is done to suppress `-Wwrite-strings`. + //char* argsHelp[2] = {(char*)"bdkd-tests", (char*)"--help"}; + //opt = parseCommandLineArgs(2, argsHelp, BDKTool::FULL_NODE); + + char* argsFull[9] = { + (char*)"bdkd-tests", + (char*)"--loglevel", (char*)"X", + (char*)"--loglinelimit", (char*)"1000", + (char*)"--logfilelimit", (char*)"10", + (char*)"--netthreads", (char*)"2", + }; + opt = parseCommandLineArgs(9, argsFull, BDKTool::FULL_NODE); + REQUIRE(opt.valid); + REQUIRE(opt.logLevel == "X"); + REQUIRE(opt.logLineLimit == 1000); + REQUIRE(opt.logFileLimit == 10); + REQUIRE(opt.netThreads == 2); + + // For coverage + ProcessOptions opt2; + char* argsNoLogLineLimit[3] = {(char*)"bdkd-tests", (char*)"--loglinelimit", (char*)"-1",}; + opt2 = parseCommandLineArgs(3, argsNoLogLineLimit, BDKTool::FULL_NODE); + REQUIRE(!opt2.valid); + char* argsNoLogFileLimit[3] = {(char*)"bdkd-tests", (char*)"--logfilelimit", (char*)"-1",}; + opt2 = parseCommandLineArgs(3, argsNoLogFileLimit, BDKTool::FULL_NODE); + REQUIRE(!opt2.valid); + char* argsNoNetThreads[3] = {(char*)"bdkd-tests", (char*)"--netthreads", (char*)"0",}; + opt2 = parseCommandLineArgs(3, argsNoNetThreads, BDKTool::FULL_NODE); + REQUIRE(!opt2.valid); + } + + SECTION("applyProcessOptions") { + // Set all options except log level manually so we can focus on its coverage later + ProcessOptions opt; + REQUIRE_FALSE(opt.valid); + REQUIRE_FALSE(applyProcessOptions(opt)); + opt.valid = true; + opt.logLevel = ""; + opt.logLineLimit = 1000; + opt.logFileLimit = 10; + opt.netThreads = 2; + REQUIRE(applyProcessOptions(opt)); + + // Focus exclusively on coverage for all valid log levels + ProcessOptions opt2; + opt2.valid = true; + + opt2.logLevel = "X"; // XTRACE + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "T"; // TRACE + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "D"; // DEBUG + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "I"; // INFO + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "W"; // WARNING + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "E"; // ERROR + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "F"; // FATAL + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "N"; // NONE + REQUIRE(applyProcessOptions(opt2)); + opt2.logLevel = "?"; // Invalid log level + REQUIRE_FALSE(applyProcessOptions(opt2)); + } + } +} + diff --git a/tests/utils/db.cpp b/tests/utils/db.cpp index 1ef248e9..57cb78c9 100644 --- a/tests/utils/db.cpp +++ b/tests/utils/db.cpp @@ -108,12 +108,26 @@ namespace TDB { REQUIRE(std::find(keys.begin(), keys.end(), getES.key) != keys.end()); } - // Read (getKeys, for coverage) + // Read (getKeys without limits, for coverage) + std::vector getBKAll = db.getKeys(pfx); + for (const Bytes& b : getBKAll) { + REQUIRE(std::find(keys.begin(), keys.end(), b) != keys.end()); + } + + // Read (getKeys with limits, for coverage) std::vector getBK = db.getKeys(pfx, keys[0], keys[7]); for (const Bytes& b : getBK) { REQUIRE(std::find(keys.begin(), keys.end(), b) != keys.end()); } + // Read (getLastByPrefix, for coverage) + Bytes getLast = db.getLastByPrefix(pfx); + bool found = false; + for (const DBEntry& getE : getB) { + if (getE.value == getLast) found = true; + } + REQUIRE(found); + // Update DBBatch newPutB; for (int i = 0; i < 32; i++) { diff --git a/tests/utils/hex.cpp b/tests/utils/hex.cpp index 5070a98b..6da4a506 100644 --- a/tests/utils/hex.cpp +++ b/tests/utils/hex.cpp @@ -20,41 +20,58 @@ namespace THex { REQUIRE_THAT(hexStrict.get(), Equals("0x")); } - SECTION("Hex Copy Constructor") { - std::string hexStr = "0x1234"; - Hex hex(hexStr, false); - Hex hexStrict(hexStr, true); - REQUIRE_THAT(hex.get(), Equals("1234")); - REQUIRE_THAT(hexStrict.get(), Equals("0x1234")); - REQUIRE_THAT(hexStr, Equals("0x1234")); + SECTION("Hex String View Constructor") { + Hex hexA1(std::string_view("0x1234"), true); + Hex hexA2(std::string_view("0X1234"), true); + Hex hexA3(std::string_view("1234"), true); + Hex hexB1(std::string_view("0x1234"), false); + Hex hexB2(std::string_view("0X1234"), false); + Hex hexB3(std::string_view("1234"), false); + REQUIRE_THAT(hexA1.get(), Equals("0x1234")); + REQUIRE_THAT(hexA2.get(), Equals("0x1234")); + REQUIRE_THAT(hexA3.get(), Equals("0x1234")); + REQUIRE_THAT(hexB1.get(), Equals("1234")); + REQUIRE_THAT(hexB2.get(), Equals("1234")); + REQUIRE_THAT(hexB3.get(), Equals("1234")); + REQUIRE_THROWS(Hex(std::string_view("01234"), true)); + REQUIRE_THROWS(Hex(std::string_view("x1234"), true)); + REQUIRE_THROWS(Hex(std::string_view("x1234"), false)); + } - bool catch1 = false; - bool catch2 = false; - std::string wrongStr = "xyzw"; - try { Hex wrongHex(wrongStr, false); } catch (std::exception &e) { catch1 = true; } - try { Hex wrongHex2(wrongStr, true); } catch (std::exception &e) { catch2 = true; } - REQUIRE(catch1 == true); - REQUIRE(catch2 == true); + SECTION("Hex Copy Constructor") { + Hex hexA1(std::string("0x1234"), true); + Hex hexA2(std::string("0X1234"), true); + Hex hexA3(std::string("1234"), true); + Hex hexB1(std::string("0x1234"), false); + Hex hexB2(std::string("0X1234"), false); + Hex hexB3(std::string("1234"), false); + REQUIRE_THAT(hexA1.get(), Equals("0x1234")); + REQUIRE_THAT(hexA2.get(), Equals("0x1234")); + REQUIRE_THAT(hexA3.get(), Equals("0x1234")); + REQUIRE_THAT(hexB1.get(), Equals("1234")); + REQUIRE_THAT(hexB2.get(), Equals("1234")); + REQUIRE_THAT(hexB3.get(), Equals("1234")); + REQUIRE_THROWS(Hex(std::string("01234"), true)); + REQUIRE_THROWS(Hex(std::string("x1234"), true)); + REQUIRE_THROWS(Hex(std::string("x1234"), false)); } SECTION("Hex Move Constructor") { - std::string hexStr = "1234"; - std::string hexStrStrict = "0x1234"; - Hex hex(std::move(hexStr), false); - Hex hexStrict(std::move(hexStrStrict), true); - REQUIRE_THAT(hex.get(), Equals("1234")); - REQUIRE_THAT(hexStrict.get(), Equals("0x1234")); - REQUIRE_THAT(hexStr, Equals("")); - REQUIRE_THAT(hexStrStrict, Equals("")); - - bool catch1 = false; - bool catch2 = false; - std::string wrongStr = "xyzw"; - std::string wrongStr2 = "xyzw"; - try { Hex wrongHex(std::move(wrongStr), false); } catch (std::exception &e) { catch1 = true; } - try { Hex wrongHex2(std::move(wrongStr2), true); } catch (std::exception &e) { catch2 = true; } - REQUIRE(catch1 == true); - REQUIRE(catch2 == true); + Hex hexA1(std::move(std::string("0x1234")), true); + Hex hexA2(std::move(std::string("0X1234")), true); + Hex hexA3(std::move(std::string("1234")), true); + Hex hexB1(std::move(std::string("0x1234")), false); + Hex hexB2(std::move(std::string("0X1234")), false); + Hex hexB3(std::move(std::string("1234")), false); + REQUIRE_THAT(hexA1.get(), Equals("0x1234")); + REQUIRE_THAT(hexA2.get(), Equals("0x1234")); + REQUIRE_THAT(hexA3.get(), Equals("0x1234")); + REQUIRE_THAT(hexB1.get(), Equals("1234")); + REQUIRE_THAT(hexB2.get(), Equals("1234")); + REQUIRE_THAT(hexB3.get(), Equals("1234")); + REQUIRE_THROWS(Hex(std::move(std::string("01234")), true)); + REQUIRE_THROWS(Hex(std::move(std::string("x1234")), true)); + REQUIRE_THROWS(Hex(std::move(std::string("x1234")), false)); } SECTION("Hex FromBytes") { @@ -83,6 +100,7 @@ namespace THex { SECTION("Hex IsValid") { REQUIRE(Hex::isValid("0x1a2b3c4d5e6f7890", true)); + REQUIRE(Hex::isValid("0X1A2B3C4D5E6F7890", true)); REQUIRE(Hex::isValid("1a2b3c4d5e6f7890", false)); REQUIRE(!Hex::isValid("0x81684g837h3892j", true)); REQUIRE(!Hex::isValid("81684g837h3892j", false)); @@ -97,11 +115,8 @@ namespace THex { Bytes bytesStr2 = Hex::toBytes(hexStr2); REQUIRE(Hex::fromBytes(bytesStr).get() == "1234"); REQUIRE(Hex::fromBytes(bytesStr2).get() == "5678"); - - bool catched = false; - std::string_view wrongStr("xyzw"); - try { Bytes s = Hex::toBytes(wrongStr); } catch (std::exception &e) { catched = true; } - REQUIRE(catched == true); + REQUIRE_THROWS(Hex::toBytes(std::string_view("xyzw"))); + REQUIRE_THROWS(Hex::toBytes(std::string_view("0xghij"))); } SECTION("Hex Bytes") { @@ -219,7 +234,7 @@ namespace THex { std::string hexStr = "0x1234"; Hex hex(hexStr, false); Hex hexStrict(hexStr, true); - hex += Hex(std::move(std::string("0x5678")), false); + hex += Hex(std::move(std::string("5678")), false); hexStrict += Hex(std::move(std::string("0x5678")), true); REQUIRE_THAT(hex.get(), Equals("12345678")); REQUIRE_THAT(hexStrict.get(), Equals("0x12345678")); diff --git a/tests/utils/intconv.cpp b/tests/utils/intconv.cpp index 97d514a8..13e953f2 100644 --- a/tests/utils/intconv.cpp +++ b/tests/utils/intconv.cpp @@ -16,10 +16,17 @@ using Catch::Matchers::Equals; namespace TUtils { TEST_CASE("IntConv Namespace", "[utils][intconv]") { SECTION("int256ToBytes Test") { - int256_t int256Input = int256_t("91830918212381802449294565349763096207758814059154440393436864477986483867239"); + // Positive int + int256_t int256Input = int256_t("23961171024934392974276419658924811645511170606486123646020719529926645772697"); auto int256Output = IntConv::int256ToBytes(int256Input); - BytesArr<32> int256ExpectedOutput = BytesArr<32> {0xcb, 0x06, 0x75, 0x32, 0x90, 0xff, 0xac, 0x16, 0x72, 0x05, 0xd0, 0xf5, 0x3b, 0x64, 0xac, 0xfd, 0x80, 0xbe, 0x11, 0xed, 0xbb, 0x26, 0xa2, 0x24, 0xbe, 0xd9, 0x23, 0x9a, 0xe6, 0x74, 0x0e, 0x67}; + BytesArr<32> int256ExpectedOutput = BytesArr<32> {0x34, 0xf9, 0x8a, 0xcd, 0x6f, 0x00, 0x53, 0xe9, 0x8d, 0xfa, 0x2f, 0x0a, 0xc4, 0x9b, 0x53, 0x02, 0x7f, 0x41, 0xee, 0x12, 0x44, 0xd9, 0x5d, 0xdb, 0x41, 0x26, 0xdc, 0x65, 0x19, 0x8b, 0xf1, 0x99}; REQUIRE(int256Output == int256ExpectedOutput); + + // Negative int + int256_t int256Input2 = int256_t("-23961171024934392974276419658924811645511170606486123646020719529926645772697"); + auto int256Output2 = IntConv::int256ToBytes(int256Input2); + BytesArr<32> int256ExpectedOutput2 = BytesArr<32> {0xcb, 0x06, 0x75, 0x32, 0x90, 0xff, 0xac, 0x16, 0x72, 0x05, 0xd0, 0xf5, 0x3b, 0x64, 0xac, 0xfd, 0x80, 0xbe, 0x11, 0xed, 0xbb, 0x26, 0xa2, 0x24, 0xbe, 0xd9, 0x23, 0x9a, 0xe6, 0x74, 0x0e, 0x67}; + REQUIRE(int256Output2 == int256ExpectedOutput2); } SECTION("int136ToBytes Test") { diff --git a/tests/utils/jsonabi.cpp b/tests/utils/jsonabi.cpp index 8f4ab66b..262b7fb8 100644 --- a/tests/utils/jsonabi.cpp +++ b/tests/utils/jsonabi.cpp @@ -16,6 +16,9 @@ namespace TJsonAbi { std::string type2 = "int"; REQUIRE(JsonAbi::isArray(type1)); REQUIRE_FALSE(JsonAbi::isArray(type2)); + // For coverage + REQUIRE_FALSE(JsonAbi::isArray("int()")); // "()" != "[]" + REQUIRE_FALSE(JsonAbi::isArray("int[)")); // "[)" != "[]" } SECTION("JsonAbi isTuple") { @@ -25,6 +28,11 @@ namespace TJsonAbi { REQUIRE(JsonAbi::isTuple(type1)); REQUIRE(JsonAbi::isTuple(type2)); REQUIRE_FALSE(JsonAbi::isTuple(type3)); + // For coverage + REQUIRE_FALSE(JsonAbi::isTuple("{int, double, float}")); // "{}" != "()" + REQUIRE_FALSE(JsonAbi::isTuple("{int, double}[]")); + REQUIRE_FALSE(JsonAbi::isTuple("{int, double, float)")); // "{)" != "()" + REQUIRE_FALSE(JsonAbi::isTuple("{int, double)[]")); } SECTION("JsonAbi countTupleArrays") { @@ -36,6 +44,9 @@ namespace TJsonAbi { REQUIRE(JsonAbi::countTupleArrays(type1) == 1); REQUIRE(JsonAbi::countTupleArrays(type2) == 2); REQUIRE(JsonAbi::countTupleArrays(type3) == 3); + // For coverage + REQUIRE(JsonAbi::countTupleArrays("(int, double){}") == 0); // "{}" != "[]" + REQUIRE(JsonAbi::countTupleArrays("(int, double){]") == 0); // "{]" != "[]" } SECTION("JsonAbi getTupleTypes") { diff --git a/tests/utils/merkle.cpp b/tests/utils/merkle.cpp index 7e150635..a95e8ddb 100644 --- a/tests/utils/merkle.cpp +++ b/tests/utils/merkle.cpp @@ -23,7 +23,7 @@ namespace TMerkle { StrConv::stringToBytes("jk"), StrConv::stringToBytes("km"), StrConv::stringToBytes("mn") }; std::vector hashedLeafs; - for(const Bytes& leaf : unhashedLeafs) hashedLeafs.emplace_back(Utils::sha3(leaf)); + for (const Bytes& leaf : unhashedLeafs) hashedLeafs.emplace_back(Utils::sha3(leaf)); Merkle tree(hashedLeafs); std::vector proof = tree.getProof(3); @@ -34,6 +34,9 @@ namespace TMerkle { REQUIRE_THAT(root.hex(), Equals("3fb0308018d8a6b4c2081699003624e9719774be2b7f65b7f9ac45f2bebc20b7")); REQUIRE(Merkle::verify(proof, leaf, root)); REQUIRE(!Merkle::verify(proof, badLeaf, root)); + + // For coverage + REQUIRE(tree.getProof(999).empty()); } SECTION("Random Merkle Tree") { diff --git a/tests/utils/options.cpp b/tests/utils/options.cpp index a3026226..568d9f21 100644 --- a/tests/utils/options.cpp +++ b/tests/utils/options.cpp @@ -27,6 +27,10 @@ namespace TOptions { if (std::filesystem::exists(testDumpPath + "/optionClassFromFileWithPrivKey")) { std::filesystem::remove_all(testDumpPath + "/optionClassFromFileWithPrivKey"); } + if (std::filesystem::exists(testDumpPath + "/optionClassFromFileWithoutPrivKey")) { + std::filesystem::remove_all(testDumpPath + "/optionClassFromFileWithoutPrivKey"); + } + PrivKey genesisPrivKey(Hex::toBytes("0xe89ef6409c467285bcae9f80ab1cfeb3487cfe61ab28fb7d36443e1daa0c2867")); uint64_t genesisTimestamp = 1678887538000000; FinalizedBlock genesis = FinalizedBlock::createNewValidBlock({},{}, Hash(), genesisTimestamp, 0, genesisPrivKey); @@ -35,6 +39,8 @@ namespace TOptions { for (const auto& privKey : validatorPrivKeys_) { genesisValidators.push_back(Secp256k1::toAddress(Secp256k1::toUPub(privKey))); } + + // Options WITH PrivKey Options optionsWithPrivKey( testDumpPath + "/optionClassFromFileWithPrivKey", "BDK/cpp/linux_x86-64/0.2.0", @@ -91,6 +97,63 @@ namespace TOptions { REQUIRE(optionsFromFileWithPrivKey.getGenesisBlock() == optionsWithPrivKey.getGenesisBlock()); REQUIRE(optionsFromFileWithPrivKey.getGenesisBalances() == optionsWithPrivKey.getGenesisBalances()); REQUIRE(optionsFromFileWithPrivKey.getGenesisValidators() == optionsWithPrivKey.getGenesisValidators()); + + // Options WITHOUT PrivKey + Options optionsWithoutPrivKey( + testDumpPath + "/optionClassFromFileWithoutPrivKey", + "BDK/cpp/linux_x86-64/0.2.0", + 1, + 8080, + Address(Hex::toBytes("0x00dead00665771855a34155f5e7405489df2c3c6")), + LOCALHOST, + 8080, + 8081, + 11, + 11, + 200, + 50, + 2000, + 10000, + 1000, + 4, + { + {boost::asio::ip::address_v4::from_string("127.0.0.1"), uint64_t(8000)}, + {boost::asio::ip::address_v4::from_string("127.0.0.1"), uint64_t(8001)} + }, + genesis, + genesisTimestamp, + genesisPrivKey, + genesisBalances, + genesisValidators, + IndexingMode::RPC + ); + + Options optionsFromFileWithoutPrivKey(Options::fromFile(testDumpPath + "/optionClassFromFileWithoutPrivKey")); + REQUIRE(optionsFromFileWithoutPrivKey.getRootPath() == optionsWithoutPrivKey.getRootPath()); + REQUIRE(optionsFromFileWithoutPrivKey.getMajorSDKVersion() == optionsWithoutPrivKey.getMajorSDKVersion()); + REQUIRE(optionsFromFileWithoutPrivKey.getMinorSDKVersion() == optionsWithoutPrivKey.getMinorSDKVersion()); + REQUIRE(optionsFromFileWithoutPrivKey.getPatchSDKVersion() == optionsWithoutPrivKey.getPatchSDKVersion()); + REQUIRE(optionsFromFileWithoutPrivKey.getWeb3ClientVersion() == optionsWithoutPrivKey.getWeb3ClientVersion()); + REQUIRE(optionsFromFileWithoutPrivKey.getVersion() == optionsWithoutPrivKey.getVersion()); + REQUIRE(optionsFromFileWithoutPrivKey.getChainOwner() == optionsWithoutPrivKey.getChainOwner()); + REQUIRE(optionsFromFileWithoutPrivKey.getChainID() == optionsWithoutPrivKey.getChainID()); + REQUIRE(optionsFromFileWithoutPrivKey.getP2PIp() == optionsWithoutPrivKey.getP2PIp()); + REQUIRE(optionsFromFileWithoutPrivKey.getP2PPort() == optionsWithoutPrivKey.getP2PPort()); + REQUIRE(optionsFromFileWithoutPrivKey.getHttpPort() == optionsWithoutPrivKey.getHttpPort()); + REQUIRE(optionsFromFileWithoutPrivKey.getMinDiscoveryConns() == optionsWithoutPrivKey.getMinDiscoveryConns()); + REQUIRE(optionsFromFileWithoutPrivKey.getMinNormalConns() == optionsWithoutPrivKey.getMinNormalConns()); + REQUIRE(optionsFromFileWithoutPrivKey.getMaxDiscoveryConns() == optionsWithoutPrivKey.getMaxDiscoveryConns()); + REQUIRE(optionsFromFileWithoutPrivKey.getMaxNormalConns() == optionsWithoutPrivKey.getMaxNormalConns()); + REQUIRE(optionsFromFileWithoutPrivKey.getEventBlockCap() == optionsWithoutPrivKey.getEventBlockCap()); + REQUIRE(optionsFromFileWithoutPrivKey.getEventLogCap() == optionsWithoutPrivKey.getEventLogCap()); + REQUIRE(optionsFromFileWithoutPrivKey.getStateDumpTrigger() == optionsWithoutPrivKey.getStateDumpTrigger()); + REQUIRE(optionsFromFileWithoutPrivKey.getMinValidators() == optionsWithoutPrivKey.getMinValidators()); + REQUIRE(optionsFromFileWithoutPrivKey.getCoinbase() == optionsWithoutPrivKey.getCoinbase()); + REQUIRE(optionsFromFileWithoutPrivKey.getIsValidator() == optionsWithoutPrivKey.getIsValidator()); + REQUIRE(optionsFromFileWithoutPrivKey.getDiscoveryNodes() == optionsWithoutPrivKey.getDiscoveryNodes()); + REQUIRE(optionsFromFileWithoutPrivKey.getGenesisBlock() == optionsWithoutPrivKey.getGenesisBlock()); + REQUIRE(optionsFromFileWithoutPrivKey.getGenesisBalances() == optionsWithoutPrivKey.getGenesisBalances()); + REQUIRE(optionsFromFileWithoutPrivKey.getGenesisValidators() == optionsWithoutPrivKey.getGenesisValidators()); } SECTION("IndexingMode Coverage") { diff --git a/tests/utils/strconv.cpp b/tests/utils/strconv.cpp index 1ebed7cf..21c4e25a 100644 --- a/tests/utils/strconv.cpp +++ b/tests/utils/strconv.cpp @@ -16,59 +16,111 @@ using Catch::Matchers::Equals; namespace TUtils { TEST_CASE("StrConv Namespace", "[utils][strconv]") { SECTION("padLeft Test") { - std::string input = "abcdef"; - std::string output = StrConv::padLeft(input, 10, '0'); - std::string output2 = StrConv::padLeft(input, 20, '1'); - std::string expectedOutput = "0000abcdef"; - std::string expectedOutput2 = "11111111111111abcdef"; - REQUIRE(output == expectedOutput); - REQUIRE(output2 == expectedOutput2); + std::string in = "abcdef"; + std::string out = StrConv::padLeft(in, 10, '0'); + std::string out2 = StrConv::padLeft(in, 20, '1'); + std::string expOut = "0000abcdef"; + std::string expOut2 = "11111111111111abcdef"; + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check 0x (for coverage) + in = "0xabcdef"; + out = StrConv::padLeft(in, 10, '0'); + out2 = StrConv::padLeft(in, 20, '1'); + expOut = "0x0000abcdef"; + expOut2 = "0x11111111111111abcdef"; + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check 0X (for coverage) + in = "0Xabcdef"; + out = StrConv::padLeft(in, 10, '0'); + out2 = StrConv::padLeft(in, 20, '1'); + expOut = "0x0000abcdef"; + expOut2 = "0x11111111111111abcdef"; + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check no padding (for coverage) + in = "abcdef"; + out = StrConv::padLeft(in, 4, '0'); + expOut = "abcdef"; + REQUIRE(out == expOut); } SECTION("padRight Test") { - std::string input = "abcdef"; - std::string output = StrConv::padRight(input, 10, '0'); - std::string output2 = StrConv::padRight(input, 20, '1'); - std::string expectedOutput = "abcdef0000"; - std::string expectedOutput2 = "abcdef11111111111111"; - REQUIRE(output == expectedOutput); - REQUIRE(output2 == expectedOutput2); + std::string in = "abcdef"; + std::string out = StrConv::padRight(in, 10, '0'); + std::string out2 = StrConv::padRight(in, 20, '1'); + std::string expOut = "abcdef0000"; + std::string expOut2 = "abcdef11111111111111"; + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check 0x (for coverage) + in = "0xabcdef"; + out = StrConv::padRight(in, 10, '0'); + out2 = StrConv::padRight(in, 20, '1'); + expOut = "0xabcdef0000"; + expOut2 = "0xabcdef11111111111111"; + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check 0X (for coverage) + in = "0Xabcdef"; + out = StrConv::padRight(in, 10, '0'); + out2 = StrConv::padRight(in, 20, '1'); + expOut = "0xabcdef0000"; + expOut2 = "0xabcdef11111111111111"; + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check no padding (for coverage) + in = "abcdef"; + out = StrConv::padRight(in, 4, '0'); + expOut = "abcdef"; + REQUIRE(out == expOut); } SECTION("padLeftBytes Test") { - Bytes inputBytes = Hex::toBytes("0xabcdef"); - Bytes outputBytes = StrConv::padLeftBytes(inputBytes, 10, 0x00); - Bytes outputBytes2 = StrConv::padLeftBytes(inputBytes, 20, 0x11); - Bytes expectedOutputBytes = Hex::toBytes("0x00000000000000abcdef"); - Bytes expectedOutputBytes2 = Hex::toBytes("0x1111111111111111111111111111111111abcdef"); - REQUIRE(outputBytes == expectedOutputBytes); - REQUIRE(outputBytes2 == expectedOutputBytes2); + Bytes in = Hex::toBytes("0xabcdef"); + Bytes out = StrConv::padLeftBytes(in, 10, 0x00); + Bytes out2 = StrConv::padLeftBytes(in, 20, 0x11); + Bytes expOut = Hex::toBytes("0x00000000000000abcdef"); + Bytes expOut2 = Hex::toBytes("0x1111111111111111111111111111111111abcdef"); + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check no padding (for coverage) + in = Hex::toBytes("0xabcdef"); + out = StrConv::padLeftBytes(in, 2, 0x00); + expOut = Hex::toBytes("0xabcdef"); + REQUIRE(out == expOut); } SECTION("padRightBytes Test") { - Bytes inputBytes = Hex::toBytes("0xabcdef"); - Bytes outputBytes = StrConv::padRightBytes(inputBytes, 10, 0x00); - Bytes outputBytes2 = StrConv::padRightBytes(inputBytes, 20, 0x11); - Bytes expectedOutputBytes = Hex::toBytes("0xabcdef00000000000000"); - Bytes expectedOutputBytes2 = Hex::toBytes("0xabcdef1111111111111111111111111111111111"); - REQUIRE(outputBytes == expectedOutputBytes); - REQUIRE(outputBytes2 == expectedOutputBytes2); + Bytes in = Hex::toBytes("0xabcdef"); + Bytes out = StrConv::padRightBytes(in, 10, 0x00); + Bytes out2 = StrConv::padRightBytes(in, 20, 0x11); + Bytes expOut = Hex::toBytes("0xabcdef00000000000000"); + Bytes expOut2 = Hex::toBytes("0xabcdef1111111111111111111111111111111111"); + REQUIRE(out == expOut); + REQUIRE(out2 == expOut2); + // Check no padding (for coverage) + in = Hex::toBytes("0xabcdef"); + out = StrConv::padRightBytes(in, 2, 0x00); + expOut = Hex::toBytes("0xabcdef"); + REQUIRE(out == expOut); } SECTION("toLower Test") { - std::string inputStr = "ABCDEF"; - std::string outputStr = inputStr; - StrConv::toLower(outputStr); - std::string expectedOutputStr = "abcdef"; - REQUIRE_THAT(outputStr, Equals(expectedOutputStr)); + std::string in = "ABCDEF"; + std::string out = in; + StrConv::toLower(out); + std::string expOut = "abcdef"; + REQUIRE_THAT(out, Equals(expOut)); } SECTION("toUpper Test") { - std::string inputStr = "abcdef"; - std::string outputStr = inputStr; - StrConv::toUpper(outputStr); - std::string expectedOutputStr = "ABCDEF"; - REQUIRE_THAT(outputStr, Equals(expectedOutputStr)); + std::string in = "abcdef"; + std::string out = in; + StrConv::toUpper(out); + std::string expOut = "ABCDEF"; + REQUIRE_THAT(out, Equals(expOut)); } SECTION("bytesToString Test") { diff --git a/tests/utils/strings.cpp b/tests/utils/strings.cpp index c40a7352..4a5ec2d1 100644 --- a/tests/utils/strings.cpp +++ b/tests/utils/strings.cpp @@ -9,6 +9,8 @@ See the LICENSE.txt file in the project root for more information. #include "../../src/utils/strings.h" // bytes/initializer.h -> bytes/view.h +#include "../../src/utils/strconv.h" + using Catch::Matchers::Equals; namespace TFixedStr { @@ -27,6 +29,13 @@ namespace TFixedStr { REQUIRE_THROWS(FixedBytes<20>(ilist)); } + SECTION("FixedBytes Bytes Range Constructor") { + Bytes b{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}; + FixedBytes<10> str1(b); + Bytes b2{0xab, 0xcd, 0xef}; + REQUIRE_THROWS(FixedBytes<5>(b2)); + } + SECTION("FixedBytes Copy Bytes Constructor") { FixedBytes<10> str1(bytes::view("1234567890")); FixedBytes<10> str2(bytes::view("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")); @@ -55,6 +64,14 @@ namespace TFixedStr { REQUIRE_THAT(std::string(reinterpret_cast(str2.data()), str2.size()), Equals("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")); } + SECTION("FixedBytes view()") { + FixedBytes<10> str(bytes::view("1234567890")); + REQUIRE(StrConv::bytesToString(str.view()) == "1234567890"); + REQUIRE(StrConv::bytesToString(str.view(0, 3)) == "123"); + REQUIRE(StrConv::bytesToString(str.view(5, 3)) == "678"); + REQUIRE_THROWS(str.view(12)); + } + SECTION("FixedBytes hex()") { FixedBytes<10> str1(bytes::view("1234567890")); FixedBytes<10> str2(bytes::view("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")); @@ -208,6 +225,17 @@ namespace TSignature { namespace TAddress { TEST_CASE("Address Class", "[utils][strings][address]") { + SECTION("Address String View Constructor") { + Address addStr(std::string_view("0x71c7656ec7ab88b098defb751b7401b5f6d8976f"), false); + Address addBytes(std::string_view("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97\x6f"), true); + REQUIRE(addStr == addBytes); + REQUIRE_THAT(addStr.hex(), Equals("71c7656ec7ab88b098defb751b7401b5f6d8976f")); + REQUIRE_THAT(addBytes.hex(), Equals("71c7656ec7ab88b098defb751b7401b5f6d8976f")); + // For coverage + REQUIRE_THROWS(Address(std::string_view("0x71c7656ec7ab88b098defb751b7401b5f6d8976h"), false)); // last char is "h" + REQUIRE_THROWS(Address("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97", true)); // missing last byte "\x6f" + } + SECTION("Address Copy Constructor") { Address addr1(Bytes({0x71, 0xc7, 0x65, 0x6e, 0xc7, 0xab, 0x88, 0xb0, 0x98, 0xde, 0xfb, 0x75, 0x1b, 0x74, 0x01, 0xb5, 0xf6, 0xd8, 0x97, 0x6f})); Address addr2(std::string("\x71\xc7\x65\x6e\xc7\xab\x88\xb0\x98\xde\xfb\x75\x1b\x74\x01\xb5\xf6\xd8\x97\x6f"), true); @@ -225,14 +253,34 @@ namespace TAddress { } SECTION("Address isValid") { - std::string addHex = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"; - std::string addHexNoPrefix = "fb6916095ca1df60bb79ce92ce3ea74c37c5d359"; + // Bytes first as it's simpler std::string addBytes = "\xfb\x69\x16\x09\x5c\xa1\xdf\x60\xbb\x79\xce\x92\xce\x3e\xa7\x4c\x37\xc5\xd3\x59"; - REQUIRE(Address::isValid(addHex, false)); + std::string addBytesWrongSize = "\xfb\x69\x16\x09\x5c\xa1\xdf\x60\xbb\x79\xce\x92\xce\x3e\xa7\x4c\x37\xc5\xd3"; // missing last byte "\x59" REQUIRE(Address::isValid(addBytes, true)); - REQUIRE(Address::isValid(addHexNoPrefix, false)); - REQUIRE(!Address::isValid(addHex, true)); REQUIRE(!Address::isValid(addBytes, false)); + REQUIRE(!Address::isValid(addBytesWrongSize, true)); + // Hex with prefix + std::string addHexPrefix = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"; + std::string addHexPrefixCaps = "0XFB6916095CA1DF60BB79CE92CE3EA74C37C5D359"; + std::string addHexPrefixWrongSize = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d3"; // missing last "59" + std::string addHexPrefixWrongFormat = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d3gh"; // last "gh" is invalid + REQUIRE(Address::isValid(addHexPrefix, false)); + REQUIRE(Address::isValid(addHexPrefixCaps, false)); + REQUIRE(!Address::isValid(addHexPrefix, true)); + REQUIRE(!Address::isValid(addHexPrefixCaps, true)); + REQUIRE(!Address::isValid(addHexPrefixWrongSize, false)); + REQUIRE(!Address::isValid(addHexPrefixWrongFormat, false)); + // Hex without prefix + std::string addHexNoPrefix = "fb6916095ca1df60bb79ce92ce3ea74c37c5d359"; + std::string addHexNoPrefixCaps = "FB6916095CA1DF60BB79CE92CE3EA74C37C5D359"; + std::string addHexNoPrefixWrongSize = "fb6916095ca1df60bb79ce92ce3ea74c37c5d3"; // missing last "59" + std::string addHexNoPrefixWrongFormat = "fb6916095ca1df60bb79ce92ce3ea74c37c5d3gh"; // last "gh" is invalid + REQUIRE(Address::isValid(addHexNoPrefix, false)); + REQUIRE(Address::isValid(addHexNoPrefixCaps, false)); + REQUIRE(!Address::isValid(addHexNoPrefix, true)); + REQUIRE(!Address::isValid(addHexNoPrefixCaps, true)); + REQUIRE(!Address::isValid(addHexNoPrefixWrongSize, false)); + REQUIRE(!Address::isValid(addHexNoPrefixWrongFormat, false)); } SECTION("Address isChksum") { @@ -276,6 +324,10 @@ namespace TStorageKey { REQUIRE_THAT(Hex::fromBytes(key3.asBytes()).get(), Equals("ffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); REQUIRE_THAT(Hex::fromBytes(key4.asBytes()).get(), Equals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); REQUIRE_THAT(Hex::fromBytes(key5.asBytes()).get(), Equals("1234567890123456789012345678901234567890aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffff0000000099999999")); + + // Testing throw for coverage + FixedBytes<5> keyWrongSize({0x00, 0x00, 0x00, 0x00, 0x00}); + REQUIRE_THROWS(StorageKey(keyWrongSize.view())); } } } diff --git a/tests/utils/tx.cpp b/tests/utils/tx.cpp index 74e0925e..71fcfbbe 100644 --- a/tests/utils/tx.cpp +++ b/tests/utils/tx.cpp @@ -782,7 +782,8 @@ namespace TTX { SECTION("TxBlock operator= (copy)") { // Copied from Simple Transaction 1 TxBlock tx(Hex::toBytes("0x02f87501826e5c8402faf0808510b3a67cc282520894eb38eab2a8d5f448d7d47005b64697e159aa284e88078cbf1fc56d4f1080c080a0763458eaffb9745026fc6360443e7ff8d171824d0410d48fdf06c08c7d4a8306a031b3d8f1753acc4239ffe0584536f12095651f72a61c684ef221aaa97a315328"), 1); - TxBlock txCopy = tx; + TxBlock txCopy(Hex::toBytes("0x02f87301808405f5e100850b5b977f998252089495944f9d42e181d76bb2c7e428410533aa3fed4a88012386f1806fe51080c080a0102fc0316ef07a9be233a270cdeb692e1666710bbdb8be67bf7d896fa96c6bafa038b6cbfdeb433911da6958a9dd3ac24d4ff39f11d1b985efca6d6d79a96a62ce"), 1); + txCopy = tx; // "TxBlock txCopy = tx;" doesn't count as "covered" in SonarQube for some reason REQUIRE(txCopy.getNonce() == 28252); REQUIRE(txCopy.getMaxFeePerGas() == uint256_t("71733509314")); REQUIRE(txCopy.getMaxPriorityFeePerGas() == uint256_t("50000000")); diff --git a/tests/utils/utils.cpp b/tests/utils/utils.cpp index 981a2d65..8d88a73e 100644 --- a/tests/utils/utils.cpp +++ b/tests/utils/utils.cpp @@ -20,6 +20,36 @@ namespace TUtils { Utils::logToFile("testing log to file"); } + SECTION("Account struct") { + // Copy constructor + Account a(uint256_t(1000), uint64_t(0)); + REQUIRE(a.balance == uint256_t(1000)); + REQUIRE(a.nonce == uint64_t(0)); + // Move constructor + uint256_t bal = 2000; + uint64_t nonce = 1; + Account b(std::move(bal), std::move(nonce)); + REQUIRE(b.balance == 2000); + REQUIRE(b.nonce == 1); + // Deserialize constructor + Bytes ser = a.serialize(); + Account c(ser); + REQUIRE(c.balance == 1000); + REQUIRE(c.nonce == 0); + // For coverage + ser[72] = 0xFF; // Invalid contract type + REQUIRE_THROWS(Account(ser)); + ser.pop_back(); // Invalid bytes size (one less) + REQUIRE_THROWS(Account(ser)); + } + + SECTION("safePrint") { + Utils::safePrint("Fail!"); + Log::logToCout = true; + Utils::safePrint("Win!"); + Log::logToCout = false; // Other tests litter terminal output with extra info if we forget to do this + } + SECTION("sha3") { std::string sha3Input = "My SHA3 Input"; auto sha3Output = Utils::sha3(StrConv::stringToBytes(sha3Input)); @@ -83,6 +113,21 @@ namespace TUtils { REQUIRE_THROWS(Utils::create_view_span(b, 0, 12)); REQUIRE_THROWS(Utils::create_view_span(sv, 0, 12)); } + + SECTION("readConfigFile") { + if (std::filesystem::exists("config.json")) { + std::filesystem::remove("config.json"); + } + json j = Utils::readConfigFile(); // Creating from scratch + json j2 = Utils::readConfigFile(); // Loading from default + REQUIRE(j == j2); + REQUIRE(j["rpcport"] == 8080); + REQUIRE(j["p2pport"] == 8081); + REQUIRE(j["seedNodes"][0] == "127.0.0.1:8086"); + REQUIRE(j["seedNodes"][1] == "127.0.0.1:8087"); + REQUIRE(j["seedNodes"][2] == "127.0.0.1:8088"); + REQUIRE(j["seedNodes"][3] == "127.0.0.1:8089"); + } } }