From 57b0651ffdfc31d1cd5c6d3c51cc43bb5b4c7a13 Mon Sep 17 00:00:00 2001 From: iphydf Date: Tue, 30 Nov 2021 18:15:27 +0000 Subject: [PATCH] test: Add some unit tests for important internal DHT functions. We definitely need more of this kind of test so refactorings don't accidentally break things in ways that happen to still work in auto tests. --- .cirrus.yml | 4 +- CMakeLists.txt | 1 + toxcore/BUILD.bazel | 10 ++++ toxcore/DHT.h | 8 ++++ toxcore/DHT_test.cc | 112 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 toxcore/DHT_test.cc diff --git a/.cirrus.yml b/.cirrus.yml index c2a274ea..17dbb186 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ --- cirrus-ci_task: container: - image: toxchat/toktok-stack:0.0.13 + image: toxchat/toktok-stack:0.0.18 cpu: 2 memory: 2G configure_script: @@ -22,7 +22,7 @@ cirrus-ci_task: cimple_task: container: - image: toxchat/toktok-stack:0.0.13 + image: toxchat/toktok-stack:0.0.18 cpu: 2 memory: 4G configure_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 98a4eabe..979c4963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -363,6 +363,7 @@ include(CompileGTest) # unit_test(toxav ring_buffer) unit_test(toxav rtp) +unit_test(toxcore DHT) unit_test(toxcore crypto_core) unit_test(toxcore mono_time) unit_test(toxcore ping_array) diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 0fdfe0ae..0bdd9c72 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -153,6 +153,16 @@ cc_library( ], ) +cc_test( + name = "DHT_test", + size = "small", + srcs = ["DHT_test.cc"], + deps = [ + ":DHT", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "DHT_srcs", hdrs = [ diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 40ce73f1..4f7b012b 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -17,6 +17,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Maximum number of clients stored per friend. */ #define MAX_FRIEND_CLIENTS 8 @@ -405,4 +409,8 @@ bool dht_non_lan_connected(const DHT *dht); uint32_t addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/toxcore/DHT_test.cc b/toxcore/DHT_test.cc new file mode 100644 index 00000000..9ecd3cb9 --- /dev/null +++ b/toxcore/DHT_test.cc @@ -0,0 +1,112 @@ +#include "DHT.h" + +#include + +#include +#include + +#include "crypto_core.h" + +namespace { + +using PublicKey = std::array; + +template +std::array to_array(T const (&arr)[N]) { + std::array stdarr; + memcpy(stdarr.data(), arr, N); + return stdarr; +} + +TEST(IdClosest, IdenticalKeysAreSameDistance) { + PublicKey pk0; + random_bytes(pk0.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk1; + random_bytes(pk1.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk2 = pk1; + + EXPECT_EQ(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); +} + +TEST(IdClosest, DistanceIsCommutative) { + for (uint32_t i = 0; i < 100; ++i) { + PublicKey pk0; + random_bytes(pk0.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk1; + random_bytes(pk1.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk2; + random_bytes(pk2.data(), CRYPTO_PUBLIC_KEY_SIZE); + + ASSERT_NE(pk1, pk2); // RNG can't produce the same random key twice + + // Two non-equal keys can't have the same distance from any given key. + EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); + + if (id_closest(pk0.data(), pk1.data(), pk2.data()) == 1) { + EXPECT_EQ(id_closest(pk0.data(), pk2.data(), pk1.data()), 2); + } + + if (id_closest(pk0.data(), pk1.data(), pk2.data()) == 2) { + EXPECT_EQ(id_closest(pk0.data(), pk2.data(), pk1.data()), 1); + } + } +} + +TEST(IdClosest, SmallXorDistanceIsCloser) { + PublicKey const pk0 = {{0xaa}}; + PublicKey const pk1 = {{0xa0}}; + PublicKey const pk2 = {{0x0a}}; + + EXPECT_EQ(id_closest(pk0.data(), pk1.data(), pk2.data()), 1); +} + +TEST(IdClosest, DistinctKeysCannotHaveTheSameDistance) { + PublicKey const pk0 = {{0x06}}; + PublicKey const pk1 = {{0x00}}; + PublicKey pk2 = {{0x00}}; + + for (uint8_t i = 1; i < 0xff; ++i) { + pk2[0] = i; + EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); + } +} + +TEST(AddToList, OverridesKeysWithCloserKeys) { + PublicKey const self_pk = {{0xaa}}; + PublicKey const keys[] = { + {{0xa0}}, // closest + {{0x0a}}, // + {{0x0b}}, // + {{0x0c}}, // + {{0x0d}}, // + {{0xa1}}, // closer than the 4 keys above + }; + + std::array nodes{}; + + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[0].data(), IP_Port(), self_pk.data())); + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[1].data(), IP_Port(), self_pk.data())); + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[2].data(), IP_Port(), self_pk.data())); + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[3].data(), IP_Port(), self_pk.data())); + + EXPECT_EQ(to_array(nodes[0].public_key), keys[0]); + EXPECT_EQ(to_array(nodes[1].public_key), keys[1]); + EXPECT_EQ(to_array(nodes[2].public_key), keys[2]); + EXPECT_EQ(to_array(nodes[3].public_key), keys[3]); + + // key 4 is less close than keys 0-3 + EXPECT_FALSE(add_to_list(nodes.data(), nodes.size(), keys[4].data(), IP_Port(), self_pk.data())); + // 5 is closer than all except key 0 + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[5].data(), IP_Port(), self_pk.data())); + + EXPECT_EQ(to_array(nodes[0].public_key), keys[0]); + EXPECT_EQ(to_array(nodes[1].public_key), keys[5]); + EXPECT_EQ(to_array(nodes[2].public_key), keys[1]); + EXPECT_EQ(to_array(nodes[3].public_key), keys[2]); +} + +} // namespace