Files
tomato/toxcore/friend_connection_test.cc
Green Sky 565efa4f39 Squashed 'external/toxcore/c-toxcore/' changes from 1828c5356..c9cdae001
c9cdae001 fix(toxav): remove extra copy of video frame on encode
4f6d4546b test: Improve the fake network library.
a2581e700 refactor(toxcore): generate `Friend_Request` and `Dht_Nodes_Response`
2aaa11770 refactor(toxcore): use Tox_Memory in generated events
5c367452b test(toxcore): fix incorrect mutex in tox_scenario_get_time
8f92e710f perf: Add a timed limit of number of cookie requests.
695b6417a test: Add some more simulated network support.
815ae9ce9 test(toxcore): fix thread-safety in scenario framework
6d85c754e test(toxcore): add unit tests for net_crypto
9c22e79cc test(support): add SimulatedEnvironment for deterministic testing
f34fcb195 chore: Update windows Dockerfile to debian stable (trixie).
ece0e8980 fix(group_moderation): allow validating unsorted sanction list signatures
a4fa754d7 refactor: rename struct Packet to struct Net_Packet
d6f330f85 cleanup: Fix some warnings from coverity.
e206bffa2 fix(group_chats): fix sync packets reverting topics
0e4715598 test: Add new scenario testing framework.
668291f44 refactor(toxcore): decouple Network_Funcs from sockaddr via IP_Port
fc4396cef fix: potential division by zero in toxav and unsafe hex parsing
8e8b352ab refactor: Add nullable annotations to struct members.
7740bb421 refactor: decouple net_crypto from DHT
1936d4296 test: add benchmark for toxav audio and video
46bfdc2df fix: correct printf format specifiers for unsigned integers
REVERT: 1828c5356 fix(toxav): remove extra copy of video frame on encode

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: c9cdae001341e701fca980c9bb9febfeb95d2902
2026-01-11 14:42:31 +01:00

181 lines
6.5 KiB
C++

#include "friend_connection.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <cstring>
#include <functional>
#include <map>
#include <memory>
#include <vector>
#include "../testing/support/public/simulated_environment.hh"
#include "DHT_test_util.hh"
#include "crypto_core.h"
#include "logger.h"
#include "mono_time.h"
#include "net_crypto.h"
#include "net_profile.h"
#include "network.h"
#include "onion_client.h"
namespace {
using namespace tox::test;
// --- Helper Class ---
template <typename DHTWrapper>
class FriendConnTestNode {
public:
FriendConnTestNode(SimulatedEnvironment &env, uint16_t port)
: dht_wrapper_(env, port)
, net_profile_(netprof_new(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory),
[mem = &dht_wrapper_.node().c_memory](Net_Profile *p) { netprof_kill(mem, p); })
, net_crypto_(nullptr, [](Net_Crypto *c) { kill_net_crypto(c); })
, onion_client_(nullptr, [](Onion_Client *c) { kill_onion_client(c); })
, friend_connections_(nullptr, [](Friend_Connections *c) { kill_friend_connections(c); })
{
logger_callback_log(
dht_wrapper_.logger(),
[](void *context, Logger_Level level, const char *file, uint32_t line, const char *func,
const char *message, void *userdata) {
fprintf(stderr, "[%d] %s:%u: %s: %s\n", level, file, line, func, message);
},
nullptr, nullptr);
// Setup NetCrypto
TCP_Proxy_Info proxy_info = {{0}, TCP_PROXY_NONE};
net_crypto_.reset(new_net_crypto(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory,
&dht_wrapper_.node().c_random, &dht_wrapper_.node().c_network, dht_wrapper_.mono_time(),
dht_wrapper_.networking(), dht_wrapper_.get_dht(), &DHTWrapper::funcs, &proxy_info,
net_profile_.get()));
new_keys(net_crypto_.get());
// Setup Onion Client
onion_client_.reset(new_onion_client(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory,
&dht_wrapper_.node().c_random, dht_wrapper_.mono_time(), net_crypto_.get(),
dht_wrapper_.get_dht(), dht_wrapper_.networking()));
// Setup Friend Connections
friend_connections_.reset(
new_friend_connections(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory,
dht_wrapper_.mono_time(), &dht_wrapper_.node().c_network, onion_client_.get(),
dht_wrapper_.get_dht(), net_crypto_.get(), dht_wrapper_.networking(), true));
}
Friend_Connections *get_friend_connections() { return friend_connections_.get(); }
Onion_Client *get_onion_client() { return onion_client_.get(); }
Net_Crypto *get_net_crypto() { return net_crypto_.get(); }
DHT *get_dht() { return dht_wrapper_.get_dht(); }
const uint8_t *dht_public_key() const { return dht_wrapper_.dht_public_key(); }
const uint8_t *real_public_key() const { return nc_get_self_public_key(net_crypto_.get()); }
const Random *get_random() { return &dht_wrapper_.node().c_random; }
IP_Port get_ip_port() const { return dht_wrapper_.get_ip_port(); }
void poll()
{
dht_wrapper_.poll();
do_net_crypto(net_crypto_.get(), nullptr);
do_onion_client(onion_client_.get());
do_friend_connections(friend_connections_.get(), nullptr);
}
~FriendConnTestNode();
private:
DHTWrapper dht_wrapper_;
std::unique_ptr<Net_Profile, std::function<void(Net_Profile *)>> net_profile_;
std::unique_ptr<Net_Crypto, void (*)(Net_Crypto *)> net_crypto_;
std::unique_ptr<Onion_Client, void (*)(Onion_Client *)> onion_client_;
std::unique_ptr<Friend_Connections, void (*)(Friend_Connections *)> friend_connections_;
};
template <typename DHTWrapper>
FriendConnTestNode<DHTWrapper>::~FriendConnTestNode() = default;
using FriendConnNode = FriendConnTestNode<WrappedDHT>;
class FriendConnectionTest : public ::testing::Test {
protected:
SimulatedEnvironment env;
};
TEST_F(FriendConnectionTest, CreationAndDestruction)
{
FriendConnNode alice(env, 33445);
EXPECT_NE(alice.get_friend_connections(), nullptr);
}
TEST_F(FriendConnectionTest, AddKillConnection)
{
FriendConnNode alice(env, 33445);
uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(alice.get_random(), friend_pk, friend_sk);
// Add Connection
int conn_id = new_friend_connection(alice.get_friend_connections(), friend_pk);
EXPECT_NE(conn_id, -1);
// Verify status (should be connecting or none initially, but ID is valid)
EXPECT_NE(
friend_con_connected(alice.get_friend_connections(), conn_id), FRIENDCONN_STATUS_NONE);
// Kill Connection
EXPECT_EQ(kill_friend_connection(alice.get_friend_connections(), conn_id), 0);
}
TEST_F(FriendConnectionTest, ConnectTwoNodes)
{
FriendConnNode alice(env, 33445);
FriendConnNode bob(env, 33446);
// Alice adds Bob as friend
int alice_conn_id
= new_friend_connection(alice.get_friend_connections(), bob.real_public_key());
ASSERT_NE(alice_conn_id, -1);
// Bob adds Alice as friend
int bob_conn_id = new_friend_connection(bob.get_friend_connections(), alice.real_public_key());
ASSERT_NE(bob_conn_id, -1);
// Helper to inject peer info into DHT and trigger connection
auto inject_peer = [](FriendConnNode &self, const FriendConnNode &peer, int conn_id) {
// 1. Tell DHT where the peer is (IP/Port + DHT Public Key)
IP_Port peer_ip = peer.get_ip_port();
addto_lists(self.get_dht(), &peer_ip, peer.dht_public_key());
// 2. Tell friend_connection the peer's DHT Public Key (normally found via Onion)
set_dht_temp_pk(self.get_friend_connections(), conn_id, peer.dht_public_key(), nullptr);
};
inject_peer(alice, bob, alice_conn_id);
inject_peer(bob, alice, bob_conn_id);
auto start = env.clock().current_time_ms();
bool connected = false;
while ((env.clock().current_time_ms() - start) < 5000) { // 5 seconds timeout
alice.poll();
bob.poll();
env.advance_time(10);
if (friend_con_connected(alice.get_friend_connections(), alice_conn_id)
== FRIENDCONN_STATUS_CONNECTED
&& friend_con_connected(bob.get_friend_connections(), bob_conn_id)
== FRIENDCONN_STATUS_CONNECTED) {
connected = true;
break;
}
}
EXPECT_TRUE(connected) << "Alice and Bob failed to connect";
}
} // namespace