Skip to content

Commit

Permalink
Tests for EOF implementation. Note: The exception field of the json i…
Browse files Browse the repository at this point in the history
…s not being tested.
  • Loading branch information
gzanitti committed Aug 18, 2023
1 parent 095f83a commit 16f1035
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 41 deletions.
7 changes: 6 additions & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -474,13 +474,18 @@ jobs:
bin/evmone-statetest ~/tests/GeneralStateTests ~/tests/LegacyTests/Constantinople/GeneralStateTests
- download_execution_tests:
repo: ipsilon/tests
rev: eof-rjumpv-20230803
rev: update-tests
legacy: false
- run:
name: "State tests (EOF)"
working_directory: ~/build
command: |
bin/evmone-statetest ~/tests/EIPTests/StateTests/stEOF
- run:
name: "EOF validation tests"
working_directory: ~/build
command: |
bin/evmone-eoftest ~/tests/EOFTests
- collect_coverage_gcc
- upload_coverage:
flags: statetests
Expand Down
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ add_subdirectory(integration)
add_subdirectory(internal_benchmarks)
add_subdirectory(state)
add_subdirectory(statetest)
add_subdirectory(eoftest)
add_subdirectory(t8n)
add_subdirectory(unittests)

set(targets evmone-bench evmone-bench-internal evmone-eofparse evmone-state evmone-statetest evmone-t8n evmone-unittests)
set(targets evmone-bench evmone-bench-internal evmone-eofparse evmone-state evmone-statetest evmone-eoftest evmone-t8n evmone-unittests)

if(EVMONE_FUZZING)
add_subdirectory(eofparsefuzz)
Expand Down
3 changes: 3 additions & 0 deletions test/eoftest/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
InheritParentConfig: true
Checks: >
-clang-analyzer-cplusplus.NewDeleteLeaks
15 changes: 15 additions & 0 deletions test/eoftest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# evmone: Fast Ethereum Virtual Machine implementation
# Copyright 2023 The evmone Authors.
# SPDX-License-Identifier: Apache-2.0

hunter_add_package(nlohmann_json)
find_package(nlohmann_json CONFIG REQUIRED)

add_executable(evmone-eoftest)
target_link_libraries(evmone-eoftest PRIVATE evmone nlohmann_json::nlohmann_json GTest::gtest)
target_include_directories(evmone-eoftest PRIVATE ${evmone_private_include_dir})
target_sources(
evmone-eoftest PRIVATE
eoftest.cpp
eoftest_runner.cpp
)
87 changes: 87 additions & 0 deletions test/eoftest/eoftest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0


#include "eoftest.hpp"
#include <CLI/CLI.hpp>
#include <evmone/evmone.h>
#include <gtest/gtest.h>
#include <filesystem>

namespace fs = std::filesystem;

namespace
{

class EOFTest : public testing::Test
{
fs::path m_json_test_file;

public:
explicit EOFTest(fs::path json_test_file) noexcept : m_json_test_file{std::move(json_test_file)}
{}

void TestBody() final
{
std::ifstream f{m_json_test_file};
evmone::test::run_eof_test(f);
}
};

void register_test(const std::string& suite_name, const fs::path& file)
{
testing::RegisterTest(suite_name.c_str(), file.stem().string().c_str(), nullptr, nullptr,
file.string().c_str(), 0, [file]() -> testing::Test* { return new EOFTest(file); });
}

void register_test_files(const fs::path& root)
{
if (is_directory(root))
{
std::vector<fs::path> test_files;
std::copy_if(fs::recursive_directory_iterator{root}, fs::recursive_directory_iterator{},
std::back_inserter(test_files), [](const fs::directory_entry& entry) {
return entry.is_regular_file() && entry.path().extension() == ".json";
});
std::sort(test_files.begin(), test_files.end());

for (const auto& p : test_files)
register_test(fs::relative(p, root).parent_path().string(), p);
}
else // Treat as a file.
{
register_test(root.parent_path().string(), root);
}
}

} // namespace


int main(int argc, char* argv[])
{
try
{
testing::InitGoogleTest(&argc, argv);
CLI::App app{"evmone eof test runner"};

std::vector<std::string> paths;
app.add_option("path", paths, "Path to test file or directory")
->required()
->check(CLI::ExistingPath);

CLI11_PARSE(app, argc, argv);

for (const auto& p : paths)
{
register_test_files(p);
}

return RUN_ALL_TESTS();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << "\n";
return -1;
}
}
13 changes: 13 additions & 0 deletions test/eoftest/eoftest.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <iostream>

namespace evmone::test
{

void run_eof_test(std::istream& input);

} // namespace evmone::test
78 changes: 78 additions & 0 deletions test/eoftest/eoftest_runner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "../utils/utils.hpp"
#include "eoftest.hpp"
#include <evmc/evmc.hpp>
#include <evmone/eof.hpp>
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>

namespace json = nlohmann;

namespace
{
struct EOFValidationTest
{
struct Case
{
struct Expectation
{
evmc_revision rev;
bool result;
};
std::string name;
evmc::bytes code;
std::vector<Expectation> expectations;
};
std::unordered_map<std::string, Case> cases;
};

void from_json(const json::json& j, EOFValidationTest::Case& o)
{
const std::optional<evmc::bytes> hex_str{evmc::from_hex(j.at("code").get<std::string>())};
if (!hex_str)
throw std::invalid_argument{"invalid hex string"};
const evmc::bytes& code{hex_str.value()};
o.code = code;

for (const auto& [rev, result] : j.at("results").items())
{
o.expectations.push_back({to_rev(rev), result.at("result").get<bool>()});
}
}

void from_json(const json::json& j, EOFValidationTest& o)
{
if (!j.is_object() || j.empty())
throw std::invalid_argument{"JSON test must be an object with single key of the test name"};

const auto& j_t = *j.begin(); // Content is in a dict with the test name.

for (const auto& [name, test] : j_t.at("vectors").items())
{
o.cases.emplace(name, test.get<EOFValidationTest::Case>());
}
}
} // namespace

namespace evmone::test
{

void run_eof_test(std::istream& input)
{
const EOFValidationTest test = json::json::parse(input).get<EOFValidationTest>();
for (const auto& [name, cases] : test.cases)
{
for (const auto& expectation : cases.expectations)
{
const EOFValidationError result = evmone::validate_eof(expectation.rev, cases.code);
const bool b_result = (result == EOFValidationError::success);
EXPECT_EQ(b_result, expectation.result)
<< name << " " << expectation.rev << " " << hex(cases.code);
}
}
}

} // namespace evmone::test
3 changes: 0 additions & 3 deletions test/statetest/statetest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ namespace json = nlohmann;
namespace evmone::test
{

/// Translates tests fork name to EVM revision
evmc_revision to_rev(std::string_view s);

struct TestMultiTransaction : state::Transaction
{
struct Indexes
Expand Down
36 changes: 1 addition & 35 deletions test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

#include "../utils/stdx/utility.hpp"
#include "../utils/utils.hpp"
#include "statetest.hpp"
#include <evmone/eof.hpp>
#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -211,41 +212,6 @@ state::State from_json<state::State>(const json::json& j)
return o;
}

evmc_revision to_rev(std::string_view s)
{
if (s == "Frontier")
return EVMC_FRONTIER;
if (s == "Homestead")
return EVMC_HOMESTEAD;
if (s == "EIP150")
return EVMC_TANGERINE_WHISTLE;
if (s == "EIP158")
return EVMC_SPURIOUS_DRAGON;
if (s == "Byzantium")
return EVMC_BYZANTIUM;
if (s == "Constantinople")
return EVMC_CONSTANTINOPLE;
if (s == "ConstantinopleFix")
return EVMC_PETERSBURG;
if (s == "Istanbul")
return EVMC_ISTANBUL;
if (s == "Berlin")
return EVMC_BERLIN;
if (s == "London")
return EVMC_LONDON;
if (s == "Merge")
return EVMC_PARIS;
if (s == "Merge+3855") // PUSH0
return EVMC_SHANGHAI;
if (s == "Shanghai")
return EVMC_SHANGHAI;
if (s == "Cancun")
return EVMC_CANCUN;
if (s == "Prague")
return EVMC_PRAGUE;
throw std::invalid_argument{"unknown revision: " + std::string{s}};
}

/// Load common parts of Transaction or TestMultiTransaction.
static void from_json_tx_common(const json::json& j, state::Transaction& o)
{
Expand Down
3 changes: 2 additions & 1 deletion test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "../state/mpt_hash.hpp"
#include "../state/rlp.hpp"
#include "../statetest/statetest.hpp"
#include "../utils/utils.hpp"
#include <evmone/evmone.h>
#include <evmone/version.h>
#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -44,7 +45,7 @@ int main(int argc, const char* argv[])
return 0;
}
if (arg == "--state.fork" && ++i < argc)
rev = evmone::test::to_rev(argv[i]);
rev = to_rev(argv[i]);
else if (arg == "--input.alloc" && ++i < argc)
alloc_file = argv[i];
else if (arg == "--input.env" && ++i < argc)
Expand Down
37 changes: 37 additions & 0 deletions test/utils/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <evmc/evmc.hpp>
#include <evmc/hex.hpp>

using evmc::bytes;
Expand All @@ -27,3 +28,39 @@ inline bytes operator""_hex(const char* s, size_t size)
{
return from_spaced_hex({s, size}).value();
}

/// Translates tests fork name to EVM revision
inline evmc_revision to_rev(std::string_view s)
{
if (s == "Frontier")
return EVMC_FRONTIER;
if (s == "Homestead")
return EVMC_HOMESTEAD;
if (s == "EIP150")
return EVMC_TANGERINE_WHISTLE;
if (s == "EIP158")
return EVMC_SPURIOUS_DRAGON;
if (s == "Byzantium")
return EVMC_BYZANTIUM;
if (s == "Constantinople")
return EVMC_CONSTANTINOPLE;
if (s == "ConstantinopleFix")
return EVMC_PETERSBURG;
if (s == "Istanbul")
return EVMC_ISTANBUL;
if (s == "Berlin")
return EVMC_BERLIN;
if (s == "London")
return EVMC_LONDON;
if (s == "Merge")
return EVMC_PARIS;
if (s == "Merge+3855") // PUSH0
return EVMC_SHANGHAI;
if (s == "Shanghai")
return EVMC_SHANGHAI;
if (s == "Cancun")
return EVMC_CANCUN;
if (s == "Prague")
return EVMC_PRAGUE;
throw std::invalid_argument{"unknown revision: " + std::string{s}};
}

0 comments on commit 16f1035

Please sign in to comment.