diff --git a/circle.yml b/circle.yml index b7a9027ec5..9d05756394 100644 --- a/circle.yml +++ b/circle.yml @@ -15,8 +15,6 @@ jobs: - run: name: "Run codespell" command: | - sudo pip3 install --upgrade pip setuptools - sudo pip3 install codespell codespell --quiet-level=4 --ignore-words=./.codespell-whitelist build: &build diff --git a/cmake/HunterConfig.cmake b/cmake/HunterConfig.cmake index 385dd2bbcb..766ba9d818 100644 --- a/cmake/HunterConfig.cmake +++ b/cmake/HunterConfig.cmake @@ -1,6 +1,6 @@ # EVMC: Ethereum Client-VM Connector API. -# Copyright 2018 The EVMC Authors. -# Licensed under the Apache License, Version 2.0. See the LICENSE file. +# Copyright 2019 The EVMC Authors. +# Licensed under the Apache License, Version 2.0. # Setup Hunter. # Hunter is going to be initialized only if building with tests, diff --git a/lib/loader/loader.c b/lib/loader/loader.c index 12b7407b80..c01d337e64 100644 --- a/lib/loader/loader.c +++ b/lib/loader/loader.c @@ -1,6 +1,6 @@ /* EVMC: Ethereum Client-VM Connector API. - * Copyright 2018 The EVMC Authors. - * Licensed under the Apache License, Version 2.0. See the LICENSE file. + * Copyright 2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. */ #include @@ -11,25 +11,29 @@ #include #include -#if _WIN32 +#if defined(EVMC_LOADER_MOCK) +#include "../../test/unittests/loader_mock.h" +#elif _WIN32 #include #define DLL_HANDLE HMODULE #define DLL_OPEN(filename) LoadLibrary(filename) #define DLL_CLOSE(handle) FreeLibrary(handle) #define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) GetProcAddress(handle, name) -#define HAVE_STRCPY_S 1 #else #include #define DLL_HANDLE void* #define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY) #define DLL_CLOSE(handle) dlclose(handle) #define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) dlsym(handle, name) -#define HAVE_STRCPY_S 0 #endif #define PATH_MAX_LENGTH 4096 -#if !HAVE_STRCPY_S +#if !_WIN32 +/* + * Provide strcpy_s() for GNU libc. + * The availability check might need to adjusted for other C standard library implementations. + */ static void strcpy_s(char* dest, size_t destsz, const char* src) { size_t len = strlen(src); diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 97fa06fbda..2f7a0b2bf2 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -1,50 +1,19 @@ # EVMC: Ethereum Client-VM Connector API. -# Copyright 2018 The EVMC Authors. -# Licensed under the Apache License, Version 2.0. See the LICENSE file. +# Copyright 2019 The EVMC Authors. +# Licensed under the Apache License, Version 2.0. -add_library(vm-mock SHARED vm_mock.c) -target_link_libraries(vm-mock PRIVATE evmc) - -add_library(vm-mock-default SHARED vm_mock_default.c) -target_link_libraries(vm-mock-default PRIVATE evmc) - - -if(UNIX) - set(cmd create_symlink) -else() - set(cmd copy) -endif() - -add_custom_command( - TARGET vm-mock POST_BUILD - - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ libaaa.so - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ double_prefix_aaa.evm - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ double-prefix-aaa.evm - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ eee-bbb.dll - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ libeee1.so - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ eee2.so - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ libeee3.x - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ eee4 - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ _ - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ lib_.so - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ ../aaa.evm - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ failure.vm - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ abi42.vm - - COMMAND ${CMAKE_COMMAND} -E ${cmd} $ default.evmc - - COMMAND ${CMAKE_COMMAND} -E touch empty.file -) +add_library(loader-mocked STATIC ${PROJECT_SOURCE_DIR}/lib/loader/loader.c) +target_link_libraries(loader-mocked PRIVATE evmc) +target_compile_definitions(loader-mocked PRIVATE EVMC_LOADER_MOCK=1) add_executable( evmc-test + loader_mock.h test_cpp.cpp test_helpers.cpp test_instructions.cpp test_loader.cpp ) -target_link_libraries(evmc-test PRIVATE evmc-example-vm instructions loader GTest::gtest GTest::main) +target_link_libraries(evmc-test PRIVATE loader-mocked evmc-example-vm instructions GTest::gtest GTest::main) set_target_properties(evmc-test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..) -add_dependencies(evmc-test vm-mock) diff --git a/test/unittests/loader_mock.h b/test/unittests/loader_mock.h new file mode 100644 index 0000000000..a012c8ff8d --- /dev/null +++ b/test/unittests/loader_mock.h @@ -0,0 +1,42 @@ +/* EVMC: Ethereum Client-VM Connector API. + * Copyright 2019 The EVMC Authors. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * @file + * The loader OS mock for opening DLLs. To be inserted in loader.c for unit tests. + */ + +static const int magic_handle = 0xE7AC; + +const char* evmc_test_library_path = NULL; +const char* evmc_test_library_symbol = NULL; +evmc_create_fn evmc_test_create_fn = NULL; + +static int evmc_test_load_library(const char* filename) +{ + if (filename && evmc_test_library_path && strcmp(filename, evmc_test_library_path) == 0) + return magic_handle; + return 0; +} + +static void evmc_test_free_library(int handle) +{ + (void)handle; +} + +static evmc_create_fn evmc_test_get_symbol_address(int handle, const char* symbol) +{ + if (handle != magic_handle) + return NULL; + + if (evmc_test_library_symbol && strcmp(symbol, evmc_test_library_symbol) == 0) + return evmc_test_create_fn; + return NULL; +} + +#define DLL_HANDLE int +#define DLL_OPEN(filename) evmc_test_load_library(filename) +#define DLL_CLOSE(handle) evmc_test_free_library(handle) +#define DLL_GET_CREATE_FN(handle, name) evmc_test_get_symbol_address(handle, name) diff --git a/test/unittests/test_loader.cpp b/test/unittests/test_loader.cpp index 3a3b332a53..38e34405c3 100644 --- a/test/unittests/test_loader.cpp +++ b/test/unittests/test_loader.cpp @@ -1,6 +1,6 @@ // EVMC: Ethereum Client-VM Connector API. -// Copyright 2018 The EVMC Authors. -// Licensed under the Apache License, Version 2.0. See the LICENSE file. +// Copyright 2019 The EVMC Authors. +// Licensed under the Apache License, Version 2.0. #include @@ -14,126 +14,115 @@ static constexpr bool is_windows = true; static constexpr bool is_windows = false; #endif -TEST(loader, nonexistent) +extern "C" { +extern const char* evmc_test_library_path; +extern const char* evmc_test_library_symbol; +extern evmc_create_fn evmc_test_create_fn; +} + +class loader : public ::testing::Test { - evmc_loader_error_code ec; - auto x = evmc_load("nonexistent", &ec); - EXPECT_EQ(ec, EVMC_LOADER_CANNOT_OPEN); - EXPECT_EQ(x, nullptr); +protected: + void setup(const char* path, const char* symbol, evmc_create_fn fn) noexcept + { + evmc_test_library_path = path; + evmc_test_library_symbol = symbol; + evmc_test_create_fn = fn; + } +}; - x = evmc_load("nonexistent", nullptr); - EXPECT_EQ(x, nullptr); +static evmc_instance* create_aaa() +{ + return (evmc_instance*)0xaaa; } -TEST(loader, longpath) +static evmc_instance* create_eee_bbb() { - std::vector path(5000, 'a'); - *path.rbegin() = 0; - - evmc_loader_error_code ec; - auto x = evmc_load(path.data(), &ec); - EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); - EXPECT_EQ(x, nullptr); + return (evmc_instance*)0xeeebbb; +} - x = evmc_load(path.data(), nullptr); - EXPECT_EQ(x, nullptr); +static evmc_instance* create_failure() +{ + return nullptr; } -TEST(loader, not_so) +static evmc_instance* create_abi42() { - auto path = "unittests/empty.file"; + static int abi_version = 42; + return reinterpret_cast(&abi_version); +} +TEST_F(loader, load_nonexistent) +{ + constexpr auto path = "nonexistent"; evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); + EXPECT_EQ(evmc_load(path, &ec), nullptr); EXPECT_EQ(ec, EVMC_LOADER_CANNOT_OPEN); - EXPECT_EQ(x, nullptr); - - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); + EXPECT_EQ(evmc_load(path, nullptr), nullptr); } -TEST(loader, null_path) +TEST_F(loader, load_long_path) { + const std::string path(5000, 'a'); evmc_loader_error_code ec; - auto x = evmc_load(nullptr, &ec); + EXPECT_EQ(evmc_load(path.c_str(), &ec), nullptr); EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); - EXPECT_EQ(x, nullptr); - - x = evmc_load(nullptr, nullptr); - EXPECT_EQ(x, nullptr); + EXPECT_EQ(evmc_load(path.c_str(), nullptr), nullptr); } -TEST(loader, empty_path) +TEST_F(loader, load_null_path) { evmc_loader_error_code ec; - auto x = evmc_load("", &ec); + EXPECT_EQ(evmc_load(nullptr, &ec), nullptr); EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); - EXPECT_EQ(x, nullptr); - - x = evmc_load("", nullptr); - EXPECT_EQ(x, nullptr); + EXPECT_EQ(evmc_load(nullptr, nullptr), nullptr); } -TEST(loader, aaa) +TEST_F(loader, load_empty_path) { - auto path = "unittests/libaaa.so"; - evmc_loader_error_code ec; - auto fn = evmc_load(path, &ec); - ASSERT_NE(fn, nullptr); - EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); - EXPECT_EQ((uintptr_t)fn(), 0xaaa); - - fn = evmc_load(path, nullptr); - ASSERT_NE(fn, nullptr); - EXPECT_EQ((uintptr_t)fn(), 0xaaa); + EXPECT_EQ(evmc_load("", &ec), nullptr); + EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); + EXPECT_EQ(evmc_load("", nullptr), nullptr); } -TEST(loader, prefix_aaa) +TEST_F(loader, load_prefix_aaa) { - auto paths = {"unittests/double-prefix-aaa.evm", "unittests/double_prefix_aaa.evm"}; + auto paths = { + "./aaa.evm", + "aaa.evm", + "unittests/libaaa.so", + "unittests/double-prefix-aaa.evm", + "unittests/double_prefix_aaa.evm", + }; for (auto& path : paths) { + setup(path, "evmc_create_aaa", create_aaa); evmc_loader_error_code ec; auto fn = evmc_load(path, &ec); - ASSERT_NE(fn, nullptr); EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); + ASSERT_NE(fn, nullptr); EXPECT_EQ((uintptr_t)fn(), 0xaaa); } } -TEST(loader, eee_bbb) +TEST_F(loader, load_eee_bbb) { - auto path = "unittests/eee-bbb.dll"; - + setup("unittests/eee-bbb.dll", "evmc_create_eee_bbb", create_eee_bbb); evmc_loader_error_code ec; - auto fn = evmc_load(path, &ec); + auto fn = evmc_load(evmc_test_library_path, &ec); ASSERT_NE(fn, nullptr); EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); EXPECT_EQ((uintptr_t)fn(), 0xeeebbb); } -#if _WIN32 -TEST(loader, nextto) -{ - // On Unix dlopen searches for system libs when the path does not contain "/". - - auto path = "aaa.evm"; - evmc_loader_error_code ec; - auto fn = evmc_load(path, &ec); - ASSERT_NE(fn, nullptr); - EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); - EXPECT_EQ((uintptr_t)fn(), 0xaaa); -} -#endif - -TEST(loader, windows_path) +TEST_F(loader, load_windows_path) { auto paths = { - "./aaa.evm", - ".\\aaa.evm", + "./eee-bbb.evm", + ".\\eee-bbb.evm", "./unittests/eee-bbb.dll", "./unittests\\eee-bbb.dll", ".\\unittests\\eee-bbb.dll", @@ -143,133 +132,59 @@ TEST(loader, windows_path) for (auto& path : paths) { - bool is_windows_path = std::strchr(path, '\\') != nullptr; + bool should_open = is_windows || std::strchr(path, '\\') == nullptr; + setup(should_open ? path : nullptr, "evmc_create_eee_bbb", create_eee_bbb); - if (is_windows_path && !is_windows) - { - evmc_loader_error_code ec; - auto fn = evmc_load(path, &ec); - EXPECT_EQ(fn, nullptr); - EXPECT_EQ(ec, EVMC_LOADER_CANNOT_OPEN); - } - else - { - evmc_loader_error_code ec; - auto fn = evmc_load(path, &ec); - EXPECT_NE(fn, nullptr); - EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); - } + evmc_loader_error_code ec; + evmc_load(path, &ec); + EXPECT_EQ(ec, should_open ? EVMC_LOADER_SUCCESS : EVMC_LOADER_CANNOT_OPEN); } } -TEST(loader, eee1) -{ - auto path = "unittests/libeee1.so"; - - evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); - EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); - EXPECT_EQ(x, nullptr); - - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); -} - -TEST(loader, eee2) -{ - auto path = "unittests/eee2.so"; - - evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); - EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); - EXPECT_EQ(x, nullptr); - - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); -} - -TEST(loader, eee3) -{ - auto path = "unittests/libeee3.x"; - - evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); - EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); - EXPECT_EQ(x, nullptr); - - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); -} - -#if !_WIN32 -TEST(loader, eee4) +TEST_F(loader, load_symbol_not_found) { - // Windows is not loading DLLs without extensions. - auto path = "unittests/eee4"; + auto paths = {"libaaa1.so", "eee2.so", "libeee3.x", "eee4", "_", "lib_.so"}; - evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); - EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); - EXPECT_EQ(x, nullptr); - - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); -} - -TEST(loader, _) -{ - // Windows is not loading DLLs without extensions. - auto path = "unittests/_"; - - evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); - EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); - EXPECT_EQ(x, nullptr); - - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); -} -#endif - -TEST(loader, lib_) -{ - // Windows is not loading DLLs without extensions. - auto path = "unittests/lib_.so"; - - evmc_loader_error_code ec; - auto x = evmc_load(path, &ec); - EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); - EXPECT_EQ(x, nullptr); + for (auto& path : paths) + { + setup(path, "evmc_create_aaa", create_aaa); - x = evmc_load(path, nullptr); - EXPECT_EQ(x, nullptr); + evmc_loader_error_code ec; + EXPECT_EQ(evmc_load(evmc_test_library_path, &ec), nullptr); + EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); + EXPECT_EQ(evmc_load(evmc_test_library_path, nullptr), nullptr); + } } -TEST(loader, load_default) +TEST_F(loader, load_default_symbol) { - auto path = "unittests/default.evmc"; + setup("default.evmc", "evmc_create", create_aaa); evmc_loader_error_code ec; - auto fn = evmc_load(path, &ec); + auto fn = evmc_load(evmc_test_library_path, &ec); EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); - EXPECT_EQ((uintptr_t)fn(), 0xdeaf); + EXPECT_EQ(fn, &create_aaa); - fn = evmc_load(path, nullptr); - EXPECT_EQ((uintptr_t)fn(), 0xdeaf); + fn = evmc_load(evmc_test_library_path, nullptr); + EXPECT_EQ(fn, &create_aaa); } -TEST(loader, load_and_create_failure) +TEST_F(loader, load_and_create_failure) { + setup("failure.vm", "evmc_create", create_failure); + evmc_loader_error_code ec; - auto vm = evmc_load_and_create("unittests/failure.vm", &ec); + auto vm = evmc_load_and_create(evmc_test_library_path, &ec); EXPECT_EQ(vm, nullptr); EXPECT_EQ(ec, EVMC_LOADER_INSTANCE_CREATION_FAILURE); } -TEST(loader, load_and_create_abi_mismatch) +TEST_F(loader, load_and_create_abi_mismatch) { + setup("abi42.vm", "evmc_create", create_abi42); + evmc_loader_error_code ec; - auto vm = evmc_load_and_create("unittests/abi42.vm", &ec); + auto vm = evmc_load_and_create(evmc_test_library_path, &ec); EXPECT_EQ(vm, nullptr); EXPECT_EQ(ec, EVMC_LOADER_ABI_VERSION_MISMATCH); } diff --git a/test/unittests/vm_mock.c b/test/unittests/vm_mock.c deleted file mode 100644 index 650d97f8b8..0000000000 --- a/test/unittests/vm_mock.c +++ /dev/null @@ -1,30 +0,0 @@ -/* EVMC: Ethereum Client-VM Connector API. - * Copyright 2018 The EVMC Authors. - * Licensed under the Apache License, Version 2.0. See the LICENSE file. - */ - -#include -#include - -EVMC_EXPORT void* evmc_create_aaa() -{ - return (void*)0xaaa; -} - -EVMC_EXPORT void* evmc_create_eee_bbb() -{ - return (void*)0xeeebbb; -} - -EVMC_EXPORT void* evmc_create_failure() -{ - return NULL; -} - -EVMC_EXPORT struct evmc_instance* evmc_create_abi42() -{ - static struct evmc_instance instance = { - .abi_version = 42, - }; - return &instance; -} diff --git a/test/unittests/vm_mock_default.c b/test/unittests/vm_mock_default.c deleted file mode 100644 index d063a365c1..0000000000 --- a/test/unittests/vm_mock_default.c +++ /dev/null @@ -1,11 +0,0 @@ -/* EVMC: Ethereum Client-VM Connector API. - * Copyright 2018 The EVMC Authors. - * Licensed under the Apache License, Version 2.0. See the LICENSE file. - */ - -#include - -EVMC_EXPORT void* evmc_create() -{ - return (void*)0xdeaf; -}