Migrate tests from gtest to catch2

pull/1561/head
lyyn 3 years ago committed by Jeff Becker
parent 4992629f20
commit ece91e87fc
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -56,7 +56,6 @@ local debian_pipeline(name, image,
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') + '-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
cmake_extra, cmake_extra,
'ninja -v', 'ninja -v',
'../contrib/ci/drone-gdb.sh ./test/testAll --gtest_color=yes',
'../contrib/ci/drone-gdb.sh ./test/catchAll --use-colour yes', '../contrib/ci/drone-gdb.sh ./test/catchAll --use-colour yes',
] + extra_cmds, ] + extra_cmds,
} }
@ -200,7 +199,6 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
'cmake .. -G Ninja -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' + 'cmake .. -G Ninja -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' +
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra, (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra,
'ninja -v', 'ninja -v',
'./test/testAll --gtest_color=yes',
'./test/catchAll --use-colour yes', './test/catchAll --use-colour yes',
] + extra_cmds, ] + extra_cmds,
} }

@ -8,84 +8,71 @@ if (WITH_HIVE)
hive_build) hive_build)
endif() endif()
# Old gtest-based tests; new tests should use Catch2, instead.
add_executable(testAll
# helpers
main.cpp
crypto/mock_crypto.cpp
dht/mock_context.cpp
test_util.cpp
# actual test cases
config/test_llarp_config_ini.cpp
crypto/test_llarp_crypto_types.cpp
crypto/test_llarp_crypto.cpp
crypto/test_llarp_key_manager.cpp
dht/test_llarp_dht_bucket.cpp
dht/test_llarp_dht_explorenetworkjob.cpp
dht/test_llarp_dht_kademlia.cpp
dht/test_llarp_dht_key.cpp
dht/test_llarp_dht_node.cpp
dht/test_llarp_dht_tx.cpp
dht/test_llarp_dht_txowner.cpp
llarp_test.cpp
net/test_llarp_net.cpp
router/test_llarp_router_version.cpp
routing/llarp_routing_transfer_traffic.cpp
routing/test_llarp_routing_obtainexitmessage.cpp
service/test_llarp_service_address.cpp
test_llarp_encrypted_frame.cpp
util/meta/test_llarp_util_memfn.cpp
util/meta/test_llarp_util_traits.cpp
util/test_llarp_util_aligned.cpp
util/test_llarp_util_bencode.cpp
util/test_llarp_util_log_level.cpp
util/thread/test_llarp_util_queue_manager.cpp
util/thread/test_llarp_util_queue.cpp
)
target_link_libraries(testAll PUBLIC gmock gtest liblokinet)
target_include_directories(testAll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
if(WIN32)
target_sources(testAll PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/win32/test.rc")
target_link_libraries(testAll PUBLIC ws2_32 iphlpapi shlwapi)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
target_link_directories(testAll PRIVATE /usr/local/lib)
endif()
add_subdirectory(Catch2) add_subdirectory(Catch2)
add_executable(catchAll add_executable(catchAll
# helpers
check_main.cpp
test_util.cpp
# actual test cases
config/test_llarp_config_definition.cpp
config/test_llarp_config_ini.cpp
config/test_llarp_config_output.cpp
crypto/test_llarp_crypto_types.cpp
crypto/test_llarp_crypto.cpp
crypto/test_llarp_key_manager.cpp
dht/test_llarp_dht_bucket.cpp
dht/test_llarp_dht_explorenetworkjob.cpp
dht/test_llarp_dht_kademlia.cpp
dht/test_llarp_dht_key.cpp
dht/test_llarp_dht_node.cpp
dht/test_llarp_dht_tx.cpp
dht/test_llarp_dht_txowner.cpp
dns/test_llarp_dns_dns.cpp
exit/test_llarp_exit_context.cpp
iwp/test_iwp_session.cpp
net/test_ip_address.cpp
net/test_llarp_net.cpp
net/test_sock_addr.cpp
nodedb/test_nodedb.cpp nodedb/test_nodedb.cpp
path/test_path.cpp path/test_path.cpp
dns/test_llarp_dns_dns.cpp peerstats/test_peer_db.cpp
peerstats/test_peer_types.cpp
regress/2020-06-08-key-backup-bug.cpp regress/2020-06-08-key-backup-bug.cpp
router/test_llarp_router_version.cpp
routing/test_llarp_routing_transfer_traffic.cpp
routing/test_llarp_routing_obtainexitmessage.cpp
service/test_llarp_service_address.cpp
service/test_llarp_service_identity.cpp
service/test_llarp_service_name.cpp
util/meta/test_llarp_util_memfn.cpp
util/meta/test_llarp_util_traits.cpp
util/thread/test_llarp_util_queue_manager.cpp
util/thread/test_llarp_util_queue.cpp
util/test_llarp_util_aligned.cpp
util/test_llarp_util_bencode.cpp
util/test_llarp_util_bits.cpp util/test_llarp_util_bits.cpp
util/test_llarp_util_decaying_hashset.cpp
util/test_llarp_util_log_level.cpp
util/test_llarp_util_printer.cpp util/test_llarp_util_printer.cpp
util/test_llarp_util_str.cpp util/test_llarp_util_str.cpp
util/test_llarp_util_decaying_hashset.cpp test_llarp_encrypted_frame.cpp
peerstats/test_peer_db.cpp test_llarp_router_contact.cpp)
peerstats/test_peer_types.cpp
config/test_llarp_config_definition.cpp
config/test_llarp_config_output.cpp
net/test_ip_address.cpp
net/test_sock_addr.cpp
service/test_llarp_service_name.cpp
exit/test_llarp_exit_context.cpp
iwp/test_iwp_session.cpp
service/test_llarp_service_identity.cpp
test_util.cpp
test_llarp_router_contact.cpp
check_main.cpp)
target_link_libraries(catchAll PUBLIC liblokinet Catch2::Catch2) target_link_libraries(catchAll PUBLIC gmock liblokinet Catch2::Catch2)
target_include_directories(catchAll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(catchAll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
if(WIN32)
target_sources(catchAll PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/win32/test.rc")
target_link_libraries(catchAll PUBLIC ws2_32 iphlpapi shlwapi)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
target_link_directories(catchAll PRIVATE /usr/local/lib)
endif()
# Custom targets to invoke the different test suites: # Custom targets to invoke the different test suites:
add_custom_target(catch COMMAND catchAll) add_custom_target(catch COMMAND catchAll)
add_custom_target(rungtest COMMAND testAll)
# Add a custom "check" target that runs all the test suites: # Add a custom "check" target that runs all the test suites:
add_custom_target(check DEPENDS rungtest catch) add_custom_target(check DEPENDS catch)

@ -1,2 +1,42 @@
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <gmock/gmock.h>
#include <util/logging/logger.hpp>
#ifdef _WIN32
#include <winsock2.h>
int
startWinsock()
{
WSADATA wsockd;
int err;
err = ::WSAStartup(MAKEWORD(2, 2), &wsockd);
if (err)
{
perror("Failed to start Windows Sockets");
return err;
}
return 0;
}
#endif
int
main(int argc, char* argv[])
{
llarp::LogSilencer shutup;
::testing::InitGoogleMock(&argc, argv);
#ifdef _WIN32
if (startWinsock())
return -1;
#endif
int result = Catch::Session().run(argc, argv);
#ifdef _WIN32
WSACleanup();
#endif
return result;
}

@ -1,60 +1,56 @@
#include <gtest/gtest.h>
#include <config/ini.hpp> #include <config/ini.hpp>
struct TestINIParser : public ::testing::Test #include <catch2/catch.hpp>
TEST_CASE("ConfigParser", "[config]")
{ {
llarp::ConfigParser parser; llarp::ConfigParser parser;
void SECTION("Parse empty")
TearDown()
{ {
parser.Clear(); REQUIRE(parser.LoadFromStr(""));
} }
};
TEST_F(TestINIParser, TestParseEmpty) SECTION("Parse one section")
{ {
ASSERT_TRUE(parser.LoadFromStr("")); llarp::ConfigParser::SectionValues_t sect;
} // this is an anti pattern don't write this kind of code with configpaser
auto assertVisit = [&sect](const auto& section) -> bool {
sect = section;
return true;
};
REQUIRE(parser.LoadFromStr("[test]\nkey=val \n"));
REQUIRE(parser.VisitSection("test", assertVisit));
auto itr = sect.find("notfound");
REQUIRE(itr == sect.end());
itr = sect.find("key");
REQUIRE(itr != sect.end());
REQUIRE(itr->second == "val");
}
TEST_F(TestINIParser, TestParseOneSection) SECTION("Parse section duplicate keys")
{ {
llarp::ConfigParser::SectionValues_t sect; REQUIRE(parser.LoadFromStr("[test]\nkey1=val1\nkey1=val2"));
// this is an anti pattern don't write this kind of code with configpaser size_t num = 0;
auto assertVisit = [&sect](const auto& section) -> bool { auto visit = [&num](const auto& section) -> bool {
sect = section; num = section.count("key1");
return true; return true;
}; };
ASSERT_TRUE(parser.LoadFromStr("[test]\nkey=val \n")); REQUIRE(parser.VisitSection("test", visit));
ASSERT_TRUE(parser.VisitSection("test", assertVisit)); REQUIRE(num == size_t(2));
auto itr = sect.find("notfound"); }
ASSERT_EQ(itr, sect.end());
itr = sect.find("key");
ASSERT_NE(itr, sect.end());
ASSERT_EQ(itr->second, "val");
}
TEST_F(TestINIParser, TestParseSectionDuplicateKeys) SECTION("No key")
{ {
ASSERT_TRUE(parser.LoadFromStr("[test]\nkey1=val1\nkey1=val2")); REQUIRE_FALSE(parser.LoadFromStr("[test]\n=1090\n"));
size_t num = 0; }
auto visit = [&num](const auto& section) -> bool {
num = section.count("key1");
return true;
};
ASSERT_TRUE(parser.VisitSection("test", visit));
ASSERT_EQ(num, size_t(2));
}
TEST_F(TestINIParser, TestNoKey) SECTION("Parse invalid")
{ {
ASSERT_FALSE(parser.LoadFromStr("[test]\n=1090\n")); REQUIRE_FALSE(
} parser.LoadFromStr("srged5ghe5\nf34wtge5\nw34tgfs4ygsd5yg=4;\n#"
"g4syhgd5\n"));
}
TEST_F(TestINIParser, TestParseInvalid) parser.Clear();
{
ASSERT_FALSE(
parser.LoadFromStr("srged5ghe5\nf34wtge5\nw34tgfs4ygsd5yg=4;\n#"
"g4syhgd5\n"));
} }

@ -1 +0,0 @@
#include <crypto/mock_crypto.hpp>

@ -9,76 +9,67 @@ namespace llarp
{ {
namespace test namespace test
{ {
struct MockCrypto final : public Crypto struct MockCrypto : public Crypto
{ {
MOCK_METHOD3(maybe_decrypt_name,std::optional<AlignedBuffer<32>>(std::string_view, llarp::SymmNonce, std::string_view)); MOCK_METHOD3(
maybe_decrypt_name,
std::optional<AlignedBuffer<32>>(std::string_view, llarp::SymmNonce, std::string_view));
MOCK_METHOD3(xchacha20,
bool(const llarp_buffer_t &, const SharedSecret &,
const TunnelNonce &));
MOCK_METHOD4(xchacha20_alt, MOCK_METHOD3(xchacha20, bool(const llarp_buffer_t&, const SharedSecret&, const TunnelNonce&));
bool(const llarp_buffer_t &, const llarp_buffer_t &,
const SharedSecret &, const byte_t *));
MOCK_METHOD4(dh_client, MOCK_METHOD4(
bool(SharedSecret &, const PubKey &, const SecretKey &, xchacha20_alt,
const TunnelNonce &)); bool(const llarp_buffer_t&, const llarp_buffer_t&, const SharedSecret&, const byte_t*));
MOCK_METHOD4(dh_server, MOCK_METHOD4(
bool(SharedSecret &, const PubKey &, const SecretKey &, dh_client, bool(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&));
const TunnelNonce &));
MOCK_METHOD4(transport_dh_client, MOCK_METHOD4(
bool(SharedSecret &, const PubKey &, const SecretKey &, dh_server, bool(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&));
const TunnelNonce &));
MOCK_METHOD4(transport_dh_server, MOCK_METHOD4(
bool(SharedSecret &, const PubKey &, const SecretKey &, transport_dh_client,
const TunnelNonce &)); bool(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&));
MOCK_METHOD2(hash, bool(byte_t *, const llarp_buffer_t &)); MOCK_METHOD4(
transport_dh_server,
bool(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&));
MOCK_METHOD2(shorthash, bool(ShortHash &, const llarp_buffer_t &)); MOCK_METHOD2(hash, bool(byte_t*, const llarp_buffer_t&));
MOCK_METHOD3(hmac, MOCK_METHOD2(shorthash, bool(ShortHash&, const llarp_buffer_t&));
bool(byte_t *, const llarp_buffer_t &,
const SharedSecret &));
MOCK_METHOD4(derive_subkey, bool(PubKey &, const PubKey &, uint64_t, const AlignedBuffer<32> *)); MOCK_METHOD3(hmac, bool(byte_t*, const llarp_buffer_t&, const SharedSecret&));
MOCK_METHOD4(derive_subkey_private, MOCK_METHOD4(derive_subkey, bool(PubKey&, const PubKey&, uint64_t, const AlignedBuffer<32>*));
bool(PrivateKey &, const SecretKey &, uint64_t, const AlignedBuffer<32> *));
MOCK_METHOD(bool, sign, (Signature &, const SecretKey &, const llarp_buffer_t &)); MOCK_METHOD4(
derive_subkey_private,
bool(PrivateKey&, const SecretKey&, uint64_t, const AlignedBuffer<32>*));
MOCK_METHOD(bool, sign, (Signature &, const PrivateKey &, const llarp_buffer_t &)); MOCK_METHOD(bool, sign, (Signature&, const SecretKey&, const llarp_buffer_t&));
MOCK_METHOD3(verify, MOCK_METHOD(bool, sign, (Signature&, const PrivateKey&, const llarp_buffer_t&));
bool(const PubKey &, const llarp_buffer_t &,
const Signature &));
MOCK_METHOD2(seed_to_secretkey, MOCK_METHOD3(verify, bool(const PubKey&, const llarp_buffer_t&, const Signature&));
bool(llarp::SecretKey &, const llarp::IdentitySecret &));
MOCK_METHOD1(randomize, void(const llarp_buffer_t &)); MOCK_METHOD2(seed_to_secretkey, bool(llarp::SecretKey&, const llarp::IdentitySecret&));
MOCK_METHOD2(randbytes, void(byte_t *, size_t)); MOCK_METHOD1(randomize, void(const llarp_buffer_t&));
MOCK_METHOD1(identity_keygen, void(SecretKey &)); MOCK_METHOD2(randbytes, void(byte_t*, size_t));
MOCK_METHOD1(encryption_keygen, void(SecretKey &)); MOCK_METHOD1(identity_keygen, void(SecretKey&));
MOCK_METHOD1(pqe_keygen, void(PQKeyPair &)); MOCK_METHOD1(encryption_keygen, void(SecretKey&));
MOCK_METHOD3(pqe_decrypt, MOCK_METHOD1(pqe_keygen, void(PQKeyPair&));
bool(const PQCipherBlock &, SharedSecret &, const byte_t *));
MOCK_METHOD3(pqe_encrypt, MOCK_METHOD3(pqe_decrypt, bool(const PQCipherBlock&, SharedSecret&, const byte_t*));
bool(PQCipherBlock &, SharedSecret &, const PQPubKey &));
MOCK_METHOD1(check_identity_privkey, bool(const SecretKey &)); MOCK_METHOD3(pqe_encrypt, bool(PQCipherBlock&, SharedSecret&, const PQPubKey&));
MOCK_METHOD1(check_identity_privkey, bool(const SecretKey&));
}; };
} // namespace test } // namespace test
} // namespace llarp } // namespace llarp

@ -2,69 +2,48 @@
#include <iostream> #include <iostream>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
namespace llarp using namespace llarp;
{
struct IdentityKeyTest : public ::testing::Test
{
llarp::sodium::CryptoLibSodium crypto;
IdentityKeyTest() TEST_CASE("Identity key")
{ {
} llarp::sodium::CryptoLibSodium crypto;
}; SecretKey secret;
crypto.identity_keygen(secret);
TEST_F(IdentityKeyTest, TestKeyGen) SECTION("Keygen")
{ {
SecretKey secret; REQUIRE_FALSE(secret.IsZero());
crypto.identity_keygen(secret);
ASSERT_FALSE(secret.IsZero());
} }
TEST_F(IdentityKeyTest, TestSignVerify) SECTION("Sign-verify")
{ {
SecretKey secret; AlignedBuffer<128> random;
crypto.identity_keygen(secret);
AlignedBuffer< 128 > random;
random.Randomize(); random.Randomize();
Signature sig; Signature sig;
const PubKey pk = secret.toPublic(); const PubKey pk = secret.toPublic();
const llarp_buffer_t buf(random.data(), random.size()); const llarp_buffer_t buf(random.data(), random.size());
ASSERT_TRUE(crypto.sign(sig, secret, buf)); REQUIRE(crypto.sign(sig, secret, buf));
ASSERT_TRUE(crypto.verify(pk, buf, sig)); REQUIRE(crypto.verify(pk, buf, sig));
random.Randomize(); random.Randomize();
// mangle body // mangle body
ASSERT_FALSE(crypto.verify(pk, buf, sig)); REQUIRE_FALSE(crypto.verify(pk, buf, sig));
} }
}
struct PQCryptoTest : public ::testing::Test TEST_CASE("PQ crypto")
{ {
llarp::sodium::CryptoLibSodium crypto; llarp::sodium::CryptoLibSodium crypto;
PQKeyPair keys; PQKeyPair keys;
crypto.pqe_keygen(keys);
PQCryptoTest() PQCipherBlock block;
{ SharedSecret shared, otherShared;
} auto c = &crypto;
void REQUIRE(keys.size() == PQ_KEYPAIRSIZE);
SetUp() REQUIRE(c->pqe_encrypt(block, shared, PQPubKey(pq_keypair_to_public(keys))));
{ REQUIRE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys)));
crypto.pqe_keygen(keys); REQUIRE(otherShared == shared);
} }
};
TEST_F(PQCryptoTest, TestCrypto)
{
PQCipherBlock block;
SharedSecret shared, otherShared;
auto c = &crypto;
ASSERT_TRUE(keys.size() == PQ_KEYPAIRSIZE);
ASSERT_TRUE(
c->pqe_encrypt(block, shared, PQPubKey(pq_keypair_to_public(keys))));
ASSERT_TRUE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys)));
ASSERT_TRUE(otherShared == shared);
}
} // namespace llarp

@ -4,7 +4,7 @@
#include <string> #include <string>
#include <test_util.hpp> #include <test_util.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
// This used to be implied via the headers above *shrug* // This used to be implied via the headers above *shrug*
#ifdef _WIN32 #ifdef _WIN32
@ -17,43 +17,37 @@ struct ToStringData
std::string output; std::string output;
}; };
struct PubKeyString : public ::testing::TestWithParam< ToStringData >
{
};
TEST_P(PubKeyString, tostring)
{
auto d = GetParam();
llarp::PubKey key(d.input);
ASSERT_EQ(key.ToString(), d.output);
}
TEST_P(PubKeyString, fromstring)
{
auto d = GetParam();
llarp::PubKey key;
ASSERT_TRUE(key.FromString(d.output));
ASSERT_EQ(key, llarp::PubKey(d.input));
}
llarp::PubKey::Data empty = {}; llarp::PubKey::Data empty = {};
llarp::PubKey::Data full = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, llarp::PubKey::Data full = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
// clang-format off // clang-format off
ToStringData toStringData[] = { std::vector<ToStringData> toStringData{
{empty, "0000000000000000000000000000000000000000000000000000000000000000"}, {empty, "0000000000000000000000000000000000000000000000000000000000000000"},
{full, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, {full, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
}; };
// clang-format on // clang-format on
INSTANTIATE_TEST_SUITE_P(TestCryptoTypes, PubKeyString, TEST_CASE("PubKey-string conversion")
::testing::ValuesIn(toStringData)); {
auto d = GENERATE(from_range(toStringData));
SECTION("To string")
{
llarp::PubKey key(d.input);
REQUIRE(key.ToString() == d.output);
}
SECTION("From string")
{
llarp::PubKey key;
REQUIRE(key.FromString(d.output));
REQUIRE(key == llarp::PubKey(d.input));
}
}
// Concerns // Concerns
// - file missing // - file missing
@ -63,32 +57,31 @@ INSTANTIATE_TEST_SUITE_P(TestCryptoTypes, PubKeyString,
// - raw buffer // - raw buffer
// - bencoded // - bencoded
struct TestCryptoTypesSecret : public ::testing::Test struct TestCryptoTypesSecret
{ {
std::string filename; std::string filename;
fs::path p; fs::path p;
TestCryptoTypesSecret() : filename(llarp::test::randFilename()), p(filename) TestCryptoTypesSecret() : filename(llarp::test::randFilename()), p(filename)
{ {}
}
}; };
TEST_F(TestCryptoTypesSecret, secret_key_from_file_missing) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_missing")
{ {
// Verify loading an empty file fails cleanly. // Verify loading an empty file fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't create a file // Verify we didn't create a file
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_empty) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_empty")
{ {
// Verify loading an empty file fails cleanly. // Verify loading an empty file fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
@ -98,156 +91,154 @@ TEST_F(TestCryptoTypesSecret, secret_key_from_file_empty)
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_smaller) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_smaller")
{ {
// Verify loading a file which is too small fails cleanly. // Verify loading a file which is too small fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
std::fill_n(std::ostream_iterator< byte_t >(f), llarp::SecretKey::SIZE / 2, std::fill_n(std::ostream_iterator<byte_t>(f), llarp::SecretKey::SIZE / 2, 0xAA);
0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_smaller_bencode) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_smaller_bencode")
{ {
// Verify loading a file which is too small fails cleanly. // Verify loading a file which is too small fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
f.write("32:", 3); f.write("32:", 3);
std::fill_n(std::ostream_iterator< byte_t >(f), 32, 0xAA); std::fill_n(std::ostream_iterator<byte_t>(f), 32, 0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_smaller_corrupt_bencode) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_smaller_corrupt_bencode")
{ {
// Verify loading a file which is too small + corrupt fails cleanly. // Verify loading a file which is too small + corrupt fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
f.write("256:", 4); f.write("256:", 4);
std::fill_n(std::ostream_iterator< byte_t >(f), 32, 0xAA); std::fill_n(std::ostream_iterator<byte_t>(f), 32, 0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_larger) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_larger")
{ {
// Verify loading a file which is too large fails cleanly. // Verify loading a file which is too large fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
std::fill_n(std::ostream_iterator< byte_t >(f), llarp::SecretKey::SIZE * 2, std::fill_n(std::ostream_iterator<byte_t>(f), llarp::SecretKey::SIZE * 2, 0xAA);
0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_larger_bencode) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_larger_bencode")
{ {
// Verify loading a file which is too large fails cleanly. // Verify loading a file which is too large fails cleanly.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
f.write("256:", 4); f.write("256:", 4);
std::fill_n(std::ostream_iterator< byte_t >(f), 256, 0xAA); std::fill_n(std::ostream_iterator<byte_t>(f), 256, 0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.LoadFromFile(filename.c_str())); REQUIRE_FALSE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_happy_raw) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_happy_raw")
{ {
// Verify loading a valid raw file succeeds. // Verify loading a valid raw file succeeds.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
std::fill_n(std::ostream_iterator< byte_t >(f), llarp::SecretKey::SIZE, 0xAA); std::fill_n(std::ostream_iterator<byte_t>(f), llarp::SecretKey::SIZE, 0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_TRUE(key.LoadFromFile(filename.c_str())); REQUIRE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_from_file_happy_bencode) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_from_file_happy_bencode")
{ {
// Verify loading a valid bencoded file succeeds. // Verify loading a valid bencoded file succeeds.
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
// Create empty file // Create empty file
std::fstream f; std::fstream f;
f.open(filename, std::ios::out | std::ios::binary); f.open(filename, std::ios::out | std::ios::binary);
f.write("64:", 4); f.write("64:", 4);
std::fill_n(std::ostream_iterator< byte_t >(f), llarp::SecretKey::SIZE, 0xAA); std::fill_n(std::ostream_iterator<byte_t>(f), llarp::SecretKey::SIZE, 0xAA);
f.close(); f.close();
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_TRUE(key.LoadFromFile(filename.c_str())); REQUIRE(key.LoadFromFile(filename.c_str()));
// Verify we didn't delete the file // Verify we didn't delete the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
} }
// Save to file // Save to file
@ -261,15 +252,24 @@ TEST_F(TestCryptoTypesSecret, secret_key_from_file_happy_bencode)
BOOL BOOL
IsRunAsAdmin() IsRunAsAdmin()
{ {
BOOL fIsRunAsAdmin = FALSE; BOOL fIsRunAsAdmin = FALSE;
DWORD dwError = ERROR_SUCCESS; DWORD dwError = ERROR_SUCCESS;
PSID pAdministratorsGroup = NULL; PSID pAdministratorsGroup = NULL;
// Allocate and initialize a SID of the administrators group. // Allocate and initialize a SID of the administrators group.
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if(!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, if (!AllocateAndInitializeSid(
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &NtAuthority,
&pAdministratorsGroup)) 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&pAdministratorsGroup))
{ {
dwError = GetLastError(); dwError = GetLastError();
goto Cleanup; goto Cleanup;
@ -277,7 +277,7 @@ IsRunAsAdmin()
// Determine whether the SID of administrators group is enabled in // Determine whether the SID of administrators group is enabled in
// the primary access token of the process. // the primary access token of the process.
if(!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin)) if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
{ {
dwError = GetLastError(); dwError = GetLastError();
goto Cleanup; goto Cleanup;
@ -285,14 +285,14 @@ IsRunAsAdmin()
Cleanup: Cleanup:
// Centralized cleanup for all allocated resources. // Centralized cleanup for all allocated resources.
if(pAdministratorsGroup) if (pAdministratorsGroup)
{ {
FreeSid(pAdministratorsGroup); FreeSid(pAdministratorsGroup);
pAdministratorsGroup = NULL; pAdministratorsGroup = NULL;
} }
// Throw the error if something failed in the function. // Throw the error if something failed in the function.
if(ERROR_SUCCESS != dwError) if (ERROR_SUCCESS != dwError)
{ {
throw dwError; throw dwError;
} }
@ -301,46 +301,46 @@ Cleanup:
} }
#endif #endif
TEST_F(TestCryptoTypesSecret, secret_key_to_missing_file) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_to_missing_file")
{ {
// Verify writing to an unwritable file fails. // Verify writing to an unwritable file fails.
// Assume we're not running as root, so can't write to [C:]/ // Assume we're not running as root, so can't write to [C:]/
// if we are root just skip this test // if we are root just skip this test
#ifndef _WIN32 #ifndef _WIN32
if(getuid() == 0) if (getuid() == 0)
return; return;
#else #else
if(IsRunAsAdmin()) if (IsRunAsAdmin())
return; return;
#endif #endif
filename = "/" + filename; filename = "/" + filename;
p = filename; p = filename;
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
ASSERT_FALSE(key.SaveToFile(filename.c_str())); REQUIRE_FALSE(key.SaveToFile(filename.c_str()));
// Verify we didn't create the file // Verify we didn't create the file
ASSERT_FALSE(fs::exists(fs::status(fs::path(filename)))); REQUIRE_FALSE(fs::exists(fs::status(fs::path(filename))));
} }
TEST_F(TestCryptoTypesSecret, secret_key_to_file) TEST_CASE_METHOD(TestCryptoTypesSecret, "secret_key_to_file")
{ {
ASSERT_FALSE(fs::exists(fs::status(p))); REQUIRE_FALSE(fs::exists(fs::status(p)));
llarp::test::FileGuard guard(p); llarp::test::FileGuard guard(p);
llarp::SecretKey key; llarp::SecretKey key;
key.Randomize(); key.Randomize();
ASSERT_TRUE(key.SaveToFile(filename.c_str())); REQUIRE(key.SaveToFile(filename.c_str()));
// Verify we created the file // Verify we created the file
ASSERT_TRUE(fs::exists(fs::status(fs::path(filename)))); REQUIRE(fs::exists(fs::status(fs::path(filename))));
llarp::SecretKey other; llarp::SecretKey other;
other.LoadFromFile(filename.c_str()); other.LoadFromFile(filename.c_str());
ASSERT_EQ(other, key); REQUIRE(other == key);
} }

@ -9,12 +9,12 @@
#include <string> #include <string>
#include <test_util.hpp> #include <test_util.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
using namespace ::llarp; using namespace ::llarp;
using namespace ::testing; using namespace ::testing;
struct KeyManagerTest : public test::LlarpTest< llarp::sodium::CryptoLibSodium > struct KeyManagerTest : public test::LlarpTest<llarp::sodium::CryptoLibSodium>
{ {
// paranoid file guards for anything KeyManager might touch // paranoid file guards for anything KeyManager might touch
test::FileGuard m_rcFileGuard; test::FileGuard m_rcFileGuard;
@ -27,8 +27,7 @@ struct KeyManagerTest : public test::LlarpTest< llarp::sodium::CryptoLibSodium >
, m_encFileGuard(our_enc_key_filename) , m_encFileGuard(our_enc_key_filename)
, m_transportFileGuard(our_transport_key_filename) , m_transportFileGuard(our_transport_key_filename)
, m_identFileGuard(our_identity_filename) , m_identFileGuard(our_identity_filename)
{ {}
}
/// generate a valid "rc.signed" file /// generate a valid "rc.signed" file
bool bool
@ -39,10 +38,10 @@ struct KeyManagerTest : public test::LlarpTest< llarp::sodium::CryptoLibSodium >
} }
}; };
TEST_F(KeyManagerTest, TestBackupFileByMoving_MovesExistingFiles) TEST_CASE_METHOD(KeyManagerTest, "Backup file by moving moves existing files")
{ {
fs::path p = test::randFilename(); fs::path p = test::randFilename();
ASSERT_FALSE(fs::exists(p)); REQUIRE_FALSE(fs::exists(p));
// touch file // touch file
std::fstream f; std::fstream f;
@ -51,33 +50,33 @@ TEST_F(KeyManagerTest, TestBackupFileByMoving_MovesExistingFiles)
KeyManager::backupFileByMoving(p.string()); KeyManager::backupFileByMoving(p.string());
ASSERT_FALSE(fs::exists(p)); REQUIRE_FALSE(fs::exists(p));
fs::path moved = p.string() + ".0.bak"; fs::path moved = p.string() + ".0.bak";
ASSERT_TRUE(fs::exists(moved)); REQUIRE(fs::exists(moved));
test::FileGuard guard(moved); test::FileGuard guard(moved);
}; };
TEST_F(KeyManagerTest, TestBackupFileByMoving_DoesntTouchNonExistentFiles) TEST_CASE_METHOD(KeyManagerTest, "Backup file by moving doesnt touch non existent files")
{ {
fs::path p = test::randFilename(); fs::path p = test::randFilename();
ASSERT_FALSE(fs::exists(p)); REQUIRE_FALSE(fs::exists(p));
KeyManager::backupFileByMoving(p.string()); KeyManager::backupFileByMoving(p.string());
ASSERT_FALSE(fs::exists(p)); REQUIRE_FALSE(fs::exists(p));
fs::path moved = p.string() + ".0.bak"; fs::path moved = p.string() + ".0.bak";
ASSERT_FALSE(fs::exists(moved)); REQUIRE_FALSE(fs::exists(moved));
} }
TEST_F(KeyManagerTest, TestBackupFileByMoving_FailsIfBackupNamesAreExausted) TEST_CASE_METHOD(KeyManagerTest, "Backup file by moving fails if backup names are exausted")
{ {
fs::path base = test::randFilename(); fs::path base = test::randFilename();
ASSERT_FALSE(fs::exists(base)); REQUIRE_FALSE(fs::exists(base));
// touch file // touch file
{ {
@ -93,9 +92,9 @@ TEST_F(KeyManagerTest, TestBackupFileByMoving_FailsIfBackupNamesAreExausted)
guards.reserve(numBackupNames); guards.reserve(numBackupNames);
// generate backup files foo.0.bak through foo.9.bak // generate backup files foo.0.bak through foo.9.bak
for (uint32_t i=0; i<numBackupNames; ++i) for (uint32_t i = 0; i < numBackupNames; ++i)
{ {
fs::path p = base.string() +"."+ std::to_string(i) +".bak"; fs::path p = base.string() + "." + std::to_string(i) + ".bak";
std::fstream f; std::fstream f;
f.open(p.string(), std::ios::out); f.open(p.string(), std::ios::out);
@ -103,49 +102,48 @@ TEST_F(KeyManagerTest, TestBackupFileByMoving_FailsIfBackupNamesAreExausted)
guards.emplace_back(p); guards.emplace_back(p);
ASSERT_TRUE(fs::exists(p)); REQUIRE(fs::exists(p));
} }
ASSERT_FALSE(KeyManager::backupFileByMoving(base.string())); REQUIRE_FALSE(KeyManager::backupFileByMoving(base.string()));
}; };
TEST_F(KeyManagerTest, TestInitialize_MakesKeyfiles) TEST_CASE_METHOD(KeyManagerTest, "Initialize makes keyfiles")
{ {
llarp::Config conf{fs::current_path()}; llarp::Config conf{fs::current_path()};
conf.Load(); conf.Load();
KeyManager keyManager; KeyManager keyManager;
ASSERT_TRUE(keyManager.initialize(conf, true, true)); REQUIRE(keyManager.initialize(conf, true, true));
// KeyManager doesn't generate RC file, but should generate others // KeyManager doesn't generate RC file, but should generate others
ASSERT_FALSE(fs::exists(our_rc_filename)); REQUIRE_FALSE(fs::exists(our_rc_filename));
ASSERT_TRUE(fs::exists(our_enc_key_filename)); REQUIRE(fs::exists(our_enc_key_filename));
ASSERT_TRUE(fs::exists(our_transport_key_filename)); REQUIRE(fs::exists(our_transport_key_filename));
ASSERT_TRUE(fs::exists(our_identity_filename)); REQUIRE(fs::exists(our_identity_filename));
} }
TEST_F(KeyManagerTest, TestInitialize_RespectsGenFlag) TEST_CASE_METHOD(KeyManagerTest, "Initialize respects gen flag")
{ {
llarp::Config conf{fs::current_path()}; llarp::Config conf{fs::current_path()};
conf.Load(); conf.Load();
KeyManager keyManager; KeyManager keyManager;
ASSERT_FALSE(keyManager.initialize(conf, false, true)); REQUIRE_FALSE(keyManager.initialize(conf, false, true));
// KeyManager shouldn't have touched any files without (genIfAbsent == true) // KeyManager shouldn't have touched any files without (genIfAbsent == true)
ASSERT_FALSE(fs::exists(our_rc_filename)); REQUIRE_FALSE(fs::exists(our_rc_filename));
ASSERT_FALSE(fs::exists(our_enc_key_filename)); REQUIRE_FALSE(fs::exists(our_enc_key_filename));
ASSERT_FALSE(fs::exists(our_transport_key_filename)); REQUIRE_FALSE(fs::exists(our_transport_key_filename));
ASSERT_FALSE(fs::exists(our_identity_filename)); REQUIRE_FALSE(fs::exists(our_identity_filename));
} }
TEST_F(KeyManagerTest, TestInitialize_DetectsBadRcFile) TEST_CASE_METHOD(KeyManagerTest, "Initialize detects bad rc file")
{ {
llarp::Config conf{fs::current_path()}; llarp::Config conf{fs::current_path()};
conf.Load(); conf.Load();
conf.lokid.whitelistRouters = false; conf.lokid.whitelistRouters = false;
std::fstream f; std::fstream f;
@ -154,26 +152,25 @@ TEST_F(KeyManagerTest, TestInitialize_DetectsBadRcFile)
f.close(); f.close();
KeyManager keyManager; KeyManager keyManager;
ASSERT_TRUE(keyManager.initialize(conf, true, true)); REQUIRE(keyManager.initialize(conf, true, true));
ASSERT_TRUE(keyManager.needBackup()); REQUIRE(keyManager.needBackup());
ASSERT_TRUE(fs::exists(our_enc_key_filename)); REQUIRE(fs::exists(our_enc_key_filename));
ASSERT_TRUE(fs::exists(our_transport_key_filename)); REQUIRE(fs::exists(our_transport_key_filename));
ASSERT_TRUE(fs::exists(our_identity_filename)); REQUIRE(fs::exists(our_identity_filename));
// test that keys are sane // test that keys are sane
SecretKey key; SecretKey key;
key.Zero(); key.Zero();
ASSERT_TRUE(key.LoadFromFile(our_enc_key_filename)); REQUIRE(key.LoadFromFile(our_enc_key_filename));
ASSERT_FALSE(key.IsZero()); REQUIRE_FALSE(key.IsZero());
key.Zero(); key.Zero();
ASSERT_TRUE(key.LoadFromFile(our_transport_key_filename)); REQUIRE(key.LoadFromFile(our_transport_key_filename));
ASSERT_FALSE(key.IsZero()); REQUIRE_FALSE(key.IsZero());
key.Zero(); key.Zero();
ASSERT_TRUE(key.LoadFromFile(our_identity_filename)); REQUIRE(key.LoadFromFile(our_identity_filename));
ASSERT_FALSE(key.IsZero()); REQUIRE_FALSE(key.IsZero());
} }

@ -1 +0,0 @@
#include <dht/mock_context.hpp>

@ -9,71 +9,99 @@ namespace llarp
{ {
namespace test namespace test
{ {
struct MockContext final : public dht::AbstractContext struct MockContext : public dht::AbstractContext
{ {
MOCK_CONST_METHOD1(StoreRC, void(const RouterContact)); MOCK_CONST_METHOD1(StoreRC, void(const RouterContact));
MOCK_METHOD2(LookupRouter, bool(const RouterID&, RouterLookupHandler)); MOCK_METHOD2(LookupRouter, bool(const RouterID&, RouterLookupHandler));
MOCK_METHOD5(LookupRouterRecursive, MOCK_METHOD5(
void(const RouterID&, const dht::Key_t&, uint64_t, LookupRouterRecursive,
const dht::Key_t&, RouterLookupHandler)); void(
const RouterID&,
MOCK_METHOD6(LookupIntroSetRelayed, const dht::Key_t&,
void(const dht::Key_t&, const dht::Key_t&, uint64_t, uint64_t,
const dht::Key_t&, uint64_t, const dht::Key_t&,
service::EncryptedIntroSetLookupHandler)); RouterLookupHandler));
MOCK_METHOD6(
LookupIntroSetRelayed,
void(
const dht::Key_t&,
const dht::Key_t&,
uint64_t,
const dht::Key_t&,
uint64_t,
service::EncryptedIntroSetLookupHandler));
MOCK_METHOD5(LookupIntroSetDirect, MOCK_METHOD5(
void(const dht::Key_t&, const dht::Key_t&, uint64_t, LookupIntroSetDirect,
const dht::Key_t&, void(
service::EncryptedIntroSetLookupHandler)); const dht::Key_t&,
const dht::Key_t&,
uint64_t,
const dht::Key_t&,
service::EncryptedIntroSetLookupHandler));
MOCK_CONST_METHOD1(HasRouterLookup, bool(const RouterID& target)); MOCK_CONST_METHOD1(HasRouterLookup, bool(const RouterID& target));
MOCK_METHOD4(LookupRouterForPath, MOCK_METHOD4(
void(const RouterID& target, uint64_t txid, LookupRouterForPath,
const PathID_t& path, const dht::Key_t& askpeer)); void(
const RouterID& target,
uint64_t txid,
const PathID_t& path,
const dht::Key_t& askpeer));
MOCK_METHOD5(LookupIntroSetForPath, MOCK_METHOD5(
void(const dht::Key_t&, uint64_t, const PathID_t&, LookupIntroSetForPath,
const dht::Key_t&, uint64_t)); void(const dht::Key_t&, uint64_t, const PathID_t&, const dht::Key_t&, uint64_t));
MOCK_METHOD3(DHTSendTo, void(const RouterID&, dht::IMessage*, bool)); MOCK_METHOD3(DHTSendTo, void(const RouterID&, dht::IMessage*, bool));
MOCK_METHOD4( MOCK_METHOD4(
HandleExploritoryRouterLookup, HandleExploritoryRouterLookup,
bool(const dht::Key_t& requester, uint64_t txid, bool(
const RouterID& target, const dht::Key_t& requester,
std::vector< std::unique_ptr< dht::IMessage > >& reply)); uint64_t txid,
const RouterID& target,
std::vector<std::unique_ptr<dht::IMessage>>& reply));
MOCK_METHOD5( MOCK_METHOD5(
LookupRouterRelayed, LookupRouterRelayed,
void(const dht::Key_t& requester, uint64_t txid, void(
const dht::Key_t& target, bool recursive, const dht::Key_t& requester,
std::vector< std::unique_ptr< dht::IMessage > >& replies)); uint64_t txid,
const dht::Key_t& target,
bool recursive,
std::vector<std::unique_ptr<dht::IMessage>>& replies));
MOCK_METHOD2(RelayRequestForPath, MOCK_METHOD2(RelayRequestForPath, bool(const PathID_t& localPath, const dht::IMessage& msg));
bool(const PathID_t& localPath, const dht::IMessage& msg));
MOCK_CONST_METHOD2(GetRCFromNodeDB, MOCK_CONST_METHOD2(GetRCFromNodeDB, bool(const dht::Key_t& k, RouterContact& rc));
bool(const dht::Key_t& k, RouterContact& rc));
MOCK_METHOD5(PropagateIntroSetTo, MOCK_METHOD5(
void(const dht::Key_t& source, uint64_t sourceTX, PropagateIntroSetTo,
const service::EncryptedIntroSet& introset, void(
const dht::Key_t& peer, uint64_t relayOrder)); const dht::Key_t& source,
MOCK_METHOD5(PropagateLocalIntroSet, uint64_t sourceTX,
void(const PathID_t& source, uint64_t sourceTX, const service::EncryptedIntroSet& introset,
const service::EncryptedIntroSet& introset, const dht::Key_t& peer,
const dht::Key_t& peer, uint64_t relayOrder)); uint64_t relayOrder));
MOCK_METHOD5(
PropagateLocalIntroSet,
void(
const PathID_t& source,
uint64_t sourceTX,
const service::EncryptedIntroSet& introset,
const dht::Key_t& peer,
uint64_t relayOrder));
MOCK_METHOD2(Init, MOCK_METHOD2(Init, void(const dht::Key_t&, AbstractRouter*));
void(const dht::Key_t&, AbstractRouter*));
MOCK_CONST_METHOD1(GetIntroSetByLocation, MOCK_CONST_METHOD1(
std::optional< llarp::service::EncryptedIntroSet >( GetIntroSetByLocation,
const llarp::dht::Key_t&)); std::optional<llarp::service::EncryptedIntroSet>(const llarp::dht::Key_t&));
MOCK_CONST_METHOD0(ExtractStatus, util::StatusObject()); MOCK_CONST_METHOD0(ExtractStatus, util::StatusObject());
@ -85,8 +113,7 @@ namespace llarp
MOCK_CONST_METHOD0(OurKey, const dht::Key_t&()); MOCK_CONST_METHOD0(OurKey, const dht::Key_t&());
MOCK_CONST_METHOD0(pendingIntrosetLookups, MOCK_CONST_METHOD0(pendingIntrosetLookups, const PendingIntrosetLookups&());
const PendingIntrosetLookups&());
MOCK_METHOD0(pendingIntrosetLookups, PendingIntrosetLookups&()); MOCK_METHOD0(pendingIntrosetLookups, PendingIntrosetLookups&());
MOCK_METHOD0(pendingRouterLookups, PendingRouterLookups&()); MOCK_METHOD0(pendingRouterLookups, PendingRouterLookups&());
@ -97,12 +124,12 @@ namespace llarp
MOCK_CONST_METHOD0(pendingExploreLookups, const PendingExploreLookups&()); MOCK_CONST_METHOD0(pendingExploreLookups, const PendingExploreLookups&());
MOCK_METHOD0(services, dht::Bucket< dht::ISNode >*()); MOCK_METHOD0(services, dht::Bucket<dht::ISNode>*());
MOCK_CONST_METHOD0(AllowTransit, const bool&()); MOCK_CONST_METHOD0(AllowTransit, const bool&());
MOCK_METHOD0(AllowTransit, bool&()); MOCK_METHOD0(AllowTransit, bool&());
MOCK_CONST_METHOD0(Nodes, dht::Bucket< dht::RCNode >*()); MOCK_CONST_METHOD0(Nodes, dht::Bucket<dht::RCNode>*());
MOCK_METHOD1(PutRCNodeAsync, void(const dht::RCNode& val)); MOCK_METHOD1(PutRCNodeAsync, void(const dht::RCNode& val));
MOCK_METHOD1(DelRCNodeAsync, void(const dht::Key_t& val)); MOCK_METHOD1(DelRCNodeAsync, void(const dht::Key_t& val));

@ -2,22 +2,22 @@
#include <dht/key.hpp> #include <dht/key.hpp>
#include <dht/node.hpp> #include <dht/node.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
using Key_t = llarp::dht::Key_t; using Key_t = llarp::dht::Key_t;
using Value_t = llarp::dht::RCNode; using Value_t = llarp::dht::RCNode;
using Bucket_t = llarp::dht::Bucket< Value_t >; using Bucket_t = llarp::dht::Bucket<Value_t>;
class TestDhtBucket : public ::testing::Test class TestDhtBucket
{ {
public: public:
TestDhtBucket() : randInt(0) TestDhtBucket() : randInt(0)
{ {
us.Fill(16); us.Fill(16);
nodes = std::make_unique< Bucket_t >(us, [&]() { return randInt++; }); nodes = std::make_unique<Bucket_t>(us, [&]() { return randInt++; });
size_t numNodes = 10; size_t numNodes = 10;
byte_t fill = 1; byte_t fill = 1;
while(numNodes) while (numNodes)
{ {
Value_t n; Value_t n;
n.ID.Fill(fill); n.ID.Fill(fill);
@ -30,10 +30,10 @@ class TestDhtBucket : public ::testing::Test
uint64_t randInt; uint64_t randInt;
llarp::dht::Key_t us; llarp::dht::Key_t us;
std::unique_ptr< Bucket_t > nodes; std::unique_ptr<Bucket_t> nodes;
}; };
TEST_F(TestDhtBucket, simple_cycle) TEST_CASE_METHOD(TestDhtBucket, "Simple cycle", "[dht]")
{ {
// Empty the current bucket. // Empty the current bucket.
nodes->Clear(); nodes->Clear();
@ -45,27 +45,27 @@ TEST_F(TestDhtBucket, simple_cycle)
nodes->PutNode(val); nodes->PutNode(val);
// Verify the value is in the bucket // Verify the value is in the bucket
ASSERT_TRUE(nodes->HasNode(val.ID)); REQUIRE(nodes->HasNode(val.ID));
ASSERT_EQ(1u, nodes->size()); REQUIRE(1u == nodes->size());
// Verify after deletion, the value is no longer in the bucket // Verify after deletion, the value is no longer in the bucket
nodes->DelNode(val.ID); nodes->DelNode(val.ID);
ASSERT_FALSE(nodes->HasNode(val.ID)); REQUIRE_FALSE(nodes->HasNode(val.ID));
// Verify deleting again succeeds; // Verify deleting again succeeds;
nodes->DelNode(val.ID); nodes->DelNode(val.ID);
ASSERT_FALSE(nodes->HasNode(val.ID)); REQUIRE_FALSE(nodes->HasNode(val.ID));
} }
TEST_F(TestDhtBucket, get_random_node_excluding) TEST_CASE_METHOD(TestDhtBucket, "get_random_node_excluding")
{ {
// Empty the current bucket. // Empty the current bucket.
nodes->Clear(); nodes->Clear();
// We expect not to find anything // We expect not to find anything
Key_t result; Key_t result;
std::set< Key_t > excludeSet; std::set<Key_t> excludeSet;
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
// Create a simple value. // Create a simple value.
Value_t val; Value_t val;
@ -73,24 +73,24 @@ TEST_F(TestDhtBucket, get_random_node_excluding)
// Add the simple value to the exclude set // Add the simple value to the exclude set
excludeSet.insert(val.ID); excludeSet.insert(val.ID);
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
// Add the simple value to the bucket // Add the simple value to the bucket
nodes->PutNode(val); nodes->PutNode(val);
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
excludeSet.clear(); excludeSet.clear();
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result); REQUIRE(val.ID == result);
// Add an element to the exclude set which isn't the bucket. // Add an element to the exclude set which isn't the bucket.
Key_t other; Key_t other;
other.Fill(0xff); other.Fill(0xff);
excludeSet.insert(other); excludeSet.insert(other);
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result); REQUIRE(val.ID == result);
// Add a node which is in both bucket and excludeSet // Add a node which is in both bucket and excludeSet
Value_t nextVal; Value_t nextVal;
@ -98,28 +98,28 @@ TEST_F(TestDhtBucket, get_random_node_excluding)
excludeSet.insert(nextVal.ID); excludeSet.insert(nextVal.ID);
nodes->PutNode(nextVal); nodes->PutNode(nextVal);
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result); REQUIRE(val.ID == result);
// Clear the excludeSet - we should still have 2 nodes in the bucket // Clear the excludeSet - we should still have 2 nodes in the bucket
excludeSet.clear(); excludeSet.clear();
randInt = 0; randInt = 0;
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result); REQUIRE(val.ID == result);
// Set the random value to be 1, we should get the other node. // Set the random value to be 1, we should get the other node.
randInt = 1; randInt = 1;
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(nextVal.ID, result); REQUIRE(nextVal.ID == result);
// Set the random value to be 100, we should get the first node. // Set the random value to be 100, we should get the first node.
randInt = 100; randInt = 100;
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); REQUIRE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result); REQUIRE(val.ID == result);
} }
TEST_F(TestDhtBucket, find_closest) TEST_CASE_METHOD(TestDhtBucket, "find_closest", "[dht]")
{ {
// Empty the current bucket. // Empty the current bucket.
nodes->Clear(); nodes->Clear();
@ -129,182 +129,182 @@ TEST_F(TestDhtBucket, find_closest)
target.Fill(0xF0); target.Fill(0xF0);
Key_t result; Key_t result;
ASSERT_FALSE(nodes->FindClosest(target, result)); REQUIRE_FALSE(nodes->FindClosest(target, result));
// Add a node to the bucket // Add a node to the bucket
Value_t first; Value_t first;
first.ID.Zero(); first.ID.Zero();
nodes->PutNode(first); nodes->PutNode(first);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(result, first.ID); REQUIRE(result == first.ID);
// Add another node to the bucket, closer to the target // Add another node to the bucket, closer to the target
Value_t second; Value_t second;
second.ID.Fill(0x10); second.ID.Fill(0x10);
nodes->PutNode(second); nodes->PutNode(second);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(result, second.ID); REQUIRE(result == second.ID);
// Add a third node to the bucket, closer to the target // Add a third node to the bucket, closer to the target
Value_t third; Value_t third;
third.ID.Fill(0x20); third.ID.Fill(0x20);
nodes->PutNode(third); nodes->PutNode(third);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(result, third.ID); REQUIRE(result == third.ID);
// Add a fourth node to the bucket, greater than the target // Add a fourth node to the bucket, greater than the target
Value_t fourth; Value_t fourth;
fourth.ID.Fill(0xF1); fourth.ID.Fill(0xF1);
nodes->PutNode(fourth); nodes->PutNode(fourth);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(result, fourth.ID); REQUIRE(result == fourth.ID);
// Add a fifth node to the bucket, equal to the target // Add a fifth node to the bucket, equal to the target
Value_t fifth; Value_t fifth;
fifth.ID.Fill(0xF0); fifth.ID.Fill(0xF0);
nodes->PutNode(fifth); nodes->PutNode(fifth);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(result, fifth.ID); REQUIRE(result == fifth.ID);
} }
TEST_F(TestDhtBucket, get_many_random) TEST_CASE_METHOD(TestDhtBucket, "get_many_random", "[dht]")
{ {
// Empty the current bucket. // Empty the current bucket.
nodes->Clear(); nodes->Clear();
// Verify behaviour with empty node set // Verify behaviour with empty node set
std::set< Key_t > result; std::set<Key_t> result;
ASSERT_FALSE(nodes->GetManyRandom(result, 0)); REQUIRE_FALSE(nodes->GetManyRandom(result, 0));
ASSERT_FALSE(nodes->GetManyRandom(result, 1)); REQUIRE_FALSE(nodes->GetManyRandom(result, 1));
// Add 5 nodes to the bucket // Add 5 nodes to the bucket
std::set< Value_t > curValues; std::set<Value_t> curValues;
std::set< Key_t > curKeys; std::set<Key_t> curKeys;
for(byte_t i = 0x00; i < 0x05; ++i) for (byte_t i = 0x00; i < 0x05; ++i)
{ {
Value_t v; Value_t v;
v.ID.Fill(i); v.ID.Fill(i);
ASSERT_TRUE(curKeys.insert(v.ID).second); REQUIRE(curKeys.insert(v.ID).second);
nodes->PutNode(v); nodes->PutNode(v);
} }
// Fetching more than the current size fails // Fetching more than the current size fails
ASSERT_EQ(5u, nodes->size()); REQUIRE(5u == nodes->size());
ASSERT_FALSE(nodes->GetManyRandom(result, nodes->size() + 1)); REQUIRE_FALSE(nodes->GetManyRandom(result, nodes->size() + 1));
// Fetching the current size succeeds // Fetching the current size succeeds
ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size())); REQUIRE(nodes->GetManyRandom(result, nodes->size()));
ASSERT_EQ(curKeys, result); REQUIRE(curKeys == result);
// Fetching a subset succeeds. // Fetching a subset succeeds.
// Note we hack this by "fixing" the random number generator // Note we hack this by "fixing" the random number generator
result.clear(); result.clear();
ASSERT_TRUE(nodes->GetManyRandom(result, 1u)); REQUIRE(nodes->GetManyRandom(result, 1u));
ASSERT_EQ(1u, result.size()); REQUIRE(1u == result.size());
ASSERT_EQ(*curKeys.begin(), *result.begin()); REQUIRE(*curKeys.begin() == *result.begin());
randInt = 0; randInt = 0;
result.clear(); result.clear();
ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size() - 1)); REQUIRE(nodes->GetManyRandom(result, nodes->size() - 1));
ASSERT_EQ(nodes->size() - 1, result.size()); REQUIRE(nodes->size() - 1 == result.size());
ASSERT_EQ(std::set< Key_t >(++curKeys.rbegin(), curKeys.rend()), result); REQUIRE(std::set<Key_t>(++curKeys.rbegin(), curKeys.rend()) == result);
} }
TEST_F(TestDhtBucket, find_close_excluding) TEST_CASE_METHOD(TestDhtBucket, "find_close_excluding", "[dht]")
{ {
// Empty the current bucket. // Empty the current bucket.
nodes->Clear(); nodes->Clear();
Key_t target; Key_t target;
target.Zero(); target.Zero();
std::set< Key_t > exclude; std::set<Key_t> exclude;
Key_t result; Key_t result;
// Empty node + exclude set fails // Empty node + exclude set fails
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude)); REQUIRE_FALSE(nodes->FindCloseExcluding(target, result, exclude));
Value_t first; Value_t first;
first.ID.Fill(0xF0); first.ID.Fill(0xF0);
exclude.insert(first.ID); exclude.insert(first.ID);
// Empty nodes fails // Empty nodes fails
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude)); REQUIRE_FALSE(nodes->FindCloseExcluding(target, result, exclude));
// Nodes and exclude set match // Nodes and exclude set match
nodes->PutNode(first); nodes->PutNode(first);
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude)); REQUIRE_FALSE(nodes->FindCloseExcluding(target, result, exclude));
// Exclude set empty // Exclude set empty
exclude.clear(); exclude.clear();
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude)); REQUIRE(nodes->FindCloseExcluding(target, result, exclude));
result = first.ID; result = first.ID;
Value_t second; Value_t second;
second.ID.Fill(0x01); second.ID.Fill(0x01);
nodes->PutNode(second); nodes->PutNode(second);
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude)); REQUIRE(nodes->FindCloseExcluding(target, result, exclude));
result = second.ID; result = second.ID;
exclude.insert(second.ID); exclude.insert(second.ID);
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude)); REQUIRE(nodes->FindCloseExcluding(target, result, exclude));
result = first.ID; result = first.ID;
} }
TEST_F(TestDhtBucket, find_many_near_excluding) TEST_CASE_METHOD(TestDhtBucket, "find_many_near_excluding", "[dht]")
{ {
// Empty the current bucket. // Empty the current bucket.
nodes->Clear(); nodes->Clear();
Key_t target; Key_t target;
target.Zero(); target.Zero();
std::set< Key_t > exclude; std::set<Key_t> exclude;
std::set< Key_t > result; std::set<Key_t> result;
// Empty node + exclude set, with size 0 succeeds // Empty node + exclude set, with size 0 succeeds
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 0, exclude)); REQUIRE(nodes->GetManyNearExcluding(target, result, 0, exclude));
ASSERT_EQ(0u, result.size()); REQUIRE(0u == result.size());
// Empty node + exclude set fails // Empty node + exclude set fails
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude)); REQUIRE_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
Value_t first; Value_t first;
first.ID.Fill(0xF0); first.ID.Fill(0xF0);
exclude.insert(first.ID); exclude.insert(first.ID);
// Empty nodes fails // Empty nodes fails
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude)); REQUIRE_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
// Nodes and exclude set match // Nodes and exclude set match
nodes->PutNode(first); nodes->PutNode(first);
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude)); REQUIRE_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
// Single node succeeds // Single node succeeds
exclude.clear(); exclude.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude)); REQUIRE(nodes->GetManyNearExcluding(target, result, 1, exclude));
ASSERT_EQ(result, std::set< Key_t >({first.ID})); REQUIRE(result == std::set<Key_t>({first.ID}));
// Trying to grab 2 nodes from a 1 node set fails // Trying to grab 2 nodes from a 1 node set fails
result.clear(); result.clear();
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 2, exclude)); REQUIRE_FALSE(nodes->GetManyNearExcluding(target, result, 2, exclude));
// two nodes finds closest // two nodes finds closest
Value_t second; Value_t second;
second.ID.Fill(0x01); second.ID.Fill(0x01);
nodes->PutNode(second); nodes->PutNode(second);
result.clear(); result.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude)); REQUIRE(nodes->GetManyNearExcluding(target, result, 1, exclude));
ASSERT_EQ(result, std::set< Key_t >({second.ID})); REQUIRE(result == std::set<Key_t>({second.ID}));
// 3 nodes finds 2 closest // 3 nodes finds 2 closest
Value_t third; Value_t third;
third.ID.Fill(0x02); third.ID.Fill(0x02);
nodes->PutNode(third); nodes->PutNode(third);
result.clear(); result.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude)); REQUIRE(nodes->GetManyNearExcluding(target, result, 2, exclude));
ASSERT_EQ(result, std::set< Key_t >({second.ID, third.ID})); REQUIRE(result == std::set<Key_t>({second.ID, third.ID}));
// 4 nodes, one in exclude set finds 2 closest // 4 nodes, one in exclude set finds 2 closest
Value_t fourth; Value_t fourth;
@ -312,27 +312,27 @@ TEST_F(TestDhtBucket, find_many_near_excluding)
nodes->PutNode(fourth); nodes->PutNode(fourth);
exclude.insert(third.ID); exclude.insert(third.ID);
result.clear(); result.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude)); REQUIRE(nodes->GetManyNearExcluding(target, result, 2, exclude));
ASSERT_EQ(result, std::set< Key_t >({second.ID, fourth.ID})); REQUIRE(result == std::set<Key_t>({second.ID, fourth.ID}));
} }
TEST_F(TestDhtBucket, TestBucketFindClosest) TEST_CASE_METHOD(TestDhtBucket, "Bucket: FindClosest", "[dht]")
{ {
llarp::dht::Key_t result; llarp::dht::Key_t result;
llarp::dht::Key_t target; llarp::dht::Key_t target;
target.Fill(5); target.Fill(5);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(target, result); REQUIRE(target == result);
const llarp::dht::Key_t oldResult = result; const llarp::dht::Key_t oldResult = result;
target.Fill(0xf5); target.Fill(0xf5);
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
ASSERT_EQ(oldResult, result); REQUIRE(oldResult == result);
} }
TEST_F(TestDhtBucket, TestBucketRandomized_1000) TEST_CASE_METHOD(TestDhtBucket, "Bucket: randomized 1000", "[dht]")
{ {
size_t moreNodes = 100; size_t moreNodes = 100;
while(moreNodes--) while (moreNodes--)
{ {
llarp::dht::RCNode n; llarp::dht::RCNode n;
n.ID.Fill(randInt); n.ID.Fill(randInt);
@ -340,31 +340,30 @@ TEST_F(TestDhtBucket, TestBucketRandomized_1000)
nodes->PutNode(n); nodes->PutNode(n);
} }
const size_t count = 1000; const size_t count = 1000;
size_t left = count; size_t left = count;
while(left--) while (left--)
{ {
llarp::dht::Key_t result; llarp::dht::Key_t result;
llarp::dht::Key_t target; llarp::dht::Key_t target;
target.Randomize(); target.Randomize();
const llarp::dht::Key_t expect = target; const llarp::dht::Key_t expect = target;
ASSERT_TRUE(nodes->FindClosest(target, result)); REQUIRE(nodes->FindClosest(target, result));
if(target == result) if (target == result)
{ {
ASSERT_GE(result ^ target, expect ^ target); REQUIRE((result ^ target) >= (expect ^ target));
ASSERT_EQ(result ^ target, expect ^ target); REQUIRE((result ^ target) == (expect ^ target));
ASSERT_EQ(result ^ target, expect ^ target); REQUIRE((result ^ target) == (expect ^ target));
} }
else else
{ {
Key_t dist = result ^ target; Key_t dist = (result ^ target);
Key_t oldDist = expect ^ target; Key_t oldDist = (expect ^ target);
ASSERT_NE(result ^ target, expect ^ target); REQUIRE((result ^ target) != (expect ^ target));
ASSERT_GE(result ^ target, expect ^ target) INFO(dist << ">=" << oldDist << "iteration=" << (count - left));
<< "result=" << result << "expect=" << expect << std::endl REQUIRE((result ^ target) >= (expect ^ target));
<< dist << ">=" << oldDist << "iteration=" << (count - left);
ASSERT_NE(result ^ target, expect ^ target); REQUIRE((result ^ target) != (expect ^ target));
} }
} }
} }

@ -4,38 +4,42 @@
#include <dht/mock_context.hpp> #include <dht/mock_context.hpp>
#include <test_util.hpp> #include <test_util.hpp>
#include <gtest/gtest.h> #include <gmock/gmock.h>
#include <catch2/catch.hpp>
using namespace llarp; using namespace llarp;
using namespace ::testing; using namespace ::testing;
using test::makeBuf; using test::makeBuf;
struct TestDhtExploreNetworkJob : public ::testing::Test struct TestDhtExploreNetworkJob
{ {
RouterID peer; RouterID peer;
test::MockContext context; test::MockContext context;
dht::ExploreNetworkJob exploreNetworkJob; dht::ExploreNetworkJob exploreNetworkJob;
TestDhtExploreNetworkJob() TestDhtExploreNetworkJob() : peer(makeBuf<RouterID>(0x01)), exploreNetworkJob(peer, &context)
: peer(makeBuf< RouterID >(0x01)), exploreNetworkJob(peer, &context) {}
~TestDhtExploreNetworkJob()
{ {
CHECK(Mock::VerifyAndClearExpectations(&context));
} }
}; };
TEST_F(TestDhtExploreNetworkJob, validate) TEST_CASE_METHOD(TestDhtExploreNetworkJob, "validate", "[dht]")
{ {
const RouterID other = makeBuf< RouterID >(0x02); const RouterID other = makeBuf<RouterID>(0x02);
ASSERT_TRUE(exploreNetworkJob.Validate(other)); REQUIRE(exploreNetworkJob.Validate(other));
} }
TEST_F(TestDhtExploreNetworkJob, start) TEST_CASE_METHOD(TestDhtExploreNetworkJob, "start", "[dht]")
{ {
// Verify input arguments are passed correctly. // Verify input arguments are passed correctly.
// The actual logic is inside the `dht::AbstractContext` implementation. // The actual logic is inside the `dht::AbstractContext` implementation.
const auto txKey = makeBuf< dht::Key_t >(0x02); const auto txKey = makeBuf<dht::Key_t>(0x02);
uint64_t txId = 4; uint64_t txId = 4;
dht::TXOwner txOwner(txKey, txId); dht::TXOwner txOwner(txKey, txId);
@ -47,10 +51,11 @@ TEST_F(TestDhtExploreNetworkJob, start)
).Times(1); ).Times(1);
// clang-format off // clang-format off
ASSERT_NO_THROW(exploreNetworkJob.Start(txOwner)); REQUIRE_NOTHROW(exploreNetworkJob.Start(txOwner));
} }
TEST_F(TestDhtExploreNetworkJob, send_reply) // TODO: sections?
TEST_CASE_METHOD(TestDhtExploreNetworkJob, "send_reply", "[dht]")
{ {
// Concerns: // Concerns:
// - Empty collection // - Empty collection
@ -62,7 +67,7 @@ TEST_F(TestDhtExploreNetworkJob, send_reply)
EXPECT_CALL(context, LookupRouter(_, _)).Times(0); EXPECT_CALL(context, LookupRouter(_, _)).Times(0);
EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr)); EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr));
ASSERT_NO_THROW(exploreNetworkJob.SendReply()); REQUIRE_NOTHROW(exploreNetworkJob.SendReply());
} }
{ {
@ -75,7 +80,7 @@ TEST_F(TestDhtExploreNetworkJob, send_reply)
EXPECT_CALL(context, LookupRouter(Ne(makeBuf<RouterID>(0x01)), _)).Times(2).WillRepeatedly(Return(true)); EXPECT_CALL(context, LookupRouter(Ne(makeBuf<RouterID>(0x01)), _)).Times(2).WillRepeatedly(Return(true));
EXPECT_CALL(context, LookupRouter(Eq(makeBuf<RouterID>(0x01)), _)).WillOnce(Return(false)); EXPECT_CALL(context, LookupRouter(Eq(makeBuf<RouterID>(0x01)), _)).WillOnce(Return(false));
ASSERT_NO_THROW(exploreNetworkJob.SendReply()); REQUIRE_NOTHROW(exploreNetworkJob.SendReply());
} }
{ {
@ -87,6 +92,6 @@ TEST_F(TestDhtExploreNetworkJob, send_reply)
EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr)); EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr));
EXPECT_CALL(context, LookupRouter(_, _)).Times(3).WillRepeatedly(Return(true)); EXPECT_CALL(context, LookupRouter(_, _)).Times(3).WillRepeatedly(Return(true));
ASSERT_NO_THROW(exploreNetworkJob.SendReply()); REQUIRE_NOTHROW(exploreNetworkJob.SendReply());
} }
} }

@ -1,10 +1,10 @@
#include <dht/kademlia.hpp> #include <dht/kademlia.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
using llarp::dht::Key_t; using llarp::dht::Key_t;
using Array = std::array< byte_t, Key_t::SIZE >; using Array = std::array<byte_t, Key_t::SIZE>;
struct XorMetricData struct XorMetricData
{ {
@ -15,34 +15,21 @@ struct XorMetricData
XorMetricData(const Array& u, const Array& l, const Array& r, bool res) XorMetricData(const Array& u, const Array& l, const Array& r, bool res)
: us(u), left(l), right(r), result(res) : us(u), left(l), right(r), result(res)
{ {}
}
}; };
std::ostream& std::ostream&
operator<<(std::ostream& stream, const XorMetricData& x) operator<<(std::ostream& stream, const XorMetricData& x)
{ {
stream << int(x.us[0]) << " " << int(x.left[0]) << " " << int(x.right[0]) stream << int(x.us[0]) << " " << int(x.left[0]) << " " << int(x.right[0]) << " " << std::boolalpha
<< " " << std::boolalpha << x.result; << x.result;
return stream; return stream;
} }
std::vector<XorMetricData>
struct XorMetric : public ::testing::TestWithParam< XorMetricData >
{
};
TEST_P(XorMetric, test)
{
auto d = GetParam();
ASSERT_EQ(llarp::dht::XorMetric{Key_t{d.us}}(Key_t{d.left}, Key_t{d.right}),
d.result);
}
std::vector< XorMetricData >
makeData() makeData()
{ {
std::vector< XorMetricData > result; std::vector<XorMetricData> result;
Array zero; Array zero;
zero.fill(0); zero.fill(0);
@ -84,5 +71,8 @@ makeData()
return result; return result;
} }
INSTANTIATE_TEST_SUITE_P(TestDhtXorMetric, XorMetric, TEST_CASE("XorMetric", "[dht]")
::testing::ValuesIn(makeData())); {
auto d = GENERATE(from_range(makeData()));
REQUIRE(llarp::dht::XorMetric{Key_t{d.us}}(Key_t{d.left}, Key_t{d.right}) == d.result);
}

@ -1,105 +1,91 @@
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <dht/key.hpp> #include <dht/key.hpp>
using namespace llarp; using namespace llarp;
using Array = std::array< byte_t, dht::Key_t::SIZE >; using Array = std::array<byte_t, dht::Key_t::SIZE>;
struct DHT : public ::testing::TestWithParam< Array > static constexpr Array emptyArray{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static constexpr Array fullArray{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
static constexpr Array seqArray{{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}};
TEST_P(DHT, constructor) std::vector<Array> data{emptyArray, fullArray, seqArray};
TEST_CASE("DHT key constructor", "[dht]")
{ {
auto d = GetParam(); auto d = GENERATE(from_range(data));
dht::Key_t a(d); dht::Key_t a(d);
dht::Key_t b(d.data()); dht::Key_t b(d.data());
dht::Key_t c; dht::Key_t c;
ASSERT_EQ(a, b); REQUIRE(a == b);
if(a.IsZero()) if (a.IsZero())
{ {
ASSERT_EQ(a, c); REQUIRE(a == c);
} }
else else
{ {
ASSERT_NE(a, c); REQUIRE(a != c);
} }
} }
static constexpr Array emptyArray{ TEST_CASE("DHT key ==", "[dht]")
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static constexpr Array fullArray{
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
static constexpr Array seqArray{
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}};
static const Array data[] = {emptyArray, fullArray, seqArray};
INSTANTIATE_TEST_SUITE_P(TestDhtKey, DHT, ::testing::ValuesIn(data));
TEST(TestDhtKey, eq)
{ {
ASSERT_EQ(dht::Key_t(emptyArray), dht::Key_t(emptyArray)); REQUIRE(dht::Key_t(emptyArray) == dht::Key_t(emptyArray));
ASSERT_EQ(dht::Key_t(fullArray), dht::Key_t(fullArray)); REQUIRE(dht::Key_t(fullArray) == dht::Key_t(fullArray));
ASSERT_EQ(dht::Key_t(seqArray), dht::Key_t(seqArray)); REQUIRE(dht::Key_t(seqArray) == dht::Key_t(seqArray));
} }
TEST(TestDhtKey, ne)
TEST_CASE("DHT key !=", "[dht]")
{ {
ASSERT_NE(dht::Key_t(emptyArray), dht::Key_t(fullArray)); REQUIRE(dht::Key_t(emptyArray) != dht::Key_t(fullArray));
ASSERT_NE(dht::Key_t(emptyArray), dht::Key_t(seqArray)); REQUIRE(dht::Key_t(emptyArray) != dht::Key_t(seqArray));
ASSERT_NE(dht::Key_t(fullArray), dht::Key_t(seqArray)); REQUIRE(dht::Key_t(fullArray) != dht::Key_t(seqArray));
} }
TEST(TestDhtKey, lt) TEST_CASE("DHT key <", "[dht]")
{ {
ASSERT_LT(dht::Key_t(emptyArray), dht::Key_t(fullArray)); REQUIRE(dht::Key_t(emptyArray) < dht::Key_t(fullArray));
ASSERT_LT(dht::Key_t(emptyArray), dht::Key_t(seqArray)); REQUIRE(dht::Key_t(emptyArray) < dht::Key_t(seqArray));
ASSERT_LT(dht::Key_t(seqArray), dht::Key_t(fullArray)); REQUIRE(dht::Key_t(seqArray) < dht::Key_t(fullArray));
} }
TEST(TestDhtKey, gt) TEST_CASE("DHT key >", "[dht]")
{ {
ASSERT_GT(dht::Key_t(fullArray), dht::Key_t(emptyArray)); REQUIRE(dht::Key_t(fullArray) > dht::Key_t(emptyArray));
ASSERT_GT(dht::Key_t(seqArray), dht::Key_t(emptyArray)); REQUIRE(dht::Key_t(seqArray) > dht::Key_t(emptyArray));
ASSERT_GT(dht::Key_t(fullArray), dht::Key_t(seqArray)); REQUIRE(dht::Key_t(fullArray) > dht::Key_t(seqArray));
} }
TEST(TestDhtKey, XOR) TEST_CASE("DHT key ^", "[dht]")
{ {
ASSERT_EQ(dht::Key_t(emptyArray), REQUIRE(dht::Key_t(emptyArray) == (dht::Key_t(emptyArray) ^ dht::Key_t(emptyArray)));
dht::Key_t(emptyArray) ^ dht::Key_t(emptyArray));
ASSERT_EQ(dht::Key_t(seqArray), REQUIRE(dht::Key_t(seqArray) == (dht::Key_t(emptyArray) ^ dht::Key_t(seqArray)));
dht::Key_t(emptyArray) ^ dht::Key_t(seqArray));
ASSERT_EQ(dht::Key_t(fullArray), REQUIRE(dht::Key_t(fullArray) == (dht::Key_t(emptyArray) ^ dht::Key_t(fullArray)));
dht::Key_t(emptyArray) ^ dht::Key_t(fullArray));
ASSERT_EQ(dht::Key_t(emptyArray), REQUIRE(dht::Key_t(emptyArray) == (dht::Key_t(fullArray) ^ dht::Key_t(fullArray)));
dht::Key_t(fullArray) ^ dht::Key_t(fullArray));
ASSERT_EQ(dht::Key_t(emptyArray), REQUIRE(dht::Key_t(emptyArray) == (dht::Key_t(seqArray) ^ dht::Key_t(seqArray)));
dht::Key_t(seqArray) ^ dht::Key_t(seqArray));
Array xorResult; Array xorResult;
std::iota(xorResult.rbegin(), xorResult.rend(), 0xE0); std::iota(xorResult.rbegin(), xorResult.rend(), 0xE0);
ASSERT_EQ(dht::Key_t(xorResult), REQUIRE(dht::Key_t(xorResult) == (dht::Key_t(seqArray) ^ dht::Key_t(fullArray)));
dht::Key_t(seqArray) ^ dht::Key_t(fullArray));
} }
TEST(TestDhtKey, TestBucketOperators) TEST_CASE("DHT key: test bucket operators", "[dht]")
{ {
dht::Key_t zero; dht::Key_t zero;
dht::Key_t one; dht::Key_t one;
@ -108,16 +94,16 @@ TEST(TestDhtKey, TestBucketOperators)
zero.Zero(); zero.Zero();
one.Fill(1); one.Fill(1);
three.Fill(3); three.Fill(3);
ASSERT_LT(zero, one); REQUIRE(zero < one);
ASSERT_LT(zero, three); REQUIRE(zero < three);
ASSERT_FALSE(zero > one); REQUIRE_FALSE(zero > one);
ASSERT_FALSE(zero > three); REQUIRE_FALSE(zero > three);
ASSERT_NE(zero, three); REQUIRE(zero != three);
ASSERT_FALSE(zero == three); REQUIRE_FALSE(zero == three);
ASSERT_EQ(zero ^ one, one); REQUIRE((zero ^ one) == one);
ASSERT_LT(one, three); REQUIRE(one < three);
ASSERT_GT(three, one); REQUIRE(three > one);
ASSERT_NE(one, three); REQUIRE(one != three);
ASSERT_FALSE(one == three); REQUIRE_FALSE(one == three);
ASSERT_EQ(one ^ three, three ^ one); REQUIRE((one ^ three) == (three ^ one));
} }

@ -2,115 +2,105 @@
#include <test_util.hpp> #include <test_util.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <gmock/gmock.h>
using namespace llarp; using namespace llarp;
using namespace ::testing;
using test::makeBuf; using test::makeBuf;
struct TestDhtRCNode : public ::testing::Test TEST_CASE("dht::RCNode construct", "[dht]")
{
};
TEST_F(TestDhtRCNode, construct)
{ {
dht::RCNode node; dht::RCNode node;
ASSERT_THAT(node.ID, Property(&dht::Key_t::IsZero, true)); REQUIRE(node.ID.IsZero());
node.ID.Fill(0xCA); node.ID.Fill(0xCA);
node.rc.last_updated = 101s; node.rc.last_updated = 101s;
dht::RCNode other{node}; dht::RCNode other{node};
ASSERT_EQ(node.ID, other.ID); REQUIRE(node.ID == other.ID);
ASSERT_EQ(node.rc, other.rc); REQUIRE(node.rc == other.rc);
RouterContact contact; RouterContact contact;
contact.pubkey.Randomize(); contact.pubkey.Randomize();
dht::RCNode fromContact{contact}; dht::RCNode fromContact{contact};
ASSERT_EQ(fromContact.ID.as_array(), contact.pubkey.as_array()); REQUIRE(fromContact.ID.as_array() == contact.pubkey.as_array());
} }
TEST_F(TestDhtRCNode, lt) TEST_CASE("dht::RCNode <", "[dht]")
{ {
dht::RCNode one; dht::RCNode one;
dht::RCNode two; dht::RCNode two;
dht::RCNode three; dht::RCNode three;
dht::RCNode eqThree; dht::RCNode eqThree;
one.rc.last_updated = 1s; one.rc.last_updated = 1s;
two.rc.last_updated = 2s; two.rc.last_updated = 2s;
three.rc.last_updated = 3s; three.rc.last_updated = 3s;
eqThree.rc.last_updated = 3s; eqThree.rc.last_updated = 3s;
// LT cases // LT cases
ASSERT_THAT(one, Lt(two)); REQUIRE(one < two);
ASSERT_THAT(one, Lt(three)); REQUIRE(one < three);
ASSERT_THAT(one, Lt(eqThree)); REQUIRE(one < eqThree);
ASSERT_THAT(two, Lt(three)); REQUIRE(two < three);
ASSERT_THAT(two, Lt(eqThree)); REQUIRE(two < eqThree);
// !LT cases // !LT cases
ASSERT_THAT(one, Not(Lt(one))); REQUIRE(!(one < one));
ASSERT_THAT(two, Not(Lt(one))); REQUIRE(!(two < one));
ASSERT_THAT(two, Not(Lt(two))); REQUIRE(!(two < two));
ASSERT_THAT(three, Not(Lt(one))); REQUIRE(!(three < one));
ASSERT_THAT(three, Not(Lt(two))); REQUIRE(!(three < two));
ASSERT_THAT(three, Not(Lt(three))); REQUIRE(!(three < three));
ASSERT_THAT(three, Not(Lt(eqThree))); REQUIRE(!(three < eqThree));
} }
struct TestDhtISNode : public ::testing::Test TEST_CASE("dht::ISNode construct", "[dht]")
{
};
TEST_F(TestDhtISNode, construct)
{ {
dht::ISNode node; dht::ISNode node;
ASSERT_THAT(node.ID, Property(&dht::Key_t::IsZero, true)); REQUIRE(node.ID.IsZero());
node.ID.Fill(0xCA); node.ID.Fill(0xCA);
node.introset.derivedSigningKey.Fill(0xDB); node.introset.derivedSigningKey.Fill(0xDB);
dht::ISNode other{node}; dht::ISNode other{node};
ASSERT_EQ(node.ID, other.ID); REQUIRE(node.ID == other.ID);
ASSERT_EQ(node.introset, other.introset); REQUIRE(node.introset == other.introset);
service::EncryptedIntroSet introSet; service::EncryptedIntroSet introSet;
introSet.derivedSigningKey.Randomize(); introSet.derivedSigningKey.Randomize();
dht::ISNode fromIntro{introSet}; dht::ISNode fromIntro{introSet};
ASSERT_EQ(fromIntro.ID.as_array(), introSet.derivedSigningKey); REQUIRE(fromIntro.ID.as_array() == introSet.derivedSigningKey);
} }
TEST_F(TestDhtISNode, lt) TEST_CASE("dht::ISNode <", "[dht]")
{ {
dht::ISNode one; dht::ISNode one;
dht::ISNode two; dht::ISNode two;
dht::ISNode three; dht::ISNode three;
dht::ISNode eqThree; dht::ISNode eqThree;
one.introset.signedAt = 1s; one.introset.signedAt = 1s;
two.introset.signedAt = 2s; two.introset.signedAt = 2s;
three.introset.signedAt = 3s; three.introset.signedAt = 3s;
eqThree.introset.signedAt = 3s; eqThree.introset.signedAt = 3s;
// LT cases // LT cases
ASSERT_THAT(one, Lt(two)); REQUIRE(one < two);
ASSERT_THAT(one, Lt(three)); REQUIRE(one < three);
ASSERT_THAT(one, Lt(eqThree)); REQUIRE(one < eqThree);
ASSERT_THAT(two, Lt(three)); REQUIRE(two < three);
ASSERT_THAT(two, Lt(eqThree)); REQUIRE(two < eqThree);
// !LT cases // !LT cases
ASSERT_THAT(one, Not(Lt(one))); REQUIRE(!(one < one));
ASSERT_THAT(two, Not(Lt(one))); REQUIRE(!(two < one));
ASSERT_THAT(two, Not(Lt(two))); REQUIRE(!(two < two));
ASSERT_THAT(three, Not(Lt(one))); REQUIRE(!(three < one));
ASSERT_THAT(three, Not(Lt(two))); REQUIRE(!(three < two));
ASSERT_THAT(three, Not(Lt(three))); REQUIRE(!(three < three));
ASSERT_THAT(three, Not(Lt(eqThree))); REQUIRE(!(three < eqThree));
} }

@ -2,7 +2,7 @@
#include <service/tag.hpp> #include <service/tag.hpp>
#include <test_util.hpp> #include <test_util.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <gmock/gmock.h> #include <gmock/gmock.h>
using namespace llarp; using namespace llarp;
@ -13,13 +13,11 @@ using llarp::test::makeBuf;
using Val_t = llarp::service::Tag; using Val_t = llarp::service::Tag;
// Mock implementation of TX. // Mock implementation of TX.
struct TestTx final : public dht::TX< dht::Key_t, Val_t > struct TestTx : public dht::TX<dht::Key_t, Val_t>
{ {
TestTx(const dht::TXOwner& asker, const dht::Key_t& k, TestTx(const dht::TXOwner& asker, const dht::Key_t& k, dht::AbstractContext* p)
dht::AbstractContext* p) : dht::TX<dht::Key_t, Val_t>(asker, k, p)
: dht::TX< dht::Key_t, Val_t >(asker, k, p) {}
{
}
MOCK_CONST_METHOD1(Validate, bool(const Val_t&)); MOCK_CONST_METHOD1(Validate, bool(const Val_t&));
@ -28,18 +26,23 @@ struct TestTx final : public dht::TX< dht::Key_t, Val_t >
MOCK_METHOD0(SendReply, void()); MOCK_METHOD0(SendReply, void());
}; };
struct TestDhtTx : public Test struct TestDhtTx
{ {
dht::TXOwner asker; dht::TXOwner asker;
dht::Key_t m_key; dht::Key_t m_key;
TestTx tx; TestTx tx;
TestDhtTx() : tx(asker, m_key, nullptr) TestDhtTx() : tx(asker, m_key, nullptr)
{}
~TestDhtTx()
{ {
CHECK(Mock::VerifyAndClearExpectations(&tx));
} }
}; };
TEST_F(TestDhtTx, on_found) // TODO: sections?
TEST_CASE_METHOD(TestDhtTx, "on_found", "[dht]")
{ {
// Concerns // Concerns
// - Validate returns true // - Validate returns true
@ -48,7 +51,7 @@ TEST_F(TestDhtTx, on_found)
// - Repeated call on failure // - Repeated call on failure
// - Repeated call on success after failure // - Repeated call on success after failure
const auto key = makeBuf< dht::Key_t >(0x00); const auto key = makeBuf<dht::Key_t>(0x00);
Val_t val("good value"); Val_t val("good value");
// Validate returns true // Validate returns true
@ -57,19 +60,19 @@ TEST_F(TestDhtTx, on_found)
tx.OnFound(key, val); tx.OnFound(key, val);
ASSERT_THAT(tx.peersAsked, Contains(key)); REQUIRE(tx.peersAsked.count(key) > 0);
ASSERT_THAT(tx.valuesFound, Contains(val)); REQUIRE_THAT(tx.valuesFound, Catch::VectorContains(val));
} }
// Repeated call on success // Repeated call on success
{ {
EXPECT_CALL(tx, Validate(val)).WillOnce(Return(true)); EXPECT_CALL(tx, Validate(val)).WillOnce(Return(true));
tx.OnFound(key, val); tx.OnFound(key, val);
ASSERT_THAT(tx.peersAsked, Contains(key)); REQUIRE(tx.peersAsked.count(key) > 0);
ASSERT_THAT(tx.valuesFound, Contains(val)); REQUIRE_THAT(tx.valuesFound, Catch::VectorContains(val));
} }
const auto key1 = makeBuf< dht::Key_t >(0x01); const auto key1 = makeBuf<dht::Key_t>(0x01);
Val_t badVal("bad value"); Val_t badVal("bad value");
// Validate returns false // Validate returns false
@ -78,8 +81,8 @@ TEST_F(TestDhtTx, on_found)
tx.OnFound(key1, badVal); tx.OnFound(key1, badVal);
ASSERT_THAT(tx.peersAsked, Contains(key1)); REQUIRE(tx.peersAsked.count(key1) > 0);
ASSERT_THAT(tx.valuesFound, Not(Contains(badVal))); REQUIRE_THAT(tx.valuesFound, !Catch::VectorContains(badVal));
} }
// Repeated call on failure // Repeated call on failure
@ -88,8 +91,8 @@ TEST_F(TestDhtTx, on_found)
tx.OnFound(key1, badVal); tx.OnFound(key1, badVal);
ASSERT_THAT(tx.peersAsked, Contains(key1)); REQUIRE(tx.peersAsked.count(key1) > 0);
ASSERT_THAT(tx.valuesFound, Not(Contains(badVal))); REQUIRE_THAT(tx.valuesFound, !Catch::VectorContains(badVal));
} }
// Repeated call on success after failure // Repeated call on success after failure
@ -98,8 +101,7 @@ TEST_F(TestDhtTx, on_found)
tx.OnFound(key1, badVal); tx.OnFound(key1, badVal);
ASSERT_THAT(tx.peersAsked, Contains(key1)); REQUIRE(tx.peersAsked.count(key1) > 0);
ASSERT_THAT(tx.valuesFound, Contains(badVal)); REQUIRE_THAT(tx.valuesFound, Catch::VectorContains(badVal));
} }
} }

@ -1,6 +1,6 @@
#include <dht/txowner.hpp> #include <dht/txowner.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
namespace namespace
{ {
@ -13,37 +13,22 @@ namespace
uint64_t id; uint64_t id;
size_t expectedHash; size_t expectedHash;
TxOwnerData(const Key_t& k, uint64_t i, size_t h) TxOwnerData(const Key_t& k, uint64_t i, size_t h) : node(k), id(i), expectedHash(h)
: node(k), id(i), expectedHash(h) {}
{
}
}; };
struct TxOwner : public ::testing::TestWithParam< TxOwnerData > TEST_CASE("TxOwner default construct", "[dht]")
{
};
TEST_F(TxOwner, default_construct)
{ {
TXOwner dc; TXOwner dc;
ASSERT_TRUE(dc.node.IsZero()); REQUIRE(dc.node.IsZero());
ASSERT_EQ(0u, dc.txid); REQUIRE(0u == dc.txid);
ASSERT_EQ(0u, TXOwner::Hash()(dc)); REQUIRE(0u == TXOwner::Hash()(dc));
}
TEST_P(TxOwner, hash)
{
// test single interactions (constructor and hash)
auto d = GetParam();
TXOwner constructor(d.node, d.id);
ASSERT_EQ(d.expectedHash, TXOwner::Hash()(constructor));
} }
std::vector< TxOwnerData > std::vector<TxOwnerData>
makeData() makeData()
{ {
std::vector< TxOwnerData > result; std::vector<TxOwnerData> result;
Key_t zero; Key_t zero;
zero.Zero(); zero.Zero();
@ -52,7 +37,7 @@ namespace
Key_t two; Key_t two;
two.Fill(0x02); two.Fill(0x02);
uint64_t max = std::numeric_limits< uint64_t >::max(); uint64_t max = std::numeric_limits<uint64_t>::max();
result.emplace_back(zero, 0, 0ull); result.emplace_back(zero, 0, 0ull);
result.emplace_back(zero, 1, 1ull); result.emplace_back(zero, 1, 1ull);
@ -67,6 +52,15 @@ namespace
return result; return result;
} }
TEST_CASE("TxOwner hash", "[dht]")
{
// test single interactions (constructor and hash)
auto d = GENERATE(from_range(makeData()));
TXOwner constructor(d.node, d.id);
REQUIRE(d.expectedHash == TXOwner::Hash()(constructor));
}
struct TxOwnerCmpData struct TxOwnerCmpData
{ {
TXOwner lhs; TXOwner lhs;
@ -76,27 +70,13 @@ namespace
TxOwnerCmpData(const TXOwner& l, const TXOwner& r, bool e, bool ls) TxOwnerCmpData(const TXOwner& l, const TXOwner& r, bool e, bool ls)
: lhs(l), rhs(r), equal(e), less(ls) : lhs(l), rhs(r), equal(e), less(ls)
{ {}
}
};
struct TxOwnerOps : public ::testing::TestWithParam< TxOwnerCmpData >
{
}; };
TEST_P(TxOwnerOps, operators) std::vector<TxOwnerCmpData>
{
// test single interactions (constructor and hash)
auto d = GetParam();
ASSERT_EQ(d.lhs == d.rhs, d.equal);
ASSERT_EQ(d.lhs < d.rhs, d.less);
}
std::vector< TxOwnerCmpData >
makeCmpData() makeCmpData()
{ {
std::vector< TxOwnerCmpData > result; std::vector<TxOwnerCmpData> result;
Key_t zero; Key_t zero;
zero.Fill(0x00); zero.Fill(0x00);
@ -114,10 +94,13 @@ namespace
return result; return result;
} }
} // namespace
INSTANTIATE_TEST_SUITE_P(TestDhtTxOwner, TxOwner, TEST_CASE("TxOwner ops", "[dht]")
::testing::ValuesIn(makeData())); {
// test single interactions (constructor and hash)
auto d = GENERATE(from_range(makeCmpData()));
INSTANTIATE_TEST_SUITE_P(TestDhtTxOwner, TxOwnerOps, REQUIRE((d.lhs == d.rhs) == d.equal);
::testing::ValuesIn(makeCmpData())); REQUIRE((d.lhs < d.rhs) == d.less);
}
} // namespace

@ -1 +0,0 @@
#include <llarp_test.hpp>

@ -1,15 +1,16 @@
#ifndef LLARP_TEST #ifndef LLARP_TEST
#define LLARP_TEST #define LLARP_TEST
#include <gtest/gtest.h>
#include <crypto/mock_crypto.hpp> #include <crypto/mock_crypto.hpp>
#include <catch2/catch.hpp>
#include <gmock/gmock.h>
namespace llarp namespace llarp
{ {
namespace test namespace test
{ {
template < typename CryptoImpl = MockCrypto > template <typename CryptoImpl = MockCrypto>
class LlarpTest : public ::testing::Test class LlarpTest
{ {
protected: protected:
CryptoImpl m_crypto; CryptoImpl m_crypto;
@ -17,9 +18,18 @@ namespace llarp
LlarpTest() : cm(&m_crypto) LlarpTest() : cm(&m_crypto)
{ {
static_assert(std::is_base_of< Crypto, CryptoImpl >::value, ""); static_assert(std::is_base_of<Crypto, CryptoImpl>::value, "");
} }
~LlarpTest()
{}
}; };
template <>
inline LlarpTest<MockCrypto>::~LlarpTest()
{
CHECK(::testing::Mock::VerifyAndClearExpectations(&m_crypto));
}
} // namespace test } // namespace test
} // namespace llarp } // namespace llarp

@ -1,99 +1,104 @@
#include <gtest/gtest.h>
#include <net/net_int.hpp> #include <net/net_int.hpp>
#include <net/ip.hpp> #include <net/ip.hpp>
#include <net/ip_range.hpp> #include <net/ip_range.hpp>
#include <net/net.hpp> #include <net/net.hpp>
struct TestNet : public ::testing::Test #include <catch2/catch.hpp>
{
};
TEST_F(TestNet, TestIn6AddrFromString) TEST_CASE("In6Addr")
{ {
llarp::huint128_t ip; llarp::huint128_t ip;
ASSERT_TRUE(ip.FromString("fc00::1"));
}
TEST_F(TestNet, TestIn6AddrFromStringFail) SECTION("From string")
{ {
llarp::huint128_t ip; REQUIRE(ip.FromString("fc00::1"));
ASSERT_FALSE(ip.FromString("10.1.1.1")); }
SECTION("From string fail")
{
REQUIRE_FALSE(ip.FromString("10.1.1.1"));
}
} }
TEST_F(TestNet, TestIn6AddrToHUIntLoopback) TEST_CASE("In6AddrToHUIntLoopback")
{ {
llarp::huint128_t loopback = {0}; llarp::huint128_t loopback = {0};
ASSERT_TRUE(loopback.FromString("::1")); REQUIRE(loopback.FromString("::1"));
in6_addr addr = IN6ADDR_LOOPBACK_INIT; in6_addr addr = IN6ADDR_LOOPBACK_INIT;
auto huint = llarp::net::In6ToHUInt(addr); auto huint = llarp::net::In6ToHUInt(addr);
ASSERT_EQ(huint, loopback); REQUIRE(huint == loopback);
} }
TEST_F(TestNet, TestIn6AddrToHUInt) TEST_CASE("In6AddrToHUInt")
{ {
llarp::huint128_t huint_parsed = {0}; llarp::huint128_t huint_parsed = {0};
ASSERT_TRUE(huint_parsed.FromString("fd00::1")); REQUIRE(huint_parsed.FromString("fd00::1"));
in6_addr addr = {{{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}}}; in6_addr addr = {{{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}}};
auto huint = llarp::net::In6ToHUInt(addr); auto huint = llarp::net::In6ToHUInt(addr);
ASSERT_EQ(huint, huint_parsed); REQUIRE(huint == huint_parsed);
huint_parsed.h++; huint_parsed.h++;
ASSERT_NE(huint, huint_parsed); REQUIRE(huint != huint_parsed);
}
TEST_F(TestNet, TestRangeContains8)
{
ASSERT_TRUE(
llarp::IPRange::FromIPv4(10, 0, 0, 1, 8).Contains(llarp::ipaddr_ipv4_bits(10, 40, 11, 6)));
}
TEST_F(TestNet, TestRangeContains24)
{
ASSERT_TRUE(llarp::IPRange::FromIPv4(10, 200, 0, 1, 24)
.Contains(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
}
TEST_F(TestNet, TestRangeContainsFail)
{
ASSERT_TRUE(!llarp::IPRange::FromIPv4(192, 168, 0, 1, 24)
.Contains(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
}
TEST_F(TestNet, TestIPv4Netmask)
{
ASSERT_TRUE(llarp::netmask_ipv4_bits(8) == llarp::huint32_t{0xFF000000});
ASSERT_TRUE(llarp::netmask_ipv4_bits(24) == llarp::huint32_t{0xFFFFFF00});
}
TEST_F(TestNet, TestBogon_10_8)
{
ASSERT_TRUE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(10, 40, 11, 6)));
}
TEST_F(TestNet, TestBogon_192_168_16)
{
ASSERT_TRUE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111)));
}
TEST_F(TestNet, TestBogon_DoD_8)
{
ASSERT_TRUE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(21, 3, 37, 70)));
} }
TEST_F(TestNet, TestBogon_127_8) TEST_CASE("Range")
{ {
ASSERT_TRUE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1))); SECTION("Contains 8")
{
REQUIRE(
llarp::IPRange::FromIPv4(10, 0, 0, 1, 8).Contains(llarp::ipaddr_ipv4_bits(10, 40, 11, 6)));
}
SECTION("Contains 24")
{
REQUIRE(llarp::IPRange::FromIPv4(10, 200, 0, 1, 24)
.Contains(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
}
SECTION("Contains fail")
{
REQUIRE(!llarp::IPRange::FromIPv4(192, 168, 0, 1, 24)
.Contains(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
}
} }
TEST_F(TestNet, TestBogon_0_8) TEST_CASE("IPv4 netmask")
{ {
ASSERT_TRUE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(0, 0, 0, 0))); REQUIRE(llarp::netmask_ipv4_bits(8) == llarp::huint32_t{0xFF000000});
REQUIRE(llarp::netmask_ipv4_bits(24) == llarp::huint32_t{0xFFFFFF00});
} }
TEST_F(TestNet, TestBogon_NonBogon) TEST_CASE("Bogon")
{ {
ASSERT_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(1, 1, 1, 1))); SECTION("Bogon_10_8")
ASSERT_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(8, 8, 6, 6))); {
ASSERT_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(141, 55, 12, 99))); REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(10, 40, 11, 6)));
ASSERT_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(79, 12, 3, 4))); }
SECTION("Bogon_192_168_16")
{
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111)));
}
SECTION("Bogon_DoD_8")
{
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(21, 3, 37, 70)));
}
SECTION("Bogon_127_8")
{
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1)));
}
SECTION("Bogon_0_8")
{
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(0, 0, 0, 0)));
}
SECTION("Non-bogon")
{
REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(1, 1, 1, 1)));
REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(8, 8, 6, 6)));
REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(141, 55, 12, 99)));
REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(79, 12, 3, 4)));
}
} }

@ -1,91 +1,87 @@
#include <gtest/gtest.h>
#include <router_version.hpp> #include <router_version.hpp>
#include "router/router.hpp" #include "router/router.hpp"
class TestRouterVersion : public ::testing::Test #include <catch2/catch.hpp>
{
}; using Catch::Matchers::Equals;
TEST_F(TestRouterVersion, TestCompatibilityWhenProtocolEqual) TEST_CASE("Compatibility when protocol equal", "[RouterVersion]")
{ {
llarp::RouterVersion v1( {0, 1, 2}, 1); llarp::RouterVersion v1({0, 1, 2}, 1);
llarp::RouterVersion v2( {0, 1, 2}, 1); llarp::RouterVersion v2({0, 1, 2}, 1);
EXPECT_TRUE(v1.IsCompatableWith(v2)); CHECK(v1.IsCompatableWith(v2));
} }
TEST_F(TestRouterVersion, TestCompatibilityWhenProtocolUnequal) TEST_CASE("Compatibility when protocol unequal", "[RouterVersion]")
{ {
llarp::RouterVersion older( {0, 1, 2}, 1); llarp::RouterVersion older({0, 1, 2}, 1);
llarp::RouterVersion newer( {0, 1, 2}, 2); llarp::RouterVersion newer({0, 1, 2}, 2);
EXPECT_FALSE(older.IsCompatableWith(newer)); CHECK_FALSE(older.IsCompatableWith(newer));
EXPECT_FALSE(newer.IsCompatableWith(older)); CHECK_FALSE(newer.IsCompatableWith(older));
} }
TEST_F(TestRouterVersion, TestEmptyCompatibility) TEST_CASE("Empty compatibility", "[RouterVersion]")
{ {
llarp::RouterVersion v1( {0, 0, 1}, LLARP_PROTO_VERSION); llarp::RouterVersion v1({0, 0, 1}, LLARP_PROTO_VERSION);
EXPECT_FALSE(v1.IsCompatableWith(llarp::emptyRouterVersion)); CHECK_FALSE(v1.IsCompatableWith(llarp::emptyRouterVersion));
} }
TEST_F(TestRouterVersion, TestIsEmpty) TEST_CASE("IsEmpty", "[RouterVersion]")
{ {
llarp::RouterVersion notEmpty( {0, 0, 1}, LLARP_PROTO_VERSION); llarp::RouterVersion notEmpty({0, 0, 1}, LLARP_PROTO_VERSION);
EXPECT_FALSE(notEmpty.IsEmpty()); CHECK_FALSE(notEmpty.IsEmpty());
EXPECT_TRUE(llarp::emptyRouterVersion.IsEmpty()); CHECK(llarp::emptyRouterVersion.IsEmpty());
} }
TEST_F(TestRouterVersion, TestClear) TEST_CASE("Clear", "[RouterVersion]")
{ {
llarp::RouterVersion version( {0, 0, 1}, LLARP_PROTO_VERSION); llarp::RouterVersion version({0, 0, 1}, LLARP_PROTO_VERSION);
EXPECT_FALSE(version.IsEmpty()); CHECK_FALSE(version.IsEmpty());
version.Clear(); version.Clear();
EXPECT_TRUE(version.IsEmpty()); CHECK(version.IsEmpty());
} }
TEST_F(TestRouterVersion, TestBEncode) TEST_CASE("BEncode", "[RouterVersion]")
{ {
llarp::RouterVersion v1235( {1, 2, 3}, 5); llarp::RouterVersion v1235({1, 2, 3}, 5);
std::array< byte_t, 128 > tmp{}; std::array<byte_t, 128> tmp{};
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
EXPECT_TRUE(v1235.BEncode(&buf)); CHECK(v1235.BEncode(&buf));
std::string s((const char*)buf.begin(), (buf.end() - buf.begin())); std::string s((const char*)buf.begin(), (buf.end() - buf.begin()));
LogInfo("bencoded: ", buf.begin()); LogInfo("bencoded: ", buf.begin());
EXPECT_STREQ((const char*)buf.begin(), "li5ei1ei2ei3ee"); CHECK_THAT((const char*)buf.begin(), Equals("li5ei1ei2ei3ee"));
} }
TEST_F(TestRouterVersion, TestBDecode) TEST_CASE("BDecode", "[RouterVersion]")
{ {
llarp::RouterVersion version; llarp::RouterVersion version;
version.Clear(); version.Clear();
const std::string bString("li9ei3ei2ei1ee"); const std::string bString("li9ei3ei2ei1ee");
llarp_buffer_t buf(bString.data(), bString.size()); llarp_buffer_t buf(bString.data(), bString.size());
EXPECT_TRUE(version.BDecode(&buf)); CHECK(version.BDecode(&buf));
llarp::RouterVersion expected( {3, 2, 1}, 9); llarp::RouterVersion expected({3, 2, 1}, 9);
EXPECT_EQ(expected, version);
CHECK(expected == version);
} }
TEST_F(TestRouterVersion, TestDecodeLongVersionArray) TEST_CASE("Decode long version array", "[RouterVersion]")
{ {
llarp::RouterVersion version; llarp::RouterVersion version;
version.Clear(); version.Clear();
const std::string bString("li9ei3ei2ei1ei2ei3ei4ei5ei6ei7ei8ei9ee"); const std::string bString("li9ei3ei2ei1ei2ei3ei4ei5ei6ei7ei8ei9ee");
llarp_buffer_t buf(bString.data(), bString.size()); llarp_buffer_t buf(bString.data(), bString.size());
EXPECT_FALSE(version.BDecode(&buf)); CHECK_FALSE(version.BDecode(&buf));
} }

@ -1,24 +0,0 @@
#include <gtest/gtest.h>
#include <routing/transfer_traffic_message.hpp>
using TransferTrafficMessage = llarp::routing::TransferTrafficMessage;
class TransferTrafficTest : public ::testing::Test
{
};
TEST_F(TransferTrafficTest, TestPutBufferOverflow)
{
TransferTrafficMessage msg;
std::array< byte_t, llarp::routing::MaxExitMTU* 2 > tmp = {{0}};
llarp_buffer_t buf(tmp);
ASSERT_FALSE(msg.PutBuffer(buf, 1));
}
TEST_F(TransferTrafficTest, TestPutBuffer)
{
TransferTrafficMessage msg;
std::array< byte_t, llarp::routing::MaxExitMTU > tmp = {{0}};
llarp_buffer_t buf(tmp);
ASSERT_TRUE(msg.PutBuffer(buf, 1));
}

@ -2,45 +2,37 @@
#include <crypto/crypto.hpp> #include <crypto/crypto.hpp>
#include <crypto/crypto_libsodium.hpp> #include <crypto/crypto_libsodium.hpp>
#include <crypto/mock_crypto.hpp>
#include <llarp_test.hpp> #include <llarp_test.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <catch2/catch.hpp>
using namespace ::testing; using namespace ::testing;
using namespace ::llarp; using namespace ::llarp;
using namespace ::llarp::test;
using ObtainExitMessage = routing::ObtainExitMessage; using ObtainExitMessage = routing::ObtainExitMessage;
class ObtainExitTest : public test::LlarpTest<>
{
public:
SecretKey alice;
ObtainExitTest()
{
// m_crypto.identity_keygen(alice);
}
};
void void
fill(Signature& s) fill(Signature& s)
{ {
s.Fill(0xFF); s.Fill(0xFF);
} }
TEST_F(ObtainExitTest, TestSignVerify) TEST_CASE_METHOD(LlarpTest<>, "Sign-verify")
{ {
EXPECT_CALL(m_crypto, sign(_, alice, _)) SecretKey alice;
.WillOnce(DoAll(WithArg< 0 >(Invoke(&fill)), Return(true))); EXPECT_CALL(m_crypto, sign(_, alice, _)).WillOnce(DoAll(WithArg<0>(Invoke(&fill)), Return(true)));
EXPECT_CALL(m_crypto, verify(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(m_crypto, verify(_, _, _)).WillOnce(Return(true));
ObtainExitMessage msg; ObtainExitMessage msg;
msg.Z.Zero(); msg.Z.Zero();
msg.S = randint(); msg.S = randint();
msg.T = randint(); msg.T = randint();
EXPECT_TRUE(msg.Sign(alice)); CHECK(msg.Sign(alice));
EXPECT_TRUE(msg.Verify()); CHECK(msg.Verify());
EXPECT_TRUE(msg.I == PubKey(seckey_topublic(alice))); CHECK(msg.I == PubKey(seckey_topublic(alice)));
EXPECT_FALSE(msg.version != LLARP_PROTO_VERSION); CHECK(msg.version == LLARP_PROTO_VERSION);
EXPECT_FALSE(msg.Z.IsZero()); CHECK_FALSE(msg.Z.IsZero());
} }

@ -0,0 +1,24 @@
#include <routing/transfer_traffic_message.hpp>
#include <catch2/catch.hpp>
using TransferTrafficMessage = llarp::routing::TransferTrafficMessage;
TEST_CASE("TransferTrafficMessage", "[TransferTrafficMessage]")
{
TransferTrafficMessage msg;
SECTION("Put buffer overflow")
{
std::array<byte_t, llarp::routing::MaxExitMTU* 2> tmp = {{0}};
llarp_buffer_t buf(tmp);
REQUIRE_FALSE(msg.PutBuffer(buf, 1));
}
SECTION("Put buffer")
{
std::array<byte_t, llarp::routing::MaxExitMTU> tmp = {{0}};
llarp_buffer_t buf(tmp);
REQUIRE(msg.PutBuffer(buf, 1));
}
}

@ -1,67 +1,58 @@
#include <service/address.hpp> #include <service/address.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
struct ServiceAddressTest : public ::testing::Test TEST_CASE("Address", "[Address]")
{ {
const std::string snode = const std::string snode = "8zfiwpgonsu5zpddpxwdurxyb19x6r96xy4qbikff99jwsziws9y.snode";
"8zfiwpgonsu5zpddpxwdurxyb19x6r96xy4qbikff99jwsziws9y.snode"; const std::string loki = "7okic5x5do3uh3usttnqz9ek3uuoemdrwzto1hciwim9f947or6y.loki";
const std::string loki =
"7okic5x5do3uh3usttnqz9ek3uuoemdrwzto1hciwim9f947or6y.loki";
const std::string sub = "lokinet.test"; const std::string sub = "lokinet.test";
const std::string invalid = const std::string invalid = "7okic5x5do3uh3usttnqz9ek3uuoemdrwzto1hciwim9f947or6y.net";
"7okic5x5do3uh3usttnqz9ek3uuoemdrwzto1hciwim9f947or6y.net";
};
TEST_F(ServiceAddressTest, TestParseBadTLD)
{
llarp::service::Address addr;
ASSERT_FALSE(addr.FromString(snode, ".net"));
ASSERT_FALSE(addr.FromString(invalid, ".net"));
}
TEST_F(ServiceAddressTest, TestParseBadTLDAppenedOnEnd)
{
llarp::service::Address addr; llarp::service::Address addr;
const std::string bad = loki + ".net";
ASSERT_FALSE(addr.FromString(bad, ".net"));
}
TEST_F(ServiceAddressTest, TestParseBadTLDAppenedOnEndWithSubdomain) SECTION("Parse bad TLD")
{ {
llarp::service::Address addr; REQUIRE_FALSE(addr.FromString(snode, ".net"));
const std::string bad = sub + "." + loki + ".net"; REQUIRE_FALSE(addr.FromString(invalid, ".net"));
ASSERT_FALSE(addr.FromString(bad, ".net")); }
SECTION("Parse bad TLD appened on end")
{
const std::string bad = loki + ".net";
REQUIRE_FALSE(addr.FromString(bad, ".net"));
}
SECTION("Parse bad TLD appened on end with subdomain")
{
const std::string bad = sub + "." + loki + ".net";
REQUIRE_FALSE(addr.FromString(bad, ".net"));
}
SECTION("Parse SNode not Loki")
{
REQUIRE(addr.FromString(snode, ".snode"));
REQUIRE_FALSE(addr.FromString(snode, ".loki"));
}
SECTION("Parse Loki not SNode")
{
REQUIRE_FALSE(addr.FromString(loki, ".snode"));
REQUIRE(addr.FromString(loki, ".loki"));
}
SECTION("Parse Loki with subdomain")
{
const std::string addr_str = sub + "." + loki;
REQUIRE(addr.FromString(addr_str, ".loki"));
REQUIRE(addr.subdomain == sub);
REQUIRE(addr.ToString() == addr_str);
};
SECTION("Parse SNode with subdomain")
{
const std::string addr_str = sub + "." + snode;
REQUIRE(addr.FromString(addr_str, ".snode"));
REQUIRE(addr.subdomain == sub);
REQUIRE(addr.ToString(".snode") == addr_str);
}
} }
TEST_F(ServiceAddressTest, TestParseSNodeNotLoki)
{
llarp::service::Address addr;
ASSERT_TRUE(addr.FromString(snode, ".snode"));
ASSERT_FALSE(addr.FromString(snode, ".loki"));
}
TEST_F(ServiceAddressTest, TestParseLokiNotSNode)
{
llarp::service::Address addr;
ASSERT_FALSE(addr.FromString(loki, ".snode"));
ASSERT_TRUE(addr.FromString(loki, ".loki"));
}
TEST_F(ServiceAddressTest, TestParseLokiWithSubdomain)
{
llarp::service::Address addr;
const std::string addr_str = sub + "." + loki;
ASSERT_TRUE(addr.FromString(addr_str, ".loki"));
ASSERT_EQ(addr.subdomain, sub);
ASSERT_EQ(addr.ToString(), addr_str);
};
TEST_F(ServiceAddressTest, TestParseSnodeWithSubdomain)
{
llarp::service::Address addr;
const std::string addr_str = sub + "." + snode;
ASSERT_TRUE(addr.FromString(addr_str, ".snode"));
ASSERT_EQ(addr.subdomain, sub);
ASSERT_EQ(addr.ToString(".snode"), addr_str);
};

@ -7,15 +7,15 @@
#include <test_util.hpp> #include <test_util.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
using namespace ::llarp; using namespace ::llarp;
using namespace ::testing; using namespace ::testing;
using EncryptedFrame = EncryptedFrame; using EncryptedFrame = EncryptedFrame;
using SecretKey = SecretKey; using SecretKey = SecretKey;
using PubKey = PubKey; using PubKey = PubKey;
using LRCR = LR_CommitRecord; using LRCR = LR_CommitRecord;
class FrameTest : public test::LlarpTest<> class FrameTest : public test::LlarpTest<>
{ {
@ -23,7 +23,7 @@ class FrameTest : public test::LlarpTest<>
SecretKey alice, bob; SecretKey alice, bob;
}; };
TEST_F(FrameTest, TestFrameCrypto) TEST_CASE_METHOD(FrameTest, "Frame crypto")
{ {
EncryptedFrame f(256); EncryptedFrame f(256);
f.Fill(0); f.Fill(0);
@ -36,28 +36,25 @@ TEST_F(FrameTest, TestFrameCrypto)
auto buf = f.Buffer(); auto buf = f.Buffer();
buf->cur = buf->base + EncryptedFrameOverheadSize; buf->cur = buf->base + EncryptedFrameOverheadSize;
ASSERT_TRUE(record.BEncode(buf)); REQUIRE(record.BEncode(buf));
EXPECT_CALL(m_crypto, randbytes(_, _)) EXPECT_CALL(m_crypto, randbytes(_, _)).WillOnce(Invoke(&test::randbytes_impl));
.WillOnce(Invoke(&test::randbytes_impl));
EXPECT_CALL(m_crypto, dh_client(_, _, alice, _)).WillOnce(Return(true)); EXPECT_CALL(m_crypto, dh_client(_, _, alice, _)).WillOnce(Return(true));
EXPECT_CALL(m_crypto, xchacha20(_, _, _)) EXPECT_CALL(m_crypto, xchacha20(_, _, _)).Times(2).WillRepeatedly(Return(true));
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(m_crypto, hmac(_, _, _)).Times(2).WillRepeatedly(Return(true)); EXPECT_CALL(m_crypto, hmac(_, _, _)).Times(2).WillRepeatedly(Return(true));
// rewind buffer // rewind buffer
buf->cur = buf->base + EncryptedFrameOverheadSize; buf->cur = buf->base + EncryptedFrameOverheadSize;
// encrypt to alice // encrypt to alice
ASSERT_TRUE(f.EncryptInPlace(alice, bob.toPublic())); REQUIRE(f.EncryptInPlace(alice, bob.toPublic()));
EXPECT_CALL(m_crypto, dh_server(_, _, _, _)).WillOnce(Return(true)); EXPECT_CALL(m_crypto, dh_server(_, _, _, _)).WillOnce(Return(true));
// decrypt from alice // decrypt from alice
ASSERT_TRUE(f.DecryptInPlace(bob)); REQUIRE(f.DecryptInPlace(bob));
LRCR otherRecord; LRCR otherRecord;
ASSERT_TRUE(otherRecord.BDecode(buf)); REQUIRE(otherRecord.BDecode(buf));
ASSERT_TRUE(otherRecord == record); REQUIRE(otherRecord == record);
} }

@ -1,7 +1,6 @@
#include <util/meta/memfn.hpp> #include <util/meta/memfn.hpp>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <gmock/gmock.h>
using namespace llarp; using namespace llarp;
@ -32,36 +31,25 @@ struct Foo
} }
}; };
TEST(MemFn, call) TEST_CASE("memFn call")
{ {
Foo foo; Foo foo;
ASSERT_FALSE(util::memFn(&Foo::empty, &foo)()); REQUIRE_FALSE(util::memFn(&Foo::empty, &foo)());
ASSERT_TRUE(util::memFn(&Foo::constEmpty, &foo)()); REQUIRE(util::memFn(&Foo::constEmpty, &foo)());
ASSERT_EQ(11, util::memFn(&Foo::arg, &foo)(10)); REQUIRE(11 == util::memFn(&Foo::arg, &foo)(10));
ASSERT_EQ(9, util::memFn(&Foo::constArg, &foo)(10)); REQUIRE(9 == util::memFn(&Foo::constArg, &foo)(10));
ASSERT_TRUE(util::memFn(&Foo::constEmpty, &foo)()); REQUIRE(util::memFn(&Foo::constEmpty, &foo)());
ASSERT_EQ(9, util::memFn(&Foo::constArg, &foo)(10)); REQUIRE(9 == util::memFn(&Foo::constArg, &foo)(10));
} }
template < typename T >
class MemFnType : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(MemFnType);
TYPED_TEST_P(MemFnType, Smoke)
{
TypeParam foo{};
ASSERT_TRUE(util::memFn(&Foo::constEmpty, &foo)());
}
REGISTER_TYPED_TEST_SUITE_P(MemFnType, Smoke);
// clang-format off // clang-format off
using MemFnTypes = ::testing::Types< using MemFnTypes = std::tuple<
Foo, const Foo>; Foo, const Foo>;
INSTANTIATE_TYPED_TEST_SUITE_P(MemFn, MemFnType, MemFnTypes);
// clang-format on // clang-format on
TEMPLATE_LIST_TEST_CASE("memFn type smoke test", "", MemFnTypes)
{
TestType foo{};
REQUIRE(util::memFn(&Foo::constEmpty, &foo)());
}

@ -2,37 +2,19 @@
#include <list> #include <list>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <gmock/gmock.h>
using namespace llarp; using namespace llarp;
TEST(traits_bottom, Smoke) TEST_CASE("traits::Bottom smoke test")
{ {
traits::Bottom bottom; traits::Bottom bottom;
(void)bottom; (void)bottom;
SUCCEED(); SUCCEED();
} }
template < typename T >
class IsContainer : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(IsContainer);
TYPED_TEST_P(IsContainer, Smoke)
{
bool expected = std::tuple_element_t< 1, TypeParam >::value;
bool result =
traits::is_container< std::tuple_element_t< 0, TypeParam > >::value;
ASSERT_EQ(expected, result);
}
REGISTER_TYPED_TEST_SUITE_P(IsContainer, Smoke);
// clang-format off // clang-format off
using ContainerTypes = ::testing::Types< using ContainerTypes = std::tuple<
std::tuple< std::vector< int >, std::integral_constant< bool, true > >, std::tuple< std::vector< int >, std::integral_constant< bool, true > >,
std::tuple< std::vector< std::string >, std::integral_constant< bool, true > >, std::tuple< std::vector< std::string >, std::integral_constant< bool, true > >,
std::tuple< std::list< std::string >, std::integral_constant< bool, true > >, std::tuple< std::list< std::string >, std::integral_constant< bool, true > >,
@ -41,8 +23,16 @@ using ContainerTypes = ::testing::Types<
std::tuple< std::tuple<std::string>, std::integral_constant< bool, false > >, std::tuple< std::tuple<std::string>, std::integral_constant< bool, false > >,
std::tuple< int, std::integral_constant< bool, false > > std::tuple< int, std::integral_constant< bool, false > >
>; >;
INSTANTIATE_TYPED_TEST_SUITE_P(traits, IsContainer, ContainerTypes); // clang-format on
TEMPLATE_LIST_TEST_CASE("is_container smoke test", "", ContainerTypes)
{
bool expected = std::tuple_element_t<1, TestType>::value;
bool result = traits::is_container<std::tuple_element_t<0, TestType>>::value;
REQUIRE(expected == result);
}
// clang-format off
struct A { }; struct A { };
struct B { }; struct B { };
struct C { }; struct C { };
@ -65,29 +55,11 @@ char f(H) { return 'H'; }
char f(I) { return 'I'; } char f(I) { return 'I'; }
char f(J) { return 'J'; } char f(J) { return 'J'; }
char f(traits::Bottom) { return '0'; } char f(traits::Bottom) { return '0'; }
// clang-format on // clang-format on
template < typename T >
class TestSwitch : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(TestSwitch);
TYPED_TEST_P(TestSwitch, Smoke)
{
char expected = std::tuple_element_t< 0, TypeParam >::value;
using InputType = typename std::tuple_element_t< 1, TypeParam >::Type;
char result = f(InputType());
ASSERT_EQ(expected, result);
}
REGISTER_TYPED_TEST_SUITE_P(TestSwitch, Smoke);
// clang-format off // clang-format off
using namespace traits::Switch; using namespace traits::Switch;
using SwitchTypes = ::testing::Types< using SwitchTypes = std::tuple<
std::tuple<std::integral_constant<char, 'A'>, Switch< 0, A, B, C, D, E, F, G, H, I, J > >, std::tuple<std::integral_constant<char, 'A'>, Switch< 0, A, B, C, D, E, F, G, H, I, J > >,
std::tuple<std::integral_constant<char, 'B'>, Switch< 1, A, B, C, D, E, F, G, H, I, J > >, std::tuple<std::integral_constant<char, 'B'>, Switch< 1, A, B, C, D, E, F, G, H, I, J > >,
std::tuple<std::integral_constant<char, 'C'>, Switch< 2, A, B, C, D, E, F, G, H, I, J > >, std::tuple<std::integral_constant<char, 'C'>, Switch< 2, A, B, C, D, E, F, G, H, I, J > >,
@ -102,8 +74,15 @@ using SwitchTypes = ::testing::Types<
std::tuple<std::integral_constant<char, 'C'>, Switch< 6, C, C, C, C, C, C, C, C, C, J > >, std::tuple<std::integral_constant<char, 'C'>, Switch< 6, C, C, C, C, C, C, C, C, C, J > >,
std::tuple<std::integral_constant<char, '0'>, Switch< 10, A, B, C, D, E, F, G, H, I, J > > std::tuple<std::integral_constant<char, '0'>, Switch< 10, A, B, C, D, E, F, G, H, I, J > >
>; >;
// clang-format off
INSTANTIATE_TYPED_TEST_SUITE_P(traits, TestSwitch, SwitchTypes); TEMPLATE_LIST_TEST_CASE("Switch smoke test", "", SwitchTypes)
{
char expected = std::tuple_element_t<0, TestType>::value;
using InputType = typename std::tuple_element_t<1, TestType>::Type;
char result = f(InputType());
REQUIRE(expected == result);
}
template<typename T> template<typename T>
using is_bool = std::is_same<T, bool>; using is_bool = std::is_same<T, bool>;
@ -126,56 +105,35 @@ selectCase()
return dispatch(Selection()); return dispatch(Selection());
} }
template < typename T > // clang-format off
class Select : public ::testing::Test using SelectTypes = std::tuple<
{
};
TYPED_TEST_SUITE_P(Select);
TYPED_TEST_P(Select, Smoke)
{
char expected = std::tuple_element_t< 0, TypeParam >::value;
char result = selectCase<std::tuple_element_t< 1, TypeParam > >();
ASSERT_EQ(expected, result);
}
REGISTER_TYPED_TEST_SUITE_P(Select, Smoke);
using SelectTypes = ::testing::Types<
std::tuple<std::integral_constant<char, '0'>, double >, std::tuple<std::integral_constant<char, '0'>, double >,
std::tuple<std::integral_constant<char, 'b'>, bool >, std::tuple<std::integral_constant<char, 'b'>, bool >,
std::tuple<std::integral_constant<char, 'c'>, char >, std::tuple<std::integral_constant<char, 'c'>, char >,
std::tuple<std::integral_constant<char, 's'>, std::string > std::tuple<std::integral_constant<char, 's'>, std::string >
>; >;
INSTANTIATE_TYPED_TEST_SUITE_P(traits, Select, SelectTypes);
// clang-format on // clang-format on
template < typename T > TEMPLATE_LIST_TEST_CASE("selectCase smoke test", "", SelectTypes)
class IsPointy : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(IsPointy);
TYPED_TEST_P(IsPointy, Smoke)
{ {
bool expected = std::tuple_element_t< 1, TypeParam >::value; char expected = std::tuple_element_t<0, TestType>::value;
bool result = char result = selectCase<std::tuple_element_t<1, TestType>>();
traits::is_pointy< std::tuple_element_t< 0, TypeParam > >::value; REQUIRE(expected == result);
ASSERT_EQ(expected, result);
} }
// clang-format on
REGISTER_TYPED_TEST_SUITE_P(IsPointy, Smoke);
// clang-format off // clang-format off
using PointerTypes = ::testing::Types< using PointerTypes = std::tuple<
std::tuple< int *, std::true_type >, std::tuple< int *, std::true_type >,
std::tuple< int, std::integral_constant< bool, false > >, std::tuple< int, std::integral_constant< bool, false > >,
std::tuple< std::shared_ptr<int>, std::true_type >, std::tuple< std::shared_ptr<int>, std::true_type >,
std::tuple< std::unique_ptr<int>, std::true_type > std::tuple< std::unique_ptr<int>, std::true_type >
>; >;
INSTANTIATE_TYPED_TEST_SUITE_P(traits, IsPointy, PointerTypes);
// clang-format on // clang-format on
TEMPLATE_LIST_TEST_CASE("is_pointy smoke test", "", PointerTypes)
{
bool expected = std::tuple_element_t<1, TestType>::value;
bool result = traits::is_pointy<std::tuple_element_t<0, TestType>>::value;
REQUIRE(expected == result);
}

@ -1,4 +1,4 @@
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <util/aligned.hpp> #include <util/aligned.hpp>
@ -7,218 +7,176 @@
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
using TestSizes = ::testing::Types< std::integral_constant< std::size_t, 8 >, using TestSizes = std::tuple<
std::integral_constant< std::size_t, 12 >, std::integral_constant<std::size_t, 8>,
std::integral_constant< std::size_t, 16 >, std::integral_constant<std::size_t, 12>,
std::integral_constant< std::size_t, 32 >, std::integral_constant<std::size_t, 16>,
std::integral_constant< std::size_t, 64 >, std::integral_constant<std::size_t, 32>,
std::integral_constant< std::size_t, 77 >, std::integral_constant<std::size_t, 64>,
std::integral_constant< std::size_t, 1024 >, std::integral_constant<std::size_t, 77>,
std::integral_constant< std::size_t, 3333 > >; std::integral_constant<std::size_t, 1024>,
std::integral_constant<std::size_t, 3333>>;
template < typename T >
struct AlignedBufferTest : public ::testing::Test TEMPLATE_LIST_TEST_CASE("AlignedBuffer", "[AlignedBuffer]", TestSizes)
{ {
}; using Buffer = llarp::AlignedBuffer<TestType::value>;
TYPED_TEST_CASE(AlignedBufferTest, TestSizes, );
TYPED_TEST(AlignedBufferTest, Constructor)
{
using Buffer = llarp::AlignedBuffer< TypeParam::value >;
Buffer b; Buffer b;
EXPECT_TRUE(b.IsZero()); CHECK(b.IsZero());
EXPECT_EQ(b.size(), TypeParam::value);
}
TYPED_TEST(AlignedBufferTest, CopyConstructor)
{
using Buffer = llarp::AlignedBuffer< TypeParam::value >;
Buffer b;
EXPECT_TRUE(b.IsZero());
Buffer c = b;
EXPECT_TRUE(c.IsZero());
c.Fill(1); SECTION("Constructor")
EXPECT_FALSE(c.IsZero()); {
CHECK(b.size() == TestType::value);
Buffer d = c; }
EXPECT_FALSE(d.IsZero());
}
TYPED_TEST(AlignedBufferTest, AltConstructors)
{
using Buffer = llarp::AlignedBuffer< TypeParam::value >;
Buffer b;
EXPECT_TRUE(b.IsZero());
b.Fill(2);
Buffer c(b.as_array()); SECTION("CopyConstructor")
EXPECT_FALSE(c.IsZero()); {
Buffer c = b;
CHECK(c.IsZero());
Buffer d(c.data()); c.Fill(1);
EXPECT_FALSE(d.IsZero()); CHECK_FALSE(c.IsZero());
}
TYPED_TEST(AlignedBufferTest, Assignment) Buffer d = c;
{ CHECK_FALSE(d.IsZero());
using Buffer = llarp::AlignedBuffer< TypeParam::value >; }
Buffer b; SECTION("AltConstructors")
EXPECT_TRUE(b.IsZero()); {
b.Fill(2);
Buffer c; Buffer c(b.as_array());
c = b; CHECK_FALSE(c.IsZero());
EXPECT_TRUE(c.IsZero());
c.Fill(1); Buffer d(c.data());
EXPECT_FALSE(c.IsZero()); CHECK_FALSE(d.IsZero());
}
Buffer d; SECTION("Assignment")
d = c; {
EXPECT_FALSE(d.IsZero()); Buffer c;
} c = b;
CHECK(c.IsZero());
TYPED_TEST(AlignedBufferTest, StreamOut) c.Fill(1);
{ CHECK_FALSE(c.IsZero());
using Buffer = llarp::AlignedBuffer< TypeParam::value >;
Buffer b; Buffer d;
EXPECT_TRUE(b.IsZero()); d = c;
CHECK_FALSE(d.IsZero());
}
std::stringstream stream; SECTION("StreamOut")
{
std::stringstream stream;
stream << b; stream << b;
EXPECT_EQ(stream.str(), std::string(TypeParam::value * 2, '0')); CHECK(stream.str() == std::string(TestType::value * 2, '0'));
stream.str(""); stream.str("");
b.Fill(255); b.Fill(255);
stream << b; stream << b;
EXPECT_EQ(stream.str(), std::string(TypeParam::value * 2, 'f')); CHECK(stream.str() == std::string(TestType::value * 2, 'f'));
} }
TYPED_TEST(AlignedBufferTest, BitwiseNot) SECTION("BitwiseNot")
{ {
using Buffer = llarp::AlignedBuffer< TypeParam::value >; Buffer c = ~b;
CHECK_FALSE(c.IsZero());
Buffer b; for (auto val : c.as_array())
EXPECT_TRUE(b.IsZero()); {
CHECK(255 == val);
}
Buffer c = ~b; Buffer d = ~c;
EXPECT_FALSE(c.IsZero()); CHECK(d.IsZero());
}
for(auto val : c.as_array()) SECTION("Operators")
{ {
EXPECT_EQ(255, val); Buffer c = b;
CHECK(b == c);
CHECK(b >= c);
CHECK(b <= c);
CHECK(c >= b);
CHECK(c <= b);
c.Fill(1);
CHECK(b != c);
CHECK(b < c);
CHECK(c > b);
} }
Buffer d = ~c; SECTION("Xor")
EXPECT_TRUE(d.IsZero()); {
} Buffer c;
b.Fill(255);
TYPED_TEST(AlignedBufferTest, Operators) c.Fill(255);
{ CHECK_FALSE(b.IsZero());
using Buffer = llarp::AlignedBuffer< TypeParam::value >; CHECK_FALSE(c.IsZero());
Buffer b; Buffer d = b ^ c;
EXPECT_TRUE(b.IsZero()); // 1 ^ 1 = 0
CHECK(d.IsZero());
Buffer c = b; // Verify unchanged
EXPECT_EQ(b, c); CHECK_FALSE(b.IsZero());
EXPECT_GE(b, c); CHECK_FALSE(c.IsZero());
EXPECT_LE(b, c);
EXPECT_GE(c, b); Buffer e, f;
EXPECT_LE(c, b); e.Fill(255);
Buffer g = e ^ f;
c.Fill(1); // 1 ^ 0 = 1
EXPECT_NE(b, c); CHECK_FALSE(g.IsZero());
EXPECT_LT(b, c);
EXPECT_GT(c, b); Buffer h, i;
} i.Fill(255);
Buffer j = h ^ i;
TYPED_TEST(AlignedBufferTest, Xor) // 0 ^ 1 = 1
{ CHECK_FALSE(j.IsZero());
using Buffer = llarp::AlignedBuffer< TypeParam::value >; }
Buffer b;
Buffer c;
b.Fill(255);
c.Fill(255);
EXPECT_FALSE(b.IsZero());
EXPECT_FALSE(c.IsZero());
Buffer d = b ^ c;
// 1 ^ 1 = 0
EXPECT_TRUE(d.IsZero());
// Verify unchanged
EXPECT_FALSE(b.IsZero());
EXPECT_FALSE(c.IsZero());
Buffer e, f;
e.Fill(255);
Buffer g = e ^ f;
// 1 ^ 0 = 1
EXPECT_FALSE(g.IsZero());
Buffer h, i;
i.Fill(255);
Buffer j = h ^ i;
// 0 ^ 1 = 1
EXPECT_FALSE(j.IsZero());
}
TYPED_TEST(AlignedBufferTest, XorAssign)
{
using Buffer = llarp::AlignedBuffer< TypeParam::value >;
Buffer b, c;
b.Fill(255);
c.Fill(255);
EXPECT_FALSE(b.IsZero());
EXPECT_FALSE(c.IsZero());
b ^= c;
EXPECT_TRUE(b.IsZero());
}
TYPED_TEST(AlignedBufferTest, Zero)
{
using Buffer = llarp::AlignedBuffer< TypeParam::value >;
Buffer b; SECTION("XorAssign")
EXPECT_TRUE(b.IsZero()); {
Buffer c;
b.Fill(255);
c.Fill(255);
CHECK_FALSE(b.IsZero());
CHECK_FALSE(c.IsZero());
b ^= c;
CHECK(b.IsZero());
}
b.Fill(127); SECTION("Zero")
EXPECT_FALSE(b.IsZero()); {
b.Fill(127);
CHECK_FALSE(b.IsZero());
b.Zero(); b.Zero();
EXPECT_TRUE(b.IsZero()); CHECK(b.IsZero());
} }
TYPED_TEST(AlignedBufferTest, TestHash) SECTION("TestHash")
{ {
using Buffer = llarp::AlignedBuffer< TypeParam::value >; using Map_t = std::unordered_map<Buffer, int, typename Buffer::Hash>;
using Map_t = std::unordered_map< Buffer, int, typename Buffer::Hash >;
Buffer k, other_k;
Buffer k, other_k; k.Randomize();
k.Randomize(); other_k.Randomize();
other_k.Randomize(); Map_t m;
Map_t m; CHECK(m.empty());
EXPECT_TRUE(m.empty()); CHECK(m.emplace(k, 1).second);
EXPECT_TRUE(m.emplace(k, 1).second); CHECK(m.find(k) != m.end());
EXPECT_TRUE(m.find(k) != m.end()); CHECK(m[k] == 1);
EXPECT_TRUE(m[k] == 1); CHECK_FALSE(m.find(other_k) != m.end());
EXPECT_FALSE(m.find(other_k) != m.end()); CHECK(m.size() == 1);
EXPECT_TRUE(m.size() == 1); Buffer k_copy = k;
Buffer k_copy = k; CHECK_FALSE(m.emplace(k_copy, 2).second);
EXPECT_FALSE(m.emplace(k_copy, 2).second); CHECK_FALSE(m[k_copy] == 2);
EXPECT_FALSE(m[k_copy] == 2); CHECK(m[k_copy] == 1);
EXPECT_TRUE(m[k_copy] == 1); }
} }

@ -6,11 +6,11 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
using TestBuffer = std::vector< byte_t >; using TestBuffer = std::vector<byte_t>;
template < typename Result > template <typename Result>
struct TestReadData struct TestReadData
{ {
TestBuffer buffer; TestBuffer buffer;
@ -18,15 +18,15 @@ struct TestReadData
Result result; Result result;
}; };
using TestReadInt = TestReadData< uint64_t >; using TestReadInt = TestReadData<uint64_t>;
using TestReadString = TestReadData< std::string >; using TestReadString = TestReadData<std::string>;
template < typename Result > template <typename Result>
std::ostream& std::ostream&
operator<<(std::ostream& os, const TestReadData< Result >& d) operator<<(std::ostream& os, const TestReadData<Result>& d)
{ {
os << "buf = [ "; os << "buf = [ ";
for(auto x : d.buffer) for (auto x : d.buffer)
{ {
os << x << " "; os << x << " ";
} }
@ -37,33 +37,16 @@ operator<<(std::ostream& os, const TestReadData< Result >& d)
return os; return os;
} }
struct ReadInt : public ::testing::TestWithParam< TestReadInt > static constexpr byte_t i = 'i';
{ static constexpr byte_t e = 'e';
}; static constexpr byte_t zero = '0';
static constexpr byte_t one = '1';
TEST_P(ReadInt, readInt) static constexpr byte_t two = '2';
{ static constexpr byte_t f = 'f';
auto d = GetParam(); static constexpr byte_t z = 'z';
llarp_buffer_t buffer(d.buffer);
uint64_t result = 0;
bool rc = bencode_read_integer(&buffer, &result);
EXPECT_EQ(rc, d.rc);
EXPECT_EQ(result, d.result);
}
static constexpr byte_t i = 'i';
static constexpr byte_t e = 'e';
static constexpr byte_t zero = '0';
static constexpr byte_t one = '1';
static constexpr byte_t two = '2';
static constexpr byte_t f = 'f';
static constexpr byte_t z = 'z';
static constexpr byte_t colon = ':'; static constexpr byte_t colon = ':';
static const TestReadInt testReadInt[] = { std::vector<TestReadInt> testReadInt{
// good cases // good cases
{{i, 0, e}, true, 0}, {{i, 0, e}, true, 0},
{{i, zero, e}, true, 0}, {{i, zero, e}, true, 0},
@ -80,28 +63,20 @@ static const TestReadInt testReadInt[] = {
{{z}, false, 0}, {{z}, false, 0},
}; };
INSTANTIATE_TEST_SUITE_P(TestBencode, ReadInt, TEST_CASE("Read int", "[bencode]")
::testing::ValuesIn(testReadInt));
struct ReadStr : public ::testing::TestWithParam< TestReadString >
{
};
TEST_P(ReadStr, readStr)
{ {
auto d = GetParam(); auto d = GENERATE(from_range(testReadInt));
llarp_buffer_t buffer(d.buffer); llarp_buffer_t buffer(d.buffer);
llarp_buffer_t result; uint64_t result = 0;
bool rc = bencode_read_string(&buffer, &result); bool rc = bencode_read_integer(&buffer, &result);
EXPECT_EQ(rc, d.rc); CHECK(rc == d.rc);
EXPECT_EQ(result.sz, d.result.size()); CHECK(result == d.result);
EXPECT_EQ(std::string(result.base, result.base + result.sz), d.result);
} }
static const TestReadString testReadStr[] = { std::vector<TestReadString> testReadStr{
// good cases // good cases
{{one, colon, 'a'}, true, "a"}, {{one, colon, 'a'}, true, "a"},
{{one, colon, 'b'}, true, "b"}, {{one, colon, 'b'}, true, "b"},
@ -118,10 +93,21 @@ static const TestReadString testReadStr[] = {
{{colon, colon}, false, ""}, {{colon, colon}, false, ""},
}; };
INSTANTIATE_TEST_SUITE_P(TestBencode, ReadStr, TEST_CASE("Read str", "[bencode]")
::testing::ValuesIn(testReadStr)); {
auto d = GENERATE(from_range(testReadStr));
llarp_buffer_t buffer(d.buffer);
llarp_buffer_t result;
bool rc = bencode_read_string(&buffer, &result);
template < typename Input > CHECK(rc == d.rc);
CHECK(result.sz == d.result.size());
CHECK(std::string(result.base, result.base + result.sz) == d.result);
}
template <typename Input>
struct TestWriteData struct TestWriteData
{ {
Input input; Input input;
@ -130,62 +116,39 @@ struct TestWriteData
std::string output; std::string output;
}; };
using TestWriteByteString = TestWriteData< std::string >; using TestWriteByteString = TestWriteData<std::string>;
using TestWriteInt = TestWriteData< uint64_t >; using TestWriteInt = TestWriteData<uint64_t>;
struct WriteByteStr : public ::testing::TestWithParam< TestWriteByteString >
{
};
TEST_P(WriteByteStr, writeByte)
{
auto d = GetParam();
std::vector< byte_t > backingBuffer(d.bufferSize, 0);
llarp_buffer_t buffer(backingBuffer);
bool rc = bencode_write_bytestring(&buffer, d.input.data(), d.input.size()); static constexpr size_t MAX_1 = static_cast<size_t>(std::numeric_limits<int16_t>::max()) + 1;
ASSERT_EQ(rc, d.rc);
ASSERT_EQ(std::string(buffer.base, buffer.cur), d.output);
}
static constexpr size_t MAX_1 = std::vector<TestWriteByteString> testWriteByteString{
static_cast< size_t >(std::numeric_limits< int16_t >::max()) + 1;
static const TestWriteByteString testWriteByteString[] = {
// good cases // good cases
{"abacus", 100, true, "6:abacus"}, {"abacus", 100, true, "6:abacus"},
{" abacus", 100, true, "8: abacus"}, {" abacus", 100, true, "8: abacus"},
{"", 100, true, "0:"}, {"", 100, true, "0:"},
{std::string("\0\0\0", 3), 100, true, std::string("3:\0\0\0", 5)}, {std::string("\0\0\0", 3), 100, true, std::string("3:\0\0\0", 5)},
{std::string(MAX_1, 'a'), MAX_1 + 100, true, {std::string(MAX_1, 'a'),
MAX_1 + 100,
true,
std::to_string(MAX_1) + std::string(":") + std::string(MAX_1, 'a')}, std::to_string(MAX_1) + std::string(":") + std::string(MAX_1, 'a')},
// bad cases // bad cases
{"a", 1, false, ""}, {"a", 1, false, ""},
}; };
INSTANTIATE_TEST_SUITE_P(TestBencode, WriteByteStr, TEST_CASE("Write byte str", "[bencode]")
::testing::ValuesIn(testWriteByteString));
struct WriteInt : public ::testing::TestWithParam< TestWriteInt >
{
};
TEST_P(WriteInt, writeInt)
{ {
auto d = GetParam(); auto d = GENERATE(from_range(testWriteByteString));
std::vector< byte_t > backingBuffer(d.bufferSize, 0); std::vector<byte_t> backingBuffer(d.bufferSize, 0);
llarp_buffer_t buffer(backingBuffer); llarp_buffer_t buffer(backingBuffer);
bool rc = bencode_write_uint64(&buffer, d.input); bool rc = bencode_write_bytestring(&buffer, d.input.data(), d.input.size());
ASSERT_EQ(rc, d.rc); REQUIRE(rc == d.rc);
ASSERT_EQ(std::string(buffer.base, buffer.cur), d.output); REQUIRE(std::string(buffer.base, buffer.cur) == d.output);
} }
static const TestWriteInt testWriteInt[] = { std::vector<TestWriteInt> testWriteInt{
// Good cases // Good cases
{0, 100, true, "i0e"}, {0, 100, true, "i0e"},
{1234, 100, true, "i1234e"}, {1234, 100, true, "i1234e"},
@ -194,59 +157,62 @@ static const TestWriteInt testWriteInt[] = {
{1234567, 3, false, ""}, {1234567, 3, false, ""},
}; };
INSTANTIATE_TEST_SUITE_P(TestBencode, WriteInt, TEST_CASE("Write int", "[bencode]")
::testing::ValuesIn(testWriteInt));
struct WriteIntValues : public ::testing::TestWithParam< uint64_t >
{ {
}; auto d = GENERATE(from_range(testWriteInt));
std::vector<byte_t> backingBuffer(d.bufferSize, 0);
llarp_buffer_t buffer(backingBuffer);
TEST_P(WriteIntValues, anyvalue) bool rc = bencode_write_uint64(&buffer, d.input);
REQUIRE(rc == d.rc);
REQUIRE(std::string(buffer.base, buffer.cur) == d.output);
}
TEST_CASE("Write int values", "[bencode]")
{ {
// test we can encode any uint64_t into a buffer. // test we can encode any uint64_t into a buffer.
uint64_t val = GetParam(); uint64_t val = GENERATE(
std::numeric_limits<uint64_t>::min(),
std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max() / 2,
std::numeric_limits<uint64_t>::max() / 3);
std::vector< byte_t > backingBuffer(100, 0); std::vector<byte_t> backingBuffer(100, 0);
{ {
llarp_buffer_t buffer(backingBuffer); llarp_buffer_t buffer(backingBuffer);
bool rc = bencode_write_uint64(&buffer, val); bool rc = bencode_write_uint64(&buffer, val);
ASSERT_TRUE(rc); REQUIRE(rc);
} }
{ {
uint64_t result = 0; uint64_t result = 0;
llarp_buffer_t buffer(backingBuffer); llarp_buffer_t buffer(backingBuffer);
bool rc = bencode_read_integer(&buffer, &result); bool rc = bencode_read_integer(&buffer, &result);
ASSERT_TRUE(rc); REQUIRE(rc);
ASSERT_EQ(result, val); REQUIRE(result == val);
} }
} }
INSTANTIATE_TEST_SUITE_P( TEST_CASE("Bencode: good uint64 entry", "[bencode]")
TestBencode, WriteIntValues,
::testing::Values(std::numeric_limits< uint64_t >::min(),
std::numeric_limits< uint64_t >::max(),
std::numeric_limits< uint64_t >::max() / 2,
std::numeric_limits< uint64_t >::max() / 3));
TEST(TestBencode, good_uint64_entry)
{ {
std::vector< byte_t > backingBuffer(100, 0); std::vector<byte_t> backingBuffer(100, 0);
llarp_buffer_t buffer(backingBuffer); llarp_buffer_t buffer(backingBuffer);
ASSERT_TRUE(bencode_write_uint64_entry(&buffer, "v", 1, 0)); REQUIRE(bencode_write_uint64_entry(&buffer, "v", 1, 0));
ASSERT_EQ(std::string(buffer.base, buffer.cur), "1:vi0e"); REQUIRE(std::string(buffer.base, buffer.cur) == "1:vi0e");
} }
TEST(TestBencode, bad_uint64_entry) TEST_CASE("Bencode: bad uint64 entry", "[bencode]")
{ {
std::vector< byte_t > otherBuffer(1, 0); std::vector<byte_t> otherBuffer(1, 0);
llarp_buffer_t buffer(otherBuffer); llarp_buffer_t buffer(otherBuffer);
ASSERT_FALSE(bencode_write_uint64_entry(&buffer, "v", 1, 0)); REQUIRE_FALSE(bencode_write_uint64_entry(&buffer, "v", 1, 0));
} }
struct ValueData struct ValueData
@ -259,172 +225,139 @@ struct ValueData
struct ListTestData struct ListTestData
{ {
std::vector< ValueData > list; std::vector<ValueData> list;
size_t bufferSize; size_t bufferSize;
std::string result; std::string result;
}; };
struct ListTest : public ::testing::TestWithParam< ListTestData > std::vector<ListTestData> listTestData{
{ {{}, 100, "le"},
{{{"", 0, true}}, 100, "l0:e"},
{{{"", 0, false}}, 100, "li0ee"},
{{{"", 0, false}, {"", 0, true}}, 100, "li0e0:e"},
{{{"", 123, false}, {"abc", 0, true}}, 100, "li123e3:abce"},
{{{"", 123, false}, {"abc", 0, true}, {"abc", 0, true}}, 100, "li123e3:abc3:abce"},
}; };
TEST_P(ListTest, list) TEST_CASE("List test", "[bencode]")
{ {
auto d = GetParam(); auto d = GENERATE(from_range(listTestData));
std::vector< byte_t > backingBuffer(d.bufferSize, 0); std::vector<byte_t> backingBuffer(d.bufferSize, 0);
llarp_buffer_t buffer(backingBuffer); llarp_buffer_t buffer(backingBuffer);
ASSERT_TRUE(bencode_start_list(&buffer)); REQUIRE(bencode_start_list(&buffer));
for(const auto& x : d.list) for (const auto& x : d.list)
{ {
if(x.isString) if (x.isString)
{ {
ASSERT_TRUE(bencode_write_bytestring(&buffer, x.theString.data(), REQUIRE(bencode_write_bytestring(&buffer, x.theString.data(), x.theString.size()));
x.theString.size()));
} }
else else
{ {
ASSERT_TRUE(bencode_write_uint64(&buffer, x.theInt)); REQUIRE(bencode_write_uint64(&buffer, x.theInt));
} }
} }
ASSERT_TRUE(bencode_end(&buffer)); REQUIRE(bencode_end(&buffer));
ASSERT_EQ(std::string(buffer.base, buffer.cur), d.result); REQUIRE(std::string(buffer.base, buffer.cur) == d.result);
} }
ListTestData listTestData[] = {
{{}, 100, "le"},
{{{"", 0, true}}, 100, "l0:e"},
{{{"", 0, false}}, 100, "li0ee"},
{{{"", 0, false}, {"", 0, true}}, 100, "li0e0:e"},
{{{"", 123, false}, {"abc", 0, true}}, 100, "li123e3:abce"},
{{{"", 123, false}, {"abc", 0, true}, {"abc", 0, true}},
100,
"li123e3:abc3:abce"},
};
INSTANTIATE_TEST_SUITE_P(TestBencode, ListTest,
::testing::ValuesIn(listTestData));
struct DictTestData struct DictTestData
{ {
std::vector< std::pair< char, ValueData > > list; std::vector<std::pair<char, ValueData>> list;
size_t bufferSize; size_t bufferSize;
std::string result; std::string result;
}; };
struct DictTest : public ::testing::TestWithParam< DictTestData > std::vector<DictTestData> dictTestData{
{ {{}, 100, "de"},
{{{'a', {"", 0, true}}}, 100, "d1:a0:e"},
{{{'b', {"", 0, false}}}, 100, "d1:bi0ee"},
{{{'c', {"", 0, false}}, {'d', {"", 0, true}}}, 100, "d1:ci0e1:d0:e"},
{{{'e', {"", 123, false}}, {'f', {"abc", 0, true}}}, 100, "d1:ei123e1:f3:abce"},
{{{'a', {"", 123, false}}, {'b', {"abc", 0, true}}, {'c', {"abc", 0, true}}},
100,
"d1:ai123e1:b3:abc1:c3:abce"},
}; };
TEST_P(DictTest, dict) TEST_CASE("Dict test", "[bencode]")
{ {
auto d = GetParam(); auto d = GENERATE(from_range(dictTestData));
std::vector< byte_t > backingBuffer(d.bufferSize, 0); std::vector<byte_t> backingBuffer(d.bufferSize, 0);
llarp_buffer_t buffer(backingBuffer); llarp_buffer_t buffer(backingBuffer);
ASSERT_TRUE(bencode_start_dict(&buffer)); REQUIRE(bencode_start_dict(&buffer));
for(const auto& x : d.list) for (const auto& x : d.list)
{ {
ASSERT_TRUE(bencode_write_bytestring(&buffer, &x.first, 1)); REQUIRE(bencode_write_bytestring(&buffer, &x.first, 1));
if(x.second.isString) if (x.second.isString)
{ {
ASSERT_TRUE(bencode_write_bytestring(&buffer, x.second.theString.data(), REQUIRE(
x.second.theString.size())); bencode_write_bytestring(&buffer, x.second.theString.data(), x.second.theString.size()));
} }
else else
{ {
ASSERT_TRUE(bencode_write_uint64(&buffer, x.second.theInt)); REQUIRE(bencode_write_uint64(&buffer, x.second.theInt));
} }
} }
ASSERT_TRUE(bencode_end(&buffer)); REQUIRE(bencode_end(&buffer));
ASSERT_EQ(std::string(buffer.base, buffer.cur), d.result); REQUIRE(std::string(buffer.base, buffer.cur) == d.result);
} }
DictTestData dictTestData[] = {
{{}, 100, "de"},
{{{'a', {"", 0, true}}}, 100, "d1:a0:e"},
{{{'b', {"", 0, false}}}, 100, "d1:bi0ee"},
{{{'c', {"", 0, false}}, {'d', {"", 0, true}}}, 100, "d1:ci0e1:d0:e"},
{{{'e', {"", 123, false}}, {'f', {"abc", 0, true}}},
100,
"d1:ei123e1:f3:abce"},
{{{'a', {"", 123, false}},
{'b', {"abc", 0, true}},
{'c', {"abc", 0, true}}},
100,
"d1:ai123e1:b3:abc1:c3:abce"},
};
INSTANTIATE_TEST_SUITE_P(TestBencode, DictTest,
::testing::ValuesIn(dictTestData));
struct ReadData struct ReadData
{ {
std::string input; std::string input;
std::vector< std::string > output; std::vector<std::string> output;
}; };
struct DictReadTest : public ::testing::TestWithParam< ReadData > std::vector<ReadData> dictReadData{
{ {"de", {}}, {"d1:a0:e", {"a", ""}}, {"d1:be", {"b"}}, {"d1:b2:23e", {"b", "23"}}};
};
TEST_P(DictReadTest, readtest) TEST_CASE("Read dict", "[bencode]")
{ {
auto d = GetParam(); auto d = GENERATE(from_range(dictReadData));
byte_t* input = byte_t* input = const_cast<byte_t*>(reinterpret_cast<const byte_t*>(d.input.data()));
const_cast< byte_t* >(reinterpret_cast< const byte_t* >(d.input.data()));
llarp_buffer_t buffer(input, input, d.input.size()); llarp_buffer_t buffer(input, input, d.input.size());
std::vector< std::string > result; std::vector<std::string> result;
ASSERT_TRUE(llarp::bencode_read_dict( REQUIRE(llarp::bencode_read_dict(
[&](llarp_buffer_t*, llarp_buffer_t* key) { [&](llarp_buffer_t*, llarp_buffer_t* key) {
if(key) if (key)
{ {
result.emplace_back(key->base, key->base + key->sz); result.emplace_back(key->base, key->base + key->sz);
} }
return true; return true;
}, },
&buffer)); &buffer));
ASSERT_EQ(result, d.output); REQUIRE(result == d.output);
} }
ReadData dictReadData[] = {{"de", {}}, std::vector<ReadData> listReadData{
{"d1:a0:e", {"a", ""}}, {"le", {}}, {"l1:ae", {"a"}}, {"l1:be", {"b"}}, {"l1:b2:23e", {"b", "23"}}};
{"d1:be", {"b"}},
{"d1:b2:23e", {"b", "23"}}};
INSTANTIATE_TEST_SUITE_P(TestBencode, DictReadTest,
::testing::ValuesIn(dictReadData));
struct ListReadTest : public ::testing::TestWithParam< ReadData >
{
};
TEST_P(ListReadTest, readtest) TEST_CASE("Read list", "[bencode]")
{ {
auto d = GetParam(); auto d = GENERATE(from_range(listReadData));
byte_t* input = byte_t* input = const_cast<byte_t*>(reinterpret_cast<const byte_t*>(d.input.data()));
const_cast< byte_t* >(reinterpret_cast< const byte_t* >(d.input.data()));
llarp_buffer_t buffer(input, input, d.input.size()); llarp_buffer_t buffer(input, input, d.input.size());
std::vector< std::string > result; std::vector<std::string> result;
ASSERT_TRUE(llarp::bencode_read_list( REQUIRE(llarp::bencode_read_list(
[&](llarp_buffer_t* b, bool cont) { [&](llarp_buffer_t* b, bool cont) {
if(cont) if (cont)
{ {
llarp_buffer_t tmp; llarp_buffer_t tmp;
bencode_read_string(b, &tmp); bencode_read_string(b, &tmp);
@ -433,18 +366,12 @@ TEST_P(ListReadTest, readtest)
return true; return true;
}, },
&buffer)); &buffer));
ASSERT_EQ(result, d.output); REQUIRE(result == d.output);
} }
ReadData listReadData[] = { TEST_CASE("Read dict to empty buffer", "[bencode]")
{"le", {}}, {"l1:ae", {"a"}}, {"l1:be", {"b"}}, {"l1:b2:23e", {"b", "23"}}};
INSTANTIATE_TEST_SUITE_P(TestBencode, ListReadTest,
::testing::ValuesIn(listReadData));
TEST(TestBencode, ReadDictEmptyBuffer)
{ {
llarp_buffer_t buf((byte_t*)nullptr, 0); llarp_buffer_t buf((byte_t*)nullptr, 0);
ASSERT_FALSE(llarp::bencode_read_dict( REQUIRE_FALSE(
[](llarp_buffer_t*, llarp_buffer_t*) { return true; }, &buf)); llarp::bencode_read_dict([](llarp_buffer_t*, llarp_buffer_t*) { return true; }, &buf));
} }

@ -6,8 +6,8 @@ TEST_CASE("DecayingHashSet test decay static time", "[decaying-hashset]")
{ {
static constexpr auto timeout = 5s; static constexpr auto timeout = 5s;
static constexpr auto now = 1s; static constexpr auto now = 1s;
llarp::util::DecayingHashSet<llarp::RouterID> hashset(timeout); llarp::util::DecayingHashSet<llarp::RouterID> hashset{timeout};
const llarp::RouterID zero; const llarp::RouterID zero{};
REQUIRE(zero.IsZero()); REQUIRE(zero.IsZero());
REQUIRE(not hashset.Contains(zero)); REQUIRE(not hashset.Contains(zero));
REQUIRE(hashset.Insert(zero, now)); REQUIRE(hashset.Insert(zero, now));
@ -20,20 +20,20 @@ TEST_CASE("DecayingHashSet test decay static time", "[decaying-hashset]")
REQUIRE(not hashset.Contains(zero)); REQUIRE(not hashset.Contains(zero));
} }
TEST_CASE("DecayingHashSet tset decay dynamic time", "[decaying-hashset]") TEST_CASE("DecayingHashSet test decay dynamic time", "[decaying-hashset]")
{ {
static constexpr llarp_time_t timeout = 5s; static constexpr llarp_time_t timeout = 5s;
const llarp_time_t now = llarp::time_now_ms(); constexpr auto now = llarp::time_now_ms;
llarp::util::DecayingHashSet<llarp::RouterID> hashset(timeout); llarp::util::DecayingHashSet<llarp::RouterID> hashset{timeout};
const llarp::RouterID zero; const llarp::RouterID zero{};
REQUIRE(zero.IsZero()); REQUIRE(zero.IsZero());
REQUIRE(not hashset.Contains(zero)); REQUIRE(not hashset.Contains(zero));
REQUIRE(hashset.Insert(zero)); REQUIRE(hashset.Insert(zero));
REQUIRE(hashset.Contains(zero)); REQUIRE(hashset.Contains(zero));
hashset.Decay(now + 1s); hashset.Decay(now() + 1s);
REQUIRE(hashset.Contains(zero)); REQUIRE(hashset.Contains(zero));
hashset.Decay(now + timeout); hashset.Decay(now() + timeout);
REQUIRE(not hashset.Contains(zero)); REQUIRE(not hashset.Contains(zero));
hashset.Decay(now + timeout + 1s); hashset.Decay(now() + timeout + 1s);
REQUIRE(not hashset.Contains(zero)); REQUIRE(not hashset.Contains(zero));
} }

@ -1,4 +1,4 @@
#include <gtest/gtest.h> #include <catch2/catch.hpp>
#include <util/logging/loglevel.hpp> #include <util/logging/loglevel.hpp>
#include <util/logging/logger.hpp> #include <util/logging/logger.hpp>
#include <config/config.hpp> #include <config/config.hpp>
@ -8,58 +8,49 @@ using TestString = std::string;
struct TestParseLog struct TestParseLog
{ {
TestString input; TestString input;
std::optional< llarp::LogLevel > level; std::optional<llarp::LogLevel> level;
}; };
struct LogLevelTest : public ::testing::TestWithParam< TestParseLog > std::vector<TestParseLog> testParseLog{// bad cases
{"bogus", {}},
{"BOGUS", {}},
{"", {}},
{" ", {}},
// good cases
{"info", llarp::eLogInfo},
{"infO", llarp::eLogInfo},
{"iNfO", llarp::eLogInfo},
{"InfO", llarp::eLogInfo},
{"INFO", llarp::eLogInfo},
{"debug", llarp::eLogDebug},
{"warn", llarp::eLogWarn},
{"error", llarp::eLogError},
{"none", llarp::eLogNone}};
TEST_CASE("parseLevel")
{ {
}; const auto data = GENERATE(from_range(testParseLog));
TEST_P(LogLevelTest, parseLevel)
{
const auto data = GetParam();
const auto maybe = llarp::LogLevelFromString(data.input); const auto maybe = llarp::LogLevelFromString(data.input);
EXPECT_EQ(maybe, data.level); CHECK(maybe == data.level);
} }
static const TestParseLog testParseLog[] = { TEST_CASE("TestLogLevelToName")
// bad cases
{"bogus", {}},
{"BOGUS", {}},
{"", {}},
{" ", {}},
// good cases
{"info", llarp::eLogInfo},
{"infO", llarp::eLogInfo},
{"iNfO", llarp::eLogInfo},
{"InfO", llarp::eLogInfo},
{"INFO", llarp::eLogInfo},
{"debug", llarp::eLogDebug},
{"warn", llarp::eLogWarn},
{"error", llarp::eLogError},
{"none", llarp::eLogNone}};
INSTANTIATE_TEST_SUITE_P(TestLogConfig, LogLevelTest,
::testing::ValuesIn(testParseLog));
TEST_F(LogLevelTest, TestLogLevelToName)
{ {
EXPECT_EQ("Trace", LogLevelToName(llarp::eLogTrace)); CHECK("Trace" == LogLevelToName(llarp::eLogTrace));
EXPECT_EQ("Debug", LogLevelToName(llarp::eLogDebug)); CHECK("Debug" == LogLevelToName(llarp::eLogDebug));
EXPECT_EQ("Info", LogLevelToName(llarp::eLogInfo)); CHECK("Info" == LogLevelToName(llarp::eLogInfo));
EXPECT_EQ("Warn", LogLevelToName(llarp::eLogWarn)); CHECK("Warn" == LogLevelToName(llarp::eLogWarn));
EXPECT_EQ("Error", LogLevelToName(llarp::eLogError)); CHECK("Error" == LogLevelToName(llarp::eLogError));
EXPECT_EQ("None", LogLevelToName(llarp::eLogNone)); CHECK("None" == LogLevelToName(llarp::eLogNone));
EXPECT_EQ("???", LogLevelToName( (llarp::LogLevel)99999 )); CHECK("???" == LogLevelToName((llarp::LogLevel)99999));
} }
TEST_F(LogLevelTest, TestLogLevelToString) TEST_CASE("TestLogLevelToString")
{ {
EXPECT_EQ("TRC", LogLevelToString(llarp::eLogTrace)); CHECK("TRC" == LogLevelToString(llarp::eLogTrace));
EXPECT_EQ("DBG", LogLevelToString(llarp::eLogDebug)); CHECK("DBG" == LogLevelToString(llarp::eLogDebug));
EXPECT_EQ("NFO", LogLevelToString(llarp::eLogInfo)); CHECK("NFO" == LogLevelToString(llarp::eLogInfo));
EXPECT_EQ("WRN", LogLevelToString(llarp::eLogWarn)); CHECK("WRN" == LogLevelToString(llarp::eLogWarn));
EXPECT_EQ("ERR", LogLevelToString(llarp::eLogError)); CHECK("ERR" == LogLevelToString(llarp::eLogError));
EXPECT_EQ("???", LogLevelToString(llarp::eLogNone)); CHECK("???" == LogLevelToString(llarp::eLogNone));
} }

@ -7,7 +7,7 @@
#include <functional> #include <functional>
#include <thread> #include <thread>
#include <gtest/gtest.h> #include <catch2/catch.hpp>
using namespace llarp; using namespace llarp;
using namespace llarp::thread; using namespace llarp::thread;
@ -24,8 +24,7 @@ class Element
public: public:
Element(double d, bool _stop = false) : data(d), shouldStop(_stop) Element(double d, bool _stop = false) : data(d), shouldStop(_stop)
{ {}
}
double double
val() const val() const
@ -46,7 +45,7 @@ operator==(const Element& lhs, const Element& rhs)
return lhs.val() == rhs.val(); return lhs.val() == rhs.val();
} }
using ObjQueue = Queue< Element >; using ObjQueue = Queue<Element>;
class Args class Args
{ {
@ -68,14 +67,8 @@ class Args
volatile size_t endSignal; volatile size_t endSignal;
Args(size_t _iterations, size_t size = 20 * 1000) Args(size_t _iterations, size_t size = 20 * 1000)
: queue(size) : queue(size), iterations(_iterations), count(0), startSignal(0), runSignal(0), endSignal(0)
, iterations(_iterations) {}
, count(0)
, startSignal(0)
, runSignal(0)
, endSignal(0)
{
}
bool bool
signal() const signal() const
@ -93,10 +86,10 @@ popFrontTester(Args& args)
args.cv.wait(lock, [&] { return args.signal(); }); args.cv.wait(lock, [&] { return args.signal(); });
} }
for(;;) for (;;)
{ {
Element e = args.queue.popFront(); Element e = args.queue.popFront();
if(e.stop()) if (e.stop())
{ {
break; break;
} }
@ -112,9 +105,9 @@ pushBackTester(Args& args)
args.cv.wait(lock, [&] { return args.signal(); }); args.cv.wait(lock, [&] { return args.signal(); });
} }
for(size_t i = 0; i < args.iterations; ++i) for (size_t i = 0; i < args.iterations; ++i)
{ {
Element e{static_cast< double >(i)}; Element e{static_cast<double>(i)};
args.queue.pushBack(e); args.queue.pushBack(e);
} }
@ -122,38 +115,34 @@ pushBackTester(Args& args)
} }
void void
abaThread(char* firstValue, char* lastValue, Queue< char* >& queue, abaThread(char* firstValue, char* lastValue, Queue<char*>& queue, util::Barrier& barrier)
util::Barrier& barrier)
{ {
barrier.Block(); barrier.Block();
for(char* val = firstValue; val <= lastValue; ++val) for (char* val = firstValue; val <= lastValue; ++val)
{ {
queue.pushBack(val); queue.pushBack(val);
} }
} }
struct Exception : public std::exception struct Exception : public std::exception
{ {};
};
struct ExceptionTester struct ExceptionTester
{ {
static std::atomic< std::thread::id > throwFrom; static std::atomic<std::thread::id> throwFrom;
void void
test() test()
{ {
if(throwFrom != std::thread::id() if (throwFrom != std::thread::id() && std::this_thread::get_id() == throwFrom)
&& std::this_thread::get_id() == throwFrom)
{ {
throw Exception(); throw Exception();
} }
} }
ExceptionTester() ExceptionTester()
{ {}
}
ExceptionTester(const ExceptionTester&) ExceptionTester(const ExceptionTester&)
{ {
@ -168,7 +157,7 @@ struct ExceptionTester
} }
}; };
std::atomic< std::thread::id > ExceptionTester::throwFrom = {std::thread::id()}; std::atomic<std::thread::id> ExceptionTester::throwFrom = {std::thread::id()};
void void
sleepNWait(std::chrono::microseconds microseconds, util::Barrier& barrier) sleepNWait(std::chrono::microseconds microseconds, util::Barrier& barrier)
@ -178,18 +167,18 @@ sleepNWait(std::chrono::microseconds microseconds, util::Barrier& barrier)
} }
void void
exceptionProducer(Queue< ExceptionTester >& queue, util::Semaphore& semaphore, exceptionProducer(
std::atomic_size_t& caught) Queue<ExceptionTester>& queue, util::Semaphore& semaphore, std::atomic_size_t& caught)
{ {
static constexpr size_t iterations = 3; static constexpr size_t iterations = 3;
for(size_t i = 0; i < iterations; ++i) for (size_t i = 0; i < iterations; ++i)
{ {
try try
{ {
queue.pushBack(ExceptionTester()); queue.pushBack(ExceptionTester());
} }
catch(const Exception&) catch (const Exception&)
{ {
++caught; ++caught;
} }
@ -204,18 +193,15 @@ struct MoveTester
size_t& moveCounter; size_t& moveCounter;
size_t value; size_t value;
explicit MoveTester(size_t& counter, size_t val) explicit MoveTester(size_t& counter, size_t val) : moved(false), moveCounter(counter), value(val)
: moved(false), moveCounter(counter), value(val) {}
{
}
explicit MoveTester(const MoveTester& rhs) = delete; explicit MoveTester(const MoveTester& rhs) = delete;
MoveTester& MoveTester&
operator=(const MoveTester& rhs) = delete; operator=(const MoveTester& rhs) = delete;
MoveTester(MoveTester&& rhs) MoveTester(MoveTester&& rhs) : moved(false), moveCounter(rhs.moveCounter), value(rhs.value)
: moved(false), moveCounter(rhs.moveCounter), value(rhs.value)
{ {
rhs.moved = true; rhs.moved = true;
moveCounter++; moveCounter++;
@ -224,8 +210,8 @@ struct MoveTester
MoveTester& MoveTester&
operator=(MoveTester&& rhs) operator=(MoveTester&& rhs)
{ {
value = rhs.value; value = rhs.value;
rhs.moved = true; rhs.moved = true;
moveCounter = rhs.moveCounter; moveCounter = rhs.moveCounter;
moveCounter++; moveCounter++;
@ -234,22 +220,22 @@ struct MoveTester
} }
}; };
TEST(TestQueue, single) TEST_CASE("single")
{ {
ObjQueue queue(1u); ObjQueue queue(1u);
ASSERT_EQ(0u, queue.size()); REQUIRE(0u == queue.size());
ASSERT_EQ(1u, queue.capacity()); REQUIRE(1u == queue.capacity());
} }
TEST(TestQueue, breathing) TEST_CASE("breathing")
{ {
static constexpr size_t DEFAULT_CAP = 10 * 1000; static constexpr size_t DEFAULT_CAP = 10 * 1000;
ObjQueue queue(DEFAULT_CAP); ObjQueue queue(DEFAULT_CAP);
ASSERT_EQ(0u, queue.size()); REQUIRE(0u == queue.size());
ASSERT_EQ(DEFAULT_CAP, queue.capacity()); REQUIRE(DEFAULT_CAP == queue.capacity());
Element e1(1.0); Element e1(1.0);
Element e2(2.0); Element e2(2.0);
@ -263,260 +249,261 @@ TEST(TestQueue, breathing)
Element p2 = queue.popFront(); Element p2 = queue.popFront();
Element p3 = queue.popFront(); Element p3 = queue.popFront();
ASSERT_EQ(e1, p1); REQUIRE(e1 == p1);
ASSERT_EQ(e2, p2); REQUIRE(e2 == p2);
ASSERT_EQ(e3, p3); REQUIRE(e3 == p3);
} }
TEST(TestQueue, singleProducerManyConsumer) TEST_CASE("Single producer many consumer")
{ {
static constexpr size_t iterations = 100 * 1000; static constexpr size_t iterations = 100 * 1000;
static constexpr size_t numThreads = 5; static constexpr size_t numThreads = 5;
std::array< std::thread, numThreads > threads; std::array<std::thread, numThreads> threads;
Args args{iterations}; Args args{iterations};
{ {
LockGuard lock(args.mutex); LockGuard lock(args.mutex);
for(size_t i = 0; i < threads.size(); ++i) for (size_t i = 0; i < threads.size(); ++i)
{ {
threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args))); threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args)));
args.cv.wait(lock, [&] { return args.count != i+1; }); args.cv.wait(lock, [&] { return args.count != i + 1; });
} }
args.runSignal++; args.runSignal++;
} }
for(size_t i = 0; i < iterations; ++i) for (size_t i = 0; i < iterations; ++i)
{ {
Element e{static_cast< double >(i)}; Element e{static_cast<double>(i)};
args.queue.pushBack(e); args.queue.pushBack(e);
} }
for(size_t i = 0; i < numThreads; ++i) for (size_t i = 0; i < numThreads; ++i)
{ {
Element e{0.0, true}; Element e{0.0, true};
args.queue.pushBack(e); args.queue.pushBack(e);
} }
for(size_t i = 0; i < numThreads; ++i) for (size_t i = 0; i < numThreads; ++i)
{ {
threads[i].join(); threads[i].join();
} }
ASSERT_EQ(0u, args.queue.size()); REQUIRE(0u == args.queue.size());
} }
TEST(TestQueue, manyProducerManyConsumer) TEST_CASE("Many producer many consumer")
{ {
static constexpr size_t iterations = 100 * 1000; static constexpr size_t iterations = 100 * 1000;
static constexpr size_t numThreads = 5; static constexpr size_t numThreads = 5;
std::array< std::thread, numThreads * 2 > threads; std::array<std::thread, numThreads * 2> threads;
Args args{iterations}; Args args{iterations};
{ {
LockGuard lock(args.mutex); LockGuard lock(args.mutex);
for(size_t i = 0; i < numThreads; ++i) for (size_t i = 0; i < numThreads; ++i)
{ {
threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args))); threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args)));
args.cv.wait(lock, [&] { return args.count != i+1; }); args.cv.wait(lock, [&] { return args.count != i + 1; });
} }
for(size_t i = 0; i < numThreads; ++i) for (size_t i = 0; i < numThreads; ++i)
{ {
threads[i + numThreads] = threads[i + numThreads] = std::thread(std::bind(&pushBackTester, std::ref(args)));
std::thread(std::bind(&pushBackTester, std::ref(args))); args.cv.wait(lock, [&] { return args.count != numThreads + i + 1; });
args.cv.wait(lock, [&] { return args.count != numThreads+i+1; });
} }
args.runSignal++; args.runSignal++;
} }
for(auto& thread : threads) for (auto& thread : threads)
{ {
thread.join(); thread.join();
} }
ASSERT_EQ(0u, args.queue.size()); REQUIRE(0u == args.queue.size());
} }
TEST(TestQueue, ABAEmpty) TEST_CASE("ABA empty")
{ {
// Verify we avoid the ABA problem, where multiple threads try to push an // Verify we avoid the ABA problem, where multiple threads try to push an
// object to the same "empty" position in the queue. // object to the same "empty" position in the queue.
static constexpr size_t numThreads = 50; static constexpr size_t numThreads = 50;
static constexpr size_t numValues = 6; static constexpr size_t numValues = 6;
static constexpr size_t numIterations = 1000; static constexpr size_t numIterations = 1000;
static constexpr size_t numEntries = numThreads * numValues; static constexpr size_t numEntries = numThreads * numValues;
char block[numEntries]; char block[numEntries];
for(size_t i = 0; i < numIterations; ++i) for (size_t i = 0; i < numIterations; ++i)
{ {
util::Barrier barrier{numThreads + 1}; util::Barrier barrier{numThreads + 1};
Queue< char* > queue{numEntries + 1}; Queue<char*> queue{numEntries + 1};
std::array< std::thread, numThreads + 1 > threads; std::array<std::thread, numThreads + 1> threads;
char* nextValue[numThreads]; char* nextValue[numThreads];
char* lastValue[numThreads]; char* lastValue[numThreads];
for(size_t j = 0; j < numThreads; ++j) for (size_t j = 0; j < numThreads; ++j)
{ {
nextValue[j] = block + (numValues * j); nextValue[j] = block + (numValues * j);
lastValue[j] = block + (numValues * (j + 1)) - 1; lastValue[j] = block + (numValues * (j + 1)) - 1;
threads[j] = std::thread([&, n=nextValue[j], l=lastValue[j]] { abaThread(n, l, queue, barrier); }); threads[j] =
std::thread([&, n = nextValue[j], l = lastValue[j]] { abaThread(n, l, queue, barrier); });
} }
threads[numThreads] = std::thread([&] { threads[numThreads] = std::thread([&] {
std::this_thread::sleep_for(100us); std::this_thread::sleep_for(100us);
barrier.Block(); barrier.Block();
}); });
for(size_t j = 0; j < numEntries; ++j) for (size_t j = 0; j < numEntries; ++j)
{ {
char* val = queue.popFront(); char* val = queue.popFront();
size_t k = 0; size_t k = 0;
for(k = 0; k < numThreads; ++k) for (k = 0; k < numThreads; ++k)
{ {
if(val == nextValue[k]) if (val == nextValue[k])
{ {
nextValue[k] += (val == lastValue[k] ? 0 : 1); nextValue[k] += (val == lastValue[k] ? 0 : 1);
ASSERT_LE(nextValue[k], lastValue[k]); REQUIRE(nextValue[k] <= lastValue[k]);
break; break;
} }
} }
ASSERT_LT(k, numThreads); REQUIRE(k < numThreads);
} }
for(auto& thread : threads) for (auto& thread : threads)
{ {
thread.join(); thread.join();
} }
ASSERT_EQ(0u, queue.size()); REQUIRE(0u == queue.size());
} }
} }
TEST(TestQueue, generationCount) TEST_CASE("Generation count")
{ {
// Verify functionality after running through a full cycle (and invoking the // Verify functionality after running through a full cycle (and invoking the
// generation rollover logic). // generation rollover logic).
// For a queue of size 3, this is currently 508 cycles, implying we need to go // For a queue of size 3, this is currently 508 cycles, implying we need to go
// through at least 3048 objects (3 * 508 * 2) to trigger this logic twice. // through at least 3048 objects (3 * 508 * 2) to trigger this logic twice.
static constexpr size_t numThreads = 6; static constexpr size_t numThreads = 6;
static constexpr size_t queueSize = 3; static constexpr size_t queueSize = 3;
static constexpr size_t numEntries = 3060; static constexpr size_t numEntries = 3060;
static constexpr size_t numValues = numEntries / numThreads; static constexpr size_t numValues = numEntries / numThreads;
char block[numEntries]; char block[numEntries];
util::Barrier barrier{numThreads + 1}; util::Barrier barrier{numThreads + 1};
Queue< char* > queue{queueSize}; Queue<char*> queue{queueSize};
std::array< std::thread, numThreads + 1 > threads; std::array<std::thread, numThreads + 1> threads;
char* nextValue[numThreads]; char* nextValue[numThreads];
char* lastValue[numThreads]; char* lastValue[numThreads];
for(size_t j = 0; j < numThreads; ++j) for (size_t j = 0; j < numThreads; ++j)
{ {
nextValue[j] = block + (numValues * j); nextValue[j] = block + (numValues * j);
lastValue[j] = block + (numValues * (j + 1)) - 1; lastValue[j] = block + (numValues * (j + 1)) - 1;
threads[j] = std::thread([&, n=nextValue[j], l=lastValue[j]] { abaThread(n, l, queue, barrier); }); threads[j] =
std::thread([&, n = nextValue[j], l = lastValue[j]] { abaThread(n, l, queue, barrier); });
} }
threads[numThreads] = std::thread([&] { threads[numThreads] = std::thread([&] {
std::this_thread::sleep_for(100ms); std::this_thread::sleep_for(100ms);
barrier.Block(); barrier.Block();
}); });
for(size_t j = 0; j < numEntries; ++j) for (size_t j = 0; j < numEntries; ++j)
{ {
char* val = queue.popFront(); char* val = queue.popFront();
size_t k = 0; size_t k = 0;
for(k = 0; k < numThreads; ++k) for (k = 0; k < numThreads; ++k)
{ {
if(val == nextValue[k]) if (val == nextValue[k])
{ {
nextValue[k] += (val == lastValue[k] ? 0 : 1); nextValue[k] += (val == lastValue[k] ? 0 : 1);
ASSERT_LE(nextValue[k], lastValue[k]); REQUIRE(nextValue[k] <= lastValue[k]);
break; break;
} }
} }
ASSERT_LT(k, numThreads); REQUIRE(k < numThreads);
} }
for(auto& thread : threads) for (auto& thread : threads)
{ {
thread.join(); thread.join();
} }
ASSERT_EQ(0u, queue.size()); REQUIRE(0u == queue.size());
} }
TEST(TestQueue, basicExceptionSafety) TEST_CASE("Basic exception safety")
{ {
ExceptionTester::throwFrom = std::this_thread::get_id(); ExceptionTester::throwFrom = std::this_thread::get_id();
Queue< ExceptionTester > queue{1}; Queue<ExceptionTester> queue{1};
ASSERT_THROW(queue.pushBack(ExceptionTester()), Exception); REQUIRE_THROWS_AS(queue.pushBack(ExceptionTester()), Exception);
ExceptionTester::throwFrom = std::thread::id(); ExceptionTester::throwFrom = std::thread::id();
} }
TEST(TestQueue, exceptionSafety) TEST_CASE("Exception safety")
{ {
ExceptionTester::throwFrom = std::thread::id(); ExceptionTester::throwFrom = std::thread::id();
static constexpr size_t queueSize = 3; static constexpr size_t queueSize = 3;
Queue< ExceptionTester > queue{queueSize}; Queue<ExceptionTester> queue{queueSize};
ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); REQUIRE(QueueReturn::Success == queue.pushBack(ExceptionTester()));
ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); REQUIRE(QueueReturn::Success == queue.pushBack(ExceptionTester()));
ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); REQUIRE(QueueReturn::Success == queue.pushBack(ExceptionTester()));
ASSERT_NE(QueueReturn::Success, queue.tryPushBack(ExceptionTester())); REQUIRE(QueueReturn::Success != queue.tryPushBack(ExceptionTester()));
util::Semaphore semaphore{0}; util::Semaphore semaphore{0};
std::atomic_size_t caught = {0}; std::atomic_size_t caught = {0};
std::thread producer(std::bind(&exceptionProducer, std::ref(queue), std::thread producer(
std::ref(semaphore), std::ref(caught))); std::bind(&exceptionProducer, std::ref(queue), std::ref(semaphore), std::ref(caught)));
ExceptionTester::throwFrom = std::this_thread::get_id(); ExceptionTester::throwFrom = std::this_thread::get_id();
ASSERT_THROW({ (void)queue.popFront(); }, Exception); REQUIRE_THROWS_AS(queue.popFront(), Exception);
using namespace std::literals; using namespace std::literals;
// Now the queue is not full, and the producer thread can start adding items. // Now the queue is not full, and the producer thread can start adding items.
ASSERT_TRUE(semaphore.waitFor(1s)); REQUIRE(semaphore.waitFor(1s));
ASSERT_EQ(queueSize, queue.size()); REQUIRE(queueSize == queue.size());
ASSERT_THROW({ (void)queue.popFront(); }, Exception); REQUIRE_THROWS_AS(queue.popFront(), Exception);
// Now the queue is not full, and the producer thread can start adding items. // Now the queue is not full, and the producer thread can start adding items.
ASSERT_TRUE(semaphore.waitFor(1s)); REQUIRE(semaphore.waitFor(1s));
ASSERT_EQ(queueSize, queue.size()); REQUIRE(queueSize == queue.size());
// Pushing into the queue with exception empties the queue. // Pushing into the queue with exception empties the queue.
ExceptionTester::throwFrom = producer.get_id(); ExceptionTester::throwFrom = producer.get_id();
@ -524,61 +511,61 @@ TEST(TestQueue, exceptionSafety)
// pop an item to unblock the pusher // pop an item to unblock the pusher
(void)queue.popFront(); (void)queue.popFront();
ASSERT_TRUE(semaphore.waitFor(1s)); REQUIRE(semaphore.waitFor(1s));
ASSERT_EQ(1u, caught); REQUIRE(1u == caught);
ASSERT_EQ(0u, queue.size()); REQUIRE(0u == queue.size());
ASSERT_TRUE(queue.empty()); REQUIRE(queue.empty());
// after throwing, the queue works fine. // after throwing, the queue works fine.
ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); REQUIRE(QueueReturn::Success == queue.pushBack(ExceptionTester()));
ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); REQUIRE(QueueReturn::Success == queue.pushBack(ExceptionTester()));
ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); REQUIRE(QueueReturn::Success == queue.pushBack(ExceptionTester()));
ASSERT_NE(QueueReturn::Success, queue.tryPushBack(ExceptionTester())); REQUIRE(QueueReturn::Success != queue.tryPushBack(ExceptionTester()));
ExceptionTester::throwFrom = std::thread::id(); ExceptionTester::throwFrom = std::thread::id();
producer.join(); producer.join();
} }
TEST(TestQueue, moveIt) TEST_CASE("Move it")
{ {
static constexpr size_t queueSize = 40; static constexpr size_t queueSize = 40;
Queue< MoveTester > queue{queueSize}; Queue<MoveTester> queue{queueSize};
size_t counter = 0; size_t counter = 0;
queue.pushBack(MoveTester{counter, 0}); queue.pushBack(MoveTester{counter, 0});
ASSERT_EQ(1u, counter); REQUIRE(1u == counter);
MoveTester tester2(counter, 2); MoveTester tester2(counter, 2);
queue.pushBack(std::move(tester2)); queue.pushBack(std::move(tester2));
ASSERT_TRUE(tester2.moved); REQUIRE(tester2.moved);
ASSERT_EQ(2u, counter); REQUIRE(2u == counter);
ASSERT_EQ(QueueReturn::Success, queue.tryPushBack(MoveTester{counter, 3})); REQUIRE(QueueReturn::Success == queue.tryPushBack(MoveTester{counter, 3}));
ASSERT_EQ(3u, counter); REQUIRE(3u == counter);
MoveTester tester4(counter, 4); MoveTester tester4(counter, 4);
ASSERT_EQ(QueueReturn::Success, queue.tryPushBack(std::move(tester4))); REQUIRE(QueueReturn::Success == queue.tryPushBack(std::move(tester4)));
ASSERT_TRUE(tester4.moved); REQUIRE(tester4.moved);
ASSERT_EQ(4u, counter); REQUIRE(4u == counter);
MoveTester popped = queue.popFront(); MoveTester popped = queue.popFront();
(void)popped; (void)popped;
ASSERT_EQ(5u, counter); REQUIRE(5u == counter);
std::optional< MoveTester > optPopped = queue.tryPopFront(); std::optional<MoveTester> optPopped = queue.tryPopFront();
ASSERT_TRUE(optPopped); REQUIRE(optPopped);
// Moved twice here to construct the optional. // Moved twice here to construct the optional.
ASSERT_EQ(6u, counter); REQUIRE(6u == counter);
} }

File diff suppressed because it is too large Load Diff

@ -90,7 +90,6 @@ Source: "{#DevPath}ui-win32\bin\release\lokinetui.exe.config"; DestDir: "{app}";
Source: "{#DevPath}ui-win32\bin\release\lokinetui.pdb"; DestDir: "{app}"; Flags: ignoreversion Source: "{#DevPath}ui-win32\bin\release\lokinetui.pdb"; DestDir: "{app}"; Flags: ignoreversion
#endif #endif
; eh, might as well ship the 32-bit port of everything else ; eh, might as well ship the 32-bit port of everything else
;Source: "{#DevPath}build\testAll.exe"; DestDir: "{app}"; Flags: ignoreversion
;Source: "{#DevPath}build\catchAll.exe"; DestDir: "{app}"; Flags: ignoreversion ;Source: "{#DevPath}build\catchAll.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#DevPath}build\lokinetctl.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "{#DevPath}build\lokinetctl.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion

Loading…
Cancel
Save