Merge commit '227425b90e9a671118026689dd30967e127a1090' as 'external/toxcore/c-toxcore'

This commit is contained in:
2023-07-25 11:53:09 +02:00
467 changed files with 116591 additions and 0 deletions

View File

@ -0,0 +1,72 @@
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
package(features = ["layering_check"])
cc_library(
name = "check_compat",
testonly = True,
hdrs = ["check_compat.h"],
)
cc_library(
name = "auto_test_support",
testonly = True,
srcs = ["auto_test_support.c"],
hdrs = ["auto_test_support.h"],
deps = [
":check_compat",
"//c-toxcore/testing:misc_tools",
"//c-toxcore/toxcore:Messenger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:tox",
],
)
flaky_tests = {
"crypto_core_test": True,
"lan_discovery_test": True,
"save_load_test": True,
"tox_many_tcp_test": True,
}
[cc_test(
name = src[:-2],
size = "small",
srcs = [src],
args = ["$(location %s)" % src] + ["$(location //c-toxcore/other/proxy)"],
data = glob(["data/*"]) + ["//c-toxcore/other/proxy"],
flaky = flaky_tests.get(
src[:-2],
False,
),
deps = [
":auto_test_support",
":check_compat",
"//c-toxcore/testing:misc_tools",
"//c-toxcore/toxav",
"//c-toxcore/toxcore:Messenger",
"//c-toxcore/toxcore:TCP_client",
"//c-toxcore/toxcore:TCP_common",
"//c-toxcore/toxcore:TCP_connection",
"//c-toxcore/toxcore:TCP_server",
"//c-toxcore/toxcore:announce",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:crypto_core",
"//c-toxcore/toxcore:forwarding",
"//c-toxcore/toxcore:friend_connection",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:net_crypto",
"//c-toxcore/toxcore:network",
"//c-toxcore/toxcore:onion",
"//c-toxcore/toxcore:onion_announce",
"//c-toxcore/toxcore:onion_client",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:tox_dispatch",
"//c-toxcore/toxcore:tox_events",
"//c-toxcore/toxcore:util",
"//c-toxcore/toxencryptsave",
"@libsodium",
"@libvpx",
],
) for src in glob(["*_test.c"])]

View File

@ -0,0 +1,86 @@
set(TEST_TIMEOUT_SECONDS "" CACHE STRING "Limit runtime of each test to the number of seconds specified")
add_library(auto_test_support
auto_test_support.c
auto_test_support.h)
target_link_modules(auto_test_support toxcore misc_tools)
function(auto_test target)
if(AUTOTEST AND NOT (MSVC AND ARGV1 STREQUAL "MSVC_DONT_BUILD"))
add_executable(auto_${target}_test ${target}_test.c)
target_link_modules(auto_${target}_test toxcore misc_tools auto_test_support)
if(NOT ARGV1 STREQUAL "DONT_RUN")
add_test(NAME ${target} COMMAND ${CROSSCOMPILING_EMULATOR} auto_${target}_test)
set_tests_properties(${target} PROPERTIES TIMEOUT "${TEST_TIMEOUT_SECONDS}")
# add the source dir as environment variable, so the testdata can be found
set_tests_properties(${target} PROPERTIES ENVIRONMENT "LLVM_PROFILE_FILE=${target}.profraw;srcdir=${CMAKE_CURRENT_SOURCE_DIR}")
endif()
endif()
endfunction()
auto_test(TCP)
auto_test(announce)
auto_test(conference)
auto_test(conference_double_invite)
auto_test(conference_invite_merge)
auto_test(conference_peer_nick)
auto_test(conference_simple)
auto_test(conference_two)
auto_test(crypto)
#auto_test(dht) # Doesn't work with UNITY_BUILD.
auto_test(dht_getnodes_api)
auto_test(encryptsave)
auto_test(file_transfer)
auto_test(file_saving)
auto_test(forwarding)
auto_test(friend_connection)
auto_test(friend_request)
auto_test(friend_request_spam)
auto_test(group_general)
auto_test(group_invite)
auto_test(group_message)
auto_test(group_moderation)
auto_test(group_save)
auto_test(group_state)
auto_test(group_sync)
auto_test(group_tcp)
auto_test(group_topic)
auto_test(invalid_tcp_proxy)
auto_test(invalid_udp_proxy)
auto_test(lan_discovery)
auto_test(lossless_packet)
auto_test(lossy_packet)
auto_test(network)
auto_test(onion)
auto_test(overflow_recvq)
auto_test(overflow_sendq)
auto_test(reconnect)
auto_test(save_friend)
auto_test(save_load)
auto_test(send_message)
auto_test(set_name)
auto_test(set_status_message)
auto_test(tox_dispatch)
auto_test(tox_events)
auto_test(tox_many)
auto_test(tox_many_tcp)
auto_test(tox_strncasecmp)
auto_test(typing)
auto_test(version)
auto_test(save_compatibility)
if(NON_HERMETIC_TESTS)
auto_test(bootstrap)
auto_test(tcp_relay)
endif()
if(BUILD_TOXAV)
auto_test(conference_av MSVC_DONT_BUILD)
auto_test(toxav_basic)
auto_test(toxav_many)
endif()
if(PROXY_TEST)
auto_test(proxy)
endif()

View File

@ -0,0 +1,248 @@
if BUILD_TESTS
noinst_LTLIBRARIES += libauto_test_support.la
libauto_test_support_la_SOURCES = ../auto_tests/auto_test_support.c ../auto_tests/auto_test_support.h
libauto_test_support_la_LIBADD = libmisc_tools.la libtoxcore.la
TESTS = \
announce_test \
conference_double_invite_test \
conference_invite_merge_test \
conference_peer_nick_test \
conference_simple_test \
conference_test \
conference_two_test \
crypto_test \
file_transfer_test \
forwarding_test \
friend_connection_test \
friend_request_test \
group_state_test \
invalid_tcp_proxy_test \
invalid_udp_proxy_test \
lan_discovery_test \
lossless_packet_test \
lossy_packet_test \
network_test \
onion_test \
overflow_recvq_test \
overflow_sendq_test \
reconnect_test \
save_compatibility_test \
save_friend_test \
send_message_test \
set_name_test \
set_status_message_test \
TCP_test \
tox_events_test \
tox_dispatch_test \
tox_many_tcp_test \
tox_many_test \
tox_strncasecmp_test \
typing_test \
version_test
if !WITH_NACL
TESTS += \
encryptsave_test \
file_saving_test
endif
AUTOTEST_CFLAGS = \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS)
AUTOTEST_LDADD = \
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
libmisc_tools.la \
libauto_test_support.la \
libtoxcore.la \
libtoxencryptsave.la \
$(LIBSODIUM_LIBS) \
$(NACL_OBJECTS) \
$(NACL_LIBS)
if BUILD_AV
TESTS += conference_av_test toxav_basic_test toxav_many_test
AUTOTEST_LDADD += libtoxav.la
endif
check_PROGRAMS = $(TESTS)
announce_test_SOURCES = ../auto_tests/announce_test.c
announce_test_CFLAGS = $(AUTOTEST_CFLAGS)
announce_test_LDADD = $(AUTOTEST_LDADD)
conference_double_invite_test_SOURCES = ../auto_tests/conference_double_invite_test.c
conference_double_invite_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_double_invite_test_LDADD = $(AUTOTEST_LDADD)
conference_invite_merge_test_SOURCES = ../auto_tests/conference_invite_merge_test.c
conference_invite_merge_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_invite_merge_test_LDADD = $(AUTOTEST_LDADD)
conference_peer_nick_test_SOURCES = ../auto_tests/conference_peer_nick_test.c
conference_peer_nick_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_peer_nick_test_LDADD = $(AUTOTEST_LDADD)
conference_simple_test_SOURCES = ../auto_tests/conference_simple_test.c
conference_simple_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_simple_test_LDADD = $(AUTOTEST_LDADD)
conference_test_SOURCES = ../auto_tests/conference_test.c
conference_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_test_LDADD = $(AUTOTEST_LDADD)
conference_two_test_SOURCES = ../auto_tests/conference_two_test.c
conference_two_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_two_test_LDADD = $(AUTOTEST_LDADD)
crypto_test_SOURCES = ../auto_tests/crypto_test.c
crypto_test_CFLAGS = $(AUTOTEST_CFLAGS)
crypto_test_LDADD = $(AUTOTEST_LDADD)
encryptsave_test_SOURCES = ../auto_tests/encryptsave_test.c
encryptsave_test_CFLAGS = $(AUTOTEST_CFLAGS)
encryptsave_test_LDADD = $(AUTOTEST_LDADD)
file_saving_test_SOURCES = ../auto_tests/file_saving_test.c
file_saving_test_CFLAGS = $(AUTOTEST_CFLAGS)
file_saving_test_LDADD = $(AUTOTEST_LDADD)
file_transfer_test_SOURCES = ../auto_tests/file_transfer_test.c
file_transfer_test_CFLAGS = $(AUTOTEST_CFLAGS)
file_transfer_test_LDADD = $(AUTOTEST_LDADD)
forwarding_test_SOURCES = ../auto_tests/forwarding_test.c
forwarding_test_CFLAGS = $(AUTOTEST_CFLAGS)
forwarding_test_LDADD = $(AUTOTEST_LDADD)
friend_connection_test_SOURCES = ../auto_tests/friend_connection_test.c
friend_connection_test_CFLAGS = $(AUTOTEST_CFLAGS)
friend_connection_test_LDADD = $(AUTOTEST_LDADD)
friend_request_test_SOURCES = ../auto_tests/friend_request_test.c
friend_request_test_CFLAGS = $(AUTOTEST_CFLAGS)
friend_request_test_LDADD = $(AUTOTEST_LDADD)
group_state_test_SOURCES = ../auto_tests/group_state_test.c
group_state_test_CFLAGS = $(AUTOTEST_CFLAGS)
group_state_test_LDADD = $(AUTOTEST_LDADD)
invalid_tcp_proxy_test_SOURCES = ../auto_tests/invalid_tcp_proxy_test.c
invalid_tcp_proxy_test_CFLAGS = $(AUTOTEST_CFLAGS)
invalid_tcp_proxy_test_LDADD = $(AUTOTEST_LDADD)
invalid_udp_proxy_test_SOURCES = ../auto_tests/invalid_udp_proxy_test.c
invalid_udp_proxy_test_CFLAGS = $(AUTOTEST_CFLAGS)
invalid_udp_proxy_test_LDADD = $(AUTOTEST_LDADD)
lan_discovery_test_SOURCES = ../auto_tests/lan_discovery_test.c
lan_discovery_test_CFLAGS = $(AUTOTEST_CFLAGS)
lan_discovery_test_LDADD = $(AUTOTEST_LDADD)
lossless_packet_test_SOURCES = ../auto_tests/lossless_packet_test.c
lossless_packet_test_CFLAGS = $(AUTOTEST_CFLAGS)
lossless_packet_test_LDADD = $(AUTOTEST_LDADD)
lossy_packet_test_SOURCES = ../auto_tests/lossy_packet_test.c
lossy_packet_test_CFLAGS = $(AUTOTEST_CFLAGS)
lossy_packet_test_LDADD = $(AUTOTEST_LDADD)
network_test_SOURCES = ../auto_tests/network_test.c
network_test_CFLAGS = $(AUTOTEST_CFLAGS)
network_test_LDADD = $(AUTOTEST_LDADD)
onion_test_SOURCES = ../auto_tests/onion_test.c
onion_test_CFLAGS = $(AUTOTEST_CFLAGS)
onion_test_LDADD = $(AUTOTEST_LDADD)
overflow_recvq_test_SOURCES = ../auto_tests/overflow_recvq_test.c
overflow_recvq_test_CFLAGS = $(AUTOTEST_CFLAGS)
overflow_recvq_test_LDADD = $(AUTOTEST_LDADD)
overflow_sendq_test_SOURCES = ../auto_tests/overflow_sendq_test.c
overflow_sendq_test_CFLAGS = $(AUTOTEST_CFLAGS)
overflow_sendq_test_LDADD = $(AUTOTEST_LDADD)
reconnect_test_SOURCES = ../auto_tests/reconnect_test.c
reconnect_test_CFLAGS = $(AUTO_TEST_CFLAGS)
reconnect_test_LDADD = $(AUTOTEST_LDADD)
save_compatibility_test_SOURCES = ../auto_tests/save_compatibility_test.c
save_compatibility_test_CFLAGS = $(AUTOTEST_CFLAGS)
save_compatibility_test_LDADD = $(AUTOTEST_LDADD)
save_friend_test_SOURCES = ../auto_tests/save_friend_test.c
save_friend_test_CFLAGS = $(AUTOTEST_CFLAGS)
save_friend_test_LDADD = $(AUTOTEST_LDADD)
send_message_test_SOURCES = ../auto_tests/send_message_test.c
send_message_test_CFLAGS = $(AUTOTEST_CFLAGS)
send_message_test_LDADD = $(AUTOTEST_LDADD)
set_name_test_SOURCES = ../auto_tests/set_name_test.c
set_name_test_CFLAGS = $(AUTOTEST_CFLAGS)
set_name_test_LDADD = $(AUTOTEST_LDADD)
set_status_message_test_SOURCES = ../auto_tests/set_status_message_test.c
set_status_message_test_CFLAGS = $(AUTOTEST_CFLAGS)
set_status_message_test_LDADD = $(AUTOTEST_LDADD)
TCP_test_SOURCES = ../auto_tests/TCP_test.c
TCP_test_CFLAGS = $(AUTOTEST_CFLAGS)
TCP_test_LDADD = $(AUTOTEST_LDADD)
tox_events_test_SOURCES = ../auto_tests/tox_events_test.c
tox_events_test_CFLAGS = $(AUTOTEST_CFLAGS)
tox_events_test_LDADD = $(AUTOTEST_LDADD)
tox_dispatch_test_SOURCES = ../auto_tests/tox_dispatch_test.c
tox_dispatch_test_CFLAGS = $(AUTOTEST_CFLAGS)
tox_dispatch_test_LDADD = $(AUTOTEST_LDADD)
tox_many_tcp_test_SOURCES = ../auto_tests/tox_many_tcp_test.c
tox_many_tcp_test_CFLAGS = $(AUTOTEST_CFLAGS)
tox_many_tcp_test_LDADD = $(AUTOTEST_LDADD)
tox_many_test_SOURCES = ../auto_tests/tox_many_test.c
tox_many_test_CFLAGS = $(AUTOTEST_CFLAGS)
tox_many_test_LDADD = $(AUTOTEST_LDADD)
tox_strncasecmp_test_SOURCES = ../auto_tests/tox_strncasecmp_test.c
tox_strncasecmp_test_CFLAGS = $(AUTOTEST_CFLAGS)
tox_strncasecmp_test_LDADD = $(AUTOTEST_LDADD)
typing_test_SOURCES = ../auto_tests/typing_test.c
typing_test_CFLAGS = $(AUTOTEST_CFLAGS)
typing_test_LDADD = $(AUTOTEST_LDADD)
version_test_SOURCES = ../auto_tests/version_test.c
version_test_CFLAGS = $(AUTOTEST_CFLAGS)
version_test_LDADD = $(AUTOTEST_LDADD)
if BUILD_AV
conference_av_test_SOURCES = ../auto_tests/conference_av_test.c
conference_av_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_av_test_LDADD = $(AUTOTEST_LDADD)
toxav_basic_test_SOURCES = ../auto_tests/toxav_basic_test.c
toxav_basic_test_CFLAGS = $(AUTOTEST_CFLAGS)
toxav_basic_test_LDADD = $(AUTOTEST_LDADD) $(AV_LIBS)
toxav_many_test_SOURCES = ../auto_tests/toxav_many_test.c
toxav_many_test_CFLAGS = $(AUTOTEST_CFLAGS)
toxav_many_test_LDADD = $(AUTOTEST_LDADD)
endif
endif
EXTRA_DIST += \
$(top_srcdir)/auto_tests/data/save.tox \
$(top_srcdir)/auto_tests/check_compat.h \
$(top_srcdir)/auto_tests/auto_test_support.h

View File

@ -0,0 +1,902 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/TCP_client.h"
#include "../toxcore/TCP_common.h"
#include "../toxcore/TCP_server.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/mono_time.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#define NUM_PORTS 3
#ifndef USE_IPV6
#define USE_IPV6 1
#endif
#if !USE_IPV6
#define net_family_ipv6 net_family_ipv4
#endif
static IP get_loopback(void)
{
IP ip;
#if USE_IPV6
ip.family = net_family_ipv6();
ip.ip.v6 = get_ip6_loopback();
#else
ip.family = net_family_ipv4();
ip.ip.v4 = get_ip4_loopback();
#endif
return ip;
}
static void do_TCP_server_delay(TCP_Server *tcp_s, Mono_Time *mono_time, int delay)
{
c_sleep(delay);
mono_time_update(mono_time);
do_TCP_server(tcp_s, mono_time);
c_sleep(delay);
}
static uint16_t ports[NUM_PORTS] = {13215, 33445, 25643};
static void test_basic(void)
{
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Logger *logger = logger_new();
logger_callback_log(logger, print_debug_logger, nullptr, nullptr);
// Attempt to create a new TCP_Server instance.
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, self_public_key, self_secret_key);
const Network *ns = system_network();
TCP_Server *tcp_s = new_TCP_server(logger, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr);
ck_assert_msg(tcp_s != nullptr, "Failed to create a TCP relay server.");
ck_assert_msg(tcp_server_listen_count(tcp_s) == NUM_PORTS,
"Failed to bind a TCP relay server to all %d attempted ports.", NUM_PORTS);
Socket sock = {0};
IP_Port localhost;
localhost.ip = get_loopback();
localhost.port = 0;
// Check all opened ports for connectivity.
for (uint8_t i = 0; i < NUM_PORTS; i++) {
sock = net_socket(ns, net_family_ipv6(), TOX_SOCK_STREAM, TOX_PROTO_TCP);
localhost.port = net_htons(ports[i]);
bool ret = net_connect(logger, sock, &localhost);
ck_assert_msg(ret, "Failed to connect to created TCP relay server on port %d (%d).", ports[i], errno);
// Leave open one connection for the next test.
if (i + 1 < NUM_PORTS) {
kill_sock(ns, sock);
}
}
// Key creation.
uint8_t f_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t f_secret_key[CRYPTO_SECRET_KEY_SIZE];
uint8_t f_nonce[CRYPTO_NONCE_SIZE];
crypto_new_keypair(rng, f_public_key, f_secret_key);
random_nonce(rng, f_nonce);
// Generation of the initial handshake.
uint8_t t_secret_key[CRYPTO_SECRET_KEY_SIZE];
uint8_t *handshake_plain = (uint8_t *)malloc(TCP_HANDSHAKE_PLAIN_SIZE);
ck_assert(handshake_plain != nullptr);
crypto_new_keypair(rng, handshake_plain, t_secret_key);
memcpy(handshake_plain + CRYPTO_PUBLIC_KEY_SIZE, f_nonce, CRYPTO_NONCE_SIZE);
uint8_t *handshake = (uint8_t *)malloc(TCP_CLIENT_HANDSHAKE_SIZE);
ck_assert(handshake != nullptr);
memcpy(handshake, f_public_key, CRYPTO_PUBLIC_KEY_SIZE);
random_nonce(rng, handshake + CRYPTO_PUBLIC_KEY_SIZE);
// Encrypting handshake
int ret = encrypt_data(self_public_key, f_secret_key, handshake + CRYPTO_PUBLIC_KEY_SIZE, handshake_plain,
TCP_HANDSHAKE_PLAIN_SIZE, handshake + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
ck_assert_msg(ret == TCP_CLIENT_HANDSHAKE_SIZE - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE),
"encrypt_data() call failed.");
free(handshake_plain);
// Sending the handshake
ck_assert_msg(net_send(ns, logger, sock, handshake, TCP_CLIENT_HANDSHAKE_SIZE - 1,
&localhost) == TCP_CLIENT_HANDSHAKE_SIZE - 1,
"An attempt to send the initial handshake minus last byte failed.");
do_TCP_server_delay(tcp_s, mono_time, 50);
ck_assert_msg(net_send(ns, logger, sock, handshake + (TCP_CLIENT_HANDSHAKE_SIZE - 1), 1, &localhost) == 1,
"The attempt to send the last byte of handshake failed.");
free(handshake);
do_TCP_server_delay(tcp_s, mono_time, 50);
// Receiving server response and decrypting it
uint8_t response[TCP_SERVER_HANDSHAKE_SIZE];
uint8_t response_plain[TCP_HANDSHAKE_PLAIN_SIZE];
ck_assert_msg(net_recv(ns, logger, sock, response, TCP_SERVER_HANDSHAKE_SIZE, &localhost) == TCP_SERVER_HANDSHAKE_SIZE,
"Could/did not receive a server response to the initial handshake.");
ret = decrypt_data(self_public_key, f_secret_key, response, response + CRYPTO_NONCE_SIZE,
TCP_SERVER_HANDSHAKE_SIZE - CRYPTO_NONCE_SIZE, response_plain);
ck_assert_msg(ret == TCP_HANDSHAKE_PLAIN_SIZE, "Failed to decrypt handshake response.");
uint8_t f_nonce_r[CRYPTO_NONCE_SIZE];
uint8_t f_shared_key[CRYPTO_SHARED_KEY_SIZE];
encrypt_precompute(response_plain, t_secret_key, f_shared_key);
memcpy(f_nonce_r, response_plain + CRYPTO_SHARED_KEY_SIZE, CRYPTO_NONCE_SIZE);
// Building a request
uint8_t r_req_p[1 + CRYPTO_PUBLIC_KEY_SIZE];
r_req_p[0] = TCP_PACKET_ROUTING_REQUEST;
memcpy(r_req_p + 1, f_public_key, CRYPTO_PUBLIC_KEY_SIZE);
uint8_t r_req[2 + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE];
uint16_t size = 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE;
size = net_htons(size);
encrypt_data_symmetric(f_shared_key, f_nonce, r_req_p, 1 + CRYPTO_PUBLIC_KEY_SIZE, r_req + 2);
increment_nonce(f_nonce);
memcpy(r_req, &size, 2);
// Sending the request at random intervals in random pieces.
for (uint32_t i = 0; i < sizeof(r_req);) {
uint8_t msg_length = rand() % 5 + 1; // msg_length = 1 to 5
if (i + msg_length >= sizeof(r_req)) {
msg_length = sizeof(r_req) - i;
}
ck_assert_msg(net_send(ns, logger, sock, r_req + i, msg_length, &localhost) == msg_length,
"Failed to send request after completing the handshake.");
i += msg_length;
c_sleep(50);
mono_time_update(mono_time);
do_TCP_server(tcp_s, mono_time);
}
// Receiving the second response and verifying its validity
uint8_t packet_resp[4096];
int recv_data_len = net_recv(ns, logger, sock, packet_resp, 2 + 2 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE, &localhost);
ck_assert_msg(recv_data_len == 2 + 2 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE,
"Failed to receive server response to request. %d", recv_data_len);
memcpy(&size, packet_resp, 2);
ck_assert_msg(net_ntohs(size) == 2 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE,
"Wrong packet size for request response.");
uint8_t packet_resp_plain[4096];
ret = decrypt_data_symmetric(f_shared_key, f_nonce_r, packet_resp + 2, recv_data_len - 2, packet_resp_plain);
ck_assert_msg(ret != -1, "Failed to decrypt the TCP server's response.");
increment_nonce(f_nonce_r);
ck_assert_msg(packet_resp_plain[0] == TCP_PACKET_ROUTING_RESPONSE, "Server sent the wrong packet id: %u",
packet_resp_plain[0]);
ck_assert_msg(packet_resp_plain[1] == 0, "Server did not refuse the connection.");
ck_assert_msg(pk_equal(packet_resp_plain + 2, f_public_key), "Server sent the wrong public key.");
// Closing connections.
kill_sock(ns, sock);
kill_TCP_server(tcp_s);
logger_kill(logger);
mono_time_free(mono_time);
}
struct sec_TCP_con {
Socket sock;
const Network *ns;
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t recv_nonce[CRYPTO_NONCE_SIZE];
uint8_t sent_nonce[CRYPTO_NONCE_SIZE];
uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
};
static struct sec_TCP_con *new_TCP_con(const Logger *logger, const Random *rng, const Network *ns, TCP_Server *tcp_s, Mono_Time *mono_time)
{
struct sec_TCP_con *sec_c = (struct sec_TCP_con *)malloc(sizeof(struct sec_TCP_con));
ck_assert(sec_c != nullptr);
sec_c->ns = ns;
Socket sock = net_socket(ns, net_family_ipv6(), TOX_SOCK_STREAM, TOX_PROTO_TCP);
IP_Port localhost;
localhost.ip = get_loopback();
localhost.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
bool ok = net_connect(logger, sock, &localhost);
ck_assert_msg(ok, "Failed to connect to the test TCP relay server.");
uint8_t f_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, sec_c->public_key, f_secret_key);
random_nonce(rng, sec_c->sent_nonce);
uint8_t t_secret_key[CRYPTO_SECRET_KEY_SIZE];
uint8_t handshake_plain[TCP_HANDSHAKE_PLAIN_SIZE];
crypto_new_keypair(rng, handshake_plain, t_secret_key);
memcpy(handshake_plain + CRYPTO_PUBLIC_KEY_SIZE, sec_c->sent_nonce, CRYPTO_NONCE_SIZE);
uint8_t handshake[TCP_CLIENT_HANDSHAKE_SIZE];
memcpy(handshake, sec_c->public_key, CRYPTO_PUBLIC_KEY_SIZE);
random_nonce(rng, handshake + CRYPTO_PUBLIC_KEY_SIZE);
int ret = encrypt_data(tcp_server_public_key(tcp_s), f_secret_key, handshake + CRYPTO_PUBLIC_KEY_SIZE, handshake_plain,
TCP_HANDSHAKE_PLAIN_SIZE, handshake + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
ck_assert_msg(ret == TCP_CLIENT_HANDSHAKE_SIZE - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE),
"Failed to encrypt the outgoing handshake.");
ck_assert_msg(net_send(ns, logger, sock, handshake, TCP_CLIENT_HANDSHAKE_SIZE - 1,
&localhost) == TCP_CLIENT_HANDSHAKE_SIZE - 1,
"Failed to send the first portion of the handshake to the TCP relay server.");
do_TCP_server_delay(tcp_s, mono_time, 50);
ck_assert_msg(net_send(ns, logger, sock, handshake + (TCP_CLIENT_HANDSHAKE_SIZE - 1), 1, &localhost) == 1,
"Failed to send last byte of handshake.");
do_TCP_server_delay(tcp_s, mono_time, 50);
uint8_t response[TCP_SERVER_HANDSHAKE_SIZE];
uint8_t response_plain[TCP_HANDSHAKE_PLAIN_SIZE];
ck_assert_msg(net_recv(sec_c->ns, logger, sock, response, TCP_SERVER_HANDSHAKE_SIZE, &localhost) == TCP_SERVER_HANDSHAKE_SIZE,
"Failed to receive server handshake response.");
ret = decrypt_data(tcp_server_public_key(tcp_s), f_secret_key, response, response + CRYPTO_NONCE_SIZE,
TCP_SERVER_HANDSHAKE_SIZE - CRYPTO_NONCE_SIZE, response_plain);
ck_assert_msg(ret == TCP_HANDSHAKE_PLAIN_SIZE, "Failed to decrypt server handshake response.");
encrypt_precompute(response_plain, t_secret_key, sec_c->shared_key);
memcpy(sec_c->recv_nonce, response_plain + CRYPTO_SHARED_KEY_SIZE, CRYPTO_NONCE_SIZE);
sec_c->sock = sock;
return sec_c;
}
static void kill_TCP_con(struct sec_TCP_con *con)
{
kill_sock(con->ns, con->sock);
free(con);
}
static int write_packet_TCP_test_connection(const Logger *logger, struct sec_TCP_con *con, const uint8_t *data,
uint16_t length)
{
VLA(uint8_t, packet, sizeof(uint16_t) + length + CRYPTO_MAC_SIZE);
uint16_t c_length = net_htons(length + CRYPTO_MAC_SIZE);
memcpy(packet, &c_length, sizeof(uint16_t));
int len = encrypt_data_symmetric(con->shared_key, con->sent_nonce, data, length, packet + sizeof(uint16_t));
if ((unsigned int)len != (SIZEOF_VLA(packet) - sizeof(uint16_t))) {
return -1;
}
increment_nonce(con->sent_nonce);
IP_Port localhost;
localhost.ip = get_loopback();
localhost.port = 0;
ck_assert_msg(net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &localhost) == SIZEOF_VLA(packet),
"Failed to send a packet.");
return 0;
}
static int read_packet_sec_TCP(const Logger *logger, struct sec_TCP_con *con, uint8_t *data, uint16_t length)
{
IP_Port localhost;
localhost.ip = get_loopback();
localhost.port = 0;
int rlen = net_recv(con->ns, logger, con->sock, data, length, &localhost);
ck_assert_msg(rlen == length, "Did not receive packet of correct length. Wanted %i, instead got %i", length, rlen);
rlen = decrypt_data_symmetric(con->shared_key, con->recv_nonce, data + 2, length - 2, data);
ck_assert_msg(rlen != -1, "Failed to decrypt a received packet from the Relay server.");
increment_nonce(con->recv_nonce);
return rlen;
}
static void test_some(void)
{
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Logger *logger = logger_new();
const Network *ns = system_network();
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Server *tcp_s = new_TCP_server(logger, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr);
ck_assert_msg(tcp_s != nullptr, "Failed to create TCP relay server");
ck_assert_msg(tcp_server_listen_count(tcp_s) == NUM_PORTS, "Failed to bind to all ports.");
struct sec_TCP_con *con1 = new_TCP_con(logger, rng, ns, tcp_s, mono_time);
struct sec_TCP_con *con2 = new_TCP_con(logger, rng, ns, tcp_s, mono_time);
struct sec_TCP_con *con3 = new_TCP_con(logger, rng, ns, tcp_s, mono_time);
uint8_t requ_p[1 + CRYPTO_PUBLIC_KEY_SIZE];
requ_p[0] = TCP_PACKET_ROUTING_REQUEST;
// Sending wrong public keys to test server response.
memcpy(requ_p + 1, con3->public_key, CRYPTO_PUBLIC_KEY_SIZE);
write_packet_TCP_test_connection(logger, con1, requ_p, sizeof(requ_p));
memcpy(requ_p + 1, con1->public_key, CRYPTO_PUBLIC_KEY_SIZE);
write_packet_TCP_test_connection(logger, con3, requ_p, sizeof(requ_p));
do_TCP_server_delay(tcp_s, mono_time, 50);
// Testing response from connection 1
uint8_t data[2048];
int len = read_packet_sec_TCP(logger, con1, data, 2 + 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE);
ck_assert_msg(len == 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE, "Wrong response packet length of %d.", len);
ck_assert_msg(data[0] == TCP_PACKET_ROUTING_RESPONSE, "Wrong response packet id of %d.", data[0]);
ck_assert_msg(data[1] == 16, "Server didn't refuse connection using wrong public key.");
ck_assert_msg(pk_equal(data + 2, con3->public_key), "Key in response packet wrong.");
// Connection 3
len = read_packet_sec_TCP(logger, con3, data, 2 + 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE);
ck_assert_msg(len == 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE, "Wrong response packet length of %d.", len);
ck_assert_msg(data[0] == TCP_PACKET_ROUTING_RESPONSE, "Wrong response packet id of %d.", data[0]);
ck_assert_msg(data[1] == 16, "Server didn't refuse connection using wrong public key.");
ck_assert_msg(pk_equal(data + 2, con1->public_key), "Key in response packet wrong.");
uint8_t test_packet[512] = {16, 17, 16, 86, 99, 127, 255, 189, 78}; // What is this packet????
write_packet_TCP_test_connection(logger, con3, test_packet, sizeof(test_packet));
write_packet_TCP_test_connection(logger, con3, test_packet, sizeof(test_packet));
write_packet_TCP_test_connection(logger, con3, test_packet, sizeof(test_packet));
do_TCP_server_delay(tcp_s, mono_time, 50);
len = read_packet_sec_TCP(logger, con1, data, 2 + 2 + CRYPTO_MAC_SIZE);
ck_assert_msg(len == 2, "wrong len %d", len);
ck_assert_msg(data[0] == TCP_PACKET_CONNECTION_NOTIFICATION, "wrong packet id %u", data[0]);
ck_assert_msg(data[1] == 16, "wrong peer id %u", data[1]);
len = read_packet_sec_TCP(logger, con3, data, 2 + 2 + CRYPTO_MAC_SIZE);
ck_assert_msg(len == 2, "wrong len %d", len);
ck_assert_msg(data[0] == TCP_PACKET_CONNECTION_NOTIFICATION, "wrong packet id %u", data[0]);
ck_assert_msg(data[1] == 16, "wrong peer id %u", data[1]);
len = read_packet_sec_TCP(logger, con1, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len);
ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1],
data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]);
len = read_packet_sec_TCP(logger, con1, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len);
ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1],
data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]);
len = read_packet_sec_TCP(logger, con1, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len);
ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1],
data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]);
write_packet_TCP_test_connection(logger, con1, test_packet, sizeof(test_packet));
write_packet_TCP_test_connection(logger, con1, test_packet, sizeof(test_packet));
write_packet_TCP_test_connection(logger, con1, test_packet, sizeof(test_packet));
do_TCP_server_delay(tcp_s, mono_time, 50);
len = read_packet_sec_TCP(logger, con3, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len);
ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1],
data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]);
len = read_packet_sec_TCP(logger, con3, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len);
ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1],
data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]);
len = read_packet_sec_TCP(logger, con3, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len);
ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1],
data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]);
uint8_t ping_packet[1 + sizeof(uint64_t)] = {TCP_PACKET_PING, 8, 6, 9, 67};
write_packet_TCP_test_connection(logger, con1, ping_packet, sizeof(ping_packet));
do_TCP_server_delay(tcp_s, mono_time, 50);
len = read_packet_sec_TCP(logger, con1, data, 2 + sizeof(ping_packet) + CRYPTO_MAC_SIZE);
ck_assert_msg(len == sizeof(ping_packet), "wrong len %d", len);
ck_assert_msg(data[0] == TCP_PACKET_PONG, "wrong packet id %u", data[0]);
ck_assert_msg(memcmp(ping_packet + 1, data + 1, sizeof(uint64_t)) == 0, "wrong packet data");
// Kill off the connections
kill_TCP_server(tcp_s);
kill_TCP_con(con1);
kill_TCP_con(con2);
kill_TCP_con(con3);
logger_kill(logger);
mono_time_free(mono_time);
}
static int response_callback_good;
static uint8_t response_callback_connection_id;
static uint8_t response_callback_public_key[CRYPTO_PUBLIC_KEY_SIZE];
static int response_callback(void *object, uint8_t connection_id, const uint8_t *public_key)
{
if (set_tcp_connection_number((TCP_Client_Connection *)(void *)((char *)object - 2), connection_id, 7) != 0) {
return 1;
}
response_callback_connection_id = connection_id;
memcpy(response_callback_public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
response_callback_good++;
return 0;
}
static int status_callback_good;
static uint8_t status_callback_connection_id;
static uint8_t status_callback_status;
static int status_callback(void *object, uint32_t number, uint8_t connection_id, uint8_t status)
{
if (object != (void *)2) {
return 1;
}
if (number != 7) {
return 1;
}
status_callback_connection_id = connection_id;
status_callback_status = status;
status_callback_good++;
return 0;
}
static int data_callback_good;
static int data_callback(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data, uint16_t length,
void *userdata)
{
if (object != (void *)3) {
return 1;
}
if (number != 7) {
return 1;
}
if (length != 5) {
return 1;
}
if (data[0] == 1 && data[1] == 2 && data[2] == 3 && data[3] == 4 && data[4] == 5) {
data_callback_good++;
return 0;
}
return 1;
}
static int oob_data_callback_good;
static uint8_t oob_pubkey[CRYPTO_PUBLIC_KEY_SIZE];
static int oob_data_callback(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length,
void *userdata)
{
if (object != (void *)4) {
return 1;
}
if (length != 5) {
return 1;
}
if (!pk_equal(public_key, oob_pubkey)) {
return 1;
}
if (data[0] == 1 && data[1] == 2 && data[2] == 3 && data[3] == 4 && data[4] == 5) {
oob_data_callback_good++;
return 0;
}
return 1;
}
static void test_client(void)
{
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Logger *logger = logger_new();
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, self_public_key, self_secret_key);
const Network *ns = system_network();
TCP_Server *tcp_s = new_TCP_server(logger, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr);
ck_assert_msg(tcp_s != nullptr, "Failed to create a TCP relay server.");
ck_assert_msg(tcp_server_listen_count(tcp_s) == NUM_PORTS, "Failed to bind the relay server to all ports.");
uint8_t f_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t f_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, f_public_key, f_secret_key);
IP_Port ip_port_tcp_s;
ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
ip_port_tcp_s.ip = get_loopback();
TCP_Client_Connection *conn = new_TCP_connection(logger, mono_time, rng, ns, &ip_port_tcp_s, self_public_key, f_public_key,
f_secret_key, nullptr);
do_TCP_connection(logger, mono_time, conn, nullptr);
c_sleep(50);
// The connection status should be unconfirmed here because we have finished
// sending our data and are awaiting a response.
ck_assert_msg(tcp_con_status(conn) == TCP_CLIENT_UNCONFIRMED, "Wrong connection status. Expected: %d, is: %d.",
TCP_CLIENT_UNCONFIRMED, tcp_con_status(conn));
do_TCP_server_delay(tcp_s, mono_time, 50); // Now let the server handle requests...
const uint8_t LOOP_SIZE = 3;
for (uint8_t i = 0; i < LOOP_SIZE; i++) {
mono_time_update(mono_time);
do_TCP_connection(logger, mono_time, conn, nullptr); // Run the connection loop.
// The status of the connection should continue to be TCP_CLIENT_CONFIRMED after multiple subsequent do_TCP_connection() calls.
ck_assert_msg(tcp_con_status(conn) == TCP_CLIENT_CONFIRMED, "Wrong connection status. Expected: %d, is: %d",
TCP_CLIENT_CONFIRMED, tcp_con_status(conn));
c_sleep(i == LOOP_SIZE - 1 ? 0 : 500); // Sleep for 500ms on all except third loop.
}
do_TCP_server_delay(tcp_s, mono_time, 50);
// And still after the server runs again.
ck_assert_msg(tcp_con_status(conn) == TCP_CLIENT_CONFIRMED, "Wrong status. Expected: %d, is: %d", TCP_CLIENT_CONFIRMED,
tcp_con_status(conn));
uint8_t f2_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t f2_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, f2_public_key, f2_secret_key);
ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
TCP_Client_Connection *conn2 = new_TCP_connection(logger, mono_time, rng, ns, &ip_port_tcp_s, self_public_key, f2_public_key,
f2_secret_key, nullptr);
// The client should call this function (defined earlier) during the routing process.
routing_response_handler(conn, response_callback, (char *)conn + 2);
// The client should call this function when it receives a connection notification.
routing_status_handler(conn, status_callback, (void *)2);
// The client should call this function when
routing_data_handler(conn, data_callback, (void *)3);
// The client should call this function when sending out of band packets.
oob_data_handler(conn, oob_data_callback, (void *)4);
// These integers will increment per successful callback.
oob_data_callback_good = response_callback_good = status_callback_good = data_callback_good = 0;
do_TCP_connection(logger, mono_time, conn, nullptr);
do_TCP_connection(logger, mono_time, conn2, nullptr);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_TCP_connection(logger, mono_time, conn, nullptr);
do_TCP_connection(logger, mono_time, conn2, nullptr);
c_sleep(50);
uint8_t data[5] = {1, 2, 3, 4, 5};
memcpy(oob_pubkey, f2_public_key, CRYPTO_PUBLIC_KEY_SIZE);
send_oob_packet(logger, conn2, f_public_key, data, 5);
send_routing_request(logger, conn, f2_public_key);
send_routing_request(logger, conn2, f_public_key);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_TCP_connection(logger, mono_time, conn, nullptr);
do_TCP_connection(logger, mono_time, conn2, nullptr);
// All callback methods save data should have run during the above network prodding.
ck_assert_msg(oob_data_callback_good == 1, "OOB callback not called");
ck_assert_msg(response_callback_good == 1, "Response callback not called.");
ck_assert_msg(pk_equal(response_callback_public_key, f2_public_key), "Wrong public key.");
ck_assert_msg(status_callback_good == 1, "Status callback not called.");
ck_assert_msg(status_callback_status == 2, "Wrong status callback status.");
ck_assert_msg(status_callback_connection_id == response_callback_connection_id,
"Status and response callback connection IDs are not equal.");
do_TCP_server_delay(tcp_s, mono_time, 50);
ck_assert_msg(send_data(logger, conn2, 0, data, 5) == 1, "Failed a send_data() call.");
do_TCP_server_delay(tcp_s, mono_time, 50);
do_TCP_connection(logger, mono_time, conn, nullptr);
do_TCP_connection(logger, mono_time, conn2, nullptr);
ck_assert_msg(data_callback_good == 1, "Data callback was not called.");
status_callback_good = 0;
send_disconnect_request(logger, conn2, 0);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_TCP_connection(logger, mono_time, conn, nullptr);
do_TCP_connection(logger, mono_time, conn2, nullptr);
ck_assert_msg(status_callback_good == 1, "Status callback not called");
ck_assert_msg(status_callback_status == 1, "Wrong status callback status.");
// Kill off all connections and servers.
kill_TCP_server(tcp_s);
kill_TCP_connection(conn);
kill_TCP_connection(conn2);
logger_kill(logger);
mono_time_free(mono_time);
}
// Test how the client handles servers that don't respond.
static void test_client_invalid(void)
{
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Logger *logger = logger_new();
const Network *ns = system_network();
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, self_public_key, self_secret_key);
uint8_t f_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t f_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, f_public_key, f_secret_key);
IP_Port ip_port_tcp_s;
ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
ip_port_tcp_s.ip = get_loopback();
TCP_Client_Connection *conn = new_TCP_connection(logger, mono_time, rng, ns, &ip_port_tcp_s,
self_public_key, f_public_key, f_secret_key, nullptr);
// Run the client's main loop but not the server.
mono_time_update(mono_time);
do_TCP_connection(logger, mono_time, conn, nullptr);
c_sleep(50);
// After 50ms of no response...
ck_assert_msg(tcp_con_status(conn) == TCP_CLIENT_CONNECTING, "Wrong status. Expected: %d, is: %d.",
TCP_CLIENT_CONNECTING, tcp_con_status(conn));
// After 5s...
c_sleep(5000);
mono_time_update(mono_time);
do_TCP_connection(logger, mono_time, conn, nullptr);
ck_assert_msg(tcp_con_status(conn) == TCP_CLIENT_CONNECTING, "Wrong status. Expected: %d, is: %d.",
TCP_CLIENT_CONNECTING, tcp_con_status(conn));
// 11s... (Should wait for 10 before giving up.)
c_sleep(6000);
mono_time_update(mono_time);
do_TCP_connection(logger, mono_time, conn, nullptr);
ck_assert_msg(tcp_con_status(conn) == TCP_CLIENT_DISCONNECTED, "Wrong status. Expected: %d, is: %d.",
TCP_CLIENT_DISCONNECTED, tcp_con_status(conn));
kill_TCP_connection(conn);
logger_kill(logger);
mono_time_free(mono_time);
}
#include "../toxcore/TCP_connection.h"
static bool tcp_data_callback_called;
static int tcp_data_callback(void *object, int id, const uint8_t *data, uint16_t length, void *userdata)
{
if (object != (void *)120397) {
return -1;
}
if (id != 123) {
return -1;
}
if (length != 6) {
return -1;
}
if (memcmp(data, "Gentoo", length) != 0) {
return -1;
}
tcp_data_callback_called = 1;
return 0;
}
static void test_tcp_connection(void)
{
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
Logger *logger = logger_new();
const Random *rng = system_random();
ck_assert(rng != nullptr);
const Network *ns = system_network();
tcp_data_callback_called = 0;
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Server *tcp_s = new_TCP_server(logger, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr);
ck_assert_msg(pk_equal(tcp_server_public_key(tcp_s), self_public_key), "Wrong public key");
TCP_Proxy_Info proxy_info;
proxy_info.proxy_type = TCP_PROXY_NONE;
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Connections *tc_1 = new_tcp_connections(logger, rng, ns, mono_time, self_secret_key, &proxy_info);
ck_assert_msg(pk_equal(tcp_connections_public_key(tc_1), self_public_key), "Wrong public key");
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Connections *tc_2 = new_tcp_connections(logger, rng, ns, mono_time, self_secret_key, &proxy_info);
ck_assert_msg(pk_equal(tcp_connections_public_key(tc_2), self_public_key), "Wrong public key");
IP_Port ip_port_tcp_s;
ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
ip_port_tcp_s.ip = get_loopback();
int connection = new_tcp_connection_to(tc_1, tcp_connections_public_key(tc_2), 123);
ck_assert_msg(connection == 0, "Connection id wrong");
ck_assert_msg(add_tcp_relay_connection(tc_1, connection, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0,
"Could not add tcp relay to connection\n");
ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
connection = new_tcp_connection_to(tc_2, tcp_connections_public_key(tc_1), 123);
ck_assert_msg(connection == 0, "Connection id wrong");
ck_assert_msg(add_tcp_relay_connection(tc_2, connection, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0,
"Could not add tcp relay to connection\n");
ck_assert_msg(new_tcp_connection_to(tc_2, tcp_connections_public_key(tc_1), 123) == -1,
"Managed to read same connection\n");
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
int ret = send_packet_tcp_connection(tc_1, 0, (const uint8_t *)"Gentoo", 6);
ck_assert_msg(ret == 0, "could not send packet.");
set_packet_tcp_connection_callback(tc_2, &tcp_data_callback, (void *) 120397);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
ck_assert_msg(tcp_data_callback_called, "could not recv packet.");
ck_assert_msg(tcp_connection_to_online_tcp_relays(tc_1, 0) == 1, "Wrong number of connected relays");
ck_assert_msg(kill_tcp_connection_to(tc_1, 0) == 0, "could not kill connection to\n");
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
ck_assert_msg(send_packet_tcp_connection(tc_1, 0, (const uint8_t *)"Gentoo", 6) == -1, "could send packet.");
ck_assert_msg(kill_tcp_connection_to(tc_2, 0) == 0, "could not kill connection to\n");
kill_TCP_server(tcp_s);
kill_tcp_connections(tc_1);
kill_tcp_connections(tc_2);
logger_kill(logger);
mono_time_free(mono_time);
}
static bool tcp_oobdata_callback_called;
static int tcp_oobdata_callback(void *object, const uint8_t *public_key, unsigned int id, const uint8_t *data,
uint16_t length, void *userdata)
{
const TCP_Connections *tcp_c = (const TCP_Connections *)object;
if (length != 6) {
return -1;
}
if (memcmp(data, "Gentoo", length) != 0) {
return -1;
}
if (tcp_send_oob_packet(tcp_c, id, public_key, data, length) == 0) {
tcp_oobdata_callback_called = 1;
}
return 0;
}
static void test_tcp_connection2(void)
{
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
Logger *logger = logger_new();
const Random *rng = system_random();
ck_assert(rng != nullptr);
const Network *ns = system_network();
tcp_oobdata_callback_called = 0;
tcp_data_callback_called = 0;
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Server *tcp_s = new_TCP_server(logger, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr);
ck_assert_msg(pk_equal(tcp_server_public_key(tcp_s), self_public_key), "Wrong public key");
TCP_Proxy_Info proxy_info;
proxy_info.proxy_type = TCP_PROXY_NONE;
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Connections *tc_1 = new_tcp_connections(logger, rng, ns, mono_time, self_secret_key, &proxy_info);
ck_assert_msg(pk_equal(tcp_connections_public_key(tc_1), self_public_key), "Wrong public key");
crypto_new_keypair(rng, self_public_key, self_secret_key);
TCP_Connections *tc_2 = new_tcp_connections(logger, rng, ns, mono_time, self_secret_key, &proxy_info);
ck_assert_msg(pk_equal(tcp_connections_public_key(tc_2), self_public_key), "Wrong public key");
IP_Port ip_port_tcp_s;
ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]);
ip_port_tcp_s.ip = get_loopback();
int connection = new_tcp_connection_to(tc_1, tcp_connections_public_key(tc_2), 123);
ck_assert_msg(connection == 0, "Connection id wrong");
ck_assert_msg(add_tcp_relay_connection(tc_1, connection, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0,
"Could not add tcp relay to connection\n");
ck_assert_msg(add_tcp_relay_global(tc_2, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0,
"Could not add global relay");
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
int ret = send_packet_tcp_connection(tc_1, 0, (const uint8_t *)"Gentoo", 6);
ck_assert_msg(ret == 0, "could not send packet.");
set_oob_packet_tcp_connection_callback(tc_2, &tcp_oobdata_callback, tc_2);
set_packet_tcp_connection_callback(tc_1, &tcp_data_callback, (void *) 120397);
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
ck_assert_msg(tcp_oobdata_callback_called, "could not recv packet.");
do_TCP_server_delay(tcp_s, mono_time, 50);
do_tcp_connections(logger, tc_1, nullptr);
do_tcp_connections(logger, tc_2, nullptr);
ck_assert_msg(tcp_data_callback_called, "could not recv packet.");
ck_assert_msg(kill_tcp_connection_to(tc_1, 0) == 0, "could not kill connection to\n");
kill_TCP_server(tcp_s);
kill_tcp_connections(tc_1);
kill_tcp_connections(tc_2);
logger_kill(logger);
mono_time_free(mono_time);
}
static void TCP_suite(void)
{
test_basic();
test_some();
test_client();
test_client_invalid();
test_tcp_connection();
test_tcp_connection2();
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
TCP_suite();
return 0;
}

View File

@ -0,0 +1,123 @@
#include <stdint.h>
#include <string.h>
#include "../toxcore/announce.h"
#include "../toxcore/tox.h"
#include "../testing/misc_tools.h"
#include "../toxcore/mono_time.h"
#include "../toxcore/forwarding.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
static void test_bucketnum(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
uint8_t key1[CRYPTO_PUBLIC_KEY_SIZE], key2[CRYPTO_PUBLIC_KEY_SIZE];
random_bytes(rng, key1, sizeof(key1));
memcpy(key2, key1, CRYPTO_PUBLIC_KEY_SIZE);
ck_assert_msg(announce_get_bucketnum(key1, key2) == 0, "Bad bucketnum");
key2[4] ^= 0x09;
key2[5] ^= 0xc5;
ck_assert_msg(announce_get_bucketnum(key1, key2) == 7, "Bad bucketnum");
key2[4] ^= 0x09;
ck_assert_msg(announce_get_bucketnum(key1, key2) == 17, "Bad bucketnum");
key2[5] ^= 0xc5;
key2[31] ^= 0x09;
ck_assert_msg(announce_get_bucketnum(key1, key2) == 4, "Bad bucketnum");
}
typedef struct Announce_Test_Data {
uint8_t data[MAX_ANNOUNCEMENT_SIZE];
uint16_t length;
bool passed;
} Announce_Test_Data;
static void test_announce_data(void *object, const uint8_t *data, uint16_t length)
{
Announce_Test_Data *test_data = (Announce_Test_Data *) object;
test_data->passed = test_data->length == length && memcmp(test_data->data, data, length) == 0;
}
static void test_store_data(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
const Network *ns = system_network();
ck_assert(ns != nullptr);
Logger *log = logger_new();
ck_assert(log != nullptr);
logger_callback_log(log, print_debug_logger, nullptr, nullptr);
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
Networking_Core *net = new_networking_no_udp(log, ns);
DHT *dht = new_dht(log, rng, ns, mono_time, net, true, true);
Forwarding *forwarding = new_forwarding(log, rng, mono_time, dht);
Announcements *announce = new_announcements(log, rng, mono_time, forwarding);
ck_assert(announce != nullptr);
/* Just to prevent CI from complaining that set_synch_offset is unused: */
announce_set_synch_offset(announce, 0);
Announce_Test_Data test_data;
random_bytes(rng, test_data.data, sizeof(test_data.data));
test_data.length = sizeof(test_data.data);
uint8_t key[CRYPTO_PUBLIC_KEY_SIZE];
random_bytes(rng, key, sizeof(key));
ck_assert_msg(!announce_on_stored(announce, key, nullptr, nullptr), "Unstored announcement exists");
ck_assert_msg(announce_store_data(announce, key, test_data.data, sizeof(test_data.data),
MAX_MAX_ANNOUNCEMENT_TIMEOUT), "Failed to store announcement");
ck_assert_msg(announce_on_stored(announce, key, test_announce_data, &test_data), "Failed to get stored announcement");
ck_assert_msg(test_data.passed, "Bad stored announcement data");
const uint8_t *const base = dht_get_self_public_key(dht);
ck_assert_msg(announce_store_data(announce, base, test_data.data, sizeof(test_data.data), 1), "failed to store base");
uint8_t test_keys[ANNOUNCE_BUCKET_SIZE + 1][CRYPTO_PUBLIC_KEY_SIZE];
for (uint8_t i = 0; i < ANNOUNCE_BUCKET_SIZE + 1; ++i) {
memcpy(test_keys[i], base, CRYPTO_PUBLIC_KEY_SIZE);
test_keys[i][i] ^= 1;
ck_assert_msg(announce_store_data(announce, test_keys[i], test_data.data, sizeof(test_data.data), 1),
"Failed to store announcement %d", i);
}
ck_assert_msg(announce_on_stored(announce, base, nullptr, nullptr), "base was evicted");
ck_assert_msg(!announce_on_stored(announce, test_keys[0], nullptr, nullptr), "furthest was not evicted");
ck_assert_msg(!announce_store_data(announce, test_keys[0], nullptr, 0, 1), "furthest evicted closer");
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
kill_networking(net);
mono_time_free(mono_time);
logger_kill(log);
}
static void basic_announce_tests(void)
{
test_bucketnum();
test_store_data();
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
basic_announce_tests();
return 0;
}

View File

@ -0,0 +1,465 @@
#include <assert.h> // assert
#include <stdlib.h> // calloc, free
#include "check_compat.h"
#include "../testing/misc_tools.h"
#include "../toxcore/Messenger.h"
#include "../toxcore/mono_time.h"
#include "../toxcore/tox_struct.h"
#include "auto_test_support.h"
#ifndef ABORT_ON_LOG_ERROR
#define ABORT_ON_LOG_ERROR true
#endif
Run_Auto_Options default_run_auto_options()
{
return (Run_Auto_Options) {
.graph = GRAPH_COMPLETE,
.init_autotox = nullptr,
.tcp_port = 33188,
};
}
// List of live bootstrap nodes. These nodes should have TCP server enabled.
static const struct BootstrapNodes {
const char *ip;
uint16_t port;
const uint8_t key[32];
} BootstrapNodes[] = {
#ifndef USE_TEST_NETWORK
{
"tox.abilinski.com", 33445,
0x10, 0xC0, 0x0E, 0xB2, 0x50, 0xC3, 0x23, 0x3E,
0x34, 0x3E, 0x2A, 0xEB, 0xA0, 0x71, 0x15, 0xA5,
0xC2, 0x89, 0x20, 0xE9, 0xC8, 0xD2, 0x94, 0x92,
0xF6, 0xD0, 0x0B, 0x29, 0x04, 0x9E, 0xDC, 0x7E,
},
{
"tox.initramfs.io", 33445,
0x02, 0x80, 0x7C, 0xF4, 0xF8, 0xBB, 0x8F, 0xB3,
0x90, 0xCC, 0x37, 0x94, 0xBD, 0xF1, 0xE8, 0x44,
0x9E, 0x9A, 0x83, 0x92, 0xC5, 0xD3, 0xF2, 0x20,
0x00, 0x19, 0xDA, 0x9F, 0x1E, 0x81, 0x2E, 0x46,
},
{
"tox.plastiras.org", 33445,
0x8E, 0x8B, 0x63, 0x29, 0x9B, 0x3D, 0x52, 0x0F,
0xB3, 0x77, 0xFE, 0x51, 0x00, 0xE6, 0x5E, 0x33,
0x22, 0xF7, 0xAE, 0x5B, 0x20, 0xA0, 0xAC, 0xED,
0x29, 0x81, 0x76, 0x9F, 0xC5, 0xB4, 0x37, 0x25,
},
{
"tox.novg.net", 33445,
0xD5, 0x27, 0xE5, 0x84, 0x7F, 0x83, 0x30, 0xD6,
0x28, 0xDA, 0xB1, 0x81, 0x4F, 0x0A, 0x42, 0x2F,
0x6D, 0xC9, 0xD0, 0xA3, 0x00, 0xE6, 0xC3, 0x57,
0x63, 0x4E, 0xE2, 0xDA, 0x88, 0xC3, 0x54, 0x63,
},
#else
{
"172.93.52.70", 33445,
0x79, 0xCA, 0xDA, 0x49, 0x74, 0xB0, 0x92, 0x6F,
0x28, 0x6F, 0x02, 0x5C, 0xD5, 0xFF, 0xDF, 0x3E,
0x65, 0x4A, 0x37, 0x58, 0xC5, 0x3E, 0x02, 0x73,
0xEC, 0xFC, 0x4D, 0x12, 0xC2, 0x1D, 0xCA, 0x48,
},
{
"tox.plastiras.org", 38445,
0x5E, 0x47, 0xBA, 0x1D, 0xC3, 0x91, 0x3E, 0xB2,
0xCB, 0xF2, 0xD6, 0x4C, 0xE4, 0xF2, 0x3D, 0x8B,
0xFE, 0x53, 0x91, 0xBF, 0xAB, 0xE5, 0xC4, 0x3C,
0x5B, 0xAD, 0x13, 0xF0, 0xA4, 0x14, 0xCD, 0x77,
},
#endif // USE_TEST_NETWORK
{ nullptr, 0, 0 },
};
void bootstrap_tox_live_network(Tox *tox, bool enable_tcp)
{
ck_assert(tox != nullptr);
for (size_t j = 0; BootstrapNodes[j].ip != nullptr; ++j) {
const char *ip = BootstrapNodes[j].ip;
uint16_t port = BootstrapNodes[j].port;
const uint8_t *key = BootstrapNodes[j].key;
Tox_Err_Bootstrap err;
tox_bootstrap(tox, ip, port, key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
fprintf(stderr, "Failed to bootstrap node %zu (%s): error %d\n", j, ip, err);
}
if (enable_tcp) {
tox_add_tcp_relay(tox, ip, port, key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
fprintf(stderr, "Failed to add TCP relay %zu (%s): error %d\n", j, ip, err);
}
}
}
}
bool all_connected(const AutoTox *autotoxes, uint32_t tox_count)
{
if (tox_count) {
ck_assert(autotoxes != nullptr);
}
for (uint32_t i = 0; i < tox_count; ++i) {
if (tox_self_get_connection_status(autotoxes[i].tox) == TOX_CONNECTION_NONE) {
return false;
}
}
return true;
}
bool all_friends_connected(const AutoTox *autotoxes, uint32_t tox_count)
{
if (tox_count) {
ck_assert(autotoxes != nullptr);
}
for (uint32_t i = 0; i < tox_count; ++i) {
const size_t friend_count = tox_self_get_friend_list_size(autotoxes[i].tox);
for (size_t j = 0; j < friend_count; ++j) {
if (tox_friend_get_connection_status(autotoxes[i].tox, j, nullptr) == TOX_CONNECTION_NONE) {
return false;
}
}
}
return true;
}
void iterate_all_wait(AutoTox *autotoxes, uint32_t tox_count, uint32_t wait)
{
if (tox_count) {
ck_assert(autotoxes != nullptr);
}
for (uint32_t i = 0; i < tox_count; ++i) {
if (autotoxes[i].alive) {
tox_iterate(autotoxes[i].tox, &autotoxes[i]);
autotoxes[i].clock += wait;
}
}
/* Also actually sleep a little, to allow for local network processing */
c_sleep(5);
}
static uint64_t get_state_clock_callback(void *user_data)
{
const uint64_t *clock = (const uint64_t *)user_data;
return *clock;
}
void set_mono_time_callback(AutoTox *autotox)
{
ck_assert(autotox != nullptr);
Mono_Time *mono_time = autotox->tox->mono_time;
autotox->clock = current_time_monotonic(mono_time);
mono_time_set_current_time_callback(mono_time, nullptr, nullptr); // set to default first
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &autotox->clock);
}
void save_autotox(AutoTox *autotox)
{
ck_assert(autotox != nullptr);
fprintf(stderr, "Saving #%u\n", autotox->index);
free(autotox->save_state);
autotox->save_state = nullptr;
autotox->save_size = tox_get_savedata_size(autotox->tox);
ck_assert_msg(autotox->save_size > 0, "save is invalid size %u", (unsigned)autotox->save_size);
autotox->save_state = (uint8_t *)malloc(autotox->save_size);
ck_assert_msg(autotox->save_state != nullptr, "malloc failed");
tox_get_savedata(autotox->tox, autotox->save_state);
}
void kill_autotox(AutoTox *autotox)
{
ck_assert(autotox != nullptr);
ck_assert(autotox->alive);
fprintf(stderr, "Killing #%u\n", autotox->index);
autotox->alive = false;
tox_kill(autotox->tox);
}
void reload(AutoTox *autotox)
{
ck_assert(autotox != nullptr);
if (autotox->alive) {
kill_autotox(autotox);
}
fprintf(stderr, "Reloading #%u\n", autotox->index);
ck_assert(autotox->save_state != nullptr);
struct Tox_Options *const options = tox_options_new(nullptr);
ck_assert(options != nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, autotox->save_state, autotox->save_size);
autotox->tox = tox_new_log(options, nullptr, &autotox->index);
ck_assert(autotox->tox != nullptr);
tox_options_free(options);
set_mono_time_callback(autotox);
autotox->alive = true;
}
static void initialise_autotox(struct Tox_Options *options, AutoTox *autotox, uint32_t index, uint32_t state_size,
Run_Auto_Options *autotest_opts)
{
autotox->index = index;
Tox_Err_New err = TOX_ERR_NEW_OK;
if (index == 0) {
struct Tox_Options *default_opts = tox_options_new(nullptr);
ck_assert(default_opts != nullptr);
if (options == nullptr) {
options = default_opts;
}
if (tox_options_get_udp_enabled(options)) {
tox_options_set_tcp_port(options, 0);
autotest_opts->tcp_port = 0;
autotox->tox = tox_new_log(options, &err, &autotox->index);
ck_assert_msg(err == TOX_ERR_NEW_OK, "unexpected tox_new error: %d", err);
} else {
// Try a few ports for the TCP relay.
for (uint16_t tcp_port = autotest_opts->tcp_port; tcp_port < autotest_opts->tcp_port + 200; ++tcp_port) {
tox_options_set_tcp_port(options, tcp_port);
autotox->tox = tox_new_log(options, &err, &autotox->index);
if (autotox->tox != nullptr) {
autotest_opts->tcp_port = tcp_port;
break;
}
ck_assert_msg(err == TOX_ERR_NEW_PORT_ALLOC, "unexpected tox_new error (expected PORT_ALLOC): %d", err);
}
}
tox_options_free(default_opts);
} else {
// No TCP relay enabled for all the other toxes.
if (options != nullptr) {
tox_options_set_tcp_port(options, 0);
}
autotox->tox = tox_new_log(options, &err, &autotox->index);
}
ck_assert_msg(autotox->tox != nullptr, "failed to create tox instance #%u (error = %d)", index, err);
set_mono_time_callback(autotox);
autotox->alive = true;
autotox->save_state = nullptr;
if (state_size > 0) {
autotox->state = calloc(1, state_size);
ck_assert(autotox->state != nullptr);
ck_assert_msg(autotox->state != nullptr, "failed to allocate state");
} else {
autotox->state = nullptr;
}
if (autotest_opts->init_autotox != nullptr) {
autotest_opts->init_autotox(autotox, index);
}
}
static void autotox_add_friend(AutoTox *autotoxes, uint32_t adding, uint32_t added)
{
uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_public_key(autotoxes[added].tox, public_key);
Tox_Err_Friend_Add err;
tox_friend_add_norequest(autotoxes[adding].tox, public_key, &err);
ck_assert(err == TOX_ERR_FRIEND_ADD_OK);
}
static void initialise_friend_graph(Graph_Type graph, uint32_t num_toxes, AutoTox *autotoxes)
{
if (graph == GRAPH_LINEAR) {
printf("toxes #%d-#%u each add adjacent toxes as friends\n", 0, num_toxes - 1);
for (uint32_t i = 0; i < num_toxes; ++i) {
for (uint32_t j = i - 1; j != i + 3; j += 2) {
if (j < num_toxes) {
autotox_add_friend(autotoxes, i, j);
}
}
}
} else if (graph == GRAPH_COMPLETE) {
printf("toxes #%d-#%u add each other as friends\n", 0, num_toxes - 1);
for (uint32_t i = 0; i < num_toxes; ++i) {
for (uint32_t j = 0; j < num_toxes; ++j) {
if (i != j) {
autotox_add_friend(autotoxes, i, j);
}
}
}
} else {
ck_abort_msg("Unknown graph type");
}
}
static void bootstrap_autotoxes(struct Tox_Options *options, uint32_t tox_count, const Run_Auto_Options *autotest_opts,
AutoTox *autotoxes)
{
const bool udp_enabled = options != nullptr ? tox_options_get_udp_enabled(options) : true;
printf("bootstrapping all toxes off tox 0\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(autotoxes[0].tox, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(autotoxes[0].tox, nullptr);
for (uint32_t i = 1; i < tox_count; ++i) {
Tox_Err_Bootstrap err;
tox_bootstrap(autotoxes[i].tox, "localhost", dht_port, dht_key, &err);
ck_assert(err == TOX_ERR_BOOTSTRAP_OK);
}
if (!udp_enabled) {
ck_assert(autotest_opts->tcp_port != 0);
printf("bootstrapping all toxes to local TCP relay running on port %d\n", autotest_opts->tcp_port);
for (uint32_t i = 0; i < tox_count; ++i) {
Tox_Err_Bootstrap err;
tox_add_tcp_relay(autotoxes[i].tox, "localhost", autotest_opts->tcp_port, dht_key, &err);
ck_assert(err == TOX_ERR_BOOTSTRAP_OK);
}
}
}
void run_auto_test(struct Tox_Options *options, uint32_t tox_count, void test(AutoTox *autotoxes),
uint32_t state_size, Run_Auto_Options *autotest_opts)
{
printf("initialising %u toxes\n", tox_count);
AutoTox *autotoxes = (AutoTox *)calloc(tox_count, sizeof(AutoTox));
ck_assert(autotoxes != nullptr);
for (uint32_t i = 0; i < tox_count; ++i) {
initialise_autotox(options, &autotoxes[i], i, state_size, autotest_opts);
}
initialise_friend_graph(autotest_opts->graph, tox_count, autotoxes);
bootstrap_autotoxes(options, tox_count, autotest_opts, autotoxes);
do {
iterate_all_wait(autotoxes, tox_count, ITERATION_INTERVAL);
} while (!all_connected(autotoxes, tox_count));
printf("toxes are online\n");
do {
iterate_all_wait(autotoxes, tox_count, ITERATION_INTERVAL);
} while (!all_friends_connected(autotoxes, tox_count));
printf("tox clients connected\n");
test(autotoxes);
for (uint32_t i = 0; i < tox_count; ++i) {
tox_kill(autotoxes[i].tox);
free(autotoxes[i].state);
free(autotoxes[i].save_state);
}
free(autotoxes);
}
static const char *tox_log_level_name(Tox_Log_Level level)
{
switch (level) {
case TOX_LOG_LEVEL_TRACE:
return "TRACE";
case TOX_LOG_LEVEL_DEBUG:
return "DEBUG";
case TOX_LOG_LEVEL_INFO:
return "INFO";
case TOX_LOG_LEVEL_WARNING:
return "WARNING";
case TOX_LOG_LEVEL_ERROR:
return "ERROR";
}
return "<unknown>";
}
void print_debug_log(Tox *m, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
const char *message, void *user_data)
{
if (level == TOX_LOG_LEVEL_TRACE) {
return;
}
const uint32_t index = user_data ? *(uint32_t *)user_data : 0;
fprintf(stderr, "[#%u] %s %s:%u\t%s:\t%s\n", index, tox_log_level_name(level), file, line, func, message);
if (level == TOX_LOG_LEVEL_ERROR && ABORT_ON_LOG_ERROR) {
fputs("Aborting test program\n", stderr);
abort();
}
}
void print_debug_logger(void *context, Logger_Level level, const char *file, int line, const char *func, const char *message, void *userdata)
{
print_debug_log(nullptr, (Tox_Log_Level) level, file, (uint32_t) line, func, message, userdata);
}
Tox *tox_new_log_lan(struct Tox_Options *options, Tox_Err_New *err, void *log_user_data, bool lan_discovery)
{
struct Tox_Options *log_options = options;
if (log_options == nullptr) {
log_options = tox_options_new(nullptr);
}
assert(log_options != nullptr);
tox_options_set_local_discovery_enabled(log_options, lan_discovery);
// Use a higher start port for non-LAN-discovery tests so it's more likely for the LAN discovery
// test to get the default port 33445.
const uint16_t start_port = lan_discovery ? 33445 : 33545;
tox_options_set_start_port(log_options, start_port);
tox_options_set_end_port(log_options, start_port + 2000);
tox_options_set_log_callback(log_options, &print_debug_log);
tox_options_set_log_user_data(log_options, log_user_data);
Tox *tox = tox_new(log_options, err);
if (options == nullptr) {
tox_options_free(log_options);
}
return tox;
}
Tox *tox_new_log(struct Tox_Options *options, Tox_Err_New *err, void *log_user_data)
{
return tox_new_log_lan(options, err, log_user_data, false);
}

View File

@ -0,0 +1,65 @@
#ifndef RUN_AUTO_TEST_H
#define RUN_AUTO_TEST_H
#include <stdlib.h> // calloc, free
#include "check_compat.h"
#include "../testing/misc_tools.h"
#include "../toxcore/Messenger.h"
#include "../toxcore/mono_time.h"
typedef struct AutoTox {
Tox *tox;
uint32_t index;
uint64_t clock;
size_t save_size;
uint8_t *save_state;
bool alive;
void *state;
} AutoTox;
bool all_connected(const AutoTox *autotoxes, uint32_t tox_count);
bool all_friends_connected(const AutoTox *autotoxes, uint32_t tox_count);
void iterate_all_wait(AutoTox *autotoxes, uint32_t tox_count, uint32_t wait);
void save_autotox(AutoTox *autotox);
void kill_autotox(AutoTox *autotox);
void reload(AutoTox *autotox);
void set_mono_time_callback(AutoTox *autotox);
typedef enum Graph_Type {
GRAPH_COMPLETE = 0,
GRAPH_LINEAR,
} Graph_Type;
typedef struct Run_Auto_Options {
Graph_Type graph;
void (*init_autotox)(AutoTox *autotox, uint32_t n);
uint16_t tcp_port;
} Run_Auto_Options;
Run_Auto_Options default_run_auto_options(void);
void run_auto_test(struct Tox_Options *options, uint32_t tox_count, void test(AutoTox *autotoxes),
uint32_t state_size, Run_Auto_Options *autotest_opts);
void bootstrap_tox_live_network(Tox *tox, bool enable_tcp);
// Use this function when setting the log callback on a Tox* object
void print_debug_log(Tox *m, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
const char *message, void *user_data);
// Use this function when setting the log callback on a Logger object
void print_debug_logger(void *context, Logger_Level level, const char *file, int line,
const char *func, const char *message, void *userdata);
Tox *tox_new_log(struct Tox_Options *options, Tox_Err_New *err, void *log_user_data);
Tox *tox_new_log_lan(struct Tox_Options *options, Tox_Err_New *err, void *log_user_data, bool lan_discovery);
#endif

View File

@ -0,0 +1,34 @@
#include <stdio.h>
#include "../testing/misc_tools.h"
#include "check_compat.h"
#include "auto_test_support.h"
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Tox *tox_udp = tox_new_log(nullptr, nullptr, nullptr);
bootstrap_tox_live_network(tox_udp, false);
printf("Waiting for connection");
do {
printf(".");
fflush(stdout);
tox_iterate(tox_udp, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_self_get_connection_status(tox_udp) == TOX_CONNECTION_NONE);
const Tox_Connection status = tox_self_get_connection_status(tox_udp);
ck_assert_msg(status == TOX_CONNECTION_UDP,
"expected connection status to be UDP, but got %d", status);
printf("Connection (UDP): %d\n", tox_self_get_connection_status(tox_udp));
tox_kill(tox_udp);
return 0;
}

View File

@ -0,0 +1,32 @@
#ifndef C_TOXCORE_AUTO_TESTS_CHECK_COMPAT_H
#define C_TOXCORE_AUTO_TESTS_CHECK_COMPAT_H
#include "../toxcore/ccompat.h"
#include <stdio.h>
#include <stdlib.h>
#define ck_assert(ok) do { \
if (!(ok)) { \
fprintf(stderr, "%s:%d: failed `%s'\n", __FILE__, __LINE__, #ok); \
abort(); \
} \
} while (0)
#define ck_assert_msg(ok, ...) do { \
if (!(ok)) { \
fprintf(stderr, "%s:%d: failed `%s': ", __FILE__, __LINE__, #ok); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
abort(); \
} \
} while (0)
#define ck_abort_msg(...) do { \
fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
abort(); \
} while (0)
#endif // C_TOXCORE_AUTO_TESTS_CHECK_COMPAT_H

View File

@ -0,0 +1,464 @@
/* Auto Tests: Conferences AV.
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include "../toxav/toxav.h"
#include "check_compat.h"
#define NUM_AV_GROUP_TOX 16
#define NUM_AV_DISCONNECT (NUM_AV_GROUP_TOX / 2)
#define NUM_AV_DISABLE (NUM_AV_GROUP_TOX / 2)
#include "auto_test_support.h"
typedef struct State {
bool invited_next;
uint32_t received_audio_peers[NUM_AV_GROUP_TOX];
uint32_t received_audio_num;
} State;
static void handle_self_connection_status(
Tox *tox, Tox_Connection connection_status, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
if (connection_status != TOX_CONNECTION_NONE) {
printf("tox #%u: is now connected\n", autotox->index);
} else {
printf("tox #%u: is now disconnected\n", autotox->index);
}
}
static void handle_friend_connection_status(
Tox *tox, uint32_t friendnumber, Tox_Connection connection_status, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
if (connection_status != TOX_CONNECTION_NONE) {
printf("tox #%u: is now connected to friend %u\n", autotox->index, friendnumber);
} else {
printf("tox #%u: is now disconnected from friend %u\n", autotox->index, friendnumber);
}
}
static void audio_callback(void *tox, uint32_t groupnumber, uint32_t peernumber,
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
sample_rate, void *user_data)
{
if (samples == 0) {
return;
}
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
for (uint32_t i = 0; i < state->received_audio_num; ++i) {
if (state->received_audio_peers[i] == peernumber) {
return;
}
}
ck_assert(state->received_audio_num < NUM_AV_GROUP_TOX);
state->received_audio_peers[state->received_audio_num] = peernumber;
++state->received_audio_num;
}
static void handle_conference_invite(
Tox *tox, uint32_t friendnumber, Tox_Conference_Type type,
const uint8_t *data, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
ck_assert_msg(type == TOX_CONFERENCE_TYPE_AV, "tox #%u: wrong conference type: %d", autotox->index, type);
ck_assert_msg(toxav_join_av_groupchat(tox, friendnumber, data, length, audio_callback, user_data) == 0,
"tox #%u: failed to join group", autotox->index);
}
static void handle_conference_connected(
Tox *tox, uint32_t conference_number, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
if (state->invited_next || tox_self_get_friend_list_size(tox) <= 1) {
return;
}
Tox_Err_Conference_Invite err;
tox_conference_invite(tox, 1, 0, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %d", autotox->index,
err);
printf("tox #%u: invited next friend\n", autotox->index);
state->invited_next = true;
}
static bool toxes_are_disconnected_from_group(uint32_t tox_count, AutoTox *autotoxes,
bool *disconnected)
{
uint32_t num_disconnected = 0;
for (uint32_t i = 0; i < tox_count; ++i) {
num_disconnected += disconnected[i];
}
for (uint32_t i = 0; i < tox_count; ++i) {
if (disconnected[i]) {
continue;
}
if (tox_conference_peer_count(autotoxes[i].tox, 0, nullptr) > tox_count - num_disconnected) {
return false;
}
}
return true;
}
static void disconnect_toxes(uint32_t tox_count, AutoTox *autotoxes,
const bool *disconnect, const bool *exclude)
{
/* Fake a network outage for a set of peers D by iterating only the other
* peers D' until the connections time out according to D', then iterating
* only D until the connections time out according to D. */
VLA(bool, disconnect_now, tox_count);
bool invert = false;
do {
for (uint32_t i = 0; i < tox_count; ++i) {
disconnect_now[i] = exclude[i] || (invert ^ disconnect[i]);
}
do {
for (uint32_t i = 0; i < tox_count; ++i) {
if (!disconnect_now[i]) {
tox_iterate(autotoxes[i].tox, &autotoxes[i]);
autotoxes[i].clock += 1000;
}
}
c_sleep(20);
} while (!toxes_are_disconnected_from_group(tox_count, autotoxes, disconnect_now));
invert = !invert;
} while (invert);
}
static bool all_connected_to_group(uint32_t tox_count, AutoTox *autotoxes)
{
for (uint32_t i = 0; i < tox_count; ++i) {
if (tox_conference_peer_count(autotoxes[i].tox, 0, nullptr) < tox_count) {
return false;
}
}
return true;
}
/**
* returns a random index at which a list of booleans is false
* (some such index is required to exist)
*/
static uint32_t random_false_index(const Random *rng, bool *list, const uint32_t length)
{
uint32_t index;
do {
index = random_u32(rng) % length;
} while (list[index]);
return index;
}
static bool all_got_audio(AutoTox *autotoxes, const bool *disabled)
{
uint32_t num_disabled = 0;
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
num_disabled += disabled[i];
}
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
State *state = (State *)autotoxes[i].state;
if (disabled[i] ^ (state->received_audio_num
!= NUM_AV_GROUP_TOX - num_disabled - 1)) {
return false;
}
}
return true;
}
static void reset_received_audio(AutoTox *autotoxes)
{
for (uint32_t j = 0; j < NUM_AV_GROUP_TOX; ++j) {
((State *)autotoxes[j].state)->received_audio_num = 0;
}
}
#define GROUP_AV_TEST_SAMPLES 960
/* must have
* GROUP_AV_AUDIO_ITERATIONS - NUM_AV_GROUP_TOX >= 2^n >= GROUP_JBUF_SIZE
* for some n, to give messages time to be relayed and to let the jitter
* buffers fill up. */
#define GROUP_AV_AUDIO_ITERATIONS (8 + NUM_AV_GROUP_TOX)
static bool test_audio(AutoTox *autotoxes, const bool *disabled, bool quiet)
{
if (!quiet) {
printf("testing sending and receiving audio\n");
}
const int16_t PCM[GROUP_AV_TEST_SAMPLES] = {0};
reset_received_audio(autotoxes);
for (uint32_t n = 0; n < GROUP_AV_AUDIO_ITERATIONS; ++n) {
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (disabled[i]) {
continue;
}
if (toxav_group_send_audio(autotoxes[i].tox, 0, PCM, GROUP_AV_TEST_SAMPLES, 1, 48000) != 0) {
if (!quiet) {
ck_abort_msg("#%u failed to send audio", autotoxes[i].index);
}
return false;
}
}
iterate_all_wait(autotoxes, NUM_AV_GROUP_TOX, ITERATION_INTERVAL);
if (all_got_audio(autotoxes, disabled)) {
return true;
}
}
if (!quiet) {
ck_abort_msg("group failed to receive audio");
}
return false;
}
static void test_eventual_audio(AutoTox *autotoxes, const bool *disabled, uint64_t timeout)
{
uint64_t start = autotoxes[0].clock;
while (autotoxes[0].clock < start + timeout) {
if (test_audio(autotoxes, disabled, true)
&& test_audio(autotoxes, disabled, true)) {
printf("audio test successful after %d seconds\n", (int)((autotoxes[0].clock - start) / 1000));
return;
}
}
printf("audio seems not to be getting through: testing again with errors.\n");
test_audio(autotoxes, disabled, false);
}
static void do_audio(AutoTox *autotoxes, uint32_t iterations)
{
const int16_t PCM[GROUP_AV_TEST_SAMPLES] = {0};
printf("running audio for %u iterations\n", iterations);
for (uint32_t f = 0; f < iterations; ++f) {
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
ck_assert_msg(toxav_group_send_audio(autotoxes[i].tox, 0, PCM, GROUP_AV_TEST_SAMPLES, 1, 48000) == 0,
"#%u failed to send audio", autotoxes[i].index);
iterate_all_wait(autotoxes, NUM_AV_GROUP_TOX, ITERATION_INTERVAL);
}
}
}
// should agree with value in groupav.c
#define GROUP_JBUF_DEAD_SECONDS 4
#define JITTER_SETTLE_TIME (GROUP_JBUF_DEAD_SECONDS*1000 + NUM_AV_GROUP_TOX*ITERATION_INTERVAL*(GROUP_AV_AUDIO_ITERATIONS+1))
static void run_conference_tests(AutoTox *autotoxes)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
bool disabled[NUM_AV_GROUP_TOX] = {0};
test_audio(autotoxes, disabled, false);
/* have everyone send audio for a bit so we can test that the audio
* sequnums dropping to 0 on restart isn't a problem */
do_audio(autotoxes, 20);
printf("letting random toxes timeout\n");
bool disconnected[NUM_AV_GROUP_TOX] = {0};
bool restarting[NUM_AV_GROUP_TOX] = {0};
ck_assert(NUM_AV_DISCONNECT < NUM_AV_GROUP_TOX);
for (uint32_t i = 0; i < NUM_AV_DISCONNECT; ++i) {
uint32_t disconnect = random_false_index(rng, disconnected, NUM_AV_GROUP_TOX);
disconnected[disconnect] = true;
if (i < NUM_AV_DISCONNECT / 2) {
restarting[disconnect] = true;
printf("Restarting #%u\n", autotoxes[disconnect].index);
} else {
printf("Disconnecting #%u\n", autotoxes[disconnect].index);
}
}
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (restarting[i]) {
save_autotox(&autotoxes[i]);
kill_autotox(&autotoxes[i]);
}
}
disconnect_toxes(NUM_AV_GROUP_TOX, autotoxes, disconnected, restarting);
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (restarting[i]) {
reload(&autotoxes[i]);
}
}
printf("reconnecting toxes\n");
do {
iterate_all_wait(autotoxes, NUM_AV_GROUP_TOX, ITERATION_INTERVAL);
} while (!all_connected_to_group(NUM_AV_GROUP_TOX, autotoxes));
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (restarting[i]) {
ck_assert_msg(!toxav_groupchat_av_enabled(autotoxes[i].tox, 0),
"#%u restarted but av enabled", autotoxes[i].index);
ck_assert_msg(toxav_groupchat_enable_av(autotoxes[i].tox, 0, audio_callback, &autotoxes[i]) == 0,
"#%u failed to re-enable av", autotoxes[i].index);
ck_assert_msg(toxav_groupchat_av_enabled(autotoxes[i].tox, 0),
"#%u av not enabled even after enabling", autotoxes[i].index);
}
}
printf("testing audio\n");
/* Allow time for the jitter buffers to reset and for the group to become
* connected enough for lossy messages to get through
* (all_connected_to_group() only checks lossless connectivity, which is a
* looser condition). */
test_eventual_audio(autotoxes, disabled, JITTER_SETTLE_TIME + NUM_AV_GROUP_TOX * 1000);
printf("testing disabling av\n");
ck_assert(NUM_AV_DISABLE < NUM_AV_GROUP_TOX);
for (uint32_t i = 0; i < NUM_AV_DISABLE; ++i) {
uint32_t disable = random_false_index(rng, disabled, NUM_AV_GROUP_TOX);
disabled[disable] = true;
printf("Disabling #%u\n", autotoxes[disable].index);
ck_assert_msg(toxav_groupchat_enable_av(autotoxes[disable].tox, 0, audio_callback, &autotoxes[disable]) != 0,
"#%u could enable already enabled av!", autotoxes[i].index);
ck_assert_msg(toxav_groupchat_disable_av(autotoxes[disable].tox, 0) == 0,
"#%u failed to disable av", autotoxes[i].index);
}
// Run test without error to clear out messages from now-disabled peers.
test_audio(autotoxes, disabled, true);
printf("testing audio with some peers having disabled their av\n");
test_audio(autotoxes, disabled, false);
for (uint32_t i = 0; i < NUM_AV_DISABLE; ++i) {
if (!disabled[i]) {
continue;
}
disabled[i] = false;
ck_assert_msg(toxav_groupchat_disable_av(autotoxes[i].tox, 0) != 0,
"#%u could disable already disabled av!", autotoxes[i].index);
ck_assert_msg(!toxav_groupchat_av_enabled(autotoxes[i].tox, 0),
"#%u av enabled after disabling", autotoxes[i].index);
ck_assert_msg(toxav_groupchat_enable_av(autotoxes[i].tox, 0, audio_callback, &autotoxes[i]) == 0,
"#%u failed to re-enable av", autotoxes[i].index);
}
printf("testing audio after re-enabling all av\n");
test_eventual_audio(autotoxes, disabled, JITTER_SETTLE_TIME);
}
static void test_groupav(AutoTox *autotoxes)
{
const time_t test_start_time = time(nullptr);
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
tox_callback_self_connection_status(autotoxes[i].tox, &handle_self_connection_status);
tox_callback_friend_connection_status(autotoxes[i].tox, &handle_friend_connection_status);
tox_callback_conference_invite(autotoxes[i].tox, &handle_conference_invite);
tox_callback_conference_connected(autotoxes[i].tox, &handle_conference_connected);
}
ck_assert_msg(toxav_add_av_groupchat(autotoxes[0].tox, audio_callback, &autotoxes[0]) != UINT32_MAX,
"failed to create group");
printf("tox #%u: inviting its first friend\n", autotoxes[0].index);
ck_assert_msg(tox_conference_invite(autotoxes[0].tox, 0, 0, nullptr) != 0, "failed to invite friend");
((State *)autotoxes[0].state)->invited_next = true;
printf("waiting for invitations to be made\n");
uint32_t invited_count = 0;
do {
iterate_all_wait(autotoxes, NUM_AV_GROUP_TOX, ITERATION_INTERVAL);
invited_count = 0;
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
invited_count += ((State *)autotoxes[i].state)->invited_next;
}
} while (invited_count != NUM_AV_GROUP_TOX - 1);
uint64_t pregroup_clock = autotoxes[0].clock;
printf("waiting for all toxes to be in the group\n");
uint32_t fully_connected_count = 0;
do {
fully_connected_count = 0;
iterate_all_wait(autotoxes, NUM_AV_GROUP_TOX, ITERATION_INTERVAL);
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
Tox_Err_Conference_Peer_Query err;
uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
peer_count = 0;
}
fully_connected_count += peer_count == NUM_AV_GROUP_TOX;
}
} while (fully_connected_count != NUM_AV_GROUP_TOX);
printf("group connected, took %d seconds\n", (int)((autotoxes[0].clock - pregroup_clock) / 1000));
run_conference_tests(autotoxes);
printf("test_many_group succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time));
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, NUM_AV_GROUP_TOX, test_groupav, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,90 @@
#include <stdbool.h>
#include <stdint.h>
typedef struct State {
bool self_online;
bool friend_online;
bool joined;
uint32_t conference;
} State;
#include "auto_test_support.h"
static void handle_conference_invite(
Tox *tox, uint32_t friend_number, Tox_Conference_Type type,
const uint8_t *cookie, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
fprintf(stderr, "handle_conference_invite(#%u, %u, %d, uint8_t[%u], _)\n",
autotox->index, friend_number, type, (unsigned)length);
fprintf(stderr, "tox%u joining conference\n", autotox->index);
ck_assert_msg(!state->joined, "invitation callback generated for already joined conference");
if (friend_number != -1) {
Tox_Err_Conference_Join err;
state->conference = tox_conference_join(tox, friend_number, cookie, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK,
"attempting to join the conference returned with an error: %d", err);
fprintf(stderr, "tox%u joined conference %u\n", autotox->index, state->conference);
state->joined = true;
}
}
static void conference_double_invite_test(AutoTox *autotoxes)
{
// Conference callbacks.
tox_callback_conference_invite(autotoxes[0].tox, handle_conference_invite);
tox_callback_conference_invite(autotoxes[1].tox, handle_conference_invite);
State *state[2];
state[0] = (State *)autotoxes[0].state;
state[1] = (State *)autotoxes[1].state;
{
// Create new conference, tox0 is the founder.
Tox_Err_Conference_New err;
state[0]->conference = tox_conference_new(autotoxes[0].tox, &err);
state[0]->joined = true;
ck_assert_msg(err == TOX_ERR_CONFERENCE_NEW_OK,
"attempting to create a new conference returned with an error: %d", err);
fprintf(stderr, "Created conference: index=%u\n", state[0]->conference);
}
{
// Invite friend.
Tox_Err_Conference_Invite err;
tox_conference_invite(autotoxes[0].tox, 0, state[0]->conference, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK,
"attempting to invite a friend returned with an error: %d", err);
fprintf(stderr, "tox0 invited tox1\n");
}
fprintf(stderr, "Waiting for invitation to arrive\n");
do {
iterate_all_wait(autotoxes, 2, ITERATION_INTERVAL);
} while (!state[0]->joined || !state[1]->joined);
fprintf(stderr, "Invitations accepted\n");
fprintf(stderr, "Sending second invitation; should be ignored\n");
tox_conference_invite(autotoxes[0].tox, 0, state[0]->conference, nullptr);
iterate_all_wait(autotoxes, 2, ITERATION_INTERVAL);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, conference_double_invite_test, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,179 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct State {
bool connected;
uint32_t conference;
} State;
#define NUM_INVITE_MERGE_TOX 5
#include "auto_test_support.h"
static void handle_conference_invite(
Tox *tox, uint32_t friend_number, Tox_Conference_Type type,
const uint8_t *cookie, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
if (friend_number != -1) {
Tox_Err_Conference_Join err;
state->conference = tox_conference_join(tox, friend_number, cookie, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK,
"attempting to join the conference returned with an error: %d", err);
fprintf(stderr, "#%u accepted invite to conference %u\n", autotox->index, state->conference);
}
}
static void handle_conference_connected(
Tox *tox, uint32_t conference_number, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
fprintf(stderr, "#%u connected to conference %u\n", autotox->index, state->conference);
state->connected = true;
}
static void wait_connected(AutoTox *autotoxes, AutoTox *autotox, uint32_t friendnumber)
{
do {
iterate_all_wait(autotoxes, NUM_INVITE_MERGE_TOX, ITERATION_INTERVAL);
} while (tox_friend_get_connection_status(autotox->tox, friendnumber, nullptr) == TOX_CONNECTION_NONE);
}
static void do_invite(AutoTox *autotoxes, AutoTox *inviter, AutoTox *invitee, uint32_t friendnum)
{
fprintf(stderr, "#%u inviting #%u\n", inviter->index, invitee->index);
Tox_Err_Conference_Invite err;
tox_conference_invite(inviter->tox, friendnum, ((State *)inviter->state)->conference, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK,
"#%u attempting to invite #%u (friendnumber %u) returned with an error: %d", inviter->index, invitee->index,
friendnum, err);
do {
iterate_all_wait(autotoxes, NUM_INVITE_MERGE_TOX, ITERATION_INTERVAL);
} while (!((State *)invitee->state)->connected);
}
static bool group_complete(AutoTox *autotoxes)
{
int c = -1, size = 0;
for (int i = 0; i < NUM_INVITE_MERGE_TOX; i++) {
if (!autotoxes[i].alive) {
continue;
}
const int ct = tox_conference_peer_count(autotoxes[i].tox, ((State *)autotoxes[i].state)->conference, nullptr);
if (c == -1) {
c = ct;
} else if (c != ct) {
return false;
}
++size;
}
return (c == size);
}
static void wait_group_complete(AutoTox *autotoxes)
{
do {
iterate_all_wait(autotoxes, NUM_INVITE_MERGE_TOX, ITERATION_INTERVAL);
} while (!group_complete(autotoxes));
}
static void conference_invite_merge_test(AutoTox *autotoxes)
{
// Test that an explicit invite between peers in different connected
// components will cause a split group to merge
for (int i = 0; i < NUM_INVITE_MERGE_TOX; i++) {
tox_callback_conference_invite(autotoxes[i].tox, &handle_conference_invite);
tox_callback_conference_connected(autotoxes[i].tox, &handle_conference_connected);
}
State *state2 = (State *)autotoxes[2].state;
{
// Create new conference, tox 2 is the founder.
Tox_Err_Conference_New err;
state2->conference = tox_conference_new(autotoxes[2].tox, &err);
state2->connected = true;
ck_assert_msg(err == TOX_ERR_CONFERENCE_NEW_OK,
"attempting to create a new conference returned with an error: %d", err);
fprintf(stderr, "Created conference: index=%u\n", state2->conference);
}
save_autotox(&autotoxes[2]);
do_invite(autotoxes, &autotoxes[2], &autotoxes[1], 0);
do_invite(autotoxes, &autotoxes[1], &autotoxes[0], 0);
save_autotox(&autotoxes[1]);
kill_autotox(&autotoxes[1]);
do {
iterate_all_wait(autotoxes, NUM_INVITE_MERGE_TOX, ITERATION_INTERVAL);
} while (tox_conference_peer_count(autotoxes[2].tox, state2->conference, nullptr) != 1);
do_invite(autotoxes, &autotoxes[2], &autotoxes[3], 1);
do_invite(autotoxes, &autotoxes[3], &autotoxes[4], 1);
kill_autotox(&autotoxes[2]);
reload(&autotoxes[1]);
uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_public_key(autotoxes[1].tox, public_key);
tox_friend_add_norequest(autotoxes[3].tox, public_key, nullptr);
tox_self_get_public_key(autotoxes[3].tox, public_key);
tox_friend_add_norequest(autotoxes[1].tox, public_key, nullptr);
wait_connected(autotoxes, &autotoxes[1], 2);
do_invite(autotoxes, &autotoxes[1], &autotoxes[3], 2);
fprintf(stderr, "Waiting for group to merge\n");
wait_group_complete(autotoxes);
fprintf(stderr, "Group merged\n");
reload(&autotoxes[2]);
wait_connected(autotoxes, &autotoxes[2], 0);
do_invite(autotoxes, &autotoxes[2], &autotoxes[1], 0);
fprintf(stderr, "Waiting for #2 to rejoin\n");
wait_group_complete(autotoxes);
kill_autotox(&autotoxes[2]);
wait_group_complete(autotoxes);
reload(&autotoxes[2]);
wait_connected(autotoxes, &autotoxes[2], 0);
wait_connected(autotoxes, &autotoxes[1], 1);
do_invite(autotoxes, &autotoxes[1], &autotoxes[2], 1);
fprintf(stderr, "Waiting for #2 to rejoin\n");
wait_group_complete(autotoxes);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, NUM_INVITE_MERGE_TOX, conference_invite_merge_test, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,137 @@
#include <stdbool.h>
#include <stdint.h>
typedef struct State {
bool self_online;
bool friend_online;
bool friend_in_group;
bool joined;
uint32_t conference;
} State;
#include "auto_test_support.h"
static void handle_conference_invite(
Tox *tox, uint32_t friend_number, Tox_Conference_Type type,
const uint8_t *cookie, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
fprintf(stderr, "handle_conference_invite(#%u, %u, %d, uint8_t[%u], _)\n",
autotox->index, friend_number, type, (unsigned)length);
fprintf(stderr, "tox%u joining conference\n", autotox->index);
Tox_Err_Conference_Join err;
state->conference = tox_conference_join(tox, friend_number, cookie, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK,
"attempting to join the conference returned with an error: %d", err);
fprintf(stderr, "tox%u joined conference %u\n", autotox->index, state->conference);
state->joined = true;
}
static void handle_peer_list_changed(Tox *tox, uint32_t conference_number, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
fprintf(stderr, "handle_peer_list_changed(#%u, %u, _)\n",
autotox->index, conference_number);
Tox_Err_Conference_Peer_Query err;
uint32_t const count = tox_conference_peer_count(tox, conference_number, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_PEER_QUERY_OK,
"failed to get conference peer count: err = %d", err);
printf("tox%u has %u peers\n", autotox->index, count);
state->friend_in_group = count == 2;
}
static void rebuild_peer_list(Tox *tox)
{
for (uint32_t conference_number = 0;
conference_number < tox_conference_get_chatlist_size(tox);
++conference_number) {
Tox_Err_Conference_Peer_Query err;
uint32_t const count = tox_conference_peer_count(tox, conference_number, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_PEER_QUERY_OK,
"failed to get conference peer count for conference %u: err = %d", conference_number, err);
for (uint32_t peer_number = 0; peer_number < count; peer_number++) {
size_t size = tox_conference_peer_get_name_size(tox, conference_number, peer_number, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_PEER_QUERY_OK,
"failed to get conference peer %u's name size (conference = %u): err = %d", peer_number, conference_number, err);
uint8_t *const name = (uint8_t *)malloc(size);
ck_assert(name != nullptr);
tox_conference_peer_get_name(tox, conference_number, peer_number, name, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_PEER_QUERY_OK,
"failed to get conference peer %u's name (conference = %u): err = %d", peer_number, conference_number, err);
free(name);
}
}
}
static void conference_peer_nick_test(AutoTox *autotoxes)
{
// Conference callbacks.
tox_callback_conference_invite(autotoxes[0].tox, handle_conference_invite);
tox_callback_conference_invite(autotoxes[1].tox, handle_conference_invite);
tox_callback_conference_peer_list_changed(autotoxes[0].tox, handle_peer_list_changed);
tox_callback_conference_peer_list_changed(autotoxes[1].tox, handle_peer_list_changed);
// Set the names of the toxes.
tox_self_set_name(autotoxes[0].tox, (const uint8_t *)"test-tox-0", 10, nullptr);
tox_self_set_name(autotoxes[1].tox, (const uint8_t *)"test-tox-1", 10, nullptr);
State *state[2];
state[0] = (State *)autotoxes[0].state;
state[1] = (State *)autotoxes[1].state;
{
// Create new conference, tox0 is the founder.
Tox_Err_Conference_New err;
state[0]->conference = tox_conference_new(autotoxes[0].tox, &err);
state[0]->joined = true;
ck_assert_msg(err == TOX_ERR_CONFERENCE_NEW_OK,
"attempting to create a new conference returned with an error: %d", err);
fprintf(stderr, "Created conference: index=%u\n", state[0]->conference);
}
{
// Invite friend.
Tox_Err_Conference_Invite err;
tox_conference_invite(autotoxes[0].tox, 0, state[0]->conference, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK,
"attempting to invite a friend returned with an error: %d", err);
fprintf(stderr, "tox0 invited tox1\n");
}
fprintf(stderr, "Waiting for invitation to arrive and peers to be in the group\n");
do {
iterate_all_wait(autotoxes, 2, ITERATION_INTERVAL);
} while (!state[0]->joined || !state[1]->joined || !state[0]->friend_in_group || !state[1]->friend_in_group);
fprintf(stderr, "Running tox0, but not tox1, waiting for tox1 to drop out\n");
do {
iterate_all_wait(autotoxes, 1, 1000);
// Rebuild peer list after every iteration.
rebuild_peer_list(autotoxes[0].tox);
} while (state[0]->friend_in_group);
fprintf(stderr, "Invitations accepted\n");
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, conference_peer_nick_test, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,259 @@
#include <stdio.h>
#include <stdlib.h>
#include "../testing/misc_tools.h"
#include "../toxcore/tox.h"
#include "auto_test_support.h"
#include "check_compat.h"
typedef struct State {
uint32_t id;
bool self_online;
bool friend_online;
bool invited_next;
bool joined;
uint32_t conference;
bool received;
uint32_t peers;
} State;
static void handle_self_connection_status(Tox *tox, Tox_Connection connection_status, void *user_data)
{
State *state = (State *)user_data;
fprintf(stderr, "self_connection_status(#%u, %d, _)\n", state->id, connection_status);
state->self_online = connection_status != TOX_CONNECTION_NONE;
}
static void handle_friend_connection_status(Tox *tox, uint32_t friend_number, Tox_Connection connection_status,
void *user_data)
{
State *state = (State *)user_data;
fprintf(stderr, "handle_friend_connection_status(#%u, %u, %d, _)\n", state->id, friend_number, connection_status);
state->friend_online = connection_status != TOX_CONNECTION_NONE;
}
static void handle_conference_invite(Tox *tox, uint32_t friend_number, Tox_Conference_Type type, const uint8_t *cookie,
size_t length, void *user_data)
{
State *state = (State *)user_data;
fprintf(stderr, "handle_conference_invite(#%u, %u, %d, uint8_t[%u], _)\n",
state->id, friend_number, type, (unsigned)length);
fprintf(stderr, "tox%u joining conference\n", state->id);
{
Tox_Err_Conference_Join err;
state->conference = tox_conference_join(tox, friend_number, cookie, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "failed to join a conference: err = %d", err);
fprintf(stderr, "tox%u Joined conference %u\n", state->id, state->conference);
state->joined = true;
}
}
static void handle_conference_message(Tox *tox, uint32_t conference_number, uint32_t peer_number,
Tox_Message_Type type, const uint8_t *message, size_t length, void *user_data)
{
State *state = (State *)user_data;
fprintf(stderr, "handle_conference_message(#%u, %u, %u, %d, uint8_t[%u], _)\n",
state->id, conference_number, peer_number, type, (unsigned)length);
fprintf(stderr, "tox%u got message: %s\n", state->id, (const char *)message);
state->received = true;
}
static void handle_conference_peer_list_changed(Tox *tox, uint32_t conference_number, void *user_data)
{
State *state = (State *)user_data;
fprintf(stderr, "handle_conference_peer_list_changed(#%u, %u, _)\n",
state->id, conference_number);
Tox_Err_Conference_Peer_Query err;
uint32_t count = tox_conference_peer_count(tox, conference_number, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
fprintf(stderr, "ERROR: %d\n", err);
exit(EXIT_FAILURE);
}
fprintf(stderr, "tox%u has %u peers online\n", state->id, count);
state->peers = count;
}
static void handle_conference_connected(Tox *tox, uint32_t conference_number, void *user_data)
{
State *state = (State *)user_data;
// We're tox2, so now we invite tox3.
if (state->id == 2 && !state->invited_next) {
Tox_Err_Conference_Invite err;
tox_conference_invite(tox, 1, state->conference, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox2 failed to invite tox3: err = %d", err);
state->invited_next = true;
fprintf(stderr, "tox2 invited tox3\n");
}
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
State state1 = {1};
State state2 = {2};
State state3 = {3};
// Create toxes.
Tox *tox1 = tox_new_log(nullptr, nullptr, &state1.id);
Tox *tox2 = tox_new_log(nullptr, nullptr, &state2.id);
Tox *tox3 = tox_new_log(nullptr, nullptr, &state3.id);
// tox1 <-> tox2, tox2 <-> tox3
uint8_t key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_public_key(tox2, key);
tox_friend_add_norequest(tox1, key, nullptr); // tox1 -> tox2
tox_self_get_public_key(tox1, key);
tox_friend_add_norequest(tox2, key, nullptr); // tox2 -> tox1
tox_self_get_public_key(tox3, key);
tox_friend_add_norequest(tox2, key, nullptr); // tox2 -> tox3
tox_self_get_public_key(tox2, key);
tox_friend_add_norequest(tox3, key, nullptr); // tox3 -> tox2
printf("bootstrapping tox2 and tox3 off tox1\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr);
tox_bootstrap(tox3, "localhost", dht_port, dht_key, nullptr);
// Connection callbacks.
tox_callback_self_connection_status(tox1, handle_self_connection_status);
tox_callback_self_connection_status(tox2, handle_self_connection_status);
tox_callback_self_connection_status(tox3, handle_self_connection_status);
tox_callback_friend_connection_status(tox1, handle_friend_connection_status);
tox_callback_friend_connection_status(tox2, handle_friend_connection_status);
tox_callback_friend_connection_status(tox3, handle_friend_connection_status);
// Conference callbacks.
tox_callback_conference_invite(tox1, handle_conference_invite);
tox_callback_conference_invite(tox2, handle_conference_invite);
tox_callback_conference_invite(tox3, handle_conference_invite);
tox_callback_conference_connected(tox1, handle_conference_connected);
tox_callback_conference_connected(tox2, handle_conference_connected);
tox_callback_conference_connected(tox3, handle_conference_connected);
tox_callback_conference_message(tox1, handle_conference_message);
tox_callback_conference_message(tox2, handle_conference_message);
tox_callback_conference_message(tox3, handle_conference_message);
tox_callback_conference_peer_list_changed(tox1, handle_conference_peer_list_changed);
tox_callback_conference_peer_list_changed(tox2, handle_conference_peer_list_changed);
tox_callback_conference_peer_list_changed(tox3, handle_conference_peer_list_changed);
// Wait for self connection.
fprintf(stderr, "Waiting for toxes to come online\n");
do {
tox_iterate(tox1, &state1);
tox_iterate(tox2, &state2);
tox_iterate(tox3, &state3);
c_sleep(100);
} while (!state1.self_online || !state2.self_online || !state3.self_online);
fprintf(stderr, "Toxes are online\n");
// Wait for friend connection.
fprintf(stderr, "Waiting for friends to connect\n");
do {
tox_iterate(tox1, &state1);
tox_iterate(tox2, &state2);
tox_iterate(tox3, &state3);
c_sleep(100);
} while (!state1.friend_online || !state2.friend_online || !state3.friend_online);
fprintf(stderr, "Friends are connected\n");
{
// Create new conference, tox1 is the founder.
Tox_Err_Conference_New err;
state1.conference = tox_conference_new(tox1, &err);
state1.joined = true;
ck_assert_msg(err == TOX_ERR_CONFERENCE_NEW_OK, "failed to create a conference: err = %d", err);
fprintf(stderr, "Created conference: id = %u\n", state1.conference);
}
{
// Invite friend.
Tox_Err_Conference_Invite err;
tox_conference_invite(tox1, 0, state1.conference, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "failed to invite a friend: err = %d", err);
state1.invited_next = true;
fprintf(stderr, "tox1 invited tox2\n");
}
fprintf(stderr, "Waiting for invitation to arrive\n");
do {
tox_iterate(tox1, &state1);
tox_iterate(tox2, &state2);
tox_iterate(tox3, &state3);
c_sleep(100);
} while (!state1.joined || !state2.joined || !state3.joined);
fprintf(stderr, "Invitations accepted\n");
fprintf(stderr, "Waiting for peers to come online\n");
do {
tox_iterate(tox1, &state1);
tox_iterate(tox2, &state2);
tox_iterate(tox3, &state3);
c_sleep(100);
} while (state1.peers == 0 || state2.peers == 0 || state3.peers == 0);
fprintf(stderr, "All peers are online\n");
{
fprintf(stderr, "tox1 sends a message to the group: \"hello!\"\n");
Tox_Err_Conference_Send_Message err;
tox_conference_send_message(tox1, state1.conference, TOX_MESSAGE_TYPE_NORMAL,
(const uint8_t *)"hello!", 7, &err);
if (err != TOX_ERR_CONFERENCE_SEND_MESSAGE_OK) {
fprintf(stderr, "ERROR: %d\n", err);
exit(EXIT_FAILURE);
}
}
fprintf(stderr, "Waiting for messages to arrive\n");
do {
tox_iterate(tox1, &state1);
tox_iterate(tox2, &state2);
tox_iterate(tox3, &state3);
c_sleep(100);
} while (!state2.received || !state3.received);
fprintf(stderr, "Messages received. Test complete.\n");
tox_kill(tox3);
tox_kill(tox2);
tox_kill(tox1);
return 0;
}

View File

@ -0,0 +1,441 @@
/* Auto Tests: Conferences.
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include "../toxcore/util.h"
#include "check_compat.h"
#define NUM_GROUP_TOX 16
#define NUM_DISCONNECT 8
#define GROUP_MESSAGE "Install Gentoo"
#define NAMELEN 9
#define NAME_FORMAT_STR "Tox #%4u"
#define NEW_NAME_FORMAT_STR "New #%4u"
typedef struct State {
bool invited_next;
} State;
#include "auto_test_support.h"
static void handle_self_connection_status(
Tox *tox, Tox_Connection connection_status, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
if (connection_status != TOX_CONNECTION_NONE) {
printf("tox #%u: is now connected\n", autotox->index);
} else {
printf("tox #%u: is now disconnected\n", autotox->index);
}
}
static void handle_friend_connection_status(
Tox *tox, uint32_t friendnumber, Tox_Connection connection_status, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
if (connection_status != TOX_CONNECTION_NONE) {
printf("tox #%u: is now connected to friend %u\n", autotox->index, friendnumber);
} else {
printf("tox #%u: is now disconnected from friend %u\n", autotox->index, friendnumber);
}
}
static void handle_conference_invite(
Tox *tox, uint32_t friendnumber, Tox_Conference_Type type,
const uint8_t *data, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%u: wrong conference type: %d", autotox->index, type);
Tox_Err_Conference_Join err;
uint32_t g_num = tox_conference_join(tox, friendnumber, data, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "tox #%u: error joining group: %d", autotox->index, err);
ck_assert_msg(g_num == 0, "tox #%u: group number was not 0", autotox->index);
// Try joining again. We should only be allowed to join once.
tox_conference_join(tox, friendnumber, data, length, &err);
ck_assert_msg(err != TOX_ERR_CONFERENCE_JOIN_OK,
"tox #%u: joining groupchat twice should be impossible.", autotox->index);
}
static void handle_conference_connected(
Tox *tox, uint32_t conference_number, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
if (state->invited_next || tox_self_get_friend_list_size(tox) <= 1) {
return;
}
Tox_Err_Conference_Invite err;
tox_conference_invite(tox, 1, 0, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %d", autotox->index,
err);
printf("tox #%u: invited next friend\n", autotox->index);
state->invited_next = true;
}
static uint32_t num_recv;
static void handle_conference_message(
Tox *tox, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
const uint8_t *message, size_t length, void *user_data)
{
if (length == (sizeof(GROUP_MESSAGE) - 1) && memcmp(message, GROUP_MESSAGE, sizeof(GROUP_MESSAGE) - 1) == 0) {
++num_recv;
}
}
static bool toxes_are_disconnected_from_group(uint32_t tox_count, AutoTox *autotoxes,
bool *disconnected)
{
uint32_t num_disconnected = 0;
for (uint32_t i = 0; i < tox_count; ++i) {
num_disconnected += disconnected[i];
}
for (uint32_t i = 0; i < tox_count; i++) {
if (disconnected[i]) {
continue;
}
if (tox_conference_peer_count(autotoxes[i].tox, 0, nullptr) > tox_count - num_disconnected) {
return false;
}
}
return true;
}
static void disconnect_toxes(uint32_t tox_count, AutoTox *autotoxes,
const bool *disconnect, const bool *exclude)
{
/* Fake a network outage for a set of peers D by iterating only the other
* peers D' until the connections time out according to D', then iterating
* only D until the connections time out according to D. */
VLA(bool, disconnect_now, tox_count);
bool invert = false;
do {
for (uint32_t i = 0; i < tox_count; ++i) {
disconnect_now[i] = exclude[i] || (invert ^ disconnect[i]);
}
do {
for (uint32_t i = 0; i < tox_count; ++i) {
if (!disconnect_now[i]) {
tox_iterate(autotoxes[i].tox, &autotoxes[i]);
autotoxes[i].clock += 1000;
}
}
c_sleep(20);
} while (!toxes_are_disconnected_from_group(tox_count, autotoxes, disconnect_now));
invert = !invert;
} while (invert);
}
static bool all_connected_to_group(uint32_t tox_count, AutoTox *autotoxes)
{
for (uint32_t i = 0; i < tox_count; i++) {
if (tox_conference_peer_count(autotoxes[i].tox, 0, nullptr) < tox_count) {
return false;
}
}
return true;
}
static bool names_propagated(uint32_t tox_count, AutoTox *autotoxes)
{
for (uint32_t i = 0; i < tox_count; ++i) {
for (uint32_t j = 0; j < tox_count; ++j) {
const size_t len = tox_conference_peer_get_name_size(autotoxes[i].tox, 0, j, nullptr);
if (len != NAMELEN) {
return false;
}
}
}
return true;
}
/**
* returns a random index at which a list of booleans is false
* (some such index is required to exist)
*/
static uint32_t random_false_index(const Random *rng, bool *list, const uint32_t length)
{
uint32_t index;
do {
index = random_u32(rng) % length;
} while (list[index]);
return index;
}
static void run_conference_tests(AutoTox *autotoxes)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
/* disabling name change propagation check for now, as it occasionally
* fails due to disconnections too short to trigger freezing */
const bool check_name_change_propagation = false;
/* each peer should freeze at least its two friends, but freezing more
* should not be necessary */
const uint32_t max_frozen = max_u32(2, NUM_DISCONNECT / 2);
printf("restricting number of frozen peers to %u\n", max_frozen);
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
Tox_Err_Conference_Set_Max_Offline err;
tox_conference_set_max_offline(autotoxes[i].tox, 0, max_frozen, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_SET_MAX_OFFLINE_OK,
"tox #%u failed to set max offline: err = %d", autotoxes[i].index, err);
}
printf("letting random toxes timeout\n");
bool disconnected[NUM_GROUP_TOX] = {0};
bool restarting[NUM_GROUP_TOX] = {0};
ck_assert(NUM_DISCONNECT < NUM_GROUP_TOX);
for (uint32_t i = 0; i < NUM_DISCONNECT; ++i) {
uint32_t disconnect = random_false_index(rng, disconnected, NUM_GROUP_TOX);
disconnected[disconnect] = true;
if (i < NUM_DISCONNECT / 2) {
restarting[disconnect] = true;
printf("Restarting #%u\n", autotoxes[disconnect].index);
} else {
printf("Disconnecting #%u\n", autotoxes[disconnect].index);
}
}
uint8_t *save[NUM_GROUP_TOX];
size_t save_size[NUM_GROUP_TOX];
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
if (restarting[i]) {
save_size[i] = tox_get_savedata_size(autotoxes[i].tox);
ck_assert_msg(save_size[i] != 0, "save is invalid size %u", (unsigned)save_size[i]);
save[i] = (uint8_t *)malloc(save_size[i]);
ck_assert_msg(save[i] != nullptr, "malloc failed");
tox_get_savedata(autotoxes[i].tox, save[i]);
tox_kill(autotoxes[i].tox);
}
}
disconnect_toxes(NUM_GROUP_TOX, autotoxes, disconnected, restarting);
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
if (restarting[i]) {
struct Tox_Options *const options = tox_options_new(nullptr);
ck_assert(options != nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, save[i], save_size[i]);
autotoxes[i].tox = tox_new_log(options, nullptr, &autotoxes[i].index);
ck_assert(autotoxes[i].tox != nullptr);
tox_options_free(options);
free(save[i]);
set_mono_time_callback(&autotoxes[i]);
tox_conference_set_max_offline(autotoxes[i].tox, 0, max_frozen, nullptr);
}
}
if (check_name_change_propagation) {
printf("changing names\n");
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
char name[NAMELEN + 1];
snprintf(name, NAMELEN + 1, NEW_NAME_FORMAT_STR, autotoxes[i].index);
tox_self_set_name(autotoxes[i].tox, (const uint8_t *)name, NAMELEN, nullptr);
}
}
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
const uint32_t num_frozen = tox_conference_offline_peer_count(autotoxes[i].tox, 0, nullptr);
ck_assert_msg(num_frozen <= max_frozen,
"tox #%u has too many offline peers: %u\n",
autotoxes[i].index, num_frozen);
}
printf("reconnecting toxes\n");
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
} while (!all_connected_to_group(NUM_GROUP_TOX, autotoxes));
printf("running conference tests\n");
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
tox_callback_conference_message(autotoxes[i].tox, &handle_conference_message);
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
}
Tox_Err_Conference_Send_Message err;
ck_assert_msg(
tox_conference_send_message(
autotoxes[random_u32(rng) % NUM_GROUP_TOX].tox, 0, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)GROUP_MESSAGE,
sizeof(GROUP_MESSAGE) - 1, &err) != 0, "failed to send group message");
ck_assert_msg(
err == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK, "failed to send group message");
num_recv = 0;
for (uint8_t j = 0; j < NUM_GROUP_TOX * 2; ++j) {
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
}
ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages");
if (check_name_change_propagation) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t j = 0; j < NUM_GROUP_TOX; ++j) {
uint8_t name[NAMELEN];
tox_conference_peer_get_name(autotoxes[i].tox, 0, j, name, nullptr);
/* Note the toxes will have been reordered */
ck_assert_msg(memcmp(name, "New", 3) == 0,
"name of #%u according to #%u not updated", autotoxes[j].index, autotoxes[i].index);
}
}
}
for (uint32_t k = NUM_GROUP_TOX; k != 0 ; --k) {
tox_conference_delete(autotoxes[k - 1].tox, 0, nullptr);
for (uint8_t j = 0; j < 10 || j < NUM_GROUP_TOX; ++j) {
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
}
for (uint32_t i = 0; i < k - 1; ++i) {
uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, nullptr);
ck_assert_msg(peer_count == (k - 1), "\n\tBad number of group peers (post check)."
"\n\t\t\tExpected: %u but tox_instance(%u) only has: %u\n\n",
k - 1, i, (unsigned)peer_count);
}
}
}
static void test_many_group(AutoTox *autotoxes)
{
const time_t test_start_time = time(nullptr);
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
tox_callback_self_connection_status(autotoxes[i].tox, &handle_self_connection_status);
tox_callback_friend_connection_status(autotoxes[i].tox, &handle_friend_connection_status);
tox_callback_conference_invite(autotoxes[i].tox, &handle_conference_invite);
tox_callback_conference_connected(autotoxes[i].tox, &handle_conference_connected);
char name[NAMELEN + 1];
snprintf(name, NAMELEN + 1, NAME_FORMAT_STR, autotoxes[i].index);
tox_self_set_name(autotoxes[i].tox, (const uint8_t *)name, NAMELEN, nullptr);
}
ck_assert_msg(tox_conference_new(autotoxes[0].tox, nullptr) != UINT32_MAX, "failed to create group");
printf("tox #%u: inviting its first friend\n", autotoxes[0].index);
ck_assert_msg(tox_conference_invite(autotoxes[0].tox, 0, 0, nullptr) != 0, "failed to invite friend");
((State *)autotoxes[0].state)->invited_next = true;
ck_assert_msg(tox_conference_set_title(autotoxes[0].tox, 0, (const uint8_t *)"Gentoo", sizeof("Gentoo") - 1,
nullptr) != 0,
"failed to set group title");
printf("waiting for invitations to be made\n");
uint32_t invited_count = 0;
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
invited_count = 0;
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
invited_count += ((State *)autotoxes[i].state)->invited_next;
}
} while (invited_count != NUM_GROUP_TOX - 1);
uint64_t pregroup_clock = autotoxes[0].clock;
printf("waiting for all toxes to be in the group\n");
uint32_t fully_connected_count = 0;
do {
fully_connected_count = 0;
printf("current peer counts: [");
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
Tox_Err_Conference_Peer_Query err;
uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
peer_count = 0;
}
fully_connected_count += peer_count == NUM_GROUP_TOX;
if (i != 0) {
printf(", ");
}
printf("%u", peer_count);
}
printf("]\n");
fflush(stdout);
} while (fully_connected_count != NUM_GROUP_TOX);
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, nullptr);
ck_assert_msg(peer_count == NUM_GROUP_TOX, "\n\tBad number of group peers (pre check)."
"\n\t\t\tExpected: %d but tox_instance(%u) only has: %u\n\n",
NUM_GROUP_TOX, i, (unsigned)peer_count);
uint8_t title[2048];
size_t ret = tox_conference_get_title_size(autotoxes[i].tox, 0, nullptr);
ck_assert_msg(ret == sizeof("Gentoo") - 1, "Wrong title length");
tox_conference_get_title(autotoxes[i].tox, 0, title, nullptr);
ck_assert_msg(memcmp("Gentoo", title, ret) == 0, "Wrong title");
}
printf("waiting for names to propagate\n");
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL);
} while (!names_propagated(NUM_GROUP_TOX, autotoxes));
printf("group connected, took %d seconds\n", (int)((autotoxes[0].clock - pregroup_clock) / 1000));
run_conference_tests(autotoxes);
printf("test_many_group succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time));
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, NUM_GROUP_TOX, test_many_group, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,27 @@
// This test checks that we can create two conferences and quit properly.
//
// This test triggers a different code path than if we only allocate a single
// conference. This is the simplest test possible that triggers it.
#include "../testing/misc_tools.h"
#include "../toxcore/tox.h"
#include "auto_test_support.h"
#include "check_compat.h"
int main(void)
{
// Create toxes.
uint32_t id = 1;
Tox *tox1 = tox_new_log(nullptr, nullptr, &id);
// Create two conferences and then exit.
Tox_Err_Conference_New err;
tox_conference_new(tox1, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_NEW_OK, "failed to create conference 1: %d", err);
tox_conference_new(tox1, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_NEW_OK, "failed to create conference 2: %d", err);
tox_kill(tox1);
return 0;
}

View File

@ -0,0 +1,354 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/net_crypto.h"
#include "check_compat.h"
static void rand_bytes(const Random *rng, uint8_t *b, size_t blen)
{
size_t i;
for (i = 0; i < blen; i++) {
b[i] = random_u08(rng);
}
}
// These test vectors are from libsodium's test suite
static const uint8_t alicesk[32] = {
0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d,
0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45,
0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a
};
static const uint8_t bobpk[32] = {
0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4,
0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37,
0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d,
0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f
};
static const uint8_t test_nonce[24] = {
0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6, 0x2b, 0x73,
0xcd, 0x62, 0xbd, 0xa8, 0x75, 0xfc, 0x73, 0xd6,
0x82, 0x19, 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37
};
static const uint8_t test_m[131] = {
0xbe, 0x07, 0x5f, 0xc5, 0x3c, 0x81, 0xf2, 0xd5,
0xcf, 0x14, 0x13, 0x16, 0xeb, 0xeb, 0x0c, 0x7b,
0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4,
0x4b, 0x66, 0x84, 0x9b, 0x64, 0x24, 0x4f, 0xfc,
0xe5, 0xec, 0xba, 0xaf, 0x33, 0xbd, 0x75, 0x1a,
0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29,
0x6c, 0xdc, 0x3c, 0x01, 0x23, 0x35, 0x61, 0xf4,
0x1d, 0xb6, 0x6c, 0xce, 0x31, 0x4a, 0xdb, 0x31,
0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d,
0xce, 0xea, 0x3a, 0x7f, 0xa1, 0x34, 0x80, 0x57,
0xe2, 0xf6, 0x55, 0x6a, 0xd6, 0xb1, 0x31, 0x8a,
0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde,
0x04, 0x89, 0x77, 0xeb, 0x48, 0xf5, 0x9f, 0xfd,
0x49, 0x24, 0xca, 0x1c, 0x60, 0x90, 0x2e, 0x52,
0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40,
0xe0, 0x82, 0xf9, 0x37, 0x76, 0x38, 0x48, 0x64,
0x5e, 0x07, 0x05
};
static const uint8_t test_c[147] = {
0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5,
0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9,
0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73,
0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce,
0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4,
0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a,
0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b,
0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72,
0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2,
0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38,
0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a,
0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae,
0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea,
0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda,
0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde,
0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3,
0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6,
0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74,
0xe3, 0x55, 0xa5
};
static void test_known(void)
{
uint8_t c[147];
uint8_t m[131];
uint16_t clen, mlen;
ck_assert_msg(sizeof(c) == sizeof(m) + CRYPTO_MAC_SIZE * sizeof(uint8_t),
"cyphertext should be CRYPTO_MAC_SIZE bytes longer than plaintext");
ck_assert_msg(sizeof(test_c) == sizeof(c), "sanity check failed");
ck_assert_msg(sizeof(test_m) == sizeof(m), "sanity check failed");
clen = encrypt_data(bobpk, alicesk, test_nonce, test_m, sizeof(test_m) / sizeof(uint8_t), c);
ck_assert_msg(memcmp(test_c, c, sizeof(c)) == 0, "cyphertext doesn't match test vector");
ck_assert_msg(clen == sizeof(c) / sizeof(uint8_t), "wrong ciphertext length");
mlen = decrypt_data(bobpk, alicesk, test_nonce, test_c, sizeof(test_c) / sizeof(uint8_t), m);
ck_assert_msg(memcmp(test_m, m, sizeof(m)) == 0, "decrypted text doesn't match test vector");
ck_assert_msg(mlen == sizeof(m) / sizeof(uint8_t), "wrong plaintext length");
}
static void test_fast_known(void)
{
uint8_t k[CRYPTO_SHARED_KEY_SIZE];
uint8_t c[147];
uint8_t m[131];
uint16_t clen, mlen;
encrypt_precompute(bobpk, alicesk, k);
ck_assert_msg(sizeof(c) == sizeof(m) + CRYPTO_MAC_SIZE * sizeof(uint8_t),
"cyphertext should be CRYPTO_MAC_SIZE bytes longer than plaintext");
ck_assert_msg(sizeof(test_c) == sizeof(c), "sanity check failed");
ck_assert_msg(sizeof(test_m) == sizeof(m), "sanity check failed");
clen = encrypt_data_symmetric(k, test_nonce, test_m, sizeof(test_m) / sizeof(uint8_t), c);
ck_assert_msg(memcmp(test_c, c, sizeof(c)) == 0, "cyphertext doesn't match test vector");
ck_assert_msg(clen == sizeof(c) / sizeof(uint8_t), "wrong ciphertext length");
mlen = decrypt_data_symmetric(k, test_nonce, test_c, sizeof(test_c) / sizeof(uint8_t), m);
ck_assert_msg(memcmp(test_m, m, sizeof(m)) == 0, "decrypted text doesn't match test vector");
ck_assert_msg(mlen == sizeof(m) / sizeof(uint8_t), "wrong plaintext length");
}
static void test_endtoend(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
// Test 100 random messages and keypairs
for (uint8_t testno = 0; testno < 100; testno++) {
uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t sk1[CRYPTO_SECRET_KEY_SIZE];
uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t sk2[CRYPTO_SECRET_KEY_SIZE];
uint8_t k1[CRYPTO_SHARED_KEY_SIZE];
uint8_t k2[CRYPTO_SHARED_KEY_SIZE];
uint8_t n[CRYPTO_NONCE_SIZE];
enum { M_SIZE = 50 };
uint8_t m[M_SIZE];
uint8_t c1[sizeof(m) + CRYPTO_MAC_SIZE];
uint8_t c2[sizeof(m) + CRYPTO_MAC_SIZE];
uint8_t c3[sizeof(m) + CRYPTO_MAC_SIZE];
uint8_t c4[sizeof(m) + CRYPTO_MAC_SIZE];
uint8_t m1[sizeof(m)];
uint8_t m2[sizeof(m)];
uint8_t m3[sizeof(m)];
uint8_t m4[sizeof(m)];
//Generate random message (random length from 10 to 50)
const uint16_t mlen = (random_u32(rng) % (M_SIZE - 10)) + 10;
rand_bytes(rng, m, mlen);
rand_bytes(rng, n, CRYPTO_NONCE_SIZE);
//Generate keypairs
crypto_new_keypair(rng, pk1, sk1);
crypto_new_keypair(rng, pk2, sk2);
//Precompute shared keys
encrypt_precompute(pk2, sk1, k1);
encrypt_precompute(pk1, sk2, k2);
ck_assert_msg(memcmp(k1, k2, CRYPTO_SHARED_KEY_SIZE) == 0, "encrypt_precompute: bad");
//Encrypt all four ways
const uint16_t c1len = encrypt_data(pk2, sk1, n, m, mlen, c1);
const uint16_t c2len = encrypt_data(pk1, sk2, n, m, mlen, c2);
const uint16_t c3len = encrypt_data_symmetric(k1, n, m, mlen, c3);
const uint16_t c4len = encrypt_data_symmetric(k2, n, m, mlen, c4);
ck_assert_msg(c1len == c2len && c1len == c3len && c1len == c4len, "cyphertext lengths differ");
ck_assert_msg(c1len == mlen + (uint16_t)CRYPTO_MAC_SIZE, "wrong cyphertext length");
ck_assert_msg(memcmp(c1, c2, c1len) == 0 && memcmp(c1, c3, c1len) == 0
&& memcmp(c1, c4, c1len) == 0, "crypertexts differ");
//Decrypt all four ways
const uint16_t m1len = decrypt_data(pk2, sk1, n, c1, c1len, m1);
const uint16_t m2len = decrypt_data(pk1, sk2, n, c1, c1len, m2);
const uint16_t m3len = decrypt_data_symmetric(k1, n, c1, c1len, m3);
const uint16_t m4len = decrypt_data_symmetric(k2, n, c1, c1len, m4);
ck_assert_msg(m1len == m2len && m1len == m3len && m1len == m4len, "decrypted text lengths differ");
ck_assert_msg(m1len == mlen, "wrong decrypted text length");
ck_assert_msg(memcmp(m1, m2, mlen) == 0 && memcmp(m1, m3, mlen) == 0
&& memcmp(m1, m4, mlen) == 0, "decrypted texts differ");
ck_assert_msg(memcmp(m1, m, mlen) == 0, "wrong decrypted text");
}
}
static void test_large_data(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
uint8_t k[CRYPTO_SHARED_KEY_SIZE];
uint8_t n[CRYPTO_NONCE_SIZE];
const size_t m1_size = MAX_CRYPTO_PACKET_SIZE - CRYPTO_MAC_SIZE;
uint8_t *m1 = (uint8_t *)malloc(m1_size);
uint8_t *c1 = (uint8_t *)malloc(m1_size + CRYPTO_MAC_SIZE);
uint8_t *m1prime = (uint8_t *)malloc(m1_size);
const size_t m2_size = MAX_CRYPTO_PACKET_SIZE - CRYPTO_MAC_SIZE;
uint8_t *m2 = (uint8_t *)malloc(m2_size);
uint8_t *c2 = (uint8_t *)malloc(m2_size + CRYPTO_MAC_SIZE);
ck_assert(m1 != nullptr && c1 != nullptr && m1prime != nullptr && m2 != nullptr && c2 != nullptr);
//Generate random messages
rand_bytes(rng, m1, m1_size);
rand_bytes(rng, m2, m2_size);
rand_bytes(rng, n, CRYPTO_NONCE_SIZE);
//Generate key
rand_bytes(rng, k, CRYPTO_SHARED_KEY_SIZE);
const uint16_t c1len = encrypt_data_symmetric(k, n, m1, m1_size, c1);
const uint16_t c2len = encrypt_data_symmetric(k, n, m2, m2_size, c2);
ck_assert_msg(c1len == m1_size + CRYPTO_MAC_SIZE, "could not encrypt");
ck_assert_msg(c2len == m2_size + CRYPTO_MAC_SIZE, "could not encrypt");
const uint16_t m1plen = decrypt_data_symmetric(k, n, c1, c1len, m1prime);
ck_assert_msg(m1plen == m1_size, "decrypted text lengths differ");
ck_assert_msg(memcmp(m1prime, m1, m1_size) == 0, "decrypted texts differ");
free(c2);
free(m2);
free(m1prime);
free(c1);
free(m1);
}
static void test_large_data_symmetric(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
uint8_t k[CRYPTO_SYMMETRIC_KEY_SIZE];
uint8_t n[CRYPTO_NONCE_SIZE];
const size_t m1_size = 16 * 16 * 16;
uint8_t *m1 = (uint8_t *)malloc(m1_size);
uint8_t *c1 = (uint8_t *)malloc(m1_size + CRYPTO_MAC_SIZE);
uint8_t *m1prime = (uint8_t *)malloc(m1_size);
ck_assert(m1 != nullptr && c1 != nullptr && m1prime != nullptr);
//Generate random messages
rand_bytes(rng, m1, m1_size);
rand_bytes(rng, n, CRYPTO_NONCE_SIZE);
//Generate key
new_symmetric_key(rng, k);
const uint16_t c1len = encrypt_data_symmetric(k, n, m1, m1_size, c1);
ck_assert_msg(c1len == m1_size + CRYPTO_MAC_SIZE, "could not encrypt data");
const uint16_t m1plen = decrypt_data_symmetric(k, n, c1, c1len, m1prime);
ck_assert_msg(m1plen == m1_size, "decrypted text lengths differ");
ck_assert_msg(memcmp(m1prime, m1, m1_size) == 0, "decrypted texts differ");
free(m1prime);
free(c1);
free(m1);
}
static void increment_nonce_number_cmp(uint8_t *nonce, uint32_t num)
{
uint32_t num1, num2;
memcpy(&num1, nonce + (CRYPTO_NONCE_SIZE - sizeof(num1)), sizeof(num1));
num1 = net_ntohl(num1);
num2 = num + num1;
if (num2 < num1) {
for (uint16_t i = CRYPTO_NONCE_SIZE - sizeof(num1); i != 0; --i) {
++nonce[i - 1];
if (nonce[i - 1] != 0) {
break;
}
}
}
num2 = net_htonl(num2);
memcpy(nonce + (CRYPTO_NONCE_SIZE - sizeof(num2)), &num2, sizeof(num2));
}
static void test_increment_nonce(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
uint32_t i;
uint8_t n[CRYPTO_NONCE_SIZE];
for (i = 0; i < CRYPTO_NONCE_SIZE; ++i) {
n[i] = random_u08(rng);
}
uint8_t n1[CRYPTO_NONCE_SIZE];
memcpy(n1, n, CRYPTO_NONCE_SIZE);
for (i = 0; i < (1 << 18); ++i) {
increment_nonce_number_cmp(n, 1);
increment_nonce(n1);
ck_assert_msg(memcmp(n, n1, CRYPTO_NONCE_SIZE) == 0, "Bad increment_nonce function");
}
for (i = 0; i < (1 << 18); ++i) {
const uint32_t r = random_u32(rng);
increment_nonce_number_cmp(n, r);
increment_nonce_number(n1, r);
ck_assert_msg(memcmp(n, n1, CRYPTO_NONCE_SIZE) == 0, "Bad increment_nonce_number function");
}
}
static void test_memzero(void)
{
uint8_t src[sizeof(test_c)];
memcpy(src, test_c, sizeof(test_c));
crypto_memzero(src, sizeof(src));
size_t i;
for (i = 0; i < sizeof(src); i++) {
ck_assert_msg(src[i] == 0, "Memory is not zeroed");
}
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_known();
test_fast_known();
test_endtoend(); /* waiting up to 15 seconds */
test_large_data();
test_large_data_symmetric();
test_increment_nonce();
test_memzero();
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,152 @@
/**
* This autotest creates a small local DHT and makes sure that each peer can crawl
* the entire DHT using the DHT getnodes api functions.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../toxcore/tox.h"
#include "../toxcore/tox_private.h"
#include "auto_test_support.h"
#include "check_compat.h"
#define NUM_TOXES 30
typedef struct Dht_Node {
uint8_t public_key[TOX_DHT_NODE_PUBLIC_KEY_SIZE];
char ip[TOX_DHT_NODE_IP_STRING_SIZE];
uint16_t port;
} Dht_Node;
typedef struct State {
Dht_Node **nodes;
size_t num_nodes;
uint8_t **public_key_list;
} State;
static void free_nodes(Dht_Node **nodes, size_t num_nodes)
{
for (size_t i = 0; i < num_nodes; ++i) {
free(nodes[i]);
}
free(nodes);
}
static bool node_crawled(Dht_Node **nodes, size_t num_nodes, const uint8_t *public_key)
{
for (size_t i = 0; i < num_nodes; ++i) {
if (memcmp(nodes[i]->public_key, public_key, TOX_DHT_NODE_PUBLIC_KEY_SIZE) == 0) {
return true;
}
}
return false;
}
static bool all_nodes_crawled(const AutoTox *autotoxes, uint32_t num_toxes, uint8_t **public_key_list)
{
for (uint32_t i = 0; i < num_toxes; ++i) {
const State *state = (const State *)autotoxes[i].state;
// make sure each peer has crawled the correct number of nodes
if (state->num_nodes < num_toxes) {
return false;
}
}
for (uint32_t i = 0; i < num_toxes; ++i) {
const State *state = (const State *)autotoxes[i].state;
// make sure each peer has the full list of public keys
for (uint32_t j = 0; j < num_toxes; ++j) {
if (!node_crawled(state->nodes, state->num_nodes, public_key_list[j])) {
return false;
}
}
}
return true;
}
static void getnodes_response_cb(Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port, void *user_data)
{
ck_assert(user_data != nullptr);
AutoTox *autotoxes = (AutoTox *)user_data;
State *state = (State *)autotoxes->state;
if (node_crawled(state->nodes, state->num_nodes, public_key)) {
return;
}
ck_assert(state->num_nodes < NUM_TOXES);
Dht_Node *node = (Dht_Node *)calloc(1, sizeof(Dht_Node));
ck_assert(node != nullptr);
memcpy(node->public_key, public_key, TOX_DHT_NODE_PUBLIC_KEY_SIZE);
snprintf(node->ip, sizeof(node->ip), "%s", ip);
node->port = port;
state->nodes[state->num_nodes] = node;
++state->num_nodes;
// ask new node to give us their close nodes to every public key
for (size_t i = 0; i < NUM_TOXES; ++i) {
tox_dht_get_nodes(tox, public_key, ip, port, state->public_key_list[i], nullptr);
}
}
static void test_dht_getnodes(AutoTox *autotoxes)
{
ck_assert(NUM_TOXES >= 2);
uint8_t **public_key_list = (uint8_t **)calloc(NUM_TOXES, sizeof(uint8_t *));
ck_assert(public_key_list != nullptr);
for (size_t i = 0; i < NUM_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
state->nodes = (Dht_Node **)calloc(NUM_TOXES, sizeof(Dht_Node *));
ck_assert(state->nodes != nullptr);
state->num_nodes = 0;
state->public_key_list = public_key_list;
public_key_list[i] = (uint8_t *)malloc(sizeof(uint8_t) * TOX_PUBLIC_KEY_SIZE);
ck_assert(public_key_list[i] != nullptr);
tox_self_get_dht_id(autotoxes[i].tox, public_key_list[i]);
tox_callback_dht_get_nodes_response(autotoxes[i].tox, getnodes_response_cb);
}
while (!all_nodes_crawled(autotoxes, NUM_TOXES, public_key_list)) {
iterate_all_wait(autotoxes, NUM_TOXES, ITERATION_INTERVAL);
}
for (size_t i = 0; i < NUM_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
free_nodes(state->nodes, state->num_nodes);
free(public_key_list[i]);
}
free(public_key_list);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, NUM_TOXES, test_dht_getnodes, sizeof(State), &options);
return 0;
}
#undef NUM_TOXES

View File

@ -0,0 +1,240 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifndef VANILLA_NACL
#include <sodium.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/tox.h"
#include "../toxencryptsave/toxencryptsave.h"
#include "auto_test_support.h"
#include "check_compat.h"
static unsigned char test_salt[TOX_PASS_SALT_LENGTH] = {0xB1, 0xC2, 0x09, 0xEE, 0x50, 0x6C, 0xF0, 0x20, 0xC4, 0xD6, 0xEB, 0xC0, 0x44, 0x51, 0x3B, 0x60, 0x4B, 0x39, 0x4A, 0xCF, 0x09, 0x53, 0x4F, 0xEA, 0x08, 0x41, 0xFA, 0xCA, 0x66, 0xD2, 0x68, 0x7F};
static unsigned char known_key[TOX_PASS_KEY_LENGTH] = {0x29, 0x36, 0x1c, 0x9e, 0x65, 0xbb, 0x46, 0x8b, 0xde, 0xa1, 0xac, 0xf, 0xd5, 0x11, 0x81, 0xc8, 0x29, 0x28, 0x17, 0x23, 0xa6, 0xc3, 0x6b, 0x77, 0x2e, 0xd7, 0xd3, 0x10, 0xeb, 0xd2, 0xf7, 0xc8};
static const char *pw = "hunter2";
static unsigned int pwlen = 7;
static unsigned char known_key2[CRYPTO_SHARED_KEY_SIZE] = {0x7a, 0xfa, 0x95, 0x45, 0x36, 0x8a, 0xa2, 0x5c, 0x40, 0xfd, 0xc0, 0xe2, 0x35, 0x8, 0x7, 0x88, 0xfa, 0xf9, 0x37, 0x86, 0xeb, 0xff, 0x50, 0x4f, 0x3, 0xe2, 0xf6, 0xd9, 0xef, 0x9, 0x17, 0x1};
// same as above, except standard opslimit instead of extra ops limit for test_known_kdf, and hash pw before kdf for compat
/* cause I'm shameless */
static void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536) {
return;
}
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_friend_add_norequest(m, public_key, nullptr);
}
}
static void test_known_kdf(void)
{
unsigned char out[CRYPTO_SHARED_KEY_SIZE];
int16_t res = crypto_pwhash_scryptsalsa208sha256(out,
CRYPTO_SHARED_KEY_SIZE,
pw,
pwlen,
test_salt,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 8,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE);
ck_assert_msg(res != -1, "crypto function failed");
ck_assert_msg(memcmp(out, known_key, CRYPTO_SHARED_KEY_SIZE) == 0, "derived key is wrong");
}
static void test_save_friend(void)
{
Tox *tox1 = tox_new_log(nullptr, nullptr, nullptr);
Tox *tox2 = tox_new_log(nullptr, nullptr, nullptr);
ck_assert_msg(tox1 || tox2, "Failed to create 2 tox instances");
tox_callback_friend_request(tox2, accept_friend_request);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
uint32_t test = tox_friend_add(tox1, address, (const uint8_t *)"Gentoo", 7, nullptr);
ck_assert_msg(test != UINT32_MAX, "Failed to add friend");
size_t size = tox_get_savedata_size(tox1);
uint8_t *data = (uint8_t *)malloc(size);
ck_assert(data != nullptr);
tox_get_savedata(tox1, data);
size_t size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t *enc_data = (uint8_t *)malloc(size2);
ck_assert(enc_data != nullptr);
Tox_Err_Encryption error1;
bool ret = tox_pass_encrypt(data, size, (const uint8_t *)"correcthorsebatterystaple", 25, enc_data, &error1);
ck_assert_msg(ret, "failed to encrypted save: %d", error1);
ck_assert_msg(tox_is_data_encrypted(enc_data), "magic number missing");
struct Tox_Options *options = tox_options_new(nullptr);
ck_assert(options != nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, enc_data, size2);
Tox_Err_New err2;
Tox *tox3 = tox_new_log(options, &err2, nullptr);
ck_assert_msg(err2 == TOX_ERR_NEW_LOAD_ENCRYPTED, "wrong error! %d. should fail with %d", err2,
TOX_ERR_NEW_LOAD_ENCRYPTED);
ck_assert_msg(tox3 == nullptr, "tox_new with error should return NULL");
uint8_t *dec_data = (uint8_t *)malloc(size);
ck_assert(dec_data != nullptr);
Tox_Err_Decryption err3;
ret = tox_pass_decrypt(enc_data, size2, (const uint8_t *)"correcthorsebatterystaple", 25, dec_data, &err3);
ck_assert_msg(ret, "failed to decrypt save: %d", err3);
tox_options_set_savedata_data(options, dec_data, size);
tox3 = tox_new_log(options, &err2, nullptr);
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to load from decrypted data: %d", err2);
uint8_t address2[TOX_PUBLIC_KEY_SIZE];
ret = tox_friend_get_public_key(tox3, 0, address2, nullptr);
ck_assert_msg(ret, "no friends!");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match!");
size = tox_get_savedata_size(tox3);
uint8_t *data2 = (uint8_t *)malloc(size);
ck_assert(data2 != nullptr);
tox_get_savedata(tox3, data2);
Tox_Err_Key_Derivation keyerr;
Tox_Pass_Key *key = tox_pass_key_derive((const uint8_t *)"123qweasdzxc", 12, &keyerr);
ck_assert_msg(key != nullptr, "pass key allocation failure");
memcpy((uint8_t *)key, test_salt, TOX_PASS_SALT_LENGTH);
memcpy((uint8_t *)key + TOX_PASS_SALT_LENGTH, known_key2, TOX_PASS_KEY_LENGTH);
size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t *encdata2 = (uint8_t *)malloc(size2);
ck_assert(encdata2 != nullptr);
ret = tox_pass_key_encrypt(key, data2, size, encdata2, &error1);
ck_assert_msg(ret, "failed to key encrypt %d", error1);
ck_assert_msg(tox_is_data_encrypted(encdata2), "magic number the second missing");
uint8_t *out1 = (uint8_t *)malloc(size);
ck_assert(out1 != nullptr);
uint8_t *out2 = (uint8_t *)malloc(size);
ck_assert(out2 != nullptr);
ret = tox_pass_decrypt(encdata2, size2, (const uint8_t *)pw, pwlen, out1, &err3);
ck_assert_msg(ret, "failed to pw decrypt %d", err3);
ret = tox_pass_key_decrypt(key, encdata2, size2, out2, &err3);
ck_assert_msg(ret, "failed to key decrypt %d", err3);
ck_assert_msg(memcmp(out1, out2, size) == 0, "differing output data");
// and now with the code in use (I only bothered with manually to debug this, and it seems a waste
// to remove the manual check now that it's there)
tox_options_set_savedata_data(options, out1, size);
Tox *tox4 = tox_new_log(options, &err2, nullptr);
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to new the third");
uint8_t address5[TOX_PUBLIC_KEY_SIZE];
ret = tox_friend_get_public_key(tox4, 0, address5, nullptr);
ck_assert_msg(ret, "no friends! the third");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match! the third");
tox_pass_key_free(key);
tox_options_free(options);
tox_kill(tox1);
tox_kill(tox2);
tox_kill(tox3);
tox_kill(tox4);
free(out2);
free(out1);
free(encdata2);
free(data2);
free(dec_data);
free(enc_data);
free(data);
}
static void test_keys(void)
{
Tox_Err_Encryption encerr;
Tox_Err_Decryption decerr;
Tox_Err_Key_Derivation keyerr;
const uint8_t *key_char = (const uint8_t *)"123qweasdzxc";
Tox_Pass_Key *key = tox_pass_key_derive(key_char, 12, &keyerr);
ck_assert_msg(key != nullptr, "generic failure 1: %d", keyerr);
const uint8_t *string = (const uint8_t *)"No Patrick, mayonnaise is not an instrument."; // 44
uint8_t encrypted[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
bool ret = tox_pass_key_encrypt(key, string, 44, encrypted, &encerr);
ck_assert_msg(ret, "generic failure 2: %d", encerr);
// Testing how tox handles encryption of large messages.
int size_large = 30 * 1024 * 1024;
int ciphertext_length2a = size_large + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
int plaintext_length2a = size_large;
uint8_t *encrypted2a = (uint8_t *)malloc(ciphertext_length2a);
ck_assert(encrypted2a != nullptr);
uint8_t *in_plaintext2a = (uint8_t *)malloc(plaintext_length2a);
ck_assert(in_plaintext2a != nullptr);
const Random *rng = system_random();
ck_assert(rng != nullptr);
random_bytes(rng, in_plaintext2a, plaintext_length2a);
ret = tox_pass_encrypt(in_plaintext2a, plaintext_length2a, key_char, 12, encrypted2a, &encerr);
ck_assert_msg(ret, "tox_pass_encrypt failure 2a: %d", encerr);
// Decryption of same message.
uint8_t *out_plaintext2a = (uint8_t *)malloc(plaintext_length2a);
ck_assert(out_plaintext2a != nullptr);
ret = tox_pass_decrypt(encrypted2a, ciphertext_length2a, key_char, 12, out_plaintext2a, &decerr);
ck_assert_msg(ret, "tox_pass_decrypt failure 2a: %d", decerr);
ck_assert_msg(memcmp(in_plaintext2a, out_plaintext2a, plaintext_length2a) == 0, "Large message decryption failed");
free(encrypted2a);
free(in_plaintext2a);
free(out_plaintext2a);
uint8_t encrypted2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
ret = tox_pass_encrypt(string, 44, key_char, 12, encrypted2, &encerr);
ck_assert_msg(ret, "generic failure 3: %d", encerr);
uint8_t out1[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
uint8_t out2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
ret = tox_pass_key_decrypt(key, encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, out1, &decerr);
ck_assert_msg(ret, "generic failure 4: %d", decerr);
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 1 failed");
ret = tox_pass_decrypt(encrypted2, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, (const uint8_t *)"123qweasdzxc", 12, out2,
&decerr);
ck_assert_msg(ret, "generic failure 5: %d", decerr);
ck_assert_msg(memcmp(out2, string, 44) == 0, "decryption 2 failed");
ret = tox_pass_decrypt(encrypted2, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, nullptr, 0, out2, &decerr);
ck_assert_msg(!ret, "Decrypt succeeded with wrong pass");
ck_assert_msg(decerr != TOX_ERR_DECRYPTION_FAILED, "Bad error code %d", decerr);
// test that pass_decrypt can decrypt things from pass_key_encrypt
ret = tox_pass_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, (const uint8_t *)"123qweasdzxc", 12, out1,
&decerr);
ck_assert_msg(ret, "generic failure 6: %d", decerr);
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 3 failed");
uint8_t salt[TOX_PASS_SALT_LENGTH];
Tox_Err_Get_Salt salt_err;
ck_assert_msg(tox_get_salt(encrypted, salt, &salt_err), "couldn't get salt");
ck_assert_msg(salt_err == TOX_ERR_GET_SALT_OK, "get_salt returned an error");
Tox_Pass_Key *key2 = tox_pass_key_derive_with_salt((const uint8_t *)"123qweasdzxc", 12, salt, &keyerr);
ck_assert_msg(key2 != nullptr, "generic failure 7: %d", keyerr);
ck_assert_msg(0 == memcmp(key, key2, TOX_PASS_KEY_LENGTH + TOX_PASS_SALT_LENGTH), "salt comparison failed");
tox_pass_key_free(key2);
tox_pass_key_free(key);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_known_kdf();
test_save_friend();
test_keys();
return 0;
}
#else // VANILLA_NACL
int main(void)
{
return 0;
}
#endif

View File

@ -0,0 +1,121 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2016 Tox project.
*/
/*
* Small test for checking if obtaining savedata, saving it to disk and using
* works correctly.
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "auto_test_support.h"
#include "check_compat.h"
#include "../toxencryptsave/toxencryptsave.h"
static const char *pphrase = "bar";
static const char *name = "foo";
static const char *savefile = "./save";
static void save_data_encrypted(void)
{
struct Tox_Options *options = tox_options_new(nullptr);
Tox *t = tox_new_log(options, nullptr, nullptr);
tox_options_free(options);
tox_self_set_name(t, (const uint8_t *)name, strlen(name), nullptr);
FILE *f = fopen(savefile, "wb");
size_t size = tox_get_savedata_size(t);
uint8_t *clear = (uint8_t *)malloc(size);
/*this function does not write any data at all*/
tox_get_savedata(t, clear);
size += TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t *cipher = (uint8_t *)malloc(size);
Tox_Err_Encryption eerr;
ck_assert_msg(tox_pass_encrypt(clear, size - TOX_PASS_ENCRYPTION_EXTRA_LENGTH, (const uint8_t *)pphrase,
strlen(pphrase), cipher,
&eerr), "Could not encrypt, error code %d.", eerr);
size_t written_value = fwrite(cipher, sizeof(*cipher), size, f);
printf("written written_value = %u of %u\n", (unsigned)written_value, (unsigned)size);
free(cipher);
free(clear);
fclose(f);
tox_kill(t);
}
static void load_data_decrypted(void)
{
FILE *f = fopen(savefile, "rb");
ck_assert(f != nullptr);
fseek(f, 0, SEEK_END);
int64_t size = ftell(f);
fseek(f, 0, SEEK_SET);
ck_assert_msg(0 <= size && size <= UINT_MAX, "file size out of range");
uint8_t *cipher = (uint8_t *)malloc(size);
ck_assert(cipher != nullptr);
uint8_t *clear = (uint8_t *)malloc(size - TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
ck_assert(clear != nullptr);
size_t read_value = fread(cipher, sizeof(*cipher), size, f);
printf("Read read_value = %u of %u\n", (unsigned)read_value, (unsigned)size);
Tox_Err_Decryption derr;
ck_assert_msg(tox_pass_decrypt(cipher, size, (const uint8_t *)pphrase, strlen(pphrase), clear, &derr),
"Could not decrypt, error code %d.", derr);
struct Tox_Options *options = tox_options_new(nullptr);
ck_assert(options != nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, clear, size);
Tox_Err_New err;
Tox *t = tox_new_log(options, &err, nullptr);
tox_options_free(options);
ck_assert_msg(t != nullptr, "tox_new returned the error value %d", err);
uint8_t *readname = (uint8_t *)malloc(tox_self_get_name_size(t));
ck_assert(readname != nullptr);
tox_self_get_name(t, readname);
ck_assert_msg(memcmp(readname, name, tox_self_get_name_size(t)) == 0,
"name returned by tox_self_get_name does not match expected result");
tox_kill(t);
free(clear);
free(cipher);
free(readname);
fclose(f);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
save_data_encrypted();
load_data_decrypted();
ck_assert_msg(remove(savefile) == 0, "Could not remove the savefile.");
return 0;
}

View File

@ -0,0 +1,367 @@
/* File transfer test.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
/* The Travis-CI container responds poorly to ::1 as a localhost address
* You're encouraged to -D FORCE_TESTS_IPV6 on a local test */
#ifdef FORCE_TESTS_IPV6
#define TOX_LOCALHOST "::1"
#else
#define TOX_LOCALHOST "127.0.0.1"
#endif
static void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_friend_add_norequest(m, public_key, nullptr);
}
}
static uint64_t size_recv;
static uint64_t sending_pos;
static uint8_t file_cmp_id[TOX_FILE_ID_LENGTH];
static uint32_t file_accepted;
static uint64_t file_size;
static void tox_file_receive(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind, uint64_t filesize,
const uint8_t *filename, size_t filename_length, void *userdata)
{
ck_assert_msg(kind == TOX_FILE_KIND_DATA, "bad kind");
ck_assert_msg(filename_length == sizeof("Gentoo.exe")
&& memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0, "bad filename");
uint8_t file_id[TOX_FILE_ID_LENGTH];
ck_assert_msg(tox_file_get_file_id(tox, friend_number, file_number, file_id, nullptr), "tox_file_get_file_id error");
ck_assert_msg(memcmp(file_id, file_cmp_id, TOX_FILE_ID_LENGTH) == 0, "bad file_id");
uint8_t empty[TOX_FILE_ID_LENGTH] = {0};
ck_assert_msg(memcmp(empty, file_cmp_id, TOX_FILE_ID_LENGTH) != 0, "empty file_id");
file_size = filesize;
if (filesize) {
sending_pos = size_recv = 1337;
Tox_Err_File_Seek err_s;
ck_assert_msg(tox_file_seek(tox, friend_number, file_number, 1337, &err_s), "tox_file_seek error");
ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_OK, "tox_file_seek wrong error");
} else {
sending_pos = size_recv = 0;
}
Tox_Err_File_Control error;
ck_assert_msg(tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, &error),
"tox_file_control failed. %i", error);
++file_accepted;
Tox_Err_File_Seek err_s;
ck_assert_msg(!tox_file_seek(tox, friend_number, file_number, 1234, &err_s), "tox_file_seek no error");
ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_DENIED, "tox_file_seek wrong error");
}
static uint32_t sendf_ok;
static void file_print_control(Tox *tox, uint32_t friend_number, uint32_t file_number, Tox_File_Control control,
void *userdata)
{
/* First send file num is 0.*/
if (file_number == 0 && control == TOX_FILE_CONTROL_RESUME) {
sendf_ok = 1;
}
}
static uint64_t max_sending;
static bool m_send_reached;
static uint8_t sending_num;
static bool file_sending_done;
static void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
size_t length, void *user_data)
{
ck_assert_msg(sendf_ok, "didn't get resume control");
ck_assert_msg(sending_pos == position, "bad position %lu", (unsigned long)position);
if (length == 0) {
ck_assert_msg(!file_sending_done, "file sending already done");
file_sending_done = 1;
return;
}
if (position + length > max_sending) {
ck_assert_msg(!m_send_reached, "requested done file transfer");
length = max_sending - position;
m_send_reached = 1;
}
VLA(uint8_t, f_data, length);
memset(f_data, sending_num, length);
Tox_Err_File_Send_Chunk error;
tox_file_send_chunk(tox, friend_number, file_number, position, f_data, length, &error);
ck_assert_msg(error == TOX_ERR_FILE_SEND_CHUNK_OK,
"could not send chunk, error num=%d pos=%d len=%d", (int)error, (int)position, (int)length);
++sending_num;
sending_pos += length;
}
static uint8_t num;
static bool file_recv;
static void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *user_data)
{
ck_assert_msg(size_recv == position, "bad position");
if (length == 0) {
file_recv = 1;
return;
}
VLA(uint8_t, f_data, length);
memset(f_data, num, length);
++num;
ck_assert_msg(memcmp(f_data, data, length) == 0, "FILE_CORRUPTED");
size_recv += length;
}
static void file_transfer_test(void)
{
printf("Starting test: few_clients\n");
uint32_t index[] = { 1, 2, 3 };
long long unsigned int cur_time = time(nullptr);
Tox_Err_New t_n_error;
Tox *tox1 = tox_new_log(nullptr, &t_n_error, &index[0]);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
Tox *tox2 = tox_new_log(nullptr, &t_n_error, &index[1]);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
Tox *tox3 = tox_new_log(nullptr, &t_n_error, &index[2]);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
ck_assert_msg(tox1 && tox2 && tox3, "Failed to create 3 tox instances");
tox_callback_friend_request(tox2, accept_friend_request);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
uint32_t test = tox_friend_add(tox3, address, (const uint8_t *)"Gentoo", 7, nullptr);
ck_assert_msg(test == 0, "Failed to add friend error code: %u", test);
uint8_t dhtKey[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dhtKey);
uint16_t dhtPort = tox_self_get_udp_port(tox1, nullptr);
tox_bootstrap(tox2, TOX_LOCALHOST, dhtPort, dhtKey, nullptr);
tox_bootstrap(tox3, TOX_LOCALHOST, dhtPort, dhtKey, nullptr);
printf("Waiting for toxes to come online\n");
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
printf("Connections: self (%d, %d, %d), friends (%d, %d)\n",
tox_self_get_connection_status(tox1),
tox_self_get_connection_status(tox2),
tox_self_get_connection_status(tox3),
tox_friend_get_connection_status(tox2, 0, nullptr),
tox_friend_get_connection_status(tox3, 0, nullptr));
c_sleep(ITERATION_INTERVAL);
} while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(tox3) == TOX_CONNECTION_NONE ||
tox_friend_get_connection_status(tox2, 0, nullptr) == TOX_CONNECTION_NONE ||
tox_friend_get_connection_status(tox3, 0, nullptr) == TOX_CONNECTION_NONE);
printf("Starting file transfer test: 100MiB file.\n");
file_accepted = file_size = sendf_ok = size_recv = 0;
file_recv = 0;
max_sending = UINT64_MAX;
uint64_t f_time = time(nullptr);
tox_callback_file_recv_chunk(tox3, write_file);
tox_callback_file_recv_control(tox2, file_print_control);
tox_callback_file_chunk_request(tox2, tox_file_chunk_request);
tox_callback_file_recv_control(tox3, file_print_control);
tox_callback_file_recv(tox3, tox_file_receive);
uint64_t totalf_size = 100 * 1024 * 1024;
uint32_t fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, nullptr, (const uint8_t *)"Gentoo.exe",
sizeof("Gentoo.exe"), nullptr);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
Tox_Err_File_Get gfierr;
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
const size_t max_iterations = INT16_MAX;
for (size_t i = 0; i < max_iterations; i++) {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
if (file_sending_done) {
ck_assert_msg(sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size && sending_pos == size_recv
&& file_accepted == 1,
"Something went wrong in file transfer %u %u %u %u %u %u %lu %lu %lu",
sendf_ok, file_recv, totalf_size == file_size, size_recv == file_size, sending_pos == size_recv,
file_accepted == 1, (unsigned long)totalf_size, (unsigned long)size_recv,
(unsigned long)sending_pos);
break;
}
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);
uint32_t tox3_interval = tox_iteration_interval(tox3);
if ((i + 1) % 500 == 0) {
printf("after %u iterations: %.2fMiB done\n", (unsigned int)i + 1, (double)size_recv / 1024 / 1024);
}
c_sleep(min_u32(tox1_interval, min_u32(tox2_interval, tox3_interval)));
}
ck_assert_msg(file_sending_done, "file sending did not complete after %u iterations: sendf_ok:%u file_recv:%u "
"totalf_size==file_size:%u size_recv==file_size:%u sending_pos==size_recv:%u file_accepted:%u "
"totalf_size:%lu size_recv:%lu sending_pos:%lu",
(unsigned int)max_iterations, sendf_ok, file_recv,
totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1,
(unsigned long)totalf_size, (unsigned long)size_recv,
(unsigned long)sending_pos);
printf("100MiB file sent in %lu seconds\n", (unsigned long)(time(nullptr) - f_time));
printf("Starting file streaming transfer test.\n");
file_sending_done = 0;
file_accepted = 0;
file_size = 0;
sendf_ok = 0;
size_recv = 0;
file_recv = 0;
tox_callback_file_recv_chunk(tox3, write_file);
tox_callback_file_recv_control(tox2, file_print_control);
tox_callback_file_chunk_request(tox2, tox_file_chunk_request);
tox_callback_file_recv_control(tox3, file_print_control);
tox_callback_file_recv(tox3, tox_file_receive);
totalf_size = UINT64_MAX;
fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, nullptr,
(const uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"), nullptr);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
max_sending = 100 * 1024;
m_send_reached = 0;
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);
uint32_t tox3_interval = tox_iteration_interval(tox3);
c_sleep(min_u32(tox1_interval, min_u32(tox2_interval, tox3_interval)));
} while (!file_sending_done);
ck_assert_msg(sendf_ok && file_recv && m_send_reached && totalf_size == file_size && size_recv == max_sending
&& sending_pos == size_recv && file_accepted == 1,
"something went wrong in file transfer %u %u %u %u %u %u %u %lu %lu %lu %lu", sendf_ok, file_recv,
m_send_reached, totalf_size == file_size, size_recv == max_sending, sending_pos == size_recv, file_accepted == 1,
(unsigned long)totalf_size, (unsigned long)file_size,
(unsigned long)size_recv, (unsigned long)sending_pos);
printf("starting file 0 transfer test.\n");
file_sending_done = 0;
file_accepted = 0;
file_size = 0;
sendf_ok = 0;
size_recv = 0;
file_recv = 0;
tox_callback_file_recv_chunk(tox3, write_file);
tox_callback_file_recv_control(tox2, file_print_control);
tox_callback_file_chunk_request(tox2, tox_file_chunk_request);
tox_callback_file_recv_control(tox3, file_print_control);
tox_callback_file_recv(tox3, tox_file_receive);
totalf_size = 0;
fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, nullptr,
(const uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"), nullptr);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
do {
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);
uint32_t tox3_interval = tox_iteration_interval(tox3);
c_sleep(min_u32(tox1_interval, min_u32(tox2_interval, tox3_interval)));
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
} while (!file_sending_done);
ck_assert_msg(sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size
&& sending_pos == size_recv && file_accepted == 1,
"something went wrong in file transfer %u %u %u %u %u %u %llu %llu %llu", sendf_ok, file_recv,
totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1,
(unsigned long long)totalf_size, (unsigned long long)size_recv,
(unsigned long long)sending_pos);
printf("file_transfer_test succeeded, took %llu seconds\n", time(nullptr) - cur_time);
tox_kill(tox1);
tox_kill(tox2);
tox_kill(tox3);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
file_transfer_test();
return 0;
}

View File

@ -0,0 +1,334 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../toxcore/tox.h"
#include "../toxcore/announce.h"
#include "../testing/misc_tools.h"
#include "../toxcore/mono_time.h"
#include "../toxcore/forwarding.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
#ifndef USE_IPV6
#define USE_IPV6 1
#endif
static inline IP get_loopback(void)
{
IP ip;
#if USE_IPV6
ip.family = net_family_ipv6();
ip.ip.v6 = get_ip6_loopback();
#else
ip.family = net_family_ipv4();
ip.ip.v4 = get_ip4_loopback();
#endif
return ip;
}
#define NUM_FORWARDER 20
#define NUM_FORWARDER_TCP 5
#define NUM_FORWARDER_DHT (NUM_FORWARDER - NUM_FORWARDER_TCP)
#define NUM_FORWARDING_ITERATIONS 1
#define FORWARD_SEND_INTERVAL 2
#define FORWARDING_BASE_PORT 36571
typedef struct Test_Data {
Networking_Core *net;
uint32_t send_back;
uint64_t sent;
bool returned;
} Test_Data;
static void test_forwarded_request_cb(void *object, const IP_Port *forwarder,
const uint8_t *sendback, uint16_t sendback_length,
const uint8_t *data, uint16_t length, void *userdata)
{
Test_Data *test_data = (Test_Data *)object;
uint8_t *index = (uint8_t *)userdata;
if (length != 12 || memcmp("hello: ", data, 8) != 0) {
printf("[%u] got unexpected data of length %d\n", *index, length);
return;
}
uint8_t reply[12];
memcpy(reply, "reply: ", 8);
memcpy(reply + 8, data + 8, 4);
ck_assert_msg(forward_reply(test_data->net, forwarder, sendback, sendback_length, reply, 12),
"[%u] forward_reply failed", *index);
}
static void test_forwarded_response_cb(void *object,
const uint8_t *data, uint16_t length, void *userdata)
{
Test_Data *test_data = (Test_Data *)object;
uint8_t *index = (uint8_t *)userdata;
if (length != 12 || memcmp("reply: ", data, 8) != 0) {
printf("[%u] got unexpected data of length %d\n", *index, length);
return;
}
uint32_t send_back;
net_unpack_u32(data + 8, &send_back);
if (test_data->send_back == send_back) {
test_data->returned = true;
}
}
static bool all_returned(Test_Data *test_data)
{
for (uint32_t i = 0; i < NUM_FORWARDER; ++i) {
if (!test_data[i].returned) {
return false;
}
}
return true;
}
typedef struct Forwarding_Subtox {
Logger *log;
Mono_Time *mono_time;
Networking_Core *net;
DHT *dht;
Net_Crypto *c;
Forwarding *forwarding;
Announcements *announce;
} Forwarding_Subtox;
static Forwarding_Subtox *new_forwarding_subtox(bool no_udp, uint32_t *index, uint16_t port)
{
Forwarding_Subtox *subtox = (Forwarding_Subtox *)calloc(1, sizeof(Forwarding_Subtox));
ck_assert(subtox != nullptr);
subtox->log = logger_new();
ck_assert(subtox->log != nullptr);
logger_callback_log(subtox->log, print_debug_logger, nullptr, index);
subtox->mono_time = mono_time_new(nullptr, nullptr);
const Random *rng= system_random();
ck_assert(rng != nullptr);
const Network *ns = system_network();
ck_assert(ns != nullptr);
if (no_udp) {
subtox->net = new_networking_no_udp(subtox->log, ns);
} else {
const IP ip = get_loopback();
subtox->net = new_networking_ex(subtox->log, ns, &ip, port, port, nullptr);
}
subtox->dht = new_dht(subtox->log, rng, ns, subtox->mono_time, subtox->net, true, true);
const TCP_Proxy_Info inf = {{{{0}}}};
subtox->c = new_net_crypto(subtox->log, rng, ns, subtox->mono_time, subtox->dht, &inf);
subtox->forwarding = new_forwarding(subtox->log, rng, subtox->mono_time, subtox->dht);
ck_assert(subtox->forwarding != nullptr);
subtox->announce = new_announcements(subtox->log, rng, subtox->mono_time, subtox->forwarding);
ck_assert(subtox->announce != nullptr);
return subtox;
}
static void kill_forwarding_subtox(Forwarding_Subtox *subtox)
{
kill_announcements(subtox->announce);
kill_forwarding(subtox->forwarding);
kill_net_crypto(subtox->c);
kill_dht(subtox->dht);
kill_networking(subtox->net);
mono_time_free(subtox->mono_time);
logger_kill(subtox->log);
free(subtox);
}
static void test_forwarding(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
const Network *ns = system_network();
ck_assert(ns != nullptr);
uint32_t index[NUM_FORWARDER];
Forwarding_Subtox *subtoxes[NUM_FORWARDER];
Test_Data test_data[NUM_FORWARDER];
const IP ip = get_loopback();
for (uint32_t i = 0; i < NUM_FORWARDER; ++i) {
index[i] = i + 1;
subtoxes[i] = new_forwarding_subtox(i < NUM_FORWARDER_TCP, &index[i], FORWARDING_BASE_PORT + i);
test_data[i].net = subtoxes[i]->net;
test_data[i].send_back = 0;
test_data[i].sent = 0;
test_data[i].returned = false;
set_callback_forwarded_request(subtoxes[i]->forwarding, test_forwarded_request_cb, &test_data[i]);
set_callback_forwarded_response(subtoxes[i]->forwarding, test_forwarded_response_cb, &test_data[i]);
set_forwarding_packet_tcp_connection_callback(nc_get_tcp_c(subtoxes[i]->c), test_forwarded_response_cb, &test_data[i]);
}
printf("testing forwarding via tcp relays and dht\n");
struct Tox_Options *opts = tox_options_new(nullptr);
uint16_t forwarder_tcp_relay_port = 36570;
Tox *relay = nullptr;
// Try a few different ports.
for (uint8_t i = 0; i < 100; ++i) {
tox_options_set_tcp_port(opts, forwarder_tcp_relay_port);
relay = tox_new_log(opts, nullptr, nullptr);
if (relay != nullptr) {
break;
}
++forwarder_tcp_relay_port;
}
tox_options_free(opts);
ck_assert_msg(relay != nullptr, "Failed to create TCP relay");
IP_Port relay_ipport_tcp = {ip, net_htons(forwarder_tcp_relay_port)};
uint8_t dpk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(relay, dpk);
printf("1-%d connected only to TCP server; %d-%d connected only to DHT\n",
NUM_FORWARDER_TCP, NUM_FORWARDER_TCP + 1, NUM_FORWARDER);
for (uint32_t i = 0; i < NUM_FORWARDER_TCP; ++i) {
set_tcp_onion_status(nc_get_tcp_c(subtoxes[i]->c), 1);
ck_assert_msg(add_tcp_relay(subtoxes[i]->c, &relay_ipport_tcp, dpk) == 0,
"Failed to add TCP relay");
}
IP_Port relay_ipport_udp = {ip, net_htons(tox_self_get_udp_port(relay, nullptr))};
for (uint32_t i = NUM_FORWARDER_TCP; i < NUM_FORWARDER; ++i) {
dht_bootstrap(subtoxes[i]->dht, &relay_ipport_udp, dpk);
}
printf("allowing DHT to populate\n");
uint16_t dht_establish_iterations = NUM_FORWARDER * 5;
for (uint32_t n = 0; n < NUM_FORWARDING_ITERATIONS; ++n) {
for (uint32_t i = 0; i < NUM_FORWARDER; ++i) {
test_data[i].sent = 0;
test_data[i].returned = false;
}
do {
for (uint32_t i = 0; i < NUM_FORWARDER; ++i) {
Forwarding_Subtox *const subtox = subtoxes[i];
mono_time_update(subtox->mono_time);
networking_poll(subtox->net, &index[i]);
do_net_crypto(subtox->c, &index[i]);
do_dht(subtox->dht);
if (dht_establish_iterations ||
test_data[i].returned ||
!mono_time_is_timeout(subtox->mono_time, test_data[i].sent, FORWARD_SEND_INTERVAL)) {
continue;
}
printf("%u", i + 1);
if (i < NUM_FORWARDER_TCP) {
printf(" --> TCPRelay");
}
const uint16_t chain_length = i < NUM_FORWARDER_TCP ? i % 5 : i % 4 + 1;
uint8_t chain_keys[4 * CRYPTO_PUBLIC_KEY_SIZE];
uint32_t chain_i = NUM_FORWARDER_TCP + (random_u32(rng) % NUM_FORWARDER_DHT);
const IP_Port first_ipp = {ip, net_htons(FORWARDING_BASE_PORT + chain_i)};
printf(" --> %u", chain_i + 1);
for (uint16_t j = 0; j < chain_length; ++j) {
// pick random different dht node:
chain_i += 1 + random_u32(rng) % (NUM_FORWARDER_DHT - 1);
chain_i = NUM_FORWARDER_TCP + (chain_i - NUM_FORWARDER_TCP) % NUM_FORWARDER_DHT;
const uint8_t *dest_pubkey = dht_get_self_public_key(subtoxes[chain_i]->dht);
memcpy(chain_keys + j * CRYPTO_PUBLIC_KEY_SIZE, dest_pubkey, CRYPTO_PUBLIC_KEY_SIZE);
printf(" --> %u", chain_i + 1);
}
printf("\n");
const uint16_t length = 12;
uint8_t data[12];
memcpy(data, "hello: ", 8);
test_data[i].send_back = random_u32(rng);
net_pack_u32(data + 8, test_data[i].send_back);
if (i < NUM_FORWARDER_TCP) {
IP_Port tcp_forwarder;
if (!get_random_tcp_conn_ip_port(subtox->c, &tcp_forwarder)) {
continue;
}
if (send_tcp_forward_request(subtox->log, subtox->c, &tcp_forwarder, &first_ipp,
chain_keys, chain_length, data, length) == 0) {
test_data[i].sent = mono_time_get(subtox->mono_time);
}
} else {
if (send_forward_request(subtox->net, &first_ipp,
chain_keys, chain_length, data, length)) {
test_data[i].sent = mono_time_get(subtox->mono_time);
}
}
}
tox_iterate(relay, nullptr);
if (dht_establish_iterations) {
--dht_establish_iterations;
if (!dht_establish_iterations) {
printf("making forward requests and expecting replies\n");
}
}
c_sleep(50);
} while (!all_returned(test_data));
// This doesn't really belong in this test.
// It can be removed once the full announce client test is in place.
printf("checking that nodes are marked as announce nodes\n");
Node_format nodes[MAX_SENT_NODES];
ck_assert(NUM_FORWARDER - NUM_FORWARDER_TCP > 1);
for (uint32_t i = NUM_FORWARDER_TCP; i < NUM_FORWARDER; ++i) {
ck_assert_msg(get_close_nodes(subtoxes[i]->dht, dht_get_self_public_key(subtoxes[i]->dht), nodes, net_family_unspec(), true,
true) > 0,
"node %u has no nodes marked as announce nodes", i);
}
}
for (uint32_t i = 0; i < NUM_FORWARDER; ++i) {
kill_forwarding_subtox(subtoxes[i]);
}
tox_kill(relay);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_forwarding();
return 0;
}

View File

@ -0,0 +1,26 @@
/* Tests that we can make a friend connection.
*
* This is the simplest test that brings up two toxes that can talk to each
* other. It's useful as a copy/pasteable starting point for testing other
* features.
*/
#include <stdint.h>
#include "auto_test_support.h"
static void friend_connection_test(AutoTox *toxes)
{
// Nothing to do here. When copying this test, add test-specific code here.
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, friend_connection_test, 0, &options);
return 0;
}

View File

@ -0,0 +1,71 @@
/* Tests what happens when spamming friend requests from lots of temporary toxes.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "../testing/misc_tools.h"
#include "auto_test_support.h"
#include "check_compat.h"
#define FR_MESSAGE "Gentoo"
// TODO(iphydf): Investigate friend request spam: receiving more than 32 at a time means any further
// friend requests are dropped on the floor and aren't seen again.
#define FR_TOX_COUNT 33
typedef struct State {
bool unused;
} State;
static void accept_friend_request(Tox *tox, const uint8_t *public_key, const uint8_t *data, size_t length,
void *userdata)
{
ck_assert_msg(length == sizeof(FR_MESSAGE) && memcmp(FR_MESSAGE, data, sizeof(FR_MESSAGE)) == 0,
"unexpected friend request message");
tox_friend_add_norequest(tox, public_key, nullptr);
}
static void test_friend_request(AutoTox *autotoxes)
{
const time_t con_time = time(nullptr);
printf("All toxes add tox1 as friend.\n");
tox_callback_friend_request(autotoxes[0].tox, accept_friend_request);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(autotoxes[0].tox, address);
for (uint32_t i = 2; i < FR_TOX_COUNT; ++i) {
Tox_Err_Friend_Add err;
tox_friend_add(autotoxes[i].tox, address, (const uint8_t *)FR_MESSAGE, sizeof(FR_MESSAGE), &err);
ck_assert_msg(err == TOX_ERR_FRIEND_ADD_OK, "tox %u failed to add friend error code: %d", autotoxes[i].index, err);
}
for (uint32_t t = 0; t < 100; ++t) {
if (all_friends_connected(autotoxes, FR_TOX_COUNT)) {
break;
}
iterate_all_wait(autotoxes, FR_TOX_COUNT, ITERATION_INTERVAL);
}
const size_t size = tox_self_get_friend_list_size(autotoxes[0].tox);
printf("Tox clients connected took %lu seconds; tox1 has %u friends.\n",
(unsigned long)(time(nullptr) - con_time), (unsigned int)size);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, FR_TOX_COUNT, test_friend_request, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,84 @@
/* Tests that we can add friends.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "../testing/misc_tools.h"
#include "auto_test_support.h"
#include "check_compat.h"
#define FR_MESSAGE "Gentoo"
static void accept_friend_request(Tox *tox, const uint8_t *public_key, const uint8_t *data, size_t length,
void *userdata)
{
ck_assert_msg(length == sizeof(FR_MESSAGE) && memcmp(FR_MESSAGE, data, sizeof(FR_MESSAGE)) == 0,
"unexpected friend request message");
tox_friend_add_norequest(tox, public_key, nullptr);
}
static void test_friend_request(void)
{
printf("Initialising 2 toxes.\n");
uint32_t index[] = { 1, 2 };
const time_t cur_time = time(nullptr);
Tox *const tox1 = tox_new_log(nullptr, nullptr, &index[0]);
Tox *const tox2 = tox_new_log(nullptr, nullptr, &index[1]);
ck_assert_msg(tox1 && tox2, "failed to create 2 tox instances");
printf("Bootstrapping tox2 off tox1.\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr);
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE);
printf("Toxes are online, took %lu seconds.\n", (unsigned long)(time(nullptr) - cur_time));
const time_t con_time = time(nullptr);
printf("Tox1 adds tox2 as friend, tox2 accepts.\n");
tox_callback_friend_request(tox2, accept_friend_request);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
const uint32_t test = tox_friend_add(tox1, address, (const uint8_t *)FR_MESSAGE, sizeof(FR_MESSAGE), nullptr);
ck_assert_msg(test == 0, "failed to add friend error code: %u", test);
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_friend_get_connection_status(tox1, 0, nullptr) != TOX_CONNECTION_UDP ||
tox_friend_get_connection_status(tox2, 0, nullptr) != TOX_CONNECTION_UDP);
printf("Tox clients connected took %lu seconds.\n", (unsigned long)(time(nullptr) - con_time));
printf("friend_request_test succeeded, took %lu seconds.\n", (unsigned long)(time(nullptr) - cur_time));
tox_kill(tox1);
tox_kill(tox2);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_friend_request();
return 0;
}

View File

@ -0,0 +1,437 @@
/*
* Tests that we can connect to a public group chat through the DHT and make basic queries
* about the group, other peers, and ourselves. We also make sure we can disconnect and
* reconnect to a group while retaining our credentials.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "auto_test_support.h"
typedef struct State {
size_t peer_joined_count;
size_t self_joined_count;
size_t peer_exit_count;
bool peer_nick;
bool peer_status;
uint32_t peer_id;
bool is_founder;
} State;
#define NUM_GROUP_TOXES 2
#define GROUP_NAME "NASA Headquarters"
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
#define TOPIC "Funny topic here"
#define TOPIC_LEN (sizeof(TOPIC) - 1)
#define PEER0_NICK "Lois"
#define PEER0_NICK_LEN (sizeof(PEER0_NICK) - 1)
#define PEER0_NICK2 "Terry Davis"
#define PEER0_NICK2_LEN (sizeof(PEER0_NICK2) - 1)
#define PEER1_NICK "Bran"
#define PEER1_NICK_LEN (sizeof(PEER1_NICK) - 1)
#define EXIT_MESSAGE "Goodbye world"
#define EXIT_MESSAGE_LEN (sizeof(EXIT_MESSAGE) - 1)
#define PEER_LIMIT 20
static bool all_group_peers_connected(AutoTox *autotoxes, uint32_t tox_count, uint32_t groupnumber, size_t name_length)
{
for (size_t i = 0; i < tox_count; ++i) {
// make sure we got an invite response
if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != name_length) {
return false;
}
// make sure we got a sync response
if (tox_group_get_peer_limit(autotoxes[i].tox, groupnumber, nullptr) != PEER_LIMIT) {
return false;
}
// make sure we're actually connected
if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) {
return false;
}
}
return true;
}
static void group_peer_join_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
// we do a connection test here for fun
Tox_Err_Group_Peer_Query pq_err;
TOX_CONNECTION connection_status = tox_group_peer_get_connection_status(tox, groupnumber, peer_id, &pq_err);
ck_assert(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(connection_status != TOX_CONNECTION_NONE);
Tox_Group_Role role = tox_group_peer_get_role(tox, groupnumber, peer_id, &pq_err);
ck_assert_msg(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK, "%d", pq_err);
Tox_User_Status status = tox_group_peer_get_status(tox, groupnumber, peer_id, &pq_err);
ck_assert_msg(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK, "%d", pq_err);
size_t peer_name_len = tox_group_peer_get_name_size(tox, groupnumber, peer_id, &pq_err);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
ck_assert(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
tox_group_peer_get_name(tox, groupnumber, peer_id, (uint8_t *) peer_name, &pq_err);
ck_assert(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK);
peer_name[peer_name_len] = 0;
// make sure we see the correct peer state on join
if (!state->is_founder) {
ck_assert_msg(role == TOX_GROUP_ROLE_FOUNDER, "wrong role: %d", role);
if (state->peer_joined_count == 0) {
ck_assert_msg(status == TOX_USER_STATUS_NONE, "wrong status: %d", status);
ck_assert_msg(peer_name_len == PEER0_NICK_LEN, "wrong nick: %s", peer_name);
ck_assert(memcmp(peer_name, PEER0_NICK, peer_name_len) == 0);
} else {
ck_assert_msg(status == TOX_USER_STATUS_BUSY, "wrong status: %d", status);
ck_assert(peer_name_len == PEER0_NICK2_LEN);
ck_assert(memcmp(peer_name, PEER0_NICK2, peer_name_len) == 0);
}
} else {
ck_assert_msg(role == TOX_GROUP_ROLE_USER, "wrong role: %d", role);
ck_assert(peer_name_len == PEER1_NICK_LEN);
ck_assert(memcmp(peer_name, PEER1_NICK, peer_name_len) == 0);
if (state->peer_joined_count == 0) {
ck_assert_msg(status == TOX_USER_STATUS_NONE, "wrong status: %d", status);
} else {
ck_assert_msg(status == TOX_USER_STATUS_AWAY, "wrong status: %d", status);
}
}
state->peer_id = peer_id;
++state->peer_joined_count;
}
static void group_peer_self_join_handler(Tox *tox, uint32_t groupnumber, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
// make sure we see our own correct peer state on join callback
Tox_Err_Group_Self_Query sq_err;
size_t self_length = tox_group_self_get_name_size(tox, groupnumber, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
uint8_t self_name[TOX_MAX_NAME_LENGTH];
tox_group_self_get_name(tox, groupnumber, self_name, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
TOX_USER_STATUS self_status = tox_group_self_get_status(tox, groupnumber, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
Tox_Group_Role self_role = tox_group_self_get_role(tox, groupnumber, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
if (state->is_founder) {
// founder doesn't get a self join callback on initial creation of group
ck_assert(self_length == PEER0_NICK2_LEN);
ck_assert(memcmp(self_name, PEER0_NICK2, self_length) == 0);
ck_assert(self_status == TOX_USER_STATUS_BUSY);
ck_assert(self_role == TOX_GROUP_ROLE_FOUNDER);
} else {
ck_assert(self_length == PEER1_NICK_LEN);
ck_assert(memcmp(self_name, PEER1_NICK, self_length) == 0);
ck_assert(self_role == TOX_GROUP_ROLE_USER);
ck_assert(self_status == TOX_USER_STATUS_NONE);
}
// make sure we see correct group state on join callback
uint8_t group_name[GROUP_NAME_LEN];
uint8_t topic[TOX_GROUP_MAX_TOPIC_LENGTH];
ck_assert(tox_group_get_peer_limit(tox, groupnumber, nullptr) == PEER_LIMIT);
ck_assert(tox_group_get_name_size(tox, groupnumber, nullptr) == GROUP_NAME_LEN);
ck_assert(tox_group_get_topic_size(tox, groupnumber, nullptr) == TOPIC_LEN);
Tox_Err_Group_State_Queries query_err;
tox_group_get_name(tox, groupnumber, group_name, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "%d", query_err);
ck_assert(memcmp(group_name, GROUP_NAME, GROUP_NAME_LEN) == 0);
tox_group_get_topic(tox, groupnumber, topic, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "%d", query_err);
ck_assert(memcmp(topic, TOPIC, TOPIC_LEN) == 0);
++state->self_joined_count;
}
static void group_peer_exit_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, Tox_Group_Exit_Type exit_type,
const uint8_t *name, size_t name_length, const uint8_t *part_message,
size_t length, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
++state->peer_exit_count;
// first exit is a disconnect. second is a real exit with a part message
if (state->peer_exit_count == 2) {
ck_assert(length == EXIT_MESSAGE_LEN);
ck_assert(memcmp(part_message, EXIT_MESSAGE, length) == 0);
}
}
static void group_peer_name_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *name,
size_t length, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
// note: we already test the name_get api call elsewhere
ck_assert(length == PEER0_NICK2_LEN);
ck_assert(memcmp(name, PEER0_NICK2, length) == 0);
state->peer_nick = true;
}
static void group_peer_status_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status,
void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
Tox_Err_Group_Peer_Query err;
TOX_USER_STATUS cur_status = tox_group_peer_get_status(tox, groupnumber, peer_id, &err);
ck_assert_msg(cur_status == status, "%d, %d", cur_status, status);
ck_assert(status == TOX_USER_STATUS_BUSY);
state->peer_status = true;
}
static void group_announce_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES == 2, "NUM_GROUP_TOXES needs to be 2");
Tox *tox0 = autotoxes[0].tox;
Tox *tox1 = autotoxes[1].tox;
State *state0 = (State *)autotoxes[0].state;
State *state1 = (State *)autotoxes[1].state;
tox_callback_group_peer_join(tox0, group_peer_join_handler);
tox_callback_group_peer_join(tox1, group_peer_join_handler);
tox_callback_group_self_join(tox0, group_peer_self_join_handler);
tox_callback_group_self_join(tox1, group_peer_self_join_handler);
tox_callback_group_peer_name(tox1, group_peer_name_handler);
tox_callback_group_peer_status(tox1, group_peer_status_handler);
tox_callback_group_peer_exit(tox1, group_peer_exit_handler);
// tox0 makes new group.
Tox_Err_Group_New err_new;
uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *) GROUP_NAME,
GROUP_NAME_LEN, (const uint8_t *)PEER0_NICK, PEER0_NICK_LEN,
&err_new);
ck_assert(err_new == TOX_ERR_GROUP_NEW_OK);
state0->is_founder = true;
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
// changes the state (for sync check purposes)
Tox_Err_Group_Founder_Set_Peer_Limit limit_set_err;
tox_group_founder_set_peer_limit(tox0, groupnumber, PEER_LIMIT, &limit_set_err);
ck_assert_msg(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK, "failed to set peer limit: %d", limit_set_err);
Tox_Err_Group_Topic_Set tp_err;
tox_group_set_topic(tox0, groupnumber, (const uint8_t *)TOPIC, TOPIC_LEN, &tp_err);
ck_assert(tp_err == TOX_ERR_GROUP_TOPIC_SET_OK);
// get the chat id of the new group.
Tox_Err_Group_State_Queries err_id;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, groupnumber, chat_id, &err_id);
ck_assert(err_id == TOX_ERR_GROUP_STATE_QUERIES_OK);
// tox1 joins it.
Tox_Err_Group_Join err_join;
tox_group_join(tox1, chat_id, (const uint8_t *)PEER1_NICK, PEER1_NICK_LEN, nullptr, 0, &err_join);
ck_assert(err_join == TOX_ERR_GROUP_JOIN_OK);
// peers see each other and themselves join
while (!state1->peer_joined_count || !state1->self_joined_count || !state0->peer_joined_count) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
// wait for group syncing to finish
while (!all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnumber, GROUP_NAME_LEN)) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
fprintf(stderr, "Peers connected to group\n");
// tox 0 changes name
Tox_Err_Group_Self_Name_Set n_err;
tox_group_self_set_name(tox0, groupnumber, (const uint8_t *)PEER0_NICK2, PEER0_NICK2_LEN, &n_err);
ck_assert(n_err == TOX_ERR_GROUP_SELF_NAME_SET_OK);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
Tox_Err_Group_Self_Query sq_err;
size_t self_length = tox_group_self_get_name_size(tox0, groupnumber, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_length == PEER0_NICK2_LEN);
uint8_t self_name[TOX_MAX_NAME_LENGTH];
tox_group_self_get_name(tox0, groupnumber, self_name, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(memcmp(self_name, PEER0_NICK2, self_length) == 0);
fprintf(stderr, "Peer 0 successfully changed nick\n");
// tox 0 changes status
Tox_Err_Group_Self_Status_Set s_err;
tox_group_self_set_status(tox0, groupnumber, TOX_USER_STATUS_BUSY, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_STATUS_SET_OK);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
TOX_USER_STATUS self_status = tox_group_self_get_status(tox0, groupnumber, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_status == TOX_USER_STATUS_BUSY);
fprintf(stderr, "Peer 0 successfully changed status to %d\n", self_status);
while (!state1->peer_nick && !state1->peer_status) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
// tox 0 and tox 1 should see the same public key for tox 0
uint8_t tox0_self_pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
tox_group_self_get_public_key(tox0, groupnumber, tox0_self_pk, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
Tox_Err_Group_Peer_Query pq_err;
uint8_t tox0_pk_query[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
tox_group_peer_get_public_key(tox1, groupnumber, state1->peer_id, tox0_pk_query, &pq_err);
ck_assert(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(memcmp(tox0_pk_query, tox0_self_pk, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) == 0);
fprintf(stderr, "Peer 0 disconnecting...\n");
// tox 0 disconnects then reconnects
Tox_Err_Group_Disconnect d_err;
tox_group_disconnect(tox0, groupnumber, &d_err);
ck_assert(d_err == TOX_ERR_GROUP_DISCONNECT_OK);
while (state1->peer_exit_count != 1) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
// tox 1 changes status while alone in the group
tox_group_self_set_status(tox1, groupnumber, TOX_USER_STATUS_AWAY, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_STATUS_SET_OK);
fprintf(stderr, "Peer 0 reconnecting...\n");
Tox_Err_Group_Reconnect r_err;
tox_group_reconnect(tox0, groupnumber, &r_err);
ck_assert(r_err == TOX_ERR_GROUP_RECONNECT_OK);
while (state1->peer_joined_count != 2 && state0->self_joined_count == 2) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
for (size_t i = 0; i < 100; ++i) { // if we don't do this the exit packet never arrives
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
while (!all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnumber, GROUP_NAME_LEN)) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
// tox 0 should have the same public key and still be founder
uint8_t tox0_self_pk2[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
tox_group_self_get_public_key(tox0, groupnumber, tox0_self_pk2, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(memcmp(tox0_self_pk2, tox0_self_pk, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) == 0);
Tox_Group_Role self_role = tox_group_self_get_role(tox0, groupnumber, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
Tox_Group_Role other_role = tox_group_peer_get_role(tox1, groupnumber, state1->peer_id, &pq_err);
ck_assert(pq_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(self_role == other_role && self_role == TOX_GROUP_ROLE_FOUNDER);
uint32_t num_groups1 = tox_group_get_number_groups(tox0);
uint32_t num_groups2 = tox_group_get_number_groups(tox1);
ck_assert(num_groups1 == num_groups2 && num_groups2 == 1);
fprintf(stderr, "Both peers exiting group...\n");
Tox_Err_Group_Leave err_exit;
tox_group_leave(tox0, groupnumber, (const uint8_t *)EXIT_MESSAGE, EXIT_MESSAGE_LEN, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
while (state1->peer_exit_count != 2) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
tox_group_leave(tox1, groupnumber, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
num_groups1 = tox_group_get_number_groups(tox0);
num_groups2 = tox_group_get_number_groups(tox1);
ck_assert(num_groups1 == num_groups2 && num_groups2 == 0);
printf("All tests passed!\n");
#endif // VANILLA_NACL
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_announce_test, sizeof(State), &options);
return 0;
}
#undef NUM_GROUP_TOXES
#undef PEER1_NICK
#undef PEER0_NICK
#undef PEER0_NICK_LEN
#undef PEER1_NICK_LEN
#undef GROUP_NAME
#undef GROUP_NAME_LEN
#undef PEER_LIMIT
#undef TOPIC
#undef TOPIC_LEN

View File

@ -0,0 +1,283 @@
/*
* Tests group invites as well as join restrictions, including password protection, privacy state,
* and peer limits. Ensures sure that the peer being blocked from joining successfully receives
* the invite fail packet with the correct message.
*
* This test also checks that many peers can successfully join the group simultaneously.
*/
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "auto_test_support.h"
#include "check_compat.h"
typedef struct State {
uint32_t num_peers;
bool peer_limit_fail;
bool password_fail;
bool connected;
size_t messages_received;
} State;
#define NUM_GROUP_TOXES 8 // must be > 7
#define PASSWORD "dadada"
#define PASS_LEN (sizeof(PASSWORD) - 1)
#define WRONG_PASS "dadadada"
#define WRONG_PASS_LEN (sizeof(WRONG_PASS) - 1)
static bool group_has_full_graph(const AutoTox *autotoxes, uint32_t group_number, uint32_t expected_peer_count)
{
for (size_t i = 7; i < NUM_GROUP_TOXES; ++i) {
const State *state = (const State *)autotoxes[i].state;
if (state->num_peers < expected_peer_count) {
return false;
}
}
const State *state0 = (const State *)autotoxes[0].state;
const State *state1 = (const State *)autotoxes[1].state;
const State *state5 = (const State *)autotoxes[5].state;
if (state0->num_peers < expected_peer_count || state1->num_peers < expected_peer_count
|| state5->num_peers < expected_peer_count) {
return false;
}
return true;
}
static void group_join_fail_handler(Tox *tox, uint32_t group_number, Tox_Group_Join_Fail fail_type, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
switch (fail_type) {
case TOX_GROUP_JOIN_FAIL_PEER_LIMIT: {
state->peer_limit_fail = true;
break;
}
case TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD: {
state->password_fail = true;
break;
}
case TOX_GROUP_JOIN_FAIL_UNKNOWN:
// intentional fallthrough
default: {
ck_assert_msg(false, "Got unknown join fail");
return;
}
}
}
static void group_self_join_handler(Tox *tox, uint32_t group_number, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
state->connected = true;
}
static void group_peer_join_handler(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
++state->num_peers;
ck_assert(state->num_peers < NUM_GROUP_TOXES);
}
static void group_invite_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES > 7, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
tox_callback_group_join_fail(autotoxes[i].tox, group_join_fail_handler);
tox_callback_group_self_join(autotoxes[i].tox, group_self_join_handler);
}
Tox *tox0 = autotoxes[0].tox;
Tox *tox1 = autotoxes[1].tox;
Tox *tox2 = autotoxes[2].tox;
Tox *tox3 = autotoxes[3].tox;
Tox *tox4 = autotoxes[4].tox;
Tox *tox5 = autotoxes[5].tox;
Tox *tox6 = autotoxes[6].tox;
State *state0 = (State *)autotoxes[0].state;
State *state2 = (State *)autotoxes[2].state;
State *state3 = (State *)autotoxes[3].state;
State *state4 = (State *)autotoxes[4].state;
State *state5 = (State *)autotoxes[5].state;
State *state6 = (State *)autotoxes[6].state;
Tox_Err_Group_New new_err;
uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)"test", 4,
(const uint8_t *)"test", 4, &new_err);
ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, groupnumber, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "%d", id_err);
// peer 1 joins public group with no password
Tox_Err_Group_Join join_err;
tox_group_join(tox1, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (state0->num_peers < 1) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 1 joined group\n");
// founder sets a password
Tox_Err_Group_Founder_Set_Password pass_set_err;
tox_group_founder_set_password(tox0, groupnumber, (const uint8_t *)PASSWORD, PASS_LEN, &pass_set_err);
ck_assert_msg(pass_set_err == TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK, "%d", pass_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 2 attempts to join with no password
tox_group_join(tox2, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state2->password_fail) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 2 successfully blocked with no password\n");
// peer 3 attempts to join with invalid password
tox_group_join(tox3, chat_id, (const uint8_t *)"Test", 4, (const uint8_t *)WRONG_PASS, WRONG_PASS_LEN, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state3->password_fail) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 3 successfully blocked with invalid password\n");
// founder sets peer limit to 1
Tox_Err_Group_Founder_Set_Peer_Limit limit_set_err;
tox_group_founder_set_peer_limit(tox0, groupnumber, 1, &limit_set_err);
ck_assert_msg(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK, "%d", limit_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 4 attempts to join with correct password
tox_group_join(tox4, chat_id, (const uint8_t *)"Test", 4, (const uint8_t *)PASSWORD, PASS_LEN, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state4->peer_limit_fail) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 4 successfully blocked from joining full group\n");
// founder removes password and increases peer limit to 100
tox_group_founder_set_password(tox0, groupnumber, nullptr, 0, &pass_set_err);
ck_assert_msg(pass_set_err == TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK, "%d", pass_set_err);
tox_group_founder_set_peer_limit(tox0, groupnumber, 100, &limit_set_err);
ck_assert_msg(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK, "%d", limit_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 5 attempts to join group
tox_group_join(tox5, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state5->connected) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 5 successfully joined the group\n");
// founder makes group private
Tox_Err_Group_Founder_Set_Privacy_State priv_err;
tox_group_founder_set_privacy_state(tox0, groupnumber, TOX_GROUP_PRIVACY_STATE_PRIVATE, &priv_err);
ck_assert_msg(priv_err == TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK, "%d", priv_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 6 attempts to join group via chat ID
tox_group_join(tox6, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
// since we don't receive a fail packet in this case we just wait a while and check if we're in the group
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 20000);
ck_assert(!state6->connected);
printf("Peer 6 failed to join private group via chat ID\n");
// founder makes group public again
tox_group_founder_set_privacy_state(tox0, groupnumber, TOX_GROUP_PRIVACY_STATE_PUBLIC, &priv_err);
ck_assert_msg(priv_err == TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK, "%d", priv_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
const uint32_t num_new_peers = NUM_GROUP_TOXES - 7;
printf("Connecting %u peers at the same time\n", num_new_peers);
for (size_t i = 7; i < NUM_GROUP_TOXES; ++i) {
tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
}
const uint32_t expected_peer_count = num_new_peers + state0->num_peers + 1;
while (!group_has_full_graph(autotoxes, groupnumber, expected_peer_count)) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Every peer sees every other peer\n");
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[i].tox, 0, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
}
printf("All tests passed!\n");
#endif // VANILLA_NACL
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_invite_test, sizeof(State), &autotest_opts);
return 0;
}
#undef NUM_GROUP_TOXES
#undef PASSWORD
#undef PASS_LEN
#undef WRONG_PASS
#undef WRONG_PASS_LEN

View File

@ -0,0 +1,546 @@
/*
* Tests message sending capabilities, including:
* - The ability to send/receive plain, action, and custom messages
* - The lossless UDP implementation
* - The packet splitting implementation
* - The ignore feature
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "auto_test_support.h"
#include "check_compat.h"
#include "../toxcore/util.h"
typedef struct State {
uint32_t peer_id;
bool peer_joined;
bool message_sent;
bool message_received;
uint32_t pseudo_msg_id;
bool private_message_received;
size_t custom_packets_received;
size_t custom_private_packets_received;
bool lossless_check;
bool wraparound_check;
int32_t last_msg_recv;
} State;
#define NUM_GROUP_TOXES 2
#define MAX_NUM_MESSAGES_LOSSLESS_TEST 300
#define MAX_NUM_MESSAGES_WRAPAROUND_TEST 9001
#define TEST_MESSAGE "Where is it I've read that someone condemned to death says or thinks, an hour before his death, that if he had to live on some high rock, on such a narrow ledge that he'd only room to stand, and the ocean, everlasting darkness, everlasting solitude, everlasting tempest around him, if he had to remain standing on a square yard of space all his life, a thousand years, eternity, it were better to live so than to die at once. Only to live, to live and live! Life, whatever it may be!"
#define TEST_MESSAGE_LEN (sizeof(TEST_MESSAGE) - 1)
#define TEST_GROUP_NAME "Utah Data Center"
#define TEST_GROUP_NAME_LEN (sizeof(TEST_GROUP_NAME) - 1)
#define TEST_PRIVATE_MESSAGE "Don't spill yer beans"
#define TEST_PRIVATE_MESSAGE_LEN (sizeof(TEST_PRIVATE_MESSAGE) - 1)
#define TEST_CUSTOM_PACKET "Why'd ya spill yer beans?"
#define TEST_CUSTOM_PACKET_LEN (sizeof(TEST_CUSTOM_PACKET) - 1)
#define TEST_CUSTOM_PRIVATE_PACKET "This is a custom private packet. Enjoy."
#define TEST_CUSTOM_PRIVATE_PACKET_LEN (sizeof(TEST_CUSTOM_PRIVATE_PACKET) - 1)
#define IGNORE_MESSAGE "Am I bothering you?"
#define IGNORE_MESSAGE_LEN (sizeof(IGNORE_MESSAGE) - 1)
#define PEER0_NICK "Thomas"
#define PEER0_NICK_LEN (sizeof(PEER0_NICK) - 1)
#define PEER1_NICK "Winslow"
#define PEER1_NICK_LEN (sizeof(PEER1_NICK) - 1)
static uint16_t get_message_checksum(const uint8_t *message, uint16_t length)
{
uint16_t sum = 0;
for (size_t i = 0; i < length; ++i) {
sum += message[i];
}
return sum;
}
static void group_invite_handler(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length,
const uint8_t *group_name, size_t group_name_length, void *user_data)
{
printf("invite arrived; accepting\n");
Tox_Err_Group_Invite_Accept err_accept;
tox_group_invite_accept(tox, friend_number, invite_data, length, (const uint8_t *)PEER0_NICK, PEER0_NICK_LEN,
nullptr, 0, &err_accept);
ck_assert(err_accept == TOX_ERR_GROUP_INVITE_ACCEPT_OK);
}
static void group_join_fail_handler(Tox *tox, uint32_t groupnumber, Tox_Group_Join_Fail fail_type, void *user_data)
{
printf("join failed: %d\n", fail_type);
}
static void group_peer_join_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert_msg(state->peer_joined == false, "Peer timedout");
printf("peer %u joined, sending message\n", peer_id);
state->peer_joined = true;
state->peer_id = peer_id;
}
static void group_custom_private_packet_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *data,
size_t length, void *user_data)
{
ck_assert_msg(length == TEST_CUSTOM_PRIVATE_PACKET_LEN,
"Failed to receive custom private packet. Invalid length: %zu\n", length);
char message_buf[TOX_MAX_CUSTOM_PACKET_SIZE + 1];
memcpy(message_buf, data, length);
message_buf[length] = 0;
Tox_Err_Group_Peer_Query q_err;
size_t peer_name_len = tox_group_peer_get_name_size(tox, groupnumber, peer_id, &q_err);
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_peer_get_name(tox, groupnumber, peer_id, (uint8_t *) peer_name, &q_err);
peer_name[peer_name_len] = 0;
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(memcmp(peer_name, PEER0_NICK, peer_name_len) == 0);
Tox_Err_Group_Self_Query s_err;
size_t self_name_len = tox_group_self_get_name_size(tox, groupnumber, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_name_len <= TOX_MAX_NAME_LENGTH);
char self_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_self_get_name(tox, groupnumber, (uint8_t *) self_name, &s_err);
self_name[self_name_len] = 0;
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(memcmp(self_name, PEER1_NICK, self_name_len) == 0);
printf("%s sent custom private packet to %s: %s\n", peer_name, self_name, message_buf);
ck_assert(memcmp(message_buf, TEST_CUSTOM_PRIVATE_PACKET, length) == 0);
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
++state->custom_private_packets_received;
}
static void group_custom_packet_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *data,
size_t length, void *user_data)
{
ck_assert_msg(length == TEST_CUSTOM_PACKET_LEN, "Failed to receive custom packet. Invalid length: %zu\n", length);
char message_buf[TOX_MAX_CUSTOM_PACKET_SIZE + 1];
memcpy(message_buf, data, length);
message_buf[length] = 0;
Tox_Err_Group_Peer_Query q_err;
size_t peer_name_len = tox_group_peer_get_name_size(tox, groupnumber, peer_id, &q_err);
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_peer_get_name(tox, groupnumber, peer_id, (uint8_t *) peer_name, &q_err);
peer_name[peer_name_len] = 0;
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(memcmp(peer_name, PEER0_NICK, peer_name_len) == 0);
Tox_Err_Group_Self_Query s_err;
size_t self_name_len = tox_group_self_get_name_size(tox, groupnumber, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_name_len <= TOX_MAX_NAME_LENGTH);
char self_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_self_get_name(tox, groupnumber, (uint8_t *) self_name, &s_err);
self_name[self_name_len] = 0;
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(memcmp(self_name, PEER1_NICK, self_name_len) == 0);
printf("%s sent custom packet to %s: %s\n", peer_name, self_name, message_buf);
ck_assert(memcmp(message_buf, TEST_CUSTOM_PACKET, length) == 0);
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
++state->custom_packets_received;
}
static void group_message_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, uint32_t pseudo_msg_id, void *user_data)
{
ck_assert(!(length == IGNORE_MESSAGE_LEN && memcmp(message, IGNORE_MESSAGE, length) == 0));
ck_assert_msg(length == TEST_MESSAGE_LEN, "Failed to receive message. Invalid length: %zu\n", length);
char message_buf[TOX_GROUP_MAX_MESSAGE_LENGTH + 1];
memcpy(message_buf, message, length);
message_buf[length] = 0;
Tox_Err_Group_Peer_Query q_err;
size_t peer_name_len = tox_group_peer_get_name_size(tox, groupnumber, peer_id, &q_err);
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_peer_get_name(tox, groupnumber, peer_id, (uint8_t *) peer_name, &q_err);
peer_name[peer_name_len] = 0;
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(memcmp(peer_name, PEER0_NICK, peer_name_len) == 0);
Tox_Err_Group_Self_Query s_err;
size_t self_name_len = tox_group_self_get_name_size(tox, groupnumber, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_name_len <= TOX_MAX_NAME_LENGTH);
char self_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_self_get_name(tox, groupnumber, (uint8_t *) self_name, &s_err);
self_name[self_name_len] = 0;
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(memcmp(self_name, PEER1_NICK, self_name_len) == 0);
printf("%s sent message to %s:(id:%u) %s\n", peer_name, self_name, pseudo_msg_id, message_buf);
ck_assert(memcmp(message_buf, TEST_MESSAGE, length) == 0);
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
state->message_received = true;
state->pseudo_msg_id = pseudo_msg_id;
}
static void group_private_message_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *user_data)
{
ck_assert_msg(length == TEST_PRIVATE_MESSAGE_LEN, "Failed to receive message. Invalid length: %zu\n", length);
char message_buf[TOX_GROUP_MAX_MESSAGE_LENGTH + 1];
memcpy(message_buf, message, length);
message_buf[length] = 0;
Tox_Err_Group_Peer_Query q_err;
size_t peer_name_len = tox_group_peer_get_name_size(tox, groupnumber, peer_id, &q_err);
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_peer_get_name(tox, groupnumber, peer_id, (uint8_t *) peer_name, &q_err);
peer_name[peer_name_len] = 0;
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(memcmp(peer_name, PEER0_NICK, peer_name_len) == 0);
Tox_Err_Group_Self_Query s_err;
size_t self_name_len = tox_group_self_get_name_size(tox, groupnumber, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_name_len <= TOX_MAX_NAME_LENGTH);
char self_name[TOX_MAX_NAME_LENGTH + 1];
tox_group_self_get_name(tox, groupnumber, (uint8_t *) self_name, &s_err);
self_name[self_name_len] = 0;
ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(memcmp(self_name, PEER1_NICK, self_name_len) == 0);
printf("%s sent private action to %s: %s\n", peer_name, self_name, message_buf);
ck_assert(memcmp(message_buf, TEST_PRIVATE_MESSAGE, length) == 0);
ck_assert(type == TOX_MESSAGE_TYPE_ACTION);
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
state->private_message_received = true;
}
static void group_message_handler_lossless_test(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, uint32_t pseudo_msg_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(length >= 4 && length <= TOX_GROUP_MAX_MESSAGE_LENGTH);
uint16_t start;
uint16_t checksum;
memcpy(&start, message, sizeof(uint16_t));
memcpy(&checksum, message + sizeof(uint16_t), sizeof(uint16_t));
ck_assert_msg(start == state->last_msg_recv + 1, "Expected %d, got start %u", state->last_msg_recv + 1, start);
ck_assert_msg(checksum == get_message_checksum(message + 4, length - 4), "Wrong checksum");
state->last_msg_recv = start;
if (state->last_msg_recv == MAX_NUM_MESSAGES_LOSSLESS_TEST) {
state->lossless_check = true;
}
}
static void group_message_handler_wraparound_test(Tox *tox, uint32_t groupnumber, uint32_t peer_id,
TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, uint32_t pseudo_msg_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(length == 2);
uint16_t num;
memcpy(&num, message, sizeof(uint16_t));
ck_assert_msg(num == state->last_msg_recv + 1, "Expected %d, got start %u", state->last_msg_recv + 1, num);
state->last_msg_recv = num;
if (state->last_msg_recv == MAX_NUM_MESSAGES_WRAPAROUND_TEST) {
state->wraparound_check = true;
}
}
static void group_message_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES >= 2, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Tox *tox0 = autotoxes[0].tox;
Tox *tox1 = autotoxes[1].tox;
State *state0 = (State *)autotoxes[0].state;
State *state1 = (State *)autotoxes[1].state;
// initialize to different values
state0->pseudo_msg_id = 0;
state1->pseudo_msg_id = 1;
tox_callback_group_invite(tox1, group_invite_handler);
tox_callback_group_join_fail(tox1, group_join_fail_handler);
tox_callback_group_peer_join(tox1, group_peer_join_handler);
tox_callback_group_join_fail(tox0, group_join_fail_handler);
tox_callback_group_peer_join(tox0, group_peer_join_handler);
tox_callback_group_message(tox0, group_message_handler);
tox_callback_group_custom_packet(tox0, group_custom_packet_handler);
tox_callback_group_custom_private_packet(tox0, group_custom_private_packet_handler);
tox_callback_group_private_message(tox0, group_private_message_handler);
Tox_Err_Group_Send_Message err_send;
fprintf(stderr, "Tox 0 creates new group and invites tox1...\n");
// tox0 makes new group.
Tox_Err_Group_New err_new;
const uint32_t group_number = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PRIVATE, (const uint8_t *)TEST_GROUP_NAME,
TEST_GROUP_NAME_LEN, (const uint8_t *)PEER1_NICK, PEER1_NICK_LEN, &err_new);
ck_assert(err_new == TOX_ERR_GROUP_NEW_OK);
// tox0 invites tox1
Tox_Err_Group_Invite_Friend err_invite;
tox_group_invite_friend(tox0, group_number, 0, &err_invite);
ck_assert(err_invite == TOX_ERR_GROUP_INVITE_FRIEND_OK);
while (!state0->message_received) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
if (state1->peer_joined && !state1->message_sent) {
tox_group_send_message(tox1, group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)TEST_MESSAGE,
TEST_MESSAGE_LEN, &state1->pseudo_msg_id, &err_send);
ck_assert(err_send == TOX_ERR_GROUP_SEND_MESSAGE_OK);
state1->message_sent = true;
}
}
ck_assert_msg(state0->pseudo_msg_id == state1->pseudo_msg_id, "id0:%u id1:%u", state0->pseudo_msg_id, state1->pseudo_msg_id);
// Make sure we're still connected to each friend
Tox_Connection conn_1 = tox_friend_get_connection_status(tox0, 0, nullptr);
Tox_Connection conn_2 = tox_friend_get_connection_status(tox1, 0, nullptr);
ck_assert(conn_1 != TOX_CONNECTION_NONE && conn_2 != TOX_CONNECTION_NONE);
// tox0 ignores tox1
Tox_Err_Group_Set_Ignore ig_err;
tox_group_set_ignore(tox0, group_number, state0->peer_id, true, &ig_err);
ck_assert_msg(ig_err == TOX_ERR_GROUP_SET_IGNORE_OK, "%d", ig_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
// tox1 sends group a message which should not be seen by tox0's message handler
tox_group_send_message(tox1, group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)IGNORE_MESSAGE,
IGNORE_MESSAGE_LEN, nullptr, &err_send);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
// tox0 unignores tox1
tox_group_set_ignore(tox0, group_number, state0->peer_id, false, &ig_err);
ck_assert_msg(ig_err == TOX_ERR_GROUP_SET_IGNORE_OK, "%d", ig_err);
fprintf(stderr, "Sending private message...\n");
// tox0 sends a private action to tox1
Tox_Err_Group_Send_Private_Message m_err;
tox_group_send_private_message(tox1, group_number, state1->peer_id, TOX_MESSAGE_TYPE_ACTION,
(const uint8_t *)TEST_PRIVATE_MESSAGE, TEST_PRIVATE_MESSAGE_LEN, &m_err);
ck_assert_msg(m_err == TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK, "%d", m_err);
fprintf(stderr, "Sending custom packets...\n");
// tox0 sends a lossless and lossy custom packet to tox1
Tox_Err_Group_Send_Custom_Packet c_err;
tox_group_send_custom_packet(tox1, group_number, true, (const uint8_t *)TEST_CUSTOM_PACKET, TEST_CUSTOM_PACKET_LEN,
&c_err);
ck_assert_msg(c_err == TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK, "%d", c_err);
tox_group_send_custom_packet(tox1, group_number, false, (const uint8_t *)TEST_CUSTOM_PACKET, TEST_CUSTOM_PACKET_LEN,
&c_err);
ck_assert_msg(c_err == TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK, "%d", c_err);
fprintf(stderr, "Sending custom private packets...\n");
// tox0 sends a lossless and lossy custom private packet to tox1
Tox_Err_Group_Send_Custom_Private_Packet cperr;
tox_group_send_custom_private_packet(tox1, group_number, state1->peer_id, true,
(const uint8_t *)TEST_CUSTOM_PRIVATE_PACKET,
TEST_CUSTOM_PRIVATE_PACKET_LEN, &cperr);
ck_assert_msg(cperr == TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_OK, "%d", cperr);
tox_group_send_custom_private_packet(tox1, group_number, state1->peer_id, false,
(const uint8_t *)TEST_CUSTOM_PRIVATE_PACKET,
TEST_CUSTOM_PRIVATE_PACKET_LEN, &cperr);
ck_assert_msg(cperr == TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_OK, "%d", cperr);
while (!state0->private_message_received || state0->custom_packets_received < 2
|| state0->custom_private_packets_received < 2) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
uint8_t m[TOX_GROUP_MAX_MESSAGE_LENGTH] = {0};
fprintf(stderr, "Doing lossless packet test...\n");
tox_callback_group_message(tox1, group_message_handler_lossless_test);
state1->last_msg_recv = -1;
// lossless and packet splitting/reassembly test
for (uint16_t i = 0; i <= MAX_NUM_MESSAGES_LOSSLESS_TEST; ++i) {
if (i % 10 == 0) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
uint16_t message_size = min_u16(4 + (random_u16(rng) % TOX_GROUP_MAX_MESSAGE_LENGTH), TOX_GROUP_MAX_MESSAGE_LENGTH);
memcpy(m, &i, sizeof(uint16_t));
for (size_t j = 4; j < message_size; ++j) {
m[j] = random_u32(rng);
}
const uint16_t checksum = get_message_checksum(m + 4, message_size - 4);
memcpy(m + 2, &checksum, sizeof(uint16_t));
tox_group_send_message(tox0, group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)m, message_size, nullptr, &err_send);
ck_assert(err_send == TOX_ERR_GROUP_SEND_MESSAGE_OK);
}
while (!state1->lossless_check) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
state1->last_msg_recv = -1;
tox_callback_group_message(tox1, group_message_handler_wraparound_test);
fprintf(stderr, "Doing wraparound test...\n");
// packet array wrap-around test
for (uint16_t i = 0; i <= MAX_NUM_MESSAGES_WRAPAROUND_TEST; ++i) {
if (i % 10 == 0) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
memcpy(m, &i, sizeof(uint16_t));
tox_group_send_message(tox0, group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)m, 2, nullptr, &err_send);
ck_assert(err_send == TOX_ERR_GROUP_SEND_MESSAGE_OK);
}
while (!state1->wraparound_check) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[i].tox, group_number, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
}
fprintf(stderr, "All tests passed!\n");
#endif // VANILLA_NACL
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_message_test, sizeof(State), &autotest_opts);
return 0;
}
#undef NUM_GROUP_TOXES
#undef PEER1_NICK
#undef PEER1_NICK_LEN
#undef PEER0_NICK
#undef PEER0_NICK_LEN
#undef TEST_GROUP_NAME
#undef TEST_GROUP_NAME_LEN
#undef TEST_MESSAGE
#undef TEST_MESSAGE_LEN
#undef TEST_PRIVATE_MESSAGE_LEN
#undef TEST_CUSTOM_PACKET
#undef TEST_CUSTOM_PACKET_LEN
#undef TEST_CUSTOM_PRIVATE_PACKET
#undef TEST_CUSTOM_PRIVATE_PACKET_LEN
#undef IGNORE_MESSAGE
#undef IGNORE_MESSAGE_LEN
#undef MAX_NUM_MESSAGES_LOSSLESS_TEST
#undef MAX_NUM_MESSAGES_WRAPAROUND_TEST

View File

@ -0,0 +1,653 @@
/*
* Tests group moderation functionality.
*
* Note that making the peer count too high will break things. This test should not be relied on
* for general group/syncing functionality.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "auto_test_support.h"
#include "check_compat.h"
#include "../toxcore/tox.h"
#define NUM_GROUP_TOXES 5
#define GROUP_NAME "NASA Headquarters"
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
typedef struct Peer {
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peer_id;
} Peer;
typedef struct State {
char self_name[TOX_MAX_NAME_LENGTH];
size_t self_name_length;
uint32_t group_number;
uint32_t num_peers;
Peer peers[NUM_GROUP_TOXES - 1];
bool mod_check;
size_t mod_event_count;
char mod_name1[TOX_MAX_NAME_LENGTH];
char mod_name2[TOX_MAX_NAME_LENGTH];
bool observer_check;
size_t observer_event_count;
char observer_name1[TOX_MAX_NAME_LENGTH];
char observer_name2[TOX_MAX_NAME_LENGTH];
bool user_check;
size_t user_event_count;
bool kick_check; // mod gets kicked
} State;
static bool all_peers_connected(AutoTox *autotoxes)
{
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
if (state->num_peers != NUM_GROUP_TOXES - 1) {
return false;
}
if (!tox_group_is_connected(autotoxes[i].tox, state->group_number, nullptr)) {
return false;
}
}
return true;
}
/*
* Waits for all peers to receive the mod event.
*/
static void check_mod_event(AutoTox *autotoxes, size_t num_peers, Tox_Group_Mod_Event event)
{
uint32_t peers_recv_changes = 0;
do {
peers_recv_changes = 0;
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
for (size_t i = 0; i < num_peers; ++i) {
State *state = (State *)autotoxes[i].state;
bool check = false;
switch (event) {
case TOX_GROUP_MOD_EVENT_MODERATOR: {
if (state->mod_check) {
check = true;
state->mod_check = false;
}
break;
}
case TOX_GROUP_MOD_EVENT_OBSERVER: {
if (state->observer_check) {
check = true;
state->observer_check = false;
}
break;
}
case TOX_GROUP_MOD_EVENT_USER: {
if (state->user_check) {
check = true;
state->user_check = false;
}
break;
}
case TOX_GROUP_MOD_EVENT_KICK: {
check = state->kick_check;
break;
}
default: {
ck_assert(0);
}
}
if (check) {
++peers_recv_changes;
}
}
} while (peers_recv_changes < num_peers - 1);
}
static uint32_t get_peer_id_by_nick(const Peer *peers, uint32_t num_peers, const char *name)
{
ck_assert(name != nullptr);
for (uint32_t i = 0; i < num_peers; ++i) {
if (memcmp(peers[i].name, name, peers[i].name_length) == 0) {
return peers[i].peer_id;
}
}
ck_assert_msg(0, "Failed to find peer id");
}
static size_t get_state_index_by_nick(const AutoTox *autotoxes, size_t num_peers, const char *name, size_t name_length)
{
ck_assert(name != nullptr && name_length <= TOX_MAX_NAME_LENGTH);
for (size_t i = 0; i < num_peers; ++i) {
State *state = (State *)autotoxes[i].state;
if (memcmp(state->self_name, name, name_length) == 0) {
return i;
}
}
ck_assert_msg(0, "Failed to find index");
}
static void group_join_fail_handler(Tox *tox, uint32_t group_number, Tox_Group_Join_Fail fail_type, void *user_data)
{
fprintf(stderr, "Failed to join group: %d", fail_type);
}
static void group_peer_join_handler(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(state->group_number == group_number);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
Tox_Err_Group_Peer_Query q_err;
size_t peer_name_len = tox_group_peer_get_name_size(tox, group_number, peer_id, &q_err);
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
tox_group_peer_get_name(tox, group_number, peer_id, (uint8_t *) peer_name, &q_err);
peer_name[peer_name_len] = 0;
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
Peer *peer = &state->peers[state->num_peers];
peer->peer_id = peer_id;
memcpy(peer->name, peer_name, peer_name_len);
peer->name_length = peer_name_len;
++state->num_peers;
ck_assert(state->num_peers < NUM_GROUP_TOXES);
}
static void handle_mod(State *state, const char *peer_name, size_t peer_name_len, Tox_Group_Role role)
{
if (state->mod_event_count == 0) {
ck_assert(memcmp(peer_name, state->mod_name1, peer_name_len) == 0);
} else if (state->mod_event_count == 1) {
ck_assert(memcmp(peer_name, state->mod_name2, peer_name_len) == 0);
} else {
ck_assert(false);
}
++state->mod_event_count;
state->mod_check = true;
ck_assert(role == TOX_GROUP_ROLE_MODERATOR);
}
static void handle_observer(State *state, const char *peer_name, size_t peer_name_len, Tox_Group_Role role)
{
if (state->observer_event_count == 0) {
ck_assert(memcmp(peer_name, state->observer_name1, peer_name_len) == 0);
} else if (state->observer_event_count == 1) {
ck_assert(memcmp(peer_name, state->observer_name2, peer_name_len) == 0);
} else {
ck_assert(false);
}
++state->observer_event_count;
state->observer_check = true;
ck_assert(role == TOX_GROUP_ROLE_OBSERVER);
}
static void handle_user(State *state, const char *peer_name, size_t peer_name_len, Tox_Group_Role role)
{
// event 1: observer1 gets promoted back to user
// event 2: observer2 gets promoted to moderator
// event 3: moderator 1 gets kicked
// event 4: moderator 2 gets demoted to moderator
if (state->user_event_count == 0) {
ck_assert(memcmp(peer_name, state->observer_name1, peer_name_len) == 0);
} else if (state->user_event_count == 1) {
ck_assert(memcmp(peer_name, state->observer_name2, peer_name_len) == 0);
} else if (state->user_event_count == 2) {
ck_assert(memcmp(peer_name, state->mod_name1, peer_name_len) == 0);
} else if (state->user_event_count == 3) {
ck_assert(memcmp(peer_name, state->mod_name2, peer_name_len) == 0);
} else {
ck_assert(false);
}
++state->user_event_count;
state->user_check = true;
ck_assert(role == TOX_GROUP_ROLE_USER);
}
static void group_mod_event_handler(Tox *tox, uint32_t group_number, uint32_t source_peer_id, uint32_t target_peer_id,
Tox_Group_Mod_Event event, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(state->group_number == group_number);
char peer_name[TOX_MAX_NAME_LENGTH + 1];
Tox_Err_Group_Peer_Query q_err;
size_t peer_name_len = tox_group_peer_get_name_size(tox, group_number, target_peer_id, &q_err);
if (q_err == TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND) { // may occurr on sync attempts
return;
}
ck_assert_msg(q_err == TOX_ERR_GROUP_PEER_QUERY_OK, "error %d", q_err);
ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH);
tox_group_peer_get_name(tox, group_number, target_peer_id, (uint8_t *) peer_name, &q_err);
peer_name[peer_name_len] = 0;
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
Tox_Group_Role role = tox_group_peer_get_role(tox, group_number, target_peer_id, &q_err);
ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK);
switch (event) {
case TOX_GROUP_MOD_EVENT_MODERATOR: {
handle_mod(state, peer_name, peer_name_len, role);
break;
}
case TOX_GROUP_MOD_EVENT_OBSERVER: {
handle_observer(state, peer_name, peer_name_len, role);
break;
}
case TOX_GROUP_MOD_EVENT_USER: {
handle_user(state, peer_name, peer_name_len, role);
break;
}
case TOX_GROUP_MOD_EVENT_KICK: {
ck_assert(memcmp(peer_name, state->mod_name1, peer_name_len) == 0);
state->kick_check = true;
break;
}
default: {
ck_assert_msg(0, "Got invalid moderator event %d", event);
return;
}
}
}
/* Checks that `peer_id` sees itself with the role `role`. */
static void check_self_role(AutoTox *autotoxes, uint32_t peer_id, Tox_Group_Role role)
{
Tox_Err_Group_Self_Query sq_err;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
uint32_t self_peer_id = tox_group_self_get_peer_id(autotoxes[i].tox, state->group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
if (self_peer_id == peer_id) {
Tox_Group_Role self_role = tox_group_self_get_role(autotoxes[i].tox, state->group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_role == role);
return;
}
}
}
/* Makes sure that a peer's role respects the voice state */
static void voice_state_message_test(AutoTox *autotox, Tox_Group_Voice_State voice_state)
{
const State *state = (State *)autotox->state;
Tox_Err_Group_Self_Query sq_err;
Tox_Group_Role self_role = tox_group_self_get_role(autotox->tox, state->group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
Tox_Err_Group_Send_Message msg_err;
bool send_ret = tox_group_send_message(autotox->tox, state->group_number, TOX_MESSAGE_TYPE_NORMAL,
(const uint8_t *)"test", 4, nullptr, &msg_err);
switch (self_role) {
case TOX_GROUP_ROLE_OBSERVER: {
ck_assert(!send_ret && msg_err == TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS);
break;
}
case TOX_GROUP_ROLE_USER: {
if (voice_state != TOX_GROUP_VOICE_STATE_ALL) {
ck_assert_msg(!send_ret && msg_err == TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS,
"%d, %d", send_ret, msg_err);
} else {
ck_assert(send_ret && msg_err == TOX_ERR_GROUP_SEND_MESSAGE_OK);
}
break;
}
case TOX_GROUP_ROLE_MODERATOR: {
if (voice_state != TOX_GROUP_VOICE_STATE_FOUNDER) {
ck_assert(send_ret && msg_err == TOX_ERR_GROUP_SEND_MESSAGE_OK);
} else {
ck_assert(!send_ret && msg_err == TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS);
}
break;
}
case TOX_GROUP_ROLE_FOUNDER: {
ck_assert(send_ret && msg_err == TOX_ERR_GROUP_SEND_MESSAGE_OK);
break;
}
}
}
static bool all_peers_got_voice_state_change(AutoTox *autotoxes, uint32_t num_toxes,
Tox_Group_Voice_State expected_voice_state)
{
Tox_Err_Group_State_Queries query_err;
for (uint32_t i = 0; i < num_toxes; ++i) {
const State *state = (State *)autotoxes[i].state;
Tox_Group_Voice_State voice_state = tox_group_get_voice_state(autotoxes[i].tox, state->group_number, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (voice_state != expected_voice_state) {
return false;
}
}
return true;
}
static void check_voice_state(AutoTox *autotoxes, uint32_t num_toxes)
{
// founder sets voice state to Moderator
const State *state = (State *)autotoxes[0].state;
Tox_Err_Group_Founder_Set_Voice_State voice_set_err;
tox_group_founder_set_voice_state(autotoxes[0].tox, state->group_number, TOX_GROUP_VOICE_STATE_MODERATOR,
&voice_set_err);
ck_assert(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK);
for (uint32_t i = 0; i < num_toxes; ++i) {
do {
iterate_all_wait(autotoxes, num_toxes, ITERATION_INTERVAL);
} while (!all_peers_got_voice_state_change(autotoxes, num_toxes, TOX_GROUP_VOICE_STATE_MODERATOR));
voice_state_message_test(&autotoxes[i], TOX_GROUP_VOICE_STATE_MODERATOR);
}
tox_group_founder_set_voice_state(autotoxes[0].tox, state->group_number, TOX_GROUP_VOICE_STATE_FOUNDER, &voice_set_err);
ck_assert(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK);
for (uint32_t i = 0; i < num_toxes; ++i) {
do {
iterate_all_wait(autotoxes, num_toxes, ITERATION_INTERVAL);
} while (!all_peers_got_voice_state_change(autotoxes, num_toxes, TOX_GROUP_VOICE_STATE_FOUNDER));
voice_state_message_test(&autotoxes[i], TOX_GROUP_VOICE_STATE_FOUNDER);
}
tox_group_founder_set_voice_state(autotoxes[0].tox, state->group_number, TOX_GROUP_VOICE_STATE_ALL, &voice_set_err);
ck_assert(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK);
for (uint32_t i = 0; i < num_toxes; ++i) {
do {
iterate_all_wait(autotoxes, num_toxes, ITERATION_INTERVAL);
} while (!all_peers_got_voice_state_change(autotoxes, num_toxes, TOX_GROUP_VOICE_STATE_ALL));
voice_state_message_test(&autotoxes[i], TOX_GROUP_VOICE_STATE_ALL);
}
}
static void group_moderation_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES >= 4, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
ck_assert_msg(NUM_GROUP_TOXES < 10, "NUM_GROUP_TOXES is too big: %d", NUM_GROUP_TOXES);
uint16_t name_length = 6;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
state->self_name_length = name_length;
snprintf(state->self_name, sizeof(state->self_name), "peer_%zu", i);
state->self_name[name_length] = 0;
tox_callback_group_join_fail(autotoxes[i].tox, group_join_fail_handler);
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
tox_callback_group_moderation(autotoxes[i].tox, group_mod_event_handler);
}
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
fprintf(stderr, "Creating new group\n");
/* Founder makes new group */
State *state0 = (State *)autotoxes[0].state;
Tox *tox0 = autotoxes[0].tox;
Tox_Err_Group_New err_new;
state0->group_number = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME,
GROUP_NAME_LEN, (const uint8_t *)state0->self_name, state0->self_name_length,
&err_new);
ck_assert_msg(err_new == TOX_ERR_GROUP_NEW_OK, "Failed to create group. error: %d\n", err_new);
/* Founder gets chat ID */
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, state0->group_number, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get chat ID. error: %d", id_err);
fprintf(stderr, "Peers attemping to join DHT group via the chat ID\n");
for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
State *state = (State *)autotoxes[i].state;
Tox_Err_Group_Join join_err;
state->group_number = tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)state->self_name,
state->self_name_length,
nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "Peer %s (%zu) failed to join group. error %d",
state->self_name, i, join_err);
c_sleep(100);
}
// make sure every peer sees every other peer before we continue
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
} while (!all_peers_connected(autotoxes));
/* manually tell the other peers the names of the peers that will be assigned new roles */
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
memcpy(state->mod_name1, state0->peers[0].name, sizeof(state->mod_name1));
memcpy(state->mod_name2, state0->peers[2].name, sizeof(state->mod_name2));
memcpy(state->observer_name1, state0->peers[1].name, sizeof(state->observer_name1));
memcpy(state->observer_name2, state0->peers[2].name, sizeof(state->observer_name2));
}
/* founder checks his own role */
Tox_Err_Group_Self_Query sq_err;
Tox_Group_Role self_role = tox_group_self_get_role(tox0, state0->group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_role == TOX_GROUP_ROLE_FOUNDER);
/* all peers should be user role except founder */
for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
State *state = (State *)autotoxes[i].state;
self_role = tox_group_self_get_role(autotoxes[i].tox, state->group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
ck_assert(self_role == TOX_GROUP_ROLE_USER);
}
/* founder sets first peer to moderator */
fprintf(stderr, "Founder setting %s to moderator\n", state0->peers[0].name);
Tox_Err_Group_Mod_Set_Role role_err;
tox_group_mod_set_role(tox0, state0->group_number, state0->peers[0].peer_id, TOX_GROUP_ROLE_MODERATOR, &role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set moderator. error: %d", role_err);
// manually flag the role setter because they don't get a callback
state0->mod_check = true;
++state0->mod_event_count;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_MODERATOR);
check_self_role(autotoxes, state0->peers[0].peer_id, TOX_GROUP_ROLE_MODERATOR);
fprintf(stderr, "All peers successfully received mod event\n");
/* founder sets second and third peer to observer */
fprintf(stderr, "Founder setting %s to observer\n", state0->peers[1].name);
tox_group_mod_set_role(tox0, state0->group_number, state0->peers[1].peer_id, TOX_GROUP_ROLE_OBSERVER, &role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set observer. error: %d", role_err);
state0->observer_check = true;
++state0->observer_event_count;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_OBSERVER);
fprintf(stderr, "All peers successfully received observer event 1\n");
fprintf(stderr, "Founder setting %s to observer\n", state0->peers[2].name);
tox_group_mod_set_role(tox0, state0->group_number, state0->peers[2].peer_id, TOX_GROUP_ROLE_OBSERVER, &role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set observer. error: %d", role_err);
state0->observer_check = true;
++state0->observer_event_count;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_OBSERVER);
check_self_role(autotoxes, state0->peers[1].peer_id, TOX_GROUP_ROLE_OBSERVER);
fprintf(stderr, "All peers successfully received observer event 2\n");
/* do voice state test here since we have at least one peer of each role */
check_voice_state(autotoxes, NUM_GROUP_TOXES);
fprintf(stderr, "Voice state respected by all peers\n");
/* New moderator promotes second peer back to user */
const uint32_t idx = get_state_index_by_nick(autotoxes, NUM_GROUP_TOXES, state0->peers[0].name,
state0->peers[0].name_length);
State *state1 = (State *)autotoxes[idx].state;
Tox *tox1 = autotoxes[idx].tox;
const uint32_t obs_peer_id = get_peer_id_by_nick(state1->peers, NUM_GROUP_TOXES - 1, state1->observer_name1);
fprintf(stderr, "%s is promoting %s back to user\n", state1->self_name, state0->peers[1].name);
tox_group_mod_set_role(tox1, state1->group_number, obs_peer_id, TOX_GROUP_ROLE_USER, &role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to promote observer back to user. error: %d",
role_err);
state1->user_check = true;
++state1->user_event_count;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_USER);
fprintf(stderr, "All peers successfully received user event\n");
/* founder assigns third peer to moderator (this triggers two events: user and moderator) */
fprintf(stderr, "Founder setting %s to moderator\n", state0->peers[2].name);
tox_group_mod_set_role(tox0, state0->group_number, state0->peers[2].peer_id, TOX_GROUP_ROLE_MODERATOR, &role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set moderator. error: %d", role_err);
state0->mod_check = true;
++state0->mod_event_count;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_MODERATOR);
check_self_role(autotoxes, state0->peers[2].peer_id, TOX_GROUP_ROLE_MODERATOR);
fprintf(stderr, "All peers successfully received moderator event\n");
/* moderator attempts to demote and kick founder */
uint32_t founder_peer_id = get_peer_id_by_nick(state1->peers, NUM_GROUP_TOXES - 1, state0->self_name);
tox_group_mod_set_role(tox1, state1->group_number, founder_peer_id, TOX_GROUP_ROLE_OBSERVER, &role_err);
ck_assert_msg(role_err != TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Mod set founder to observer");
Tox_Err_Group_Mod_Kick_Peer k_err;
tox_group_mod_kick_peer(tox1, state1->group_number, founder_peer_id, &k_err);
ck_assert_msg(k_err != TOX_ERR_GROUP_MOD_KICK_PEER_OK, "Mod kicked founder");
/* founder kicks moderator (this triggers two events: user and kick) */
fprintf(stderr, "Founder is kicking %s\n", state0->peers[0].name);
tox_group_mod_kick_peer(tox0, state0->group_number, state0->peers[0].peer_id, &k_err);
ck_assert_msg(k_err == TOX_ERR_GROUP_MOD_KICK_PEER_OK, "Failed to kick peer. error: %d", k_err);
state0->kick_check = true;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_KICK);
fprintf(stderr, "All peers successfully received kick event\n");
fprintf(stderr, "Founder is demoting moderator to user\n");
tox_group_mod_set_role(tox0, state0->group_number, state0->peers[2].peer_id, TOX_GROUP_ROLE_USER, &role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to demote peer 3 to User. error: %d", role_err);
state0->user_check = true;
++state0->user_event_count;
check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_USER);
check_self_role(autotoxes, state0->peers[2].peer_id, TOX_GROUP_ROLE_USER);
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
const State *state = (const State *)autotoxes[i].state;
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[i].tox, state->group_number, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
}
fprintf(stderr, "All tests passed!\n");
#endif // VANILLA_NACL
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_moderation_test, sizeof(State), &options);
return 0;
}
#undef NUM_GROUP_TOXES
#undef GROUP_NAME
#undef GROUP_NAME_LEN

View File

@ -0,0 +1,300 @@
/*
* Tests that we can save a groupchat and load a groupchat with the saved data.
*/
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "auto_test_support.h"
typedef struct State {
bool peer_joined;
} State;
#define NUM_GROUP_TOXES 2
#define GROUP_NAME "The Test Chamber"
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
#define TOPIC "They're waiting for you Gordon..."
#define TOPIC_LEN (sizeof(TOPIC) - 1)
#define NEW_PRIV_STATE TOX_GROUP_PRIVACY_STATE_PRIVATE
#define PASSWORD "password123"
#define PASS_LEN (sizeof(PASSWORD) - 1)
#define PEER_LIMIT 69
#define PEER0_NICK "Mike"
#define PEER0_NICK_LEN (sizeof(PEER0_NICK) -1)
#define NEW_USER_STATUS TOX_USER_STATUS_BUSY
static void group_invite_handler(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length,
const uint8_t *group_name, size_t group_name_length, void *user_data)
{
Tox_Err_Group_Invite_Accept err_accept;
tox_group_invite_accept(tox, friend_number, invite_data, length, (const uint8_t *)"test2", 5,
nullptr, 0, &err_accept);
ck_assert(err_accept == TOX_ERR_GROUP_INVITE_ACCEPT_OK);
}
static void group_peer_join_handler(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
state->peer_joined = true;
}
/* Checks that group has the same state according to the above defines
*
* Returns 0 if state is correct.
* Returns a value < 0 if state is incorrect.
*/
static int has_correct_group_state(const Tox *tox, uint32_t group_number, const uint8_t *expected_chat_id)
{
Tox_Err_Group_State_Queries query_err;
Tox_Group_Privacy_State priv_state = tox_group_get_privacy_state(tox, group_number, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (priv_state != NEW_PRIV_STATE) {
return -1;
}
size_t pass_len = tox_group_get_password_size(tox, group_number, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
uint8_t password[TOX_GROUP_MAX_PASSWORD_SIZE];
tox_group_get_password(tox, group_number, password, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (pass_len != PASS_LEN || memcmp(password, PASSWORD, pass_len) != 0) {
return -2;
}
size_t gname_len = tox_group_get_name_size(tox, group_number, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
uint8_t group_name[TOX_GROUP_MAX_GROUP_NAME_LENGTH];
tox_group_get_name(tox, group_number, group_name, &query_err);
if (gname_len != GROUP_NAME_LEN || memcmp(group_name, GROUP_NAME, gname_len) != 0) {
return -3;
}
if (tox_group_get_peer_limit(tox, group_number, nullptr) != PEER_LIMIT) {
return -4;
}
Tox_Group_Topic_Lock topic_lock = tox_group_get_topic_lock(tox, group_number, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (topic_lock != TOX_GROUP_TOPIC_LOCK_DISABLED) {
return -5;
}
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox, group_number, chat_id, &id_err);
ck_assert(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (memcmp(chat_id, expected_chat_id, TOX_GROUP_CHAT_ID_SIZE) != 0) {
return -6;
}
return 0;
}
static int has_correct_self_state(const Tox *tox, uint32_t group_number, const uint8_t *expected_self_pk)
{
Tox_Err_Group_Self_Query sq_err;
size_t self_length = tox_group_self_get_name_size(tox, group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
uint8_t self_name[TOX_MAX_NAME_LENGTH];
tox_group_self_get_name(tox, group_number, self_name, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
if (self_length != PEER0_NICK_LEN || memcmp(self_name, PEER0_NICK, self_length) != 0) {
return -1;
}
TOX_USER_STATUS self_status = tox_group_self_get_status(tox, group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
if (self_status != NEW_USER_STATUS) {
return -2;
}
Tox_Group_Role self_role = tox_group_self_get_role(tox, group_number, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
if (self_role != TOX_GROUP_ROLE_FOUNDER) {
return -3;
}
uint8_t self_pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
tox_group_self_get_public_key(tox, group_number, self_pk, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
if (memcmp(self_pk, expected_self_pk, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) != 0) {
return -4;
}
return 0;
}
static void group_save_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES > 1, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_invite(autotoxes[i].tox, group_invite_handler);
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
}
Tox *tox0 = autotoxes[0].tox;
const State *state0 = (State *)autotoxes[0].state;
const State *state1 = (State *)autotoxes[1].state;
Tox_Err_Group_New err_new;
const uint32_t group_number = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PRIVATE, (const uint8_t *)GROUP_NAME,
GROUP_NAME_LEN, (const uint8_t *)"test", 4, &err_new);
ck_assert(err_new == TOX_ERR_GROUP_NEW_OK);
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
Tox_Err_Group_State_Queries id_err;
tox_group_get_chat_id(tox0, group_number, chat_id, &id_err);
ck_assert(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
uint8_t founder_pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
Tox_Err_Group_Self_Query sq_err;
tox_group_self_get_public_key(tox0, group_number, founder_pk, &sq_err);
ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK);
Tox_Err_Group_Invite_Friend err_invite;
tox_group_invite_friend(tox0, group_number, 0, &err_invite);
ck_assert(err_invite == TOX_ERR_GROUP_INVITE_FRIEND_OK);
while (!state0->peer_joined && !state1->peer_joined) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("tox0 invites tox1 to group\n");
// change group state
Tox_Err_Group_Topic_Set top_err;
tox_group_set_topic(tox0, group_number, (const uint8_t *)TOPIC, TOPIC_LEN, &top_err);
ck_assert(top_err == TOX_ERR_GROUP_TOPIC_SET_OK);
Tox_Err_Group_Founder_Set_Topic_Lock lock_set_err;
tox_group_founder_set_topic_lock(tox0, group_number, TOX_GROUP_TOPIC_LOCK_DISABLED, &lock_set_err);
ck_assert(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK);
Tox_Err_Group_Founder_Set_Privacy_State priv_err;
tox_group_founder_set_privacy_state(tox0, group_number, NEW_PRIV_STATE, &priv_err);
ck_assert(priv_err == TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK);
Tox_Err_Group_Founder_Set_Password pass_set_err;
tox_group_founder_set_password(tox0, group_number, (const uint8_t *)PASSWORD, PASS_LEN, &pass_set_err);
ck_assert(pass_set_err == TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK);
Tox_Err_Group_Founder_Set_Peer_Limit limit_set_err;
tox_group_founder_set_peer_limit(tox0, group_number, PEER_LIMIT, &limit_set_err);
ck_assert(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK);
// change self state
Tox_Err_Group_Self_Name_Set n_err;
tox_group_self_set_name(tox0, group_number, (const uint8_t *)PEER0_NICK, PEER0_NICK_LEN, &n_err);
ck_assert(n_err == TOX_ERR_GROUP_SELF_NAME_SET_OK);
Tox_Err_Group_Self_Status_Set s_err;
tox_group_self_set_status(tox0, group_number, NEW_USER_STATUS, &s_err);
ck_assert(s_err == TOX_ERR_GROUP_SELF_STATUS_SET_OK);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
printf("tox0 changes group state\n");
const size_t save_length = tox_get_savedata_size(tox0);
uint8_t *save = (uint8_t *)malloc(save_length);
ck_assert(save != nullptr);
tox_get_savedata(tox0, save);
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
tox_group_leave(autotoxes[i].tox, group_number, nullptr, 0, nullptr);
}
struct Tox_Options *const options = tox_options_new(nullptr);
ck_assert(options != nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, save, save_length);
Tox *new_tox = tox_new_log(options, nullptr, nullptr);
ck_assert(new_tox != nullptr);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
printf("tox0 saves group and reloads client\n");
const int group_ret = has_correct_group_state(new_tox, group_number, chat_id);
ck_assert_msg(group_ret == 0, "incorrect group state: %d", group_ret);
const int self_ret = has_correct_self_state(new_tox, group_number, founder_pk);
ck_assert_msg(self_ret == 0, "incorrect self state: %d", self_ret);
tox_group_leave(new_tox, group_number, nullptr, 0, nullptr);
free(save);
tox_options_free(options);
tox_kill(new_tox);
printf("All tests passed!\n");
#endif // VANILLA_NACL
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_save_test, sizeof(State), &autotest_opts);
return 0;
}
#undef NUM_GROUP_TOXES
#undef GROUP_NAME
#undef GROUP_NAME_LEN
#undef TOPIC
#undef TOPIC_LEN
#undef NEW_PRIV_STATE
#undef PASSWORD
#undef PASS_LEN
#undef PEER_LIMIT
#undef PEER0_NICK
#undef PEER0_NICK_LEN
#undef NEW_USER_STATUS

View File

@ -0,0 +1,345 @@
/*
* Tests that we can successfully change the group state and that all peers in the group
* receive the correct state changes.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include "auto_test_support.h"
#include "check_compat.h"
#define NUM_GROUP_TOXES 5
#define PEER_LIMIT_1 NUM_GROUP_TOXES
#define PEER_LIMIT_2 50
#define PASSWORD "dadada"
#define PASS_LEN (sizeof(PASSWORD) - 1)
#define GROUP_NAME "The Crystal Palace"
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
#define PEER0_NICK "David"
#define PEER0_NICK_LEN (sizeof(PEER0_NICK) - 1)
typedef struct State {
size_t num_peers;
} State;
static bool all_group_peers_connected(const AutoTox *autotoxes, uint32_t tox_count, uint32_t groupnumber,
size_t name_length, uint32_t peer_limit)
{
for (uint32_t i = 0; i < tox_count; ++i) {
// make sure we got an invite response
if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != name_length) {
return false;
}
// make sure we got a sync response
if (tox_group_get_peer_limit(autotoxes[i].tox, groupnumber, nullptr) != peer_limit) {
return false;
}
// make sure we're actually connected
if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) {
return false;
}
const State *state = (const State *)autotoxes[i].state;
// make sure all peers are connected to one another
if (state->num_peers < NUM_GROUP_TOXES - 1) {
return false;
}
}
return true;
}
static void group_topic_lock_handler(Tox *tox, uint32_t groupnumber, Tox_Group_Topic_Lock topic_lock,
void *user_data)
{
Tox_Err_Group_State_Queries err;
Tox_Group_Topic_Lock current_topic_lock = tox_group_get_topic_lock(tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(current_topic_lock == topic_lock, "topic locks don't match in callback: %d %d",
topic_lock, current_topic_lock);
}
static void group_voice_state_handler(Tox *tox, uint32_t groupnumber, Tox_Group_Voice_State voice_state,
void *user_data)
{
Tox_Err_Group_State_Queries err;
Tox_Group_Voice_State current_voice_state = tox_group_get_voice_state(tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(current_voice_state == voice_state, "voice states don't match in callback: %d %d",
voice_state, current_voice_state);
}
static void group_privacy_state_handler(Tox *tox, uint32_t groupnumber, Tox_Group_Privacy_State privacy_state,
void *user_data)
{
Tox_Err_Group_State_Queries err;
Tox_Group_Privacy_State current_pstate = tox_group_get_privacy_state(tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(current_pstate == privacy_state, "privacy states don't match in callback");
}
static void group_peer_limit_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_limit, void *user_data)
{
Tox_Err_Group_State_Queries err;
uint32_t current_plimit = tox_group_get_peer_limit(tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(peer_limit == current_plimit,
"Peer limits don't match in callback: %u, %u\n", peer_limit, current_plimit);
}
static void group_password_handler(Tox *tox, uint32_t groupnumber, const uint8_t *password, size_t length,
void *user_data)
{
Tox_Err_Group_State_Queries err;
size_t curr_pwlength = tox_group_get_password_size(tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert(length == curr_pwlength);
uint8_t current_password[TOX_GROUP_MAX_PASSWORD_SIZE];
tox_group_get_password(tox, groupnumber, current_password, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(memcmp(current_password, password, length) == 0,
"Passwords don't match: %s, %s", password, current_password);
}
static void group_peer_join_handler(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
++state->num_peers;
ck_assert(state->num_peers < NUM_GROUP_TOXES);
}
/* Returns 0 if group state is equal to the state passed to this function.
* Returns negative integer if state is invalid.
*/
static int check_group_state(const Tox *tox, uint32_t groupnumber, uint32_t peer_limit,
Tox_Group_Privacy_State priv_state, Tox_Group_Voice_State voice_state,
const uint8_t *password, size_t pass_len, Tox_Group_Topic_Lock topic_lock)
{
Tox_Err_Group_State_Queries query_err;
Tox_Group_Privacy_State my_priv_state = tox_group_get_privacy_state(tox, groupnumber, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get privacy state: %d", query_err);
if (my_priv_state != priv_state) {
return -1;
}
uint32_t my_peer_limit = tox_group_get_peer_limit(tox, groupnumber, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get peer limit: %d", query_err);
if (my_peer_limit != peer_limit) {
return -2;
}
size_t my_pass_len = tox_group_get_password_size(tox, groupnumber, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get password size: %d", query_err);
if (my_pass_len != pass_len) {
return -5;
}
if (password != nullptr && my_pass_len > 0) {
ck_assert(my_pass_len <= TOX_GROUP_MAX_PASSWORD_SIZE);
uint8_t my_pass[TOX_GROUP_MAX_PASSWORD_SIZE];
tox_group_get_password(tox, groupnumber, my_pass, &query_err);
my_pass[my_pass_len] = 0;
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get password: %d", query_err);
if (memcmp(my_pass, password, my_pass_len) != 0) {
return -6;
}
}
/* Group name should never change */
size_t my_gname_len = tox_group_get_name_size(tox, groupnumber, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get group name size: %d", query_err);
if (my_gname_len != GROUP_NAME_LEN) {
return -7;
}
ck_assert(my_gname_len <= TOX_GROUP_MAX_GROUP_NAME_LENGTH);
uint8_t my_gname[TOX_GROUP_MAX_GROUP_NAME_LENGTH];
tox_group_get_name(tox, groupnumber, my_gname, &query_err);
my_gname[my_gname_len] = 0;
if (memcmp(my_gname, (const uint8_t *)GROUP_NAME, my_gname_len) != 0) {
return -8;
}
Tox_Group_Topic_Lock current_topic_lock = tox_group_get_topic_lock(tox, groupnumber, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get topic lock: %d", query_err);
if (current_topic_lock != topic_lock) {
return -9;
}
Tox_Group_Voice_State current_voice_state = tox_group_get_voice_state(tox, groupnumber, &query_err);
ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get voice state: %d", query_err);
if (current_voice_state != voice_state) {
return -10;
}
return 0;
}
static void set_group_state(Tox *tox, uint32_t groupnumber, uint32_t peer_limit, Tox_Group_Privacy_State priv_state,
Tox_Group_Voice_State voice_state, const uint8_t *password, size_t pass_len,
Tox_Group_Topic_Lock topic_lock)
{
Tox_Err_Group_Founder_Set_Peer_Limit limit_set_err;
tox_group_founder_set_peer_limit(tox, groupnumber, peer_limit, &limit_set_err);
ck_assert_msg(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK, "failed to set peer limit: %d", limit_set_err);
Tox_Err_Group_Founder_Set_Privacy_State priv_err;
tox_group_founder_set_privacy_state(tox, groupnumber, priv_state, &priv_err);
ck_assert_msg(priv_err == TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK, "failed to set privacy state: %d", priv_err);
Tox_Err_Group_Founder_Set_Password pass_set_err;
tox_group_founder_set_password(tox, groupnumber, password, pass_len, &pass_set_err);
ck_assert_msg(pass_set_err == TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK, "failed to set password: %d", pass_set_err);
Tox_Err_Group_Founder_Set_Topic_Lock lock_set_err;
tox_group_founder_set_topic_lock(tox, groupnumber, topic_lock, &lock_set_err);
ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to set topic lock: %d",
lock_set_err);
Tox_Err_Group_Founder_Set_Voice_State voice_set_err;
tox_group_founder_set_voice_state(tox, groupnumber, voice_state, &voice_set_err);
ck_assert_msg(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK, "failed to set voice state: %d",
voice_set_err);
}
static void group_state_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES >= 3, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_privacy_state(autotoxes[i].tox, group_privacy_state_handler);
tox_callback_group_peer_limit(autotoxes[i].tox, group_peer_limit_handler);
tox_callback_group_password(autotoxes[i].tox, group_password_handler);
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
tox_callback_group_voice_state(autotoxes[i].tox, group_voice_state_handler);
tox_callback_group_topic_lock(autotoxes[i].tox, group_topic_lock_handler);
}
Tox *tox0 = autotoxes[0].tox;
/* Tox 0 creates a group and is the founder of a newly created group */
Tox_Err_Group_New new_err;
uint32_t groupnum = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, GROUP_NAME_LEN,
(const uint8_t *)PEER0_NICK, PEER0_NICK_LEN, &new_err);
ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err);
/* Founder sets default group state before anyone else joins */
set_group_state(tox0, groupnum, PEER_LIMIT_1, TOX_GROUP_PRIVACY_STATE_PUBLIC, TOX_GROUP_VOICE_STATE_ALL,
(const uint8_t *)PASSWORD, PASS_LEN, TOX_GROUP_TOPIC_LOCK_ENABLED);
/* Founder gets the Chat ID and implicitly shares it publicly */
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, groupnum, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "tox_group_get_chat_id failed %d", id_err);
/* All other peers join the group using the Chat ID and password */
for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
Tox_Err_Group_Join join_err;
tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, (const uint8_t *)PASSWORD, PASS_LEN,
&join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "tox_group_join failed: %d", join_err);
}
fprintf(stderr, "Peers attempting to join group\n");
/* Keep checking if all instances have connected to the group until test times out */
while (!all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnum, GROUP_NAME_LEN, PEER_LIMIT_1)) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
/* Change group state and check that all peers received the changes */
set_group_state(tox0, groupnum, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE, TOX_GROUP_VOICE_STATE_MODERATOR,
nullptr, 0, TOX_GROUP_TOPIC_LOCK_DISABLED);
fprintf(stderr, "Changing state\n");
while (1) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
uint32_t count = 0;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
if (check_group_state(autotoxes[i].tox, groupnum, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE,
TOX_GROUP_VOICE_STATE_MODERATOR, nullptr, 0, TOX_GROUP_TOPIC_LOCK_DISABLED) == 0) {
++count;
}
}
if (count == NUM_GROUP_TOXES) {
fprintf(stderr, "%u peers successfully received state changes\n", count);
break;
}
}
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[i].tox, groupnum, nullptr, 0, &err_exit);
ck_assert_msg(err_exit == TOX_ERR_GROUP_LEAVE_OK, "%d", err_exit);
}
fprintf(stderr, "All tests passed!\n");
#endif /* VANILLA_NACL */
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_state_test, sizeof(State), &autotest_opts);
return 0;
}
#undef PEER0_NICK
#undef PEER0_NICK_LEN
#undef GROUP_NAME_LEN
#undef GROUP_NAME
#undef PASS_LEN
#undef PASSWORD
#undef PEER_LIMIT_2
#undef PEER_LIMIT_1
#undef NUM_GROUP_TOXES

View File

@ -0,0 +1,464 @@
/*
* Tests syncing capabilities of groups: we attempt to have multiple peers change the
* group state in a number of ways and make sure that all peers end up with the same
* resulting state after a short period.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "auto_test_support.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
// these should be kept relatively low so integration tests don't always flake out
// but they can be increased for local stress testing
#define NUM_GROUP_TOXES 5
#define ROLE_SPAM_ITERATIONS 1
#define TOPIC_SPAM_ITERATIONS 1
typedef struct Peers {
uint32_t num_peers;
int64_t *peer_ids;
} Peers;
typedef struct State {
uint8_t callback_topic[TOX_GROUP_MAX_TOPIC_LENGTH];
size_t topic_length;
Peers *peers;
} State;
static int add_peer(Peers *peers, uint32_t peer_id)
{
const uint32_t new_idx = peers->num_peers;
int64_t *tmp_list = (int64_t *)realloc(peers->peer_ids, sizeof(int64_t) * (peers->num_peers + 1));
if (tmp_list == nullptr) {
return -1;
}
++peers->num_peers;
tmp_list[new_idx] = (int64_t)peer_id;
peers->peer_ids = tmp_list;
return 0;
}
static int del_peer(Peers *peers, uint32_t peer_id)
{
bool found_peer = false;
int64_t i;
for (i = 0; i < peers->num_peers; ++i) {
if (peers->peer_ids[i] == peer_id) {
found_peer = true;
break;
}
}
if (!found_peer) {
return -1;
}
--peers->num_peers;
if (peers->num_peers == 0) {
free(peers->peer_ids);
peers->peer_ids = nullptr;
return 0;
}
if (peers->num_peers != i) {
peers->peer_ids[i] = peers->peer_ids[peers->num_peers];
}
peers->peer_ids[peers->num_peers] = -1;
int64_t *tmp_list = (int64_t *)realloc(peers->peer_ids, sizeof(int64_t) * (peers->num_peers));
if (tmp_list == nullptr) {
return -1;
}
peers->peer_ids = tmp_list;
return 0;
}
static void peers_cleanup(Peers *peers)
{
free(peers->peer_ids);
free(peers);
}
static void group_peer_join_handler(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(add_peer(state->peers, peer_id) == 0);
}
static void group_peer_exit_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, Tox_Group_Exit_Type exit_type,
const uint8_t *name, size_t name_length, const uint8_t *part_message,
size_t length, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(del_peer(state->peers, peer_id) == 0);
}
static void group_topic_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic,
size_t length, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(length <= TOX_GROUP_MAX_TOPIC_LENGTH);
memcpy(state->callback_topic, (const char *)topic, length);
state->topic_length = length;
}
static bool all_peers_connected(const AutoTox *autotoxes, uint32_t groupnumber)
{
for (uint32_t i = 0; i < NUM_GROUP_TOXES; ++i) {
// make sure we got an invite response
if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != 4) {
return false;
}
// make sure we're actually connected
if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) {
return false;
}
const State *state = (const State *)autotoxes[i].state;
// make sure all peers are connected to one another
if (state->peers->num_peers == NUM_GROUP_TOXES - 1) {
return false;
}
}
return true;
}
static unsigned int get_peer_roles_checksum(const Tox *tox, const State *state, uint32_t groupnumber)
{
Tox_Group_Role role = tox_group_self_get_role(tox, groupnumber, nullptr);
unsigned int checksum = (unsigned int)role;
for (size_t i = 0; i < state->peers->num_peers; ++i) {
role = tox_group_peer_get_role(tox, groupnumber, (uint32_t)state->peers->peer_ids[i], nullptr);
checksum += (unsigned int)role;
}
return checksum;
}
static bool all_peers_see_same_roles(const AutoTox *autotoxes, uint32_t num_peers, uint32_t groupnumber)
{
const State *state0 = (const State *)autotoxes[0].state;
unsigned int expected_checksum = get_peer_roles_checksum(autotoxes[0].tox, state0, groupnumber);
for (size_t i = 0; i < num_peers; ++i) {
const State *state = (const State *)autotoxes[i].state;
unsigned int checksum = get_peer_roles_checksum(autotoxes[i].tox, state, groupnumber);
if (checksum != expected_checksum) {
return false;
}
}
return true;
}
static void role_spam(const Random *rng, AutoTox *autotoxes, uint32_t num_peers, uint32_t num_demoted,
uint32_t groupnumber)
{
const State *state0 = (const State *)autotoxes[0].state;
Tox *tox0 = autotoxes[0].tox;
for (size_t iters = 0; iters < ROLE_SPAM_ITERATIONS; ++iters) {
// founder randomly promotes or demotes one of the non-mods
uint32_t idx = min_u32(random_u32(rng) % num_demoted, state0->peers->num_peers);
Tox_Group_Role f_role = random_u32(rng) % 2 == 0 ? TOX_GROUP_ROLE_MODERATOR : TOX_GROUP_ROLE_USER;
int64_t peer_id = state0->peers->peer_ids[idx];
if (peer_id >= 0) {
tox_group_mod_set_role(tox0, groupnumber, (uint32_t)peer_id, f_role, nullptr);
}
// mods randomly promote or demote one of the non-mods
for (uint32_t i = 1; i < num_peers; ++i) {
const State *state_i = (const State *)autotoxes[i].state;
for (uint32_t j = num_demoted; j < num_peers; ++j) {
if (i >= state_i->peers->num_peers) {
continue;
}
const State *state_j = (const State *)autotoxes[j].state;
Tox_Group_Role role = random_u32(rng) % 2 == 0 ? TOX_GROUP_ROLE_USER : TOX_GROUP_ROLE_OBSERVER;
peer_id = state_j->peers->peer_ids[i];
if (peer_id >= 0) {
tox_group_mod_set_role(autotoxes[j].tox, groupnumber, (uint32_t)peer_id, role, nullptr);
}
}
}
iterate_all_wait(autotoxes, num_peers, ITERATION_INTERVAL);
}
do {
iterate_all_wait(autotoxes, num_peers, ITERATION_INTERVAL);
} while (!all_peers_see_same_roles(autotoxes, num_peers, groupnumber));
}
/* All peers attempt to set a unique topic.
*
* Return true if all peers successfully changed the topic.
*/
static bool set_topic_all_peers(const Random *rng, AutoTox *autotoxes, size_t num_peers, uint32_t groupnumber)
{
for (size_t i = 0; i < num_peers; ++i) {
char new_topic[TOX_GROUP_MAX_TOPIC_LENGTH];
snprintf(new_topic, sizeof(new_topic), "peer %zu's topic %u", i, random_u32(rng));
const size_t length = strlen(new_topic);
Tox_Err_Group_Topic_Set err;
tox_group_set_topic(autotoxes[i].tox, groupnumber, (const uint8_t *)new_topic, length, &err);
if (err != TOX_ERR_GROUP_TOPIC_SET_OK) {
return false;
}
}
return true;
}
/* Returns true if all peers have the same topic, and the topic from the get_topic API function
* matches the last topic they received in the topic callback.
*/
static bool all_peers_have_same_topic(const AutoTox *autotoxes, uint32_t num_peers, uint32_t groupnumber)
{
uint8_t expected_topic[TOX_GROUP_MAX_TOPIC_LENGTH];
Tox_Err_Group_State_Queries query_err;
size_t expected_topic_length = tox_group_get_topic_size(autotoxes[0].tox, groupnumber, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
tox_group_get_topic(autotoxes[0].tox, groupnumber, expected_topic, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
const State *state0 = (const State *)autotoxes[0].state;
if (expected_topic_length != state0->topic_length) {
return false;
}
if (memcmp(state0->callback_topic, expected_topic, expected_topic_length) != 0) {
return false;
}
for (size_t i = 1; i < num_peers; ++i) {
size_t topic_length = tox_group_get_topic_size(autotoxes[i].tox, groupnumber, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (topic_length != expected_topic_length) {
return false;
}
uint8_t topic[TOX_GROUP_MAX_TOPIC_LENGTH];
tox_group_get_topic(autotoxes[i].tox, groupnumber, topic, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (memcmp(expected_topic, (const char *)topic, topic_length) != 0) {
return false;
}
const State *state = (const State *)autotoxes[i].state;
if (topic_length != state->topic_length) {
return false;
}
if (memcmp(state->callback_topic, (const char *)topic, topic_length) != 0) {
return false;
}
}
return true;
}
static void topic_spam(const Random *rng, AutoTox *autotoxes, uint32_t num_peers, uint32_t groupnumber)
{
for (size_t i = 0; i < TOPIC_SPAM_ITERATIONS; ++i) {
do {
iterate_all_wait(autotoxes, num_peers, ITERATION_INTERVAL);
} while (!set_topic_all_peers(rng, autotoxes, num_peers, groupnumber));
}
fprintf(stderr, "all peers set the topic at the same time\n");
do {
iterate_all_wait(autotoxes, num_peers, ITERATION_INTERVAL);
} while (!all_peers_have_same_topic(autotoxes, num_peers, groupnumber));
fprintf(stderr, "all peers see the same topic\n");
}
static void group_sync_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert(NUM_GROUP_TOXES >= 5);
const Random *rng = system_random();
ck_assert(rng != nullptr);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
tox_callback_group_topic(autotoxes[i].tox, group_topic_handler);
tox_callback_group_peer_exit(autotoxes[i].tox, group_peer_exit_handler);
State *state = (State *)autotoxes[i].state;
state->peers = (Peers *)calloc(1, sizeof(Peers));
ck_assert(state->peers != nullptr);
}
Tox *tox0 = autotoxes[0].tox;
State *state0 = (State *)autotoxes[0].state;
Tox_Err_Group_New err_new;
uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *) "test", 4,
(const uint8_t *)"test", 4, &err_new);
ck_assert(err_new == TOX_ERR_GROUP_NEW_OK);
fprintf(stderr, "tox0 creats new group and invites all his friends");
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, groupnumber, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "%d", id_err);
for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
Tox_Err_Group_Join join_err;
tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
} while (!all_peers_connected(autotoxes, groupnumber));
fprintf(stderr, "%d peers joined the group\n", NUM_GROUP_TOXES);
Tox_Err_Group_Founder_Set_Topic_Lock lock_set_err;
tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED, &lock_set_err);
ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to disable topic lock: %d",
lock_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
fprintf(stderr, "founder disabled topic lock; all peers try to set the topic\n");
topic_spam(rng, autotoxes, NUM_GROUP_TOXES, groupnumber);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED, &lock_set_err);
ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to enable topic lock: %d",
lock_set_err);
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
} while (!all_peers_have_same_topic(autotoxes, NUM_GROUP_TOXES, groupnumber)
&& !all_peers_see_same_roles(autotoxes, NUM_GROUP_TOXES, groupnumber)
&& state0->peers->num_peers != NUM_GROUP_TOXES - 1);
Tox_Err_Group_Mod_Set_Role role_err;
for (size_t i = 0; i < state0->peers->num_peers; ++i) {
tox_group_mod_set_role(tox0, groupnumber, (uint32_t)state0->peers->peer_ids[i], TOX_GROUP_ROLE_MODERATOR,
&role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set moderator. error: %d", role_err);
}
fprintf(stderr, "founder enabled topic lock and set all peers to moderator role\n");
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
} while (!all_peers_see_same_roles(autotoxes, NUM_GROUP_TOXES, groupnumber));
topic_spam(rng, autotoxes, NUM_GROUP_TOXES, groupnumber);
const unsigned int num_demoted = state0->peers->num_peers / 2;
fprintf(stderr, "founder demoting %u moderators to user\n", num_demoted);
for (size_t i = 0; i < num_demoted; ++i) {
tox_group_mod_set_role(tox0, groupnumber, (uint32_t)state0->peers->peer_ids[i], TOX_GROUP_ROLE_USER,
&role_err);
ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set user. error: %d", role_err);
}
do {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
} while (!all_peers_see_same_roles(autotoxes, NUM_GROUP_TOXES, groupnumber));
fprintf(stderr, "Remaining moderators spam change non-moderator roles\n");
role_spam(rng, autotoxes, NUM_GROUP_TOXES, num_demoted, groupnumber);
fprintf(stderr, "All peers see the same roles\n");
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
tox_group_leave(autotoxes[i].tox, groupnumber, nullptr, 0, nullptr);
State *state = (State *)autotoxes[i].state;
peers_cleanup(state->peers);
}
fprintf(stderr, "All tests passed!\n");
#endif // VANILLA_NACL
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_sync_test, sizeof(State), &autotest_opts);
return 0;
}
#undef NUM_GROUP_TOXES
#undef ROLE_SPAM_ITERATIONS
#undef TOPIC_SPAM_ITERATIONS

View File

@ -0,0 +1,255 @@
/*
* Does a basic functionality test for TCP connections.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "auto_test_support.h"
#ifdef USE_TEST_NETWORK
#define NUM_GROUP_TOXES 2
#define CODEWORD "RONALD MCDONALD"
#define CODEWORD_LEN (sizeof(CODEWORD) - 1)
typedef struct State {
size_t num_peers;
bool got_code;
bool got_second_code;
uint32_t peer_id[NUM_GROUP_TOXES - 1];
} State;
static void group_invite_handler(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length,
const uint8_t *group_name, size_t group_name_length, void *user_data)
{
printf("Accepting friend invite\n");
Tox_Err_Group_Invite_Accept err_accept;
tox_group_invite_accept(tox, friend_number, invite_data, length, (const uint8_t *)"test", 4,
nullptr, 0, &err_accept);
ck_assert(err_accept == TOX_ERR_GROUP_INVITE_ACCEPT_OK);
}
static void group_peer_join_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
fprintf(stderr, "joined: %zu, %u\n", state->num_peers, peer_id);
ck_assert_msg(state->num_peers < NUM_GROUP_TOXES - 1, "%zu", state->num_peers);
state->peer_id[state->num_peers++] = peer_id;
}
static void group_private_message_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(length == CODEWORD_LEN);
ck_assert(memcmp(CODEWORD, message, length) == 0);
printf("Codeword: %s\n", CODEWORD);
state->got_code = true;
}
static void group_message_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, uint32_t message_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
ck_assert(length == CODEWORD_LEN);
ck_assert(memcmp(CODEWORD, message, length) == 0);
printf("Codeword: %s\n", CODEWORD);
state->got_second_code = true;
}
/*
* We need different constants to make TCP run smoothly. TODO(Jfreegman): is this because of the group
* implementation or just an autotest quirk?
*/
#define GROUP_ITERATION_INTERVAL 100
static void iterate_group(AutoTox *autotoxes, uint32_t num_toxes, size_t interval)
{
for (uint32_t i = 0; i < num_toxes; i++) {
if (autotoxes[i].alive) {
tox_iterate(autotoxes[i].tox, &autotoxes[i]);
autotoxes[i].clock += interval;
}
}
c_sleep(50);
}
static bool all_peers_connected(AutoTox *autotoxes)
{
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL);
size_t count = 0;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
const State *state = (const State *)autotoxes[i].state;
if (state->num_peers == NUM_GROUP_TOXES - 1) {
++count;
}
}
return count == NUM_GROUP_TOXES;
}
static bool all_peers_got_code(AutoTox *autotoxes)
{
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL);
size_t count = 0;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
const State *state = (const State *)autotoxes[i].state;
if (state->got_code) {
++count;
}
}
return count == NUM_GROUP_TOXES - 1;
}
static void group_tcp_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert(NUM_GROUP_TOXES >= 2);
State *state0 = (State *)autotoxes[0].state;
State *state1 = (State *)autotoxes[1].state;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
tox_callback_group_private_message(autotoxes[i].tox, group_private_message_handler);
}
tox_callback_group_message(autotoxes[1].tox, group_message_handler);
tox_callback_group_invite(autotoxes[1].tox, group_invite_handler);
Tox_Err_Group_New new_err;
uint32_t groupnumber = tox_group_new(autotoxes[0].tox, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)"test", 4,
(const uint8_t *)"test", 4, &new_err);
ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err);
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL);
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(autotoxes[0].tox, groupnumber, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "%d", id_err);
printf("Tox 0 created new group...\n");
for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
Tox_Err_Group_Join jerr;
tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"test", 4, nullptr, 0, &jerr);
ck_assert_msg(jerr == TOX_ERR_GROUP_JOIN_OK, "%d", jerr);
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL * 10);
}
while (!all_peers_connected(autotoxes))
;
printf("%d peers successfully joined. Waiting for code...\n", NUM_GROUP_TOXES);
printf("Tox 0 sending secret code to all peers\n");
for (size_t i = 0; i < NUM_GROUP_TOXES - 1; ++i) {
Tox_Err_Group_Send_Private_Message perr;
tox_group_send_private_message(autotoxes[0].tox, groupnumber, state0->peer_id[i],
TOX_MESSAGE_TYPE_NORMAL,
(const uint8_t *)CODEWORD, CODEWORD_LEN, &perr);
ck_assert_msg(perr == TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK, "%d", perr);
}
while (!all_peers_got_code(autotoxes))
;
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[1].tox, groupnumber, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL);
state0->num_peers = 0;
state1->num_peers = 0;
// now do a friend invite to make sure the TCP-specific logic for friend invites is okay
printf("Tox1 leaves group and Tox0 does a friend group invite for tox1\n");
Tox_Err_Group_Invite_Friend err_invite;
tox_group_invite_friend(autotoxes[0].tox, groupnumber, 0, &err_invite);
ck_assert(err_invite == TOX_ERR_GROUP_INVITE_FRIEND_OK);
while (state0->num_peers == 0 && state1->num_peers == 0) {
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL);
}
printf("Tox 1 successfully joined. Waiting for code...\n");
Tox_Err_Group_Send_Message merr;
tox_group_send_message(autotoxes[0].tox, groupnumber, TOX_MESSAGE_TYPE_NORMAL,
(const uint8_t *)CODEWORD, CODEWORD_LEN, nullptr, &merr);
ck_assert(merr == TOX_ERR_GROUP_SEND_MESSAGE_OK);
while (!state1->got_second_code) {
iterate_group(autotoxes, NUM_GROUP_TOXES, GROUP_ITERATION_INTERVAL);
}
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
tox_group_leave(autotoxes[i].tox, groupnumber, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
}
printf("Test passed!\n");
#endif // VANILLA_NACL
}
#endif // USE_TEST_NETWORK
int main(void)
{
#ifdef USE_TEST_NETWORK // TODO(Jfreegman): Enable this test when the mainnet works with DHT groupchats
setvbuf(stdout, nullptr, _IONBF, 0);
struct Tox_Options *options = (struct Tox_Options *)calloc(1, sizeof(struct Tox_Options));
ck_assert(options != nullptr);
tox_options_default(options);
tox_options_set_udp_enabled(options, false);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(options, NUM_GROUP_TOXES, group_tcp_test, sizeof(State), &autotest_opts);
tox_options_free(options);
#endif
return 0;
}
#ifdef USE_TEST_NETWORK
#undef NUM_GROUP_TOXES
#undef CODEWORD_LEN
#undef CODEWORD
#endif // USE_TEST_NETWORK

View File

@ -0,0 +1,345 @@
/*
* Tests that we can successfully change the group topic, that all peers receive topic changes
* and that the topic lock works as intended.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "auto_test_support.h"
#include "check_compat.h"
#include "../toxcore/tox.h"
#include "../toxcore/group_chats.h"
#define NUM_GROUP_TOXES 3
#define TOPIC "They're waiting for you Gordon...in the test chamber"
#define TOPIC_LEN (sizeof(TOPIC) - 1)
#define TOPIC2 "They're waiting for you Gordon...in the test chamber 2.0"
#define TOPIC_LEN2 (sizeof(TOPIC2) - 1)
#define GROUP_NAME "The Test Chamber"
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
#define PEER0_NICK "Koresh"
#define PEER0_NICK_LEN (sizeof(PEER0_NICK) - 1)
typedef struct State {
uint32_t peer_id; // the id of the peer we set to observer
} State;
static bool all_group_peers_connected(const AutoTox *autotoxes, uint32_t tox_count, uint32_t groupnumber,
size_t name_length, uint32_t peer_limit)
{
for (uint32_t i = 0; i < tox_count; ++i) {
// make sure we got an invite
if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != name_length) {
return false;
}
// make sure we got a sync response
if (peer_limit != 0 && tox_group_get_peer_limit(autotoxes[i].tox, groupnumber, nullptr) != peer_limit) {
return false;
}
// make sure we're actually connected
if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) {
return false;
}
}
return true;
}
static void group_peer_join_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
state->peer_id = peer_id;
}
static void group_topic_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic,
size_t length, void *user_data)
{
ck_assert(length <= TOX_GROUP_MAX_TOPIC_LENGTH);
Tox_Err_Group_State_Queries query_err;
uint8_t topic2[TOX_GROUP_MAX_TOPIC_LENGTH];
tox_group_get_topic(tox, groupnumber, topic2, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
size_t topic_length = tox_group_get_topic_size(tox, groupnumber, &query_err);
ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(topic_length == length && memcmp(topic, topic2, length) == 0,
"topic differs in callback: %s, %s", topic, topic2);
}
static void group_topic_lock_handler(Tox *tox, uint32_t groupnumber, Tox_Group_Topic_Lock topic_lock, void *user_data)
{
Tox_Err_Group_State_Queries err;
Tox_Group_Topic_Lock current_lock = tox_group_get_topic_lock(tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
ck_assert_msg(topic_lock == current_lock, "topic locks differ in callback");
}
/* Sets group topic.
*
* Return true on success.
*/
static bool set_topic(Tox *tox, uint32_t groupnumber, const char *topic, size_t length)
{
Tox_Err_Group_Topic_Set err;
tox_group_set_topic(tox, groupnumber, (const uint8_t *)topic, length, &err);
return err == TOX_ERR_GROUP_TOPIC_SET_OK;
}
/* Returns 0 if group topic matches expected topic.
* Returns a value < 0 on failure.
*/
static int check_topic(const Tox *tox, uint32_t groupnumber, const char *expected_topic, size_t expected_length)
{
Tox_Err_Group_State_Queries query_err;
size_t topic_length = tox_group_get_topic_size(tox, groupnumber, &query_err);
if (query_err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
return -1;
}
if (expected_length != topic_length) {
return -2;
}
uint8_t topic[TOX_GROUP_MAX_TOPIC_LENGTH];
tox_group_get_topic(tox, groupnumber, topic, &query_err);
if (query_err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
return -3;
}
if (memcmp(expected_topic, (const char *)topic, topic_length) != 0) {
return -4;
}
return 0;
}
static void wait_topic_lock(AutoTox *autotoxes, uint32_t groupnumber, Tox_Group_Topic_Lock expected_lock)
{
while (1) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
uint32_t count = 0;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
Tox_Err_Group_State_Queries err;
Tox_Group_Topic_Lock topic_lock = tox_group_get_topic_lock(autotoxes[i].tox, groupnumber, &err);
ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
if (topic_lock == expected_lock) {
++count;
}
}
if (count == NUM_GROUP_TOXES) {
break;
}
}
}
/* Waits for all peers in group to see the same topic */
static void wait_state_topic(AutoTox *autotoxes, uint32_t groupnumber, const char *topic, size_t length)
{
while (1) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
uint32_t count = 0;
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
const int c_ret = check_topic(autotoxes[i].tox, groupnumber, topic, length);
if (c_ret == 0) {
++count;
}
}
if (count == NUM_GROUP_TOXES) {
break;
}
}
}
/* All peers attempt to set the topic.
*
* Returns the number of peers who succeeeded.
*/
static uint32_t set_topic_all_peers(const Random *rng, AutoTox *autotoxes, size_t num_peers, uint32_t groupnumber)
{
uint32_t change_count = 0;
for (size_t i = 0; i < num_peers; ++i) {
char new_topic[TOX_GROUP_MAX_TOPIC_LENGTH];
snprintf(new_topic, sizeof(new_topic), "peer %zu changes topic %u", i, random_u32(rng));
size_t length = strlen(new_topic);
if (set_topic(autotoxes[i].tox, groupnumber, new_topic, length)) {
wait_state_topic(autotoxes, groupnumber, new_topic, length);
++change_count;
} else {
fprintf(stderr, "Peer %zu couldn't set the topic\n", i);
}
}
return change_count;
}
static void group_topic_test(AutoTox *autotoxes)
{
#ifndef VANILLA_NACL
ck_assert_msg(NUM_GROUP_TOXES >= 3, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Tox *tox0 = autotoxes[0].tox;
const State *state0 = (const State *)autotoxes[0].state;
tox_callback_group_peer_join(tox0, group_peer_join_handler);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_topic(autotoxes[i].tox, group_topic_handler);
tox_callback_group_topic_lock(autotoxes[i].tox, group_topic_lock_handler);
}
/* Tox1 creates a group and is the founder of a newly created group */
Tox_Err_Group_New new_err;
const uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME,
GROUP_NAME_LEN,
(const uint8_t *)PEER0_NICK, PEER0_NICK_LEN, &new_err);
ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
/* Founder sets group topic before anyone else joins */
const bool s_ret = set_topic(tox0, groupnumber, TOPIC, TOPIC_LEN);
ck_assert_msg(s_ret, "Founder failed to set topic");
/* Founder gets the Chat ID and implicitly shares it publicly */
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, groupnumber, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "tox_group_get_chat_id failed %d", id_err);
/* All other peers join the group using the Chat ID */
for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
Tox_Err_Group_Join join_err;
tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "tox_group_join failed: %d", join_err);
c_sleep(100);
}
fprintf(stderr, "Peers attempting to join group\n");
all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnumber, GROUP_NAME_LEN, MAX_GC_PEERS_DEFAULT);
wait_state_topic(autotoxes, groupnumber, TOPIC, TOPIC_LEN);
/* Founder disables topic lock */
Tox_Err_Group_Founder_Set_Topic_Lock lock_set_err;
tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED, &lock_set_err);
ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to disable topic lock: %d",
lock_set_err);
fprintf(stderr, "Topic lock disabled\n");
/* make sure every peer sees the topic lock state change */
wait_topic_lock(autotoxes, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED);
/* All peers should be able to change the topic now */
uint32_t change_count = set_topic_all_peers(rng, autotoxes, NUM_GROUP_TOXES, groupnumber);
ck_assert_msg(change_count == NUM_GROUP_TOXES, "%u peers changed the topic with topic lock disabled", change_count);
/* founder silences the last peer he saw join */
Tox_Err_Group_Mod_Set_Role merr;
tox_group_mod_set_role(tox0, groupnumber, state0->peer_id, TOX_GROUP_ROLE_OBSERVER, &merr);
ck_assert_msg(merr == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set %u to observer role: %d", state0->peer_id, merr);
fprintf(stderr, "Random peer is set to observer\n");
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
/* All peers except one should now be able to change the topic */
change_count = set_topic_all_peers(rng, autotoxes, NUM_GROUP_TOXES, groupnumber);
ck_assert_msg(change_count == NUM_GROUP_TOXES - 1, "%u peers changed the topic with a silenced peer", change_count);
/* Founder enables topic lock and sets topic back to original */
tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED, &lock_set_err);
ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to enable topic lock: %d",
lock_set_err);
fprintf(stderr, "Topic lock enabled\n");
/* Wait for all peers to get topic lock state change */
wait_topic_lock(autotoxes, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED);
const bool s3_ret = set_topic(tox0, groupnumber, TOPIC2, TOPIC_LEN2);
ck_assert_msg(s3_ret, "Founder failed to set topic second time");
wait_state_topic(autotoxes, groupnumber, TOPIC2, TOPIC_LEN2);
/* No peer excluding the founder should be able to set the topic */
change_count = set_topic_all_peers(rng, &autotoxes[1], NUM_GROUP_TOXES - 1, groupnumber);
ck_assert_msg(change_count == 0, "%u peers changed the topic with topic lock enabled", change_count);
/* A final check that the topic is unchanged */
wait_state_topic(autotoxes, groupnumber, TOPIC2, TOPIC_LEN2);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[i].tox, groupnumber, nullptr, 0, &err_exit);
ck_assert_msg(err_exit == TOX_ERR_GROUP_LEAVE_OK, "%d", err_exit);
}
fprintf(stderr, "All tests passed!\n");
#endif /* VANILLA_NACL */
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_topic_test, sizeof(State), &autotest_opts);
return 0;
}
#undef TOPIC
#undef TOPIC_LEN
#undef TOPIC2
#undef TOPIC_LEN2
#undef NUM_GROUP_TOXES
#undef GROUP_NAME
#undef GROUP_NAME_LEN
#undef PEER0_NICK
#undef PEER0_NICK_LEN

View File

@ -0,0 +1,48 @@
// Test to make sure that when UDP is disabled, and we set an invalid proxy,
// i.e. one that doesn't run a proxy server, then we don't get any connection.
#include <stdio.h>
#include "../testing/misc_tools.h"
#include "auto_test_support.h"
#include "check_compat.h"
static uint8_t const key[] = {
0x15, 0xE9, 0xC3, 0x09, 0xCF, 0xCB, 0x79, 0xFD,
0xDF, 0x0E, 0xBA, 0x05, 0x7D, 0xAB, 0xB4, 0x9F,
0xE1, 0x5F, 0x38, 0x03, 0xB1, 0xBF, 0xF0, 0x65,
0x36, 0xAE, 0x2E, 0x5B, 0xA5, 0xE4, 0x69, 0x0E,
};
// Try to bootstrap for 30 seconds.
#define NUM_ITERATIONS (unsigned)(30.0 / (ITERATION_INTERVAL / 1000.0))
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
struct Tox_Options *opts = tox_options_new(nullptr);
tox_options_set_udp_enabled(opts, false);
tox_options_set_proxy_type(opts, TOX_PROXY_TYPE_SOCKS5);
tox_options_set_proxy_host(opts, "localhost");
tox_options_set_proxy_port(opts, 51724);
Tox *tox = tox_new_log(opts, nullptr, nullptr);
tox_options_free(opts);
tox_add_tcp_relay(tox, "tox.ngc.zone", 33445, key, nullptr);
tox_bootstrap(tox, "tox.ngc.zone", 33445, key, nullptr);
printf("Waiting for connection...\n");
for (uint16_t i = 0; i < NUM_ITERATIONS; i++) {
tox_iterate(tox, nullptr);
c_sleep(ITERATION_INTERVAL);
// None of the iterations should have a connection.
const Tox_Connection status = tox_self_get_connection_status(tox);
ck_assert_msg(status == TOX_CONNECTION_NONE,
"unexpectedly got a connection (%d)", status);
}
tox_kill(tox);
return 0;
}

View File

@ -0,0 +1,48 @@
// Test that if UDP is enabled, and a proxy is provided that does not support
// UDP proxying, we disable UDP.
#include <stdio.h>
#include "../testing/misc_tools.h"
#include "auto_test_support.h"
#include "check_compat.h"
static uint8_t const key[] = {
0x15, 0xE9, 0xC3, 0x09, 0xCF, 0xCB, 0x79, 0xFD,
0xDF, 0x0E, 0xBA, 0x05, 0x7D, 0xAB, 0xB4, 0x9F,
0xE1, 0x5F, 0x38, 0x03, 0xB1, 0xBF, 0xF0, 0x65,
0x36, 0xAE, 0x2E, 0x5B, 0xA5, 0xE4, 0x69, 0x0E,
};
// Try to bootstrap for 30 seconds.
#define NUM_ITERATIONS (unsigned)(30.0 / (ITERATION_INTERVAL / 1000.0))
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
struct Tox_Options *opts = tox_options_new(nullptr);
tox_options_set_udp_enabled(opts, true);
tox_options_set_proxy_type(opts, TOX_PROXY_TYPE_SOCKS5);
tox_options_set_proxy_host(opts, "localhost");
tox_options_set_proxy_port(opts, 51724);
Tox *tox = tox_new_log(opts, nullptr, nullptr);
tox_options_free(opts);
tox_add_tcp_relay(tox, "tox.ngc.zone", 33445, key, nullptr);
tox_bootstrap(tox, "tox.ngc.zone", 33445, key, nullptr);
printf("Waiting for connection...");
for (uint16_t i = 0; i < NUM_ITERATIONS; i++) {
tox_iterate(tox, nullptr);
c_sleep(ITERATION_INTERVAL);
// None of the iterations should have a connection.
const Tox_Connection status = tox_self_get_connection_status(tox);
ck_assert_msg(status == TOX_CONNECTION_NONE,
"unexpectedly got a connection (%d)", status);
}
tox_kill(tox);
return 0;
}

View File

@ -0,0 +1,53 @@
#include <stdio.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/tox_struct.h"
#include "auto_test_support.h"
static uint64_t get_state_clock_callback(void *user_data)
{
const uint64_t *clock = (const uint64_t *)user_data;
return *clock;
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Tox *tox1 = tox_new_log_lan(nullptr, nullptr, nullptr, /* lan_discovery */true);
Tox *tox2 = tox_new_log_lan(nullptr, nullptr, nullptr, /* lan_discovery */true);
ck_assert(tox1 != nullptr);
ck_assert(tox2 != nullptr);
uint64_t clock = current_time_monotonic(tox1->mono_time);
Mono_Time *mono_time;
mono_time = tox1->mono_time;
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
mono_time = tox2->mono_time;
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
printf("Waiting for LAN discovery. This loop will attempt to run until successful.");
do {
printf(".");
fflush(stdout);
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(5);
clock += 100;
} while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE);
printf(" %d <-> %d\n",
tox_self_get_connection_status(tox1),
tox_self_get_connection_status(tox2));
tox_kill(tox2);
tox_kill(tox1);
return 0;
}

View File

@ -0,0 +1,67 @@
/* Tests that we can send lossless packets.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/util.h"
#include "check_compat.h"
typedef struct State {
bool custom_packet_received;
} State;
#include "auto_test_support.h"
#define LOSSLESS_PACKET_FILLER 160
static void handle_lossless_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data)
{
uint8_t *cmp_packet = (uint8_t *)malloc(tox_max_custom_packet_size());
ck_assert(cmp_packet != nullptr);
memset(cmp_packet, LOSSLESS_PACKET_FILLER, tox_max_custom_packet_size());
if (length == tox_max_custom_packet_size() && memcmp(data, cmp_packet, tox_max_custom_packet_size()) == 0) {
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
state->custom_packet_received = true;
}
free(cmp_packet);
}
static void test_lossless_packet(AutoTox *autotoxes)
{
tox_callback_friend_lossless_packet(autotoxes[1].tox, &handle_lossless_packet);
const size_t packet_size = tox_max_custom_packet_size() + 1;
uint8_t *packet = (uint8_t *)malloc(packet_size);
ck_assert(packet != nullptr);
memset(packet, LOSSLESS_PACKET_FILLER, packet_size);
bool ret = tox_friend_send_lossless_packet(autotoxes[0].tox, 0, packet, packet_size, nullptr);
ck_assert_msg(ret == false, "should not be able to send custom packets this big %i", ret);
ret = tox_friend_send_lossless_packet(autotoxes[0].tox, 0, packet, tox_max_custom_packet_size(), nullptr);
ck_assert_msg(ret == true, "tox_friend_send_lossless_packet fail %i", ret);
free(packet);
do {
iterate_all_wait(autotoxes, 2, ITERATION_INTERVAL);
} while (!((State *)autotoxes[1].state)->custom_packet_received);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, test_lossless_packet, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,67 @@
/* Tests that we can send lossy packets.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/util.h"
#include "check_compat.h"
typedef struct State {
bool custom_packet_received;
} State;
#include "auto_test_support.h"
#define LOSSY_PACKET_FILLER 200
static void handle_lossy_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data)
{
uint8_t *cmp_packet = (uint8_t *)malloc(tox_max_custom_packet_size());
ck_assert(cmp_packet != nullptr);
memset(cmp_packet, LOSSY_PACKET_FILLER, tox_max_custom_packet_size());
if (length == tox_max_custom_packet_size() && memcmp(data, cmp_packet, tox_max_custom_packet_size()) == 0) {
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
state->custom_packet_received = true;
}
free(cmp_packet);
}
static void test_lossy_packet(AutoTox *autotoxes)
{
tox_callback_friend_lossy_packet(autotoxes[1].tox, &handle_lossy_packet);
const size_t packet_size = tox_max_custom_packet_size() + 1;
uint8_t *packet = (uint8_t *)malloc(packet_size);
ck_assert(packet != nullptr);
memset(packet, LOSSY_PACKET_FILLER, packet_size);
bool ret = tox_friend_send_lossy_packet(autotoxes[0].tox, 0, packet, packet_size, nullptr);
ck_assert_msg(ret == false, "should not be able to send custom packets this big %i", ret);
ret = tox_friend_send_lossy_packet(autotoxes[0].tox, 0, packet, tox_max_custom_packet_size(), nullptr);
ck_assert_msg(ret == true, "tox_friend_send_lossy_packet fail %i", ret);
free(packet);
do {
iterate_all_wait(autotoxes, 2, ITERATION_INTERVAL);
} while (!((State *)autotoxes[1].state)->custom_packet_received);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, test_lossy_packet, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,173 @@
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/network.h"
#include "check_compat.h"
#ifndef USE_IPV6
#define USE_IPV6 1
#endif
static void test_addr_resolv_localhost(void)
{
#ifdef __CYGWIN__
/* force initialization of network stack
* normally this should happen automatically
* cygwin doesn't do it for every network related function though
* e.g. not for getaddrinfo... */
net_socket(0, 0, 0);
errno = 0;
#endif
const Network *ns = system_network();
ck_assert(ns != nullptr);
const char localhost[] = "localhost";
IP ip;
ip_init(&ip, 0); // ipv6enabled = 0
bool res = addr_resolve_or_parse_ip(ns, localhost, &ip, nullptr);
int error = net_error();
char *strerror = net_new_strerror(error);
ck_assert_msg(res, "Resolver failed: %d, %s", error, strerror);
net_kill_strerror(strerror);
Ip_Ntoa ip_str;
ck_assert_msg(net_family_is_ipv4(ip.family), "Expected family TOX_AF_INET, got %u.", ip.family.value);
const uint32_t loopback = get_ip4_loopback().uint32;
ck_assert_msg(ip.ip.v4.uint32 == loopback, "Expected 127.0.0.1, got %s.",
net_ip_ntoa(&ip, &ip_str));
ip_init(&ip, 1); // ipv6enabled = 1
res = addr_resolve_or_parse_ip(ns, localhost, &ip, nullptr);
#if USE_IPV6
int localhost_split = 0;
if (!net_family_is_ipv6(ip.family)) {
res = addr_resolve_or_parse_ip(ns, "ip6-localhost", &ip, nullptr);
localhost_split = 1;
}
error = net_error();
strerror = net_new_strerror(error);
ck_assert_msg(res, "Resolver failed: %d, %s", error, strerror);
net_kill_strerror(strerror);
ck_assert_msg(net_family_is_ipv6(ip.family), "Expected family TOX_AF_INET6 (%d), got %u.", TOX_AF_INET6,
ip.family.value);
IP6 ip6_loopback = get_ip6_loopback();
ck_assert_msg(!memcmp(&ip.ip.v6, &ip6_loopback, sizeof(IP6)), "Expected ::1, got %s.",
net_ip_ntoa(&ip, &ip_str));
if (localhost_split) {
printf("Localhost seems to be split in two.\n");
return;
}
#endif
ip_init(&ip, 1); // ipv6enabled = 1
ip.family = net_family_unspec();
IP extra;
ip_reset(&extra);
res = addr_resolve_or_parse_ip(ns, localhost, &ip, &extra);
error = net_error();
strerror = net_new_strerror(error);
ck_assert_msg(res, "Resolver failed: %d, %s", error, strerror);
net_kill_strerror(strerror);
#if USE_IPV6
ck_assert_msg(net_family_is_ipv6(ip.family), "Expected family TOX_AF_INET6 (%d), got %u.", TOX_AF_INET6,
ip.family.value);
ck_assert_msg(!memcmp(&ip.ip.v6, &ip6_loopback, sizeof(IP6)), "Expected ::1, got %s.",
net_ip_ntoa(&ip, &ip_str));
ck_assert_msg(net_family_is_ipv4(extra.family), "Expected family TOX_AF_INET (%d), got %u.", TOX_AF_INET,
extra.family.value);
ck_assert_msg(extra.ip.v4.uint32 == loopback, "Expected 127.0.0.1, got %s.",
net_ip_ntoa(&ip, &ip_str));
#elif 0
// TODO(iphydf): Fix this to work on IPv6-supporting systems.
ck_assert_msg(net_family_is_ipv4(ip.family), "Expected family TOX_AF_INET (%d), got %u.", TOX_AF_INET, ip.family.value);
ck_assert_msg(ip.ip.v4.uint32 == loopback, "Expected 127.0.0.1, got %s.",
net_ip_ntoa(&ip, &ip_str));
#endif
}
static void test_ip_equal(void)
{
int res;
IP ip1, ip2;
ip_reset(&ip1);
ip_reset(&ip2);
res = ip_equal(nullptr, nullptr);
ck_assert_msg(res == 0, "ip_equal(NULL, NULL): expected result 0, got %d.", res);
res = ip_equal(&ip1, nullptr);
ck_assert_msg(res == 0, "ip_equal(PTR, NULL): expected result 0, got %d.", res);
res = ip_equal(nullptr, &ip1);
ck_assert_msg(res == 0, "ip_equal(NULL, PTR): expected result 0, got %d.", res);
ip1.family = net_family_ipv4();
ip1.ip.v4.uint32 = net_htonl(0x7F000001);
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res == 0, "ip_equal( {TOX_AF_INET, 127.0.0.1}, {TOX_AF_UNSPEC, 0} ): "
"expected result 0, got %d.", res);
ip2.family = net_family_ipv4();
ip2.ip.v4.uint32 = net_htonl(0x7F000001);
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res != 0, "ip_equal( {TOX_AF_INET, 127.0.0.1}, {TOX_AF_INET, 127.0.0.1} ): "
"expected result != 0, got 0.");
ip2.ip.v4.uint32 = net_htonl(0x7F000002);
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res == 0, "ip_equal( {TOX_AF_INET, 127.0.0.1}, {TOX_AF_INET, 127.0.0.2} ): "
"expected result 0, got %d.", res);
ip2.family = net_family_ipv6();
ip2.ip.v6.uint32[0] = 0;
ip2.ip.v6.uint32[1] = 0;
ip2.ip.v6.uint32[2] = net_htonl(0xFFFF);
ip2.ip.v6.uint32[3] = net_htonl(0x7F000001);
ck_assert_msg(ipv6_ipv4_in_v6(&ip2.ip.v6) != 0,
"ipv6_ipv4_in_v6(::ffff:127.0.0.1): expected != 0, got 0.");
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res != 0, "ip_equal( {TOX_AF_INET, 127.0.0.1}, {TOX_AF_INET6, ::ffff:127.0.0.1} ): "
"expected result != 0, got 0.");
IP6 ip6_loopback = get_ip6_loopback();
memcpy(&ip2.ip.v6, &ip6_loopback, sizeof(IP6));
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res == 0, "ip_equal( {TOX_AF_INET, 127.0.0.1}, {TOX_AF_INET6, ::1} ): expected result 0, got %d.", res);
memcpy(&ip1, &ip2, sizeof(IP));
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res != 0, "ip_equal( {TOX_AF_INET6, ::1}, {TOX_AF_INET6, ::1} ): expected result != 0, got 0.");
ip2.ip.v6.uint8[15]++;
res = ip_equal(&ip1, &ip2);
ck_assert_msg(res == 0, "ip_equal( {TOX_AF_INET6, ::1}, {TOX_AF_INET6, ::2} ): expected result 0, got %d.", res);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_addr_resolv_localhost();
test_ip_equal();
return 0;
}

View File

@ -0,0 +1,668 @@
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/mono_time.h"
#include "../toxcore/onion.h"
#include "../toxcore/onion_announce.h"
#include "../toxcore/onion_client.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
#ifndef USE_IPV6
#define USE_IPV6 1
#endif
static inline IP get_loopback(void)
{
IP ip;
#if USE_IPV6
ip.family = net_family_ipv6();
ip.ip.v6 = get_ip6_loopback();
#else
ip.family = net_family_ipv4();
ip.ip.v4 = get_ip4_loopback();
#endif
return ip;
}
static void do_onion(Mono_Time *mono_time, Onion *onion)
{
mono_time_update(mono_time);
networking_poll(onion->net, nullptr);
do_dht(onion->dht);
}
static int handled_test_1;
static int handle_test_1(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
const char req_message[] = "Install Gentoo";
uint8_t req_packet[1 + sizeof(req_message)];
req_packet[0] = NET_PACKET_ANNOUNCE_REQUEST;
memcpy(req_packet + 1, req_message, sizeof(req_message));
if (memcmp(packet, req_packet, sizeof(req_packet)) != 0) {
return 1;
}
const char res_message[] = "install gentoo";
uint8_t res_packet[1 + sizeof(res_message)];
res_packet[0] = NET_PACKET_ANNOUNCE_RESPONSE;
memcpy(res_packet + 1, res_message, sizeof(res_message));
if (send_onion_response(onion->net, source, res_packet, sizeof(res_packet),
packet + sizeof(res_packet)) == -1) {
return 1;
}
handled_test_1 = 1;
return 0;
}
static int handled_test_2;
static int handle_test_2(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
const char res_message[] = "install gentoo";
uint8_t res_packet[1 + sizeof(res_message)];
res_packet[0] = NET_PACKET_ANNOUNCE_RESPONSE;
memcpy(res_packet + 1, res_message, sizeof(res_message));
if (length != sizeof(res_packet)) {
return 1;
}
if (memcmp(packet, res_packet, sizeof(res_packet)) != 0) {
return 1;
}
handled_test_2 = 1;
return 0;
}
#if 0
void print_client_id(uint8_t *client_id, uint32_t length)
{
uint32_t j;
for (j = 0; j < length; j++) {
printf("%02X", client_id[j]);
}
printf("\n");
}
#endif
static uint8_t sb_data[ONION_ANNOUNCE_SENDBACK_DATA_LENGTH];
static int handled_test_3;
static uint8_t test_3_pub_key[CRYPTO_PUBLIC_KEY_SIZE];
static uint8_t test_3_ping_id[CRYPTO_SHA256_SIZE];
static int handle_test_3(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
if (length < ONION_ANNOUNCE_RESPONSE_MIN_SIZE || length > ONION_ANNOUNCE_RESPONSE_MAX_SIZE) {
return 1;
}
uint8_t plain[2 + CRYPTO_SHA256_SIZE];
#if 0
print_client_id(packet, length);
#endif
int len = decrypt_data(test_3_pub_key, dht_get_self_secret_key(onion->dht),
packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH,
packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE,
2 + CRYPTO_SHA256_SIZE + CRYPTO_MAC_SIZE, plain);
if (len == -1) {
return 1;
}
if (memcmp(packet + 1, sb_data, ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) != 0) {
return 1;
}
memcpy(test_3_ping_id, plain + 1, CRYPTO_SHA256_SIZE);
#if 0
print_client_id(test_3_ping_id, sizeof(test_3_ping_id));
#endif
handled_test_3 = 1;
return 0;
}
/* TODO: DEPRECATE */
static int handle_test_3_old(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
Onion *onion = (Onion *)object;
if (length < ONION_ANNOUNCE_RESPONSE_MIN_SIZE || length > ONION_ANNOUNCE_RESPONSE_MAX_SIZE) {
return 1;
}
uint8_t plain[2 + CRYPTO_SHA256_SIZE];
#if 0
print_client_id(packet, length);
#endif
int len = decrypt_data(test_3_pub_key, dht_get_self_secret_key(onion->dht),
packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH,
packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE,
1 + CRYPTO_SHA256_SIZE + CRYPTO_MAC_SIZE, plain);
if (len == -1) {
return 1;
}
if (memcmp(packet + 1, sb_data, ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) != 0) {
return 1;
}
memcpy(test_3_ping_id, plain + 1, CRYPTO_SHA256_SIZE);
#if 0
print_client_id(test_3_ping_id, sizeof(test_3_ping_id));
#endif
handled_test_3 = 1;
return 0;
}
static uint8_t nonce[CRYPTO_NONCE_SIZE];
static int handled_test_4;
static int handle_test_4(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
if (length != (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + sizeof("Install gentoo") +
CRYPTO_MAC_SIZE)) {
return 1;
}
uint8_t plain[sizeof("Install gentoo")] = {0};
if (memcmp(nonce, packet + 1, CRYPTO_NONCE_SIZE) != 0) {
return 1;
}
int len = decrypt_data(packet + 1 + CRYPTO_NONCE_SIZE, dht_get_self_secret_key(onion->dht), packet + 1,
packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, sizeof("Install gentoo") + CRYPTO_MAC_SIZE, plain);
if (len == -1) {
return 1;
}
if (memcmp(plain, "Install gentoo", sizeof("Install gentoo")) != 0) {
return 1;
}
handled_test_4 = 1;
return 0;
}
/** Create and send a onion packet.
*
* Use Onion_Path path to send data of length to dest.
* Maximum length of data is ONION_MAX_DATA_SIZE.
*/
static void send_onion_packet(const Networking_Core *net, const Random *rng, const Onion_Path *path, const IP_Port *dest, const uint8_t *data, uint16_t length)
{
uint8_t packet[ONION_MAX_PACKET_SIZE];
const int len = create_onion_packet(rng, packet, sizeof(packet), path, dest, data, length);
ck_assert_msg(len != -1, "failed to create onion packet");
ck_assert_msg(sendpacket(net, &path->ip_port1, packet, len) == len, "failed to send onion packet");
}
/** Initialize networking.
* Added for reverse compatibility with old new_networking calls.
*/
static Networking_Core *new_networking(const Logger *log, const Network *ns, const IP *ip, uint16_t port)
{
return new_networking_ex(log, ns, ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), nullptr);
}
static void test_basic(void)
{
uint32_t index[] = { 1, 2, 3 };
const Network *ns = system_network();
Logger *log1 = logger_new();
logger_callback_log(log1, print_debug_logger, nullptr, &index[0]);
Logger *log2 = logger_new();
logger_callback_log(log2, print_debug_logger, nullptr, &index[1]);
const Random *rng = system_random();
ck_assert(rng != nullptr);
Mono_Time *mono_time1 = mono_time_new(nullptr, nullptr);
Mono_Time *mono_time2 = mono_time_new(nullptr, nullptr);
IP ip = get_loopback();
Onion *onion1 = new_onion(log1, mono_time1, rng, new_dht(log1, rng, ns, mono_time1, new_networking(log1, ns, &ip, 36567), true, false));
Onion *onion2 = new_onion(log2, mono_time2, rng, new_dht(log2, rng, ns, mono_time2, new_networking(log2, ns, &ip, 36568), true, false));
ck_assert_msg((onion1 != nullptr) && (onion2 != nullptr), "Onion failed initializing.");
networking_registerhandler(onion2->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_test_1, onion2);
IP_Port on1 = {ip, net_port(onion1->net)};
Node_format n1;
memcpy(n1.public_key, dht_get_self_public_key(onion1->dht), CRYPTO_PUBLIC_KEY_SIZE);
n1.ip_port = on1;
IP_Port on2 = {ip, net_port(onion2->net)};
Node_format n2;
memcpy(n2.public_key, dht_get_self_public_key(onion2->dht), CRYPTO_PUBLIC_KEY_SIZE);
n2.ip_port = on2;
const char req_message[] = "Install Gentoo";
uint8_t req_packet[1 + sizeof(req_message)];
req_packet[0] = NET_PACKET_ANNOUNCE_REQUEST;
memcpy(req_packet + 1, req_message, sizeof(req_message));
Node_format nodes[4];
nodes[0] = n1;
nodes[1] = n2;
nodes[2] = n1;
nodes[3] = n2;
Onion_Path path;
create_onion_path(rng, onion1->dht, &path, nodes);
send_onion_packet(onion1->net, rng, &path, &nodes[3].ip_port, req_packet, sizeof(req_packet));
handled_test_1 = 0;
do {
do_onion(mono_time1, onion1);
do_onion(mono_time2, onion2);
} while (handled_test_1 == 0);
networking_registerhandler(onion1->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_test_2, onion1);
handled_test_2 = 0;
do {
do_onion(mono_time1, onion1);
do_onion(mono_time2, onion2);
} while (handled_test_2 == 0);
Onion_Announce *onion1_a = new_onion_announce(log1, rng, mono_time1, onion1->dht);
Onion_Announce *onion2_a = new_onion_announce(log2, rng, mono_time2, onion2->dht);
networking_registerhandler(onion1->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_test_3, onion1);
networking_registerhandler(onion1->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, &handle_test_3_old, onion1);
ck_assert_msg((onion1_a != nullptr) && (onion2_a != nullptr), "Onion_Announce failed initializing.");
uint8_t zeroes[64] = {0};
random_bytes(rng, sb_data, sizeof(sb_data));
uint64_t s;
memcpy(&s, sb_data, sizeof(uint64_t));
memcpy(test_3_pub_key, nodes[3].public_key, CRYPTO_PUBLIC_KEY_SIZE);
int ret = send_announce_request(onion1->net, rng, &path, &nodes[3],
dht_get_self_public_key(onion1->dht),
dht_get_self_secret_key(onion1->dht),
zeroes,
dht_get_self_public_key(onion1->dht),
dht_get_self_public_key(onion1->dht), s);
ck_assert_msg(ret == 0, "Failed to create/send onion announce_request packet.");
handled_test_3 = 0;
do {
do_onion(mono_time1, onion1);
do_onion(mono_time2, onion2);
c_sleep(50);
} while (handled_test_3 == 0);
printf("test 3 complete\n");
random_bytes(rng, sb_data, sizeof(sb_data));
memcpy(&s, sb_data, sizeof(uint64_t));
memcpy(onion_announce_entry_public_key(onion2_a, 1), dht_get_self_public_key(onion2->dht), CRYPTO_PUBLIC_KEY_SIZE);
onion_announce_entry_set_time(onion2_a, 1, mono_time_get(mono_time2));
networking_registerhandler(onion1->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_test_4, onion1);
send_announce_request(onion1->net, rng, &path, &nodes[3],
dht_get_self_public_key(onion1->dht),
dht_get_self_secret_key(onion1->dht),
test_3_ping_id,
dht_get_self_public_key(onion1->dht),
dht_get_self_public_key(onion1->dht), s);
do {
do_onion(mono_time1, onion1);
do_onion(mono_time2, onion2);
c_sleep(50);
} while (memcmp(onion_announce_entry_public_key(onion2_a, ONION_ANNOUNCE_MAX_ENTRIES - 2),
dht_get_self_public_key(onion1->dht),
CRYPTO_PUBLIC_KEY_SIZE) != 0);
c_sleep(1000);
Logger *log3 = logger_new();
logger_callback_log(log3, print_debug_logger, nullptr, &index[2]);
Mono_Time *mono_time3 = mono_time_new(nullptr, nullptr);
Onion *onion3 = new_onion(log3, mono_time3, rng, new_dht(log3, rng, ns, mono_time3, new_networking(log3, ns, &ip, 36569), true, false));
ck_assert_msg((onion3 != nullptr), "Onion failed initializing.");
random_nonce(rng, nonce);
ret = send_data_request(onion3->net, rng, &path, &nodes[3].ip_port,
dht_get_self_public_key(onion1->dht),
dht_get_self_public_key(onion1->dht),
nonce, (const uint8_t *)"Install gentoo", sizeof("Install gentoo"));
ck_assert_msg(ret == 0, "Failed to create/send onion data_request packet.");
handled_test_4 = 0;
do {
do_onion(mono_time1, onion1);
do_onion(mono_time2, onion2);
c_sleep(50);
} while (handled_test_4 == 0);
printf("test 4 complete\n");
kill_onion_announce(onion2_a);
kill_onion_announce(onion1_a);
{
Onion *onion = onion3;
Networking_Core *net = dht_get_net(onion->dht);
DHT *dht = onion->dht;
kill_onion(onion);
kill_dht(dht);
kill_networking(net);
mono_time_free(mono_time3);
logger_kill(log3);
}
{
Onion *onion = onion2;
Networking_Core *net = dht_get_net(onion->dht);
DHT *dht = onion->dht;
kill_onion(onion);
kill_dht(dht);
kill_networking(net);
mono_time_free(mono_time2);
logger_kill(log2);
}
{
Onion *onion = onion1;
Networking_Core *net = dht_get_net(onion->dht);
DHT *dht = onion->dht;
kill_onion(onion);
kill_dht(dht);
kill_networking(net);
mono_time_free(mono_time1);
logger_kill(log1);
}
}
typedef struct {
Logger *log;
Mono_Time *mono_time;
Onion *onion;
Onion_Announce *onion_a;
Onion_Client *onion_c;
} Onions;
static Onions *new_onions(const Random *rng, uint16_t port, uint32_t *index)
{
IP ip = get_loopback();
ip.ip.v6.uint8[15] = 1;
const Network *ns = system_network();
Onions *on = (Onions *)malloc(sizeof(Onions));
if (!on) {
return nullptr;
}
on->log = logger_new();
if (!on->log) {
free(on);
return nullptr;
}
logger_callback_log(on->log, print_debug_logger, nullptr, index);
on->mono_time = mono_time_new(nullptr, nullptr);
if (!on->mono_time) {
logger_kill(on->log);
free(on);
return nullptr;
}
Networking_Core *net = new_networking(on->log, ns, &ip, port);
if (!net) {
mono_time_free(on->mono_time);
logger_kill(on->log);
free(on);
return nullptr;
}
DHT *dht = new_dht(on->log, rng, ns, on->mono_time, net, true, false);
if (!dht) {
kill_networking(net);
mono_time_free(on->mono_time);
logger_kill(on->log);
free(on);
return nullptr;
}
on->onion = new_onion(on->log, on->mono_time, rng, dht);
if (!on->onion) {
kill_dht(dht);
kill_networking(net);
mono_time_free(on->mono_time);
logger_kill(on->log);
free(on);
return nullptr;
}
on->onion_a = new_onion_announce(on->log, rng, on->mono_time, dht);
if (!on->onion_a) {
kill_onion(on->onion);
kill_dht(dht);
kill_networking(net);
mono_time_free(on->mono_time);
logger_kill(on->log);
free(on);
return nullptr;
}
TCP_Proxy_Info inf = {{{{0}}}};
on->onion_c = new_onion_client(on->log, rng, on->mono_time, new_net_crypto(on->log, rng, ns, on->mono_time, dht, &inf));
if (!on->onion_c) {
kill_onion_announce(on->onion_a);
kill_onion(on->onion);
kill_dht(dht);
kill_networking(net);
mono_time_free(on->mono_time);
logger_kill(on->log);
free(on);
return nullptr;
}
return on;
}
static void do_onions(Onions *on)
{
mono_time_update(on->mono_time);
networking_poll(on->onion->net, nullptr);
do_dht(on->onion->dht);
do_onion_client(on->onion_c);
}
static void kill_onions(Onions *on)
{
Networking_Core *net = dht_get_net(on->onion->dht);
DHT *dht = on->onion->dht;
Net_Crypto *c = onion_get_net_crypto(on->onion_c);
kill_onion_client(on->onion_c);
kill_onion_announce(on->onion_a);
kill_onion(on->onion);
kill_net_crypto(c);
kill_dht(dht);
kill_networking(net);
mono_time_free(on->mono_time);
logger_kill(on->log);
free(on);
}
#define NUM_ONIONS 50
#define NUM_FIRST 7
#define NUM_LAST 37
static bool first_ip, last_ip;
static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port)
{
if (NUM_FIRST == number) {
first_ip = 1;
return;
}
if (NUM_LAST == number) {
last_ip = 1;
return;
}
ck_abort_msg("Error.");
}
static bool first, last;
static uint8_t first_dht_pk[CRYPTO_PUBLIC_KEY_SIZE];
static uint8_t last_dht_pk[CRYPTO_PUBLIC_KEY_SIZE];
static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key, void *userdata)
{
if ((NUM_FIRST == number && !first) || (NUM_LAST == number && !last)) {
Onions *on = (Onions *)object;
uint32_t token = 0;
int ret = dht_addfriend(on->onion->dht, dht_public_key, &dht_ip_callback, object, number, &token);
ck_assert_msg(ret == 0, "dht_addfriend() did not return 0");
ck_assert_msg(token == 1, "Count not 1, count is %u", token);
if (NUM_FIRST == number && !first) {
first = 1;
if (memcmp(dht_public_key, last_dht_pk, CRYPTO_PUBLIC_KEY_SIZE) != 0) {
ck_abort_msg("Error wrong dht key.");
}
return;
}
if (NUM_LAST == number && !last) {
last = 1;
if (memcmp(dht_public_key, first_dht_pk, CRYPTO_PUBLIC_KEY_SIZE) != 0) {
ck_abort_msg("Error wrong dht key.");
}
return;
}
ck_abort_msg("Error.");
}
}
static void test_announce(void)
{
uint32_t i, j;
uint32_t index[NUM_ONIONS];
Onions *onions[NUM_ONIONS];
const Random *rng = system_random();
ck_assert(rng != nullptr);
for (i = 0; i < NUM_ONIONS; ++i) {
index[i] = i + 1;
onions[i] = new_onions(rng, i + 36655, &index[i]);
ck_assert_msg(onions[i] != nullptr, "Failed to create onions. %u", i);
}
IP ip = get_loopback();
for (i = 3; i < NUM_ONIONS; ++i) {
IP_Port ip_port = {ip, net_port(onions[i - 1]->onion->net)};
dht_bootstrap(onions[i]->onion->dht, &ip_port, dht_get_self_public_key(onions[i - 1]->onion->dht));
IP_Port ip_port1 = {ip, net_port(onions[i - 2]->onion->net)};
dht_bootstrap(onions[i]->onion->dht, &ip_port1, dht_get_self_public_key(onions[i - 2]->onion->dht));
IP_Port ip_port2 = {ip, net_port(onions[i - 3]->onion->net)};
dht_bootstrap(onions[i]->onion->dht, &ip_port2, dht_get_self_public_key(onions[i - 3]->onion->dht));
}
uint32_t connected = 0;
do {
connected = 0;
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
connected += dht_isconnected(onions[i]->onion->dht);
}
c_sleep(50);
} while (connected != NUM_ONIONS);
printf("connected\n");
for (i = 0; i < 25 * 2; ++i) {
for (j = 0; j < NUM_ONIONS; ++j) {
do_onions(onions[j]);
}
c_sleep(50);
}
memcpy(first_dht_pk, dht_get_self_public_key(onions[NUM_FIRST]->onion->dht), CRYPTO_PUBLIC_KEY_SIZE);
memcpy(last_dht_pk, dht_get_self_public_key(onions[NUM_LAST]->onion->dht), CRYPTO_PUBLIC_KEY_SIZE);
printf("adding friend\n");
int frnum_f = onion_addfriend(onions[NUM_FIRST]->onion_c,
nc_get_self_public_key(onion_get_net_crypto(onions[NUM_LAST]->onion_c)));
int frnum = onion_addfriend(onions[NUM_LAST]->onion_c,
nc_get_self_public_key(onion_get_net_crypto(onions[NUM_FIRST]->onion_c)));
onion_dht_pk_callback(onions[NUM_FIRST]->onion_c, frnum_f, &dht_pk_callback, onions[NUM_FIRST], NUM_FIRST);
onion_dht_pk_callback(onions[NUM_LAST]->onion_c, frnum, &dht_pk_callback, onions[NUM_LAST], NUM_LAST);
IP_Port ip_port;
do {
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
}
c_sleep(50);
} while (!first || !last);
printf("Waiting for ips\n");
do {
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
}
c_sleep(50);
} while (!first_ip || !last_ip);
onion_getfriendip(onions[NUM_LAST]->onion_c, frnum, &ip_port);
ck_assert_msg(ip_port.port == net_port(onions[NUM_FIRST]->onion->net), "Port in returned ip not correct.");
for (i = 0; i < NUM_ONIONS; ++i) {
kill_onions(onions[i]);
}
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_basic();
test_announce();
return 0;
}

View File

@ -0,0 +1,64 @@
/* Try to overflow the net_crypto packet buffer.
*/
#include <stdint.h>
typedef struct State {
uint32_t recv_count;
} State;
#include "auto_test_support.h"
#define NUM_MSGS 40000
static void handle_friend_message(Tox *tox, uint32_t friend_number, Tox_Message_Type type,
const uint8_t *message, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
state->recv_count++;
}
static void net_crypto_overflow_test(AutoTox *autotoxes)
{
tox_callback_friend_message(autotoxes[0].tox, handle_friend_message);
printf("sending many messages to tox0\n");
for (uint32_t tox_index = 1; tox_index < 3; tox_index++) {
for (uint32_t i = 0; i < NUM_MSGS; i++) {
uint8_t message[128] = {0};
snprintf((char *)message, sizeof(message), "%u-%u", tox_index, i);
Tox_Err_Friend_Send_Message err;
tox_friend_send_message(autotoxes[tox_index].tox, 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof message, &err);
if (err == TOX_ERR_FRIEND_SEND_MESSAGE_SENDQ) {
printf("tox%u sent %u messages to friend 0\n", tox_index, i);
break;
}
ck_assert_msg(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK,
"tox%u failed to send message number %u: %d", tox_index, i, err);
}
}
// TODO(iphydf): Wait until all messages have arrived. Currently, not all
// messages arrive, so this test would always fail.
for (uint32_t i = 0; i < 200; i++) {
iterate_all_wait(autotoxes, 3, ITERATION_INTERVAL);
}
printf("tox%u received %u messages\n", autotoxes[0].index, ((State *)autotoxes[0].state)->recv_count);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 3, net_crypto_overflow_test, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,46 @@
/* Try to overflow the net_crypto packet buffer.
*/
#include <stdint.h>
#include "auto_test_support.h"
#define NUM_MSGS 40000
static void net_crypto_overflow_test(AutoTox *autotoxes)
{
const uint8_t message[] = {0};
bool errored = false;
for (uint32_t i = 0; i < NUM_MSGS; i++) {
Tox_Err_Friend_Send_Message err;
tox_friend_send_message(autotoxes[0].tox, 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof message, &err);
if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK) {
errored = true;
}
if (errored) {
// As soon as we get the first error, we expect the same error (SENDQ)
// every time we try to send.
ck_assert_msg(err == TOX_ERR_FRIEND_SEND_MESSAGE_SENDQ,
"expected SENDQ error on message %u, but got %d", i, err);
} else {
ck_assert_msg(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK,
"failed to send message number %u: %d", i, err);
}
}
ck_assert_msg(errored, "expected SENDQ error at some point (increase NUM_MSGS?)");
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, net_crypto_overflow_test, 0, &options);
return 0;
}

View File

@ -0,0 +1,129 @@
/* Tests that we can send messages to friends.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "auto_test_support.h"
static void *proxy_routine(void *arg)
{
const char *proxy_bin = (const char *)arg;
ck_assert(proxy_bin != nullptr);
printf("starting http/sock5 proxy: %s\n", proxy_bin);
ck_assert(system(proxy_bin) == 0);
return nullptr;
}
static bool try_bootstrap(Tox *tox1, Tox *tox2, Tox *tox3, Tox *tox4)
{
for (uint32_t i = 0; i < 400; ++i) {
if (tox_self_get_connection_status(tox1) != TOX_CONNECTION_NONE &&
tox_self_get_connection_status(tox2) != TOX_CONNECTION_NONE &&
tox_self_get_connection_status(tox3) != TOX_CONNECTION_NONE &&
tox_self_get_connection_status(tox4) != TOX_CONNECTION_NONE) {
printf("%d %d %d %d\n",
tox_self_get_connection_status(tox1),
tox_self_get_connection_status(tox2),
tox_self_get_connection_status(tox3),
tox_self_get_connection_status(tox4));
return true;
}
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
tox_iterate(tox4, nullptr);
if (i % 10 == 0) {
printf("%d %d %d %d\n",
tox_self_get_connection_status(tox1),
tox_self_get_connection_status(tox2),
tox_self_get_connection_status(tox3),
tox_self_get_connection_status(tox4));
}
c_sleep(tox_iteration_interval(tox1));
}
return false;
}
int main(int argc, char **argv)
{
setvbuf(stdout, nullptr, _IONBF, 0);
if (argc >= 3) {
char *proxy_bin = argv[2];
pthread_t proxy_thread;
pthread_create(&proxy_thread, nullptr, proxy_routine, proxy_bin);
c_sleep(100);
}
const uint16_t tcp_port = 8082;
uint32_t index[] = { 1, 2, 3, 4 };
struct Tox_Options *tox_options = tox_options_new(nullptr);
ck_assert(tox_options != nullptr);
// tox1 is a TCP server and has UDP enabled.
tox_options_set_udp_enabled(tox_options, true);
tox_options_set_tcp_port(tox_options, tcp_port);
Tox *tox1 = tox_new_log(tox_options, nullptr, &index[0]);
ck_assert(tox1 != nullptr);
// Get tox1's DHT key and port.
uint8_t dht_pk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_pk);
uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
ck_assert(dht_port != 0);
// tox2 is a regular DHT node bootstrapping against tox1.
tox_options_set_udp_enabled(tox_options, true);
tox_options_set_tcp_port(tox_options, 0);
Tox *tox2 = tox_new_log(tox_options, nullptr, &index[1]);
ck_assert(tox2 != nullptr);
// tox2 bootstraps against tox1 with UDP.
ck_assert(tox_bootstrap(tox2, "127.0.0.1", dht_port, dht_pk, nullptr));
// tox3 has UDP disabled and connects to tox1 via an HTTP proxy
tox_options_set_udp_enabled(tox_options, false);
tox_options_set_proxy_host(tox_options, "127.0.0.1");
tox_options_set_proxy_port(tox_options, 8080);
tox_options_set_proxy_type(tox_options, TOX_PROXY_TYPE_HTTP);
Tox *tox3 = tox_new_log(tox_options, nullptr, &index[2]);
ck_assert(tox3 != nullptr);
// tox4 has UDP disabled and connects to tox1 via a SOCKS5 proxy
tox_options_set_udp_enabled(tox_options, false);
tox_options_set_proxy_host(tox_options, "127.0.0.1");
tox_options_set_proxy_port(tox_options, 8081);
tox_options_set_proxy_type(tox_options, TOX_PROXY_TYPE_SOCKS5);
Tox *tox4 = tox_new_log(tox_options, nullptr, &index[3]);
ck_assert(tox4 != nullptr);
// tox3 and tox4 bootstrap against tox1 and add it as a TCP relay
ck_assert(tox_bootstrap(tox3, "127.0.0.1", dht_port, dht_pk, nullptr));
ck_assert(tox_add_tcp_relay(tox3, "127.0.0.1", tcp_port, dht_pk, nullptr));
ck_assert(tox_bootstrap(tox4, "127.0.0.1", dht_port, dht_pk, nullptr));
ck_assert(tox_add_tcp_relay(tox4, "127.0.0.1", tcp_port, dht_pk, nullptr));
int ret = 1;
if (try_bootstrap(tox1, tox2, tox3, tox4)) {
ret = 0;
}
tox_options_free(tox_options);
tox_kill(tox4);
tox_kill(tox3);
tox_kill(tox2);
tox_kill(tox1);
return ret;
}

View File

@ -0,0 +1,102 @@
/* Auto Tests: Reconnection.
*
* This test checks that when a tox instance is suspended for long enough that
* its friend connections time out, those connections are promptly
* re-established when the instance is resumed.
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/friend_connection.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "check_compat.h"
#define TOX_COUNT 2
#define RECONNECT_TIME_MAX (FRIEND_CONNECTION_TIMEOUT + 3)
#include "auto_test_support.h"
static uint32_t tox_connected_count(uint32_t tox_count, AutoTox *autotoxes, uint32_t index)
{
const size_t friend_count = tox_self_get_friend_list_size(autotoxes[index].tox);
uint32_t connected_count = 0;
for (size_t j = 0; j < friend_count; j++) {
if (tox_friend_get_connection_status(autotoxes[index].tox, j, nullptr) != TOX_CONNECTION_NONE) {
++connected_count;
}
}
return connected_count;
}
static bool all_disconnected_from(uint32_t tox_count, AutoTox *autotoxes, uint32_t index)
{
for (uint32_t i = 0; i < tox_count; i++) {
if (i == index) {
continue;
}
if (tox_connected_count(tox_count, autotoxes, i) >= tox_count - 1) {
return false;
}
}
return true;
}
static void test_reconnect(AutoTox *autotoxes)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
const time_t test_start_time = time(nullptr);
printf("letting connections settle\n");
do {
iterate_all_wait(autotoxes, TOX_COUNT, ITERATION_INTERVAL);
} while (time(nullptr) - test_start_time < 2);
const uint16_t disconnect = random_u16(rng) % TOX_COUNT;
printf("disconnecting #%u\n", autotoxes[disconnect].index);
do {
for (uint16_t i = 0; i < TOX_COUNT; ++i) {
if (i != disconnect) {
tox_iterate(autotoxes[i].tox, &autotoxes[i]);
autotoxes[i].clock += 1000;
}
}
c_sleep(20);
} while (!all_disconnected_from(TOX_COUNT, autotoxes, disconnect));
const uint64_t reconnect_start_time = autotoxes[0].clock;
printf("reconnecting\n");
do {
iterate_all_wait(autotoxes, TOX_COUNT, ITERATION_INTERVAL);
} while (!all_friends_connected(autotoxes, TOX_COUNT));
const uint64_t reconnect_time = autotoxes[0].clock - reconnect_start_time;
ck_assert_msg(reconnect_time <= RECONNECT_TIME_MAX * 1000, "reconnection took %d seconds; expected at most %d seconds",
(int)(reconnect_time / 1000), RECONNECT_TIME_MAX);
printf("test_reconnect succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time));
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, TOX_COUNT, test_reconnect, 0, &options);
return 0;
}

View File

@ -0,0 +1,161 @@
// Tests to make sure new save code is compatible with old save files
#include "../testing/misc_tools.h"
#include "../toxcore/tox.h"
#include "auto_test_support.h"
#include "check_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LOADED_SAVE_FILE "../auto_tests/data/save.tox"
// Information from the save file
#define EXPECTED_NAME "name"
#define EXPECTED_NAME_SIZE strlen(EXPECTED_NAME)
#define EXPECTED_STATUS_MESSAGE "Hello World"
#define EXPECTED_STATUS_MESSAGE_SIZE strlen(EXPECTED_STATUS_MESSAGE)
#define EXPECTED_NUM_FRIENDS 1
#define EXPECTED_NOSPAM "4C762C7D"
#define EXPECTED_TOX_ID "B70E97D41F69B7F4C42A5BC7BD7A76B95B8030BE1B7C0E9E6FC19FC4ABEB195B4C762C7D800B"
static size_t get_file_size(const char *save_path)
{
FILE *const fp = fopen(save_path, "rb");
if (fp == nullptr) {
return 0;
}
fseek(fp, 0, SEEK_END);
const size_t size = ftell(fp);
fclose(fp);
return size;
}
static uint8_t *read_save(const char *save_path, size_t *length)
{
const size_t size = get_file_size(save_path);
if (size == 0) {
return nullptr;
}
FILE *const fp = fopen(save_path, "rb");
if (!fp) {
return nullptr;
}
uint8_t *const data = (uint8_t *)malloc(size);
if (!data) {
fclose(fp);
return nullptr;
}
if (fread(data, size, 1, fp) != 1) {
free(data);
fclose(fp);
return nullptr;
}
*length = size;
fclose(fp);
return data;
}
static void test_save_compatibility(const char *save_path)
{
struct Tox_Options options = {0};
tox_options_default(&options);
size_t size = 0;
uint8_t *save_data = read_save(save_path, &size);
ck_assert_msg(save_data != nullptr, "error while reading save file '%s'", save_path);
options.savedata_data = save_data;
options.savedata_length = size;
options.savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
size_t index = 0;
Tox_Err_New err;
Tox *tox = tox_new_log(&options, &err, &index);
ck_assert_msg(tox, "failed to create tox, error number: %d", err);
free(save_data);
const size_t name_size = tox_self_get_name_size(tox);
ck_assert_msg(name_size == EXPECTED_NAME_SIZE, "name sizes do not match expected %zu got %zu", EXPECTED_NAME_SIZE,
name_size);
uint8_t *name = (uint8_t *)malloc(tox_self_get_name_size(tox));
ck_assert(name != nullptr);
tox_self_get_name(tox, name);
ck_assert_msg(strncmp((const char *)name, EXPECTED_NAME, name_size) == 0,
"names do not match, expected %s got %s", EXPECTED_NAME, name);
const size_t status_message_size = tox_self_get_status_message_size(tox);
ck_assert_msg(status_message_size == EXPECTED_STATUS_MESSAGE_SIZE,
"status message sizes do not match, expected %zu got %zu", EXPECTED_STATUS_MESSAGE_SIZE, status_message_size);
uint8_t *status_message = (uint8_t *)malloc(tox_self_get_status_message_size(tox));
ck_assert(status_message != nullptr);
tox_self_get_status_message(tox, status_message);
ck_assert_msg(strncmp((const char *)status_message, EXPECTED_STATUS_MESSAGE, status_message_size) == 0,
"status messages do not match, expected %s got %s",
EXPECTED_STATUS_MESSAGE, status_message);
const size_t num_friends = tox_self_get_friend_list_size(tox);
ck_assert_msg(num_friends == EXPECTED_NUM_FRIENDS,
"number of friends do not match, expected %d got %zu", EXPECTED_NUM_FRIENDS, num_friends);
const uint32_t nospam = tox_self_get_nospam(tox);
char nospam_str[TOX_NOSPAM_SIZE * 2 + 1];
const size_t length = snprintf(nospam_str, sizeof(nospam_str), "%08X", nospam);
nospam_str[length] = '\0';
ck_assert_msg(strcmp(nospam_str, EXPECTED_NOSPAM) == 0,
"nospam does not match, expected %s got %s", EXPECTED_NOSPAM, nospam_str);
uint8_t tox_id[TOX_ADDRESS_SIZE];
char tox_id_str[TOX_ADDRESS_SIZE * 2 + 1] = {0};
tox_self_get_address(tox, tox_id);
to_hex(tox_id_str, tox_id, TOX_ADDRESS_SIZE);
ck_assert_msg(strncmp(tox_id_str, EXPECTED_TOX_ID, TOX_ADDRESS_SIZE * 2) == 0,
"tox ids do not match, expected %s got %s", EXPECTED_TOX_ID, tox_id_str);
/* Giving the tox a chance to error on iterate due to corrupted loaded structures */
tox_iterate(tox, nullptr);
free(status_message);
free(name);
tox_kill(tox);
}
int main(int argc, char *argv[])
{
char base_path[4096] = {0};
if (argc <= 1) {
const char *srcdir = getenv("srcdir");
if (srcdir == nullptr) {
srcdir = ".";
}
snprintf(base_path, sizeof(base_path), "%s", srcdir);
} else {
snprintf(base_path, sizeof(base_path), "%s", argv[1]);
base_path[strrchr(base_path, '/') - base_path] = '\0';
}
char save_path[4096 + sizeof(LOADED_SAVE_FILE)];
snprintf(save_path, sizeof(save_path), "%s/%s", base_path, LOADED_SAVE_FILE);
test_save_compatibility(save_path);
return 0;
}

View File

@ -0,0 +1,159 @@
/* Auto Tests: Save and load friends.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/tox.h"
#include "auto_test_support.h"
#include "check_compat.h"
struct test_data {
uint8_t *name;
uint8_t *status_message;
bool received_name;
bool received_status_message;
};
static void set_random(Tox *m, const Random *rng, bool (*setter)(Tox *, const uint8_t *, size_t, Tox_Err_Set_Info *), size_t length)
{
VLA(uint8_t, text, length);
uint32_t i;
for (i = 0; i < length; ++i) {
text[i] = random_u08(rng);
}
setter(m, text, SIZEOF_VLA(text), nullptr);
}
static void alloc_string(uint8_t **to, size_t length)
{
free(*to);
*to = (uint8_t *)malloc(length);
ck_assert(*to != nullptr);
}
static void set_string(uint8_t **to, const uint8_t *from, size_t length)
{
alloc_string(to, length);
memcpy(*to, from, length);
}
static void namechange_callback(Tox *tox, uint32_t friend_number, const uint8_t *name, size_t length, void *user_data)
{
struct test_data *to_compare = (struct test_data *)user_data;
set_string(&to_compare->name, name, length);
to_compare->received_name = true;
}
static void statuschange_callback(Tox *tox, uint32_t friend_number, const uint8_t *message, size_t length,
void *user_data)
{
struct test_data *to_compare = (struct test_data *)user_data;
set_string(&to_compare->status_message, message, length);
to_compare->received_status_message = true;
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Tox *const tox1 = tox_new_log(nullptr, nullptr, nullptr);
Tox *const tox2 = tox_new_log(nullptr, nullptr, nullptr);
printf("bootstrapping tox2 off tox1\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr);
struct test_data to_compare = {nullptr};
uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_public_key(tox1, public_key);
tox_friend_add_norequest(tox2, public_key, nullptr);
tox_self_get_public_key(tox2, public_key);
tox_friend_add_norequest(tox1, public_key, nullptr);
uint8_t *reference_name = (uint8_t *)malloc(tox_max_name_length());
uint8_t *reference_status = (uint8_t *)malloc(tox_max_status_message_length());
ck_assert(reference_name != nullptr);
ck_assert(reference_status != nullptr);
const Random *rng = system_random();
ck_assert(rng != nullptr);
set_random(tox1, rng, tox_self_set_name, tox_max_name_length());
set_random(tox2, rng, tox_self_set_name, tox_max_name_length());
set_random(tox1, rng, tox_self_set_status_message, tox_max_status_message_length());
set_random(tox2, rng, tox_self_set_status_message, tox_max_status_message_length());
tox_self_get_name(tox2, reference_name);
tox_self_get_status_message(tox2, reference_status);
tox_callback_friend_name(tox1, namechange_callback);
tox_callback_friend_status_message(tox1, statuschange_callback);
while (true) {
if (tox_self_get_connection_status(tox1) &&
tox_self_get_connection_status(tox2) &&
tox_friend_get_connection_status(tox1, 0, nullptr) == TOX_CONNECTION_UDP) {
printf("Connected.\n");
break;
}
tox_iterate(tox1, &to_compare);
tox_iterate(tox2, nullptr);
c_sleep(tox_iteration_interval(tox1));
}
while (true) {
if (to_compare.received_name && to_compare.received_status_message) {
printf("Exchanged names and status messages.\n");
break;
}
tox_iterate(tox1, &to_compare);
tox_iterate(tox2, nullptr);
c_sleep(tox_iteration_interval(tox1));
}
size_t save_size = tox_get_savedata_size(tox1);
uint8_t *savedata = (uint8_t *)malloc(save_size);
tox_get_savedata(tox1, savedata);
struct Tox_Options *const options = tox_options_new(nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, savedata, save_size);
Tox *const tox_to_compare = tox_new_log(options, nullptr, nullptr);
alloc_string(&to_compare.name, tox_friend_get_name_size(tox_to_compare, 0, nullptr));
tox_friend_get_name(tox_to_compare, 0, to_compare.name, nullptr);
alloc_string(&to_compare.status_message, tox_friend_get_status_message_size(tox_to_compare, 0, nullptr));
tox_friend_get_status_message(tox_to_compare, 0, to_compare.status_message, nullptr);
ck_assert_msg(memcmp(reference_name, to_compare.name, tox_max_name_length()) == 0,
"incorrect name: should be all zeroes");
ck_assert_msg(memcmp(reference_status, to_compare.status_message, tox_max_status_message_length()) == 0,
"incorrect status message: should be all zeroes");
tox_options_free(options);
tox_kill(tox1);
tox_kill(tox2);
tox_kill(tox_to_compare);
free(savedata);
free(to_compare.name);
free(to_compare.status_message);
free(reference_status);
free(reference_name);
return 0;
}

View File

@ -0,0 +1,268 @@
/* Tests that we can save and load Tox data.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_struct.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
/* The Travis-CI container responds poorly to ::1 as a localhost address
* You're encouraged to -D FORCE_TESTS_IPV6 on a local test */
#ifdef TOX_LOCALHOST
#undef TOX_LOCALHOST
#endif
#ifdef FORCE_TESTS_IPV6
#define TOX_LOCALHOST "::1"
#else
#define TOX_LOCALHOST "127.0.0.1"
#endif
#ifdef TCP_RELAY_PORT
#undef TCP_RELAY_PORT
#endif
#define TCP_RELAY_PORT 33431
static void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_friend_add_norequest(m, public_key, nullptr);
}
}
static unsigned int connected_t1;
static void tox_connection_status(Tox *tox, Tox_Connection connection_status, void *user_data)
{
if (connected_t1 && !connection_status) {
ck_abort_msg("Tox went offline");
}
ck_assert_msg(connection_status != TOX_CONNECTION_NONE, "wrong status %d", connection_status);
connected_t1 = connection_status;
}
/* validate that:
* a) saving stays within the confined space
* b) a saved state can be loaded back successfully
* c) a second save is of equal size
* d) the second save is of equal content */
static void reload_tox(Tox **tox, struct Tox_Options *const in_opts, void *user_data)
{
const size_t extra = 64;
const size_t save_size1 = tox_get_savedata_size(*tox);
ck_assert_msg(save_size1 != 0, "save is invalid size %u", (unsigned)save_size1);
printf("%u\n", (unsigned)save_size1);
uint8_t *buffer = (uint8_t *)malloc(save_size1 + 2 * extra);
ck_assert_msg(buffer != nullptr, "malloc failed");
memset(buffer, 0xCD, extra);
memset(buffer + extra + save_size1, 0xCD, extra);
tox_get_savedata(*tox, buffer + extra);
tox_kill(*tox);
for (size_t i = 0; i < extra; ++i) {
ck_assert_msg(buffer[i] == 0xCD, "Buffer underwritten from tox_get_savedata() @%u", (unsigned)i);
ck_assert_msg(buffer[extra + save_size1 + i] == 0xCD, "Buffer overwritten from tox_get_savedata() @%u", (unsigned)i);
}
struct Tox_Options *const options = (in_opts == nullptr) ? tox_options_new(nullptr) : in_opts;
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, buffer + extra, save_size1);
*tox = tox_new_log(options, nullptr, user_data);
if (in_opts == nullptr) {
tox_options_free(options);
}
ck_assert_msg(*tox != nullptr, "Failed to load back stored buffer");
const size_t save_size2 = tox_get_savedata_size(*tox);
ck_assert_msg(save_size1 == save_size2, "Tox save data changed in size from a store/load cycle: %u -> %u",
(unsigned)save_size1, (unsigned)save_size2);
uint8_t *buffer2 = (uint8_t *)malloc(save_size2);
ck_assert_msg(buffer2 != nullptr, "malloc failed");
tox_get_savedata(*tox, buffer2);
ck_assert_msg(!memcmp(buffer + extra, buffer2, save_size2), "Tox state changed by store/load/store cycle");
free(buffer2);
free(buffer);
}
typedef struct Time_Data {
pthread_mutex_t lock;
uint64_t clock;
} Time_Data;
static uint64_t get_state_clock_callback(void *user_data)
{
Time_Data *time_data = (Time_Data *)user_data;
pthread_mutex_lock(&time_data->lock);
uint64_t clock = time_data->clock;
pthread_mutex_unlock(&time_data->lock);
return clock;
}
static void increment_clock(Time_Data *time_data, uint64_t count)
{
pthread_mutex_lock(&time_data->lock);
time_data->clock += count;
pthread_mutex_unlock(&time_data->lock);
}
static void set_current_time_callback(Tox *tox, Time_Data *time_data)
{
Mono_Time *mono_time = tox->mono_time;
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, time_data);
}
static void test_few_clients(void)
{
uint32_t index[] = { 1, 2, 3 };
time_t con_time = 0, cur_time = time(nullptr);
struct Tox_Options *opts1 = tox_options_new(nullptr);
tox_options_set_tcp_port(opts1, TCP_RELAY_PORT);
Tox_Err_New t_n_error;
Tox *tox1 = tox_new_log(opts1, &t_n_error, &index[0]);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "Failed to create tox instance: %d", t_n_error);
tox_options_free(opts1);
struct Tox_Options *opts2 = tox_options_new(nullptr);
tox_options_set_udp_enabled(opts2, false);
tox_options_set_local_discovery_enabled(opts2, false);
Tox *tox2 = tox_new_log(opts2, &t_n_error, &index[1]);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "Failed to create tox instance: %d", t_n_error);
struct Tox_Options *opts3 = tox_options_new(nullptr);
tox_options_set_local_discovery_enabled(opts3, false);
Tox *tox3 = tox_new_log(opts3, &t_n_error, &index[2]);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "Failed to create tox instance: %d", t_n_error);
ck_assert_msg(tox1 && tox2 && tox3, "Failed to create 3 tox instances");
Time_Data time_data;
ck_assert_msg(pthread_mutex_init(&time_data.lock, nullptr) == 0, "Failed to init time_data mutex");
time_data.clock = current_time_monotonic(tox1->mono_time);
set_current_time_callback(tox1, &time_data);
set_current_time_callback(tox2, &time_data);
set_current_time_callback(tox3, &time_data);
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
printf("using tox1 as tcp relay for tox2\n");
tox_add_tcp_relay(tox2, TOX_LOCALHOST, TCP_RELAY_PORT, dht_key, nullptr);
printf("bootstrapping toxes off tox1\n");
tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr);
tox_bootstrap(tox3, "localhost", dht_port, dht_key, nullptr);
connected_t1 = 0;
tox_callback_self_connection_status(tox1, tox_connection_status);
tox_callback_friend_request(tox2, accept_friend_request);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
uint32_t test = tox_friend_add(tox3, address, (const uint8_t *)"Gentoo", 7, nullptr);
ck_assert_msg(test == 0, "Failed to add friend error code: %u", test);
uint8_t off = 1;
while (true) {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
if (tox_self_get_connection_status(tox1) && tox_self_get_connection_status(tox2)
&& tox_self_get_connection_status(tox3)) {
if (off) {
printf("Toxes are online, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
con_time = time(nullptr);
off = 0;
}
if (tox_friend_get_connection_status(tox2, 0, nullptr) == TOX_CONNECTION_TCP
&& tox_friend_get_connection_status(tox3, 0, nullptr) == TOX_CONNECTION_TCP) {
break;
}
}
increment_clock(&time_data, 200);
c_sleep(5);
}
ck_assert_msg(connected_t1, "Tox1 isn't connected. %u", connected_t1);
printf("tox clients connected took %lu seconds\n", (unsigned long)(time(nullptr) - con_time));
// We're done with this callback, so unset it to ensure we don't fail the
// test if tox1 goes offline while tox2 and 3 are reloaded.
tox_callback_self_connection_status(tox1, nullptr);
reload_tox(&tox2, opts2, &index[1]);
reload_tox(&tox3, opts3, &index[2]);
cur_time = time(nullptr);
off = 1;
while (true) {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
tox_iterate(tox3, nullptr);
if (tox_self_get_connection_status(tox1) && tox_self_get_connection_status(tox2)
&& tox_self_get_connection_status(tox3)) {
if (off) {
printf("Toxes are online again after reloading, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
con_time = time(nullptr);
off = 0;
}
if (tox_friend_get_connection_status(tox2, 0, nullptr) == TOX_CONNECTION_TCP
&& tox_friend_get_connection_status(tox3, 0, nullptr) == TOX_CONNECTION_TCP) {
break;
}
}
increment_clock(&time_data, 100);
c_sleep(5);
}
printf("tox clients connected took %lu seconds\n", (unsigned long)(time(nullptr) - con_time));
printf("test_few_clients succeeded, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
tox_kill(tox1);
tox_kill(tox2);
tox_kill(tox3);
tox_options_free(opts2);
tox_options_free(opts3);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_few_clients();
return 0;
}

View File

@ -0,0 +1,79 @@
/* Tests that we can send messages to friends.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
typedef struct State {
bool message_received;
} State;
#include "auto_test_support.h"
#define MESSAGE_FILLER 'G'
static void message_callback(
Tox *m, uint32_t friendnumber, Tox_Message_Type type,
const uint8_t *string, size_t length, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
if (type != TOX_MESSAGE_TYPE_NORMAL) {
ck_abort_msg("Bad type");
}
const size_t cmp_msg_len = tox_max_message_length();
uint8_t *cmp_msg = (uint8_t *)malloc(cmp_msg_len);
ck_assert(cmp_msg != nullptr);
memset(cmp_msg, MESSAGE_FILLER, cmp_msg_len);
if (length == tox_max_message_length() && memcmp(string, cmp_msg, cmp_msg_len) == 0) {
state->message_received = true;
}
free(cmp_msg);
}
static void send_message_test(AutoTox *autotoxes)
{
tox_callback_friend_message(autotoxes[1].tox, &message_callback);
const size_t msgs_len = tox_max_message_length() + 1;
uint8_t *msgs = (uint8_t *)malloc(msgs_len);
memset(msgs, MESSAGE_FILLER, msgs_len);
Tox_Err_Friend_Send_Message errm;
tox_friend_send_message(autotoxes[0].tox, 0, TOX_MESSAGE_TYPE_NORMAL, msgs, msgs_len, &errm);
ck_assert_msg(errm == TOX_ERR_FRIEND_SEND_MESSAGE_TOO_LONG, "tox_max_message_length() is too small? error=%d", errm);
tox_friend_send_message(autotoxes[0].tox, 0, TOX_MESSAGE_TYPE_NORMAL, msgs, tox_max_message_length(), &errm);
ck_assert_msg(errm == TOX_ERR_FRIEND_SEND_MESSAGE_OK, "tox_max_message_length() is too big? error=%d", errm);
free(msgs);
do {
iterate_all_wait(autotoxes, 2, ITERATION_INTERVAL);
} while (!((State *)autotoxes[1].state)->message_received);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
struct Tox_Options *tox_options = tox_options_new(nullptr);
ck_assert(tox_options != nullptr);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
tox_options_set_ipv6_enabled(tox_options, true);
run_auto_test(tox_options, 2, send_message_test, sizeof(State), &options);
tox_options_set_ipv6_enabled(tox_options, false);
run_auto_test(tox_options, 2, send_message_test, sizeof(State), &options);
tox_options_free(tox_options);
return 0;
}

View File

@ -0,0 +1,100 @@
/* Tests that we can set our name.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
#define NICKNAME "Gentoo"
static void nickchange_callback(Tox *tox, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{
ck_assert_msg(length == sizeof(NICKNAME), "Name length not correct: %d != %d", (uint16_t)length,
(uint16_t)sizeof(NICKNAME));
ck_assert_msg(memcmp(string, NICKNAME, sizeof(NICKNAME)) == 0, "Name not correct: %s", (const char *)string);
bool *nickname_updated = (bool *)userdata;
*nickname_updated = true;
}
static void test_set_name(void)
{
printf("initialising 2 toxes\n");
uint32_t index[] = { 1, 2 };
const time_t cur_time = time(nullptr);
Tox *const tox1 = tox_new_log(nullptr, nullptr, &index[0]);
Tox *const tox2 = tox_new_log(nullptr, nullptr, &index[1]);
ck_assert_msg(tox1 && tox2, "failed to create 2 tox instances");
printf("tox1 adds tox2 as friend, tox2 adds tox1\n");
uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_public_key(tox2, public_key);
tox_friend_add_norequest(tox1, public_key, nullptr);
tox_self_get_public_key(tox1, public_key);
tox_friend_add_norequest(tox2, public_key, nullptr);
printf("bootstrapping tox2 off tox1\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr);
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE);
printf("toxes are online, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
const time_t con_time = time(nullptr);
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_friend_get_connection_status(tox1, 0, nullptr) != TOX_CONNECTION_UDP ||
tox_friend_get_connection_status(tox2, 0, nullptr) != TOX_CONNECTION_UDP);
printf("tox clients connected took %lu seconds\n", (unsigned long)(time(nullptr) - con_time));
tox_callback_friend_name(tox2, nickchange_callback);
Tox_Err_Set_Info err_n;
bool ret = tox_self_set_name(tox1, (const uint8_t *)NICKNAME, sizeof(NICKNAME), &err_n);
ck_assert_msg(ret && err_n == TOX_ERR_SET_INFO_OK, "tox_self_set_name failed because %d\n", err_n);
bool nickname_updated = false;
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, &nickname_updated);
c_sleep(ITERATION_INTERVAL);
} while (!nickname_updated);
ck_assert_msg(tox_friend_get_name_size(tox2, 0, nullptr) == sizeof(NICKNAME), "Name length not correct");
uint8_t temp_name[sizeof(NICKNAME)];
tox_friend_get_name(tox2, 0, temp_name, nullptr);
ck_assert_msg(memcmp(temp_name, NICKNAME, sizeof(NICKNAME)) == 0, "Name not correct");
printf("test_set_name succeeded, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
tox_kill(tox1);
tox_kill(tox2);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_set_name();
return 0;
}

View File

@ -0,0 +1,105 @@
/* Tests that we can set our status message
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
#define STATUS_MESSAGE "Installing Gentoo"
static void status_callback(Tox *tox, uint32_t friend_number, const uint8_t *message, size_t length, void *user_data)
{
ck_assert_msg(length == sizeof(STATUS_MESSAGE) &&
memcmp(message, STATUS_MESSAGE, sizeof(STATUS_MESSAGE)) == 0,
"incorrect data in status callback");
bool *status_updated = (bool *)user_data;
*status_updated = true;
}
static void test_set_status_message(void)
{
printf("initialising 2 toxes\n");
uint32_t index[] = { 1, 2 };
const time_t cur_time = time(nullptr);
Tox *const tox1 = tox_new_log(nullptr, nullptr, &index[0]);
Tox *const tox2 = tox_new_log(nullptr, nullptr, &index[1]);
ck_assert_msg(tox1 && tox2, "failed to create 2 tox instances");
printf("tox1 adds tox2 as friend, tox2 adds tox1\n");
uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_public_key(tox2, public_key);
tox_friend_add_norequest(tox1, public_key, nullptr);
tox_self_get_public_key(tox1, public_key);
tox_friend_add_norequest(tox2, public_key, nullptr);
printf("bootstrapping tox2 off tox1\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(tox1, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr);
tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr);
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE);
printf("toxes are online, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
const time_t con_time = time(nullptr);
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_friend_get_connection_status(tox1, 0, nullptr) != TOX_CONNECTION_UDP ||
tox_friend_get_connection_status(tox2, 0, nullptr) != TOX_CONNECTION_UDP);
printf("tox clients connected took %lu seconds\n", (unsigned long)(time(nullptr) - con_time));
Tox_Err_Set_Info err_n;
tox_callback_friend_status_message(tox2, status_callback);
bool ret = tox_self_set_status_message(tox1, (const uint8_t *)STATUS_MESSAGE, sizeof(STATUS_MESSAGE),
&err_n);
ck_assert_msg(ret && err_n == TOX_ERR_SET_INFO_OK, "tox_self_set_status_message failed because %d\n", err_n);
bool status_updated = false;
do {
tox_iterate(tox1, nullptr);
tox_iterate(tox2, &status_updated);
c_sleep(ITERATION_INTERVAL);
} while (!status_updated);
ck_assert_msg(tox_friend_get_status_message_size(tox2, 0, nullptr) == sizeof(STATUS_MESSAGE),
"status message length not correct");
uint8_t cmp_status[sizeof(STATUS_MESSAGE)];
tox_friend_get_status_message(tox2, 0, cmp_status, nullptr);
ck_assert_msg(memcmp(cmp_status, STATUS_MESSAGE, sizeof(STATUS_MESSAGE)) == 0,
"status message not correct");
printf("test_set_status_message succeeded, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
tox_kill(tox1);
tox_kill(tox2);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_set_status_message();
return 0;
}

View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include "../testing/misc_tools.h"
#include "check_compat.h"
#include "auto_test_support.h"
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
struct Tox_Options *opts = tox_options_new(nullptr);
tox_options_set_udp_enabled(opts, false);
Tox *tox_tcp = tox_new_log(opts, nullptr, nullptr);
tox_options_free(opts);
bootstrap_tox_live_network(tox_tcp, true);
printf("Waiting for connection");
do {
printf(".");
fflush(stdout);
tox_iterate(tox_tcp, nullptr);
c_sleep(ITERATION_INTERVAL);
} while (tox_self_get_connection_status(tox_tcp) == TOX_CONNECTION_NONE);
const Tox_Connection status = tox_self_get_connection_status(tox_tcp);
ck_assert_msg(status == TOX_CONNECTION_TCP,
"expected TCP connection, but got %d", status);
printf("Connection (TCP): %d\n", status);
tox_kill(tox_tcp);
return 0;
}

View File

@ -0,0 +1,164 @@
/* Auto Tests: Many clients.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_dispatch.h"
#include "../toxcore/tox_events.h"
#include "auto_test_support.h"
#include "check_compat.h"
// Set to true to produce an msgpack file at /tmp/test.mp.
static const bool want_dump_events = false;
static void handle_events_friend_message(Tox *tox, const Tox_Event_Friend_Message *event, void *user_data)
{
bool *success = (bool *)user_data;
ck_assert(tox_event_friend_message_get_message_length(event) == sizeof("hello"));
const uint8_t *msg = tox_event_friend_message_get_message(event);
ck_assert_msg(memcmp(msg, "hello", sizeof("hello")) == 0,
"message was not expected 'hello' but '%s'", (const char *)msg);
*success = true;
}
static void dump_events(const char *path, const Tox_Events *events)
{
if (want_dump_events) {
FILE *fh = fopen(path, "w");
ck_assert(fh != nullptr);
const uint32_t len = tox_events_bytes_size(events);
uint8_t *buf = (uint8_t *)malloc(len);
ck_assert(buf != nullptr);
tox_events_get_bytes(events, buf);
fwrite(buf, 1, len, fh);
free(buf);
fclose(fh);
}
}
static void print_events(Tox_Events *events)
{
const uint32_t size = tox_events_bytes_size(events);
uint8_t *bytes = (uint8_t *)malloc(size);
ck_assert(bytes != nullptr);
tox_events_get_bytes(events, bytes);
Tox_Events *events_copy = tox_events_load(bytes, size);
ck_assert(events_copy != nullptr);
free(bytes);
ck_assert(tox_events_equal(events, events_copy));
tox_events_free(events_copy);
tox_events_free(events);
}
static bool await_message(Tox **toxes, const Tox_Dispatch *dispatch)
{
for (uint32_t i = 0; i < 100; ++i) {
// Ignore events on tox 1.
print_events(tox_events_iterate(toxes[0], false, nullptr));
// Check if tox 2 got the message from tox 1.
Tox_Events *events = tox_events_iterate(toxes[1], false, nullptr);
dump_events("/tmp/test.mp", events);
bool success = false;
tox_dispatch_invoke(dispatch, events, toxes[1], &success);
print_events(events);
if (success) {
return true;
}
c_sleep(tox_iteration_interval(toxes[0]));
}
return false;
}
static void test_tox_events(void)
{
uint8_t message[sizeof("hello")];
memcpy(message, "hello", sizeof(message));
Tox *toxes[2];
uint32_t index[2];
for (uint32_t i = 0; i < 2; ++i) {
index[i] = i + 1;
toxes[i] = tox_new_log(nullptr, nullptr, &index[i]);
tox_events_init(toxes[i]);
ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
}
Tox_Err_Dispatch_New err_new;
Tox_Dispatch *dispatch = tox_dispatch_new(&err_new);
ck_assert_msg(dispatch != nullptr, "failed to create event dispatcher");
ck_assert(err_new == TOX_ERR_DISPATCH_NEW_OK);
tox_events_callback_friend_message(dispatch, handle_events_friend_message);
uint8_t pk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(toxes[0], pk);
tox_bootstrap(toxes[1], "localhost", tox_self_get_udp_port(toxes[0], nullptr), pk, nullptr);
tox_self_get_public_key(toxes[0], pk);
tox_friend_add_norequest(toxes[1], pk, nullptr);
tox_self_get_public_key(toxes[1], pk);
tox_friend_add_norequest(toxes[0], pk, nullptr);
printf("bootstrapping and connecting 2 toxes\n");
while (tox_self_get_connection_status(toxes[0]) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(toxes[1]) == TOX_CONNECTION_NONE) {
// Ignore connection events for now.
print_events(tox_events_iterate(toxes[0], false, nullptr));
print_events(tox_events_iterate(toxes[1], false, nullptr));
c_sleep(tox_iteration_interval(toxes[0]));
}
printf("toxes online, waiting for friend connection\n");
while (tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_NONE ||
tox_friend_get_connection_status(toxes[1], 0, nullptr) == TOX_CONNECTION_NONE) {
// Ignore connection events for now.
print_events(tox_events_iterate(toxes[0], false, nullptr));
print_events(tox_events_iterate(toxes[1], false, nullptr));
c_sleep(tox_iteration_interval(toxes[0]));
}
printf("friends are connected via %s, now sending message\n",
tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_TCP ? "TCP" : "UDP");
Tox_Err_Friend_Send_Message err;
tox_friend_send_message(toxes[0], 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof(message), &err);
ck_assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK);
ck_assert(await_message(toxes, dispatch));
tox_dispatch_free(dispatch);
for (uint32_t i = 0; i < 2; ++i) {
tox_kill(toxes[i]);
}
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_tox_events();
return 0;
}

View File

@ -0,0 +1,123 @@
/* Auto Tests: Many clients.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_events.h"
#include "../toxcore/tox_struct.h"
#include "auto_test_support.h"
#include "check_compat.h"
static bool await_message(Tox **toxes)
{
for (uint32_t i = 0; i < 100; ++i) {
// Ignore events on tox 1.
tox_events_free(tox_events_iterate(toxes[0], false, nullptr));
// Check if tox 2 got the message from tox 1.
Tox_Events *events = tox_events_iterate(toxes[1], false, nullptr);
if (events != nullptr) {
ck_assert(tox_events_get_friend_message_size(events) == 1);
const Tox_Event_Friend_Message *msg_event = tox_events_get_friend_message(events, 0);
ck_assert(tox_event_friend_message_get_message_length(msg_event) == sizeof("hello"));
const uint8_t *msg = tox_event_friend_message_get_message(msg_event);
ck_assert_msg(memcmp(msg, "hello", sizeof("hello")) == 0,
"message was not expected 'hello' but '%s'", (const char *)msg);
tox_events_free(events);
return true;
}
c_sleep(tox_iteration_interval(toxes[0]));
}
return false;
}
static uint64_t get_state_clock_callback(void *user_data)
{
const uint64_t *clock = (const uint64_t *)user_data;
return *clock;
}
static void test_tox_events(void)
{
uint8_t message[sizeof("hello")];
memcpy(message, "hello", sizeof(message));
Tox *toxes[2];
uint32_t index[2];
for (uint32_t i = 0; i < 2; ++i) {
index[i] = i + 1;
toxes[i] = tox_new_log(nullptr, nullptr, &index[i]);
tox_events_init(toxes[i]);
ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
}
uint64_t clock = current_time_monotonic(toxes[0]->mono_time);
Mono_Time *mono_time;
mono_time = toxes[0]->mono_time;
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
mono_time = toxes[1]->mono_time;
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
uint8_t pk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(toxes[0], pk);
tox_bootstrap(toxes[1], "localhost", tox_self_get_udp_port(toxes[0], nullptr), pk, nullptr);
tox_self_get_public_key(toxes[0], pk);
tox_friend_add_norequest(toxes[1], pk, nullptr);
tox_self_get_public_key(toxes[1], pk);
tox_friend_add_norequest(toxes[0], pk, nullptr);
printf("bootstrapping and connecting 2 toxes\n");
while (tox_self_get_connection_status(toxes[0]) == TOX_CONNECTION_NONE ||
tox_self_get_connection_status(toxes[1]) == TOX_CONNECTION_NONE) {
// Ignore connection events for now.
tox_events_free(tox_events_iterate(toxes[0], false, nullptr));
tox_events_free(tox_events_iterate(toxes[1], false, nullptr));
clock += 100;
c_sleep(5);
}
printf("toxes online, waiting for friend connection\n");
while (tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_NONE ||
tox_friend_get_connection_status(toxes[1], 0, nullptr) == TOX_CONNECTION_NONE) {
// Ignore connection events for now.
tox_events_free(tox_events_iterate(toxes[0], false, nullptr));
tox_events_free(tox_events_iterate(toxes[1], false, nullptr));
clock += 100;
c_sleep(5);
}
printf("friends are connected via %s, now sending message\n",
tox_friend_get_connection_status(toxes[0], 0, nullptr) == TOX_CONNECTION_TCP ? "TCP" : "UDP");
Tox_Err_Friend_Send_Message err;
tox_friend_send_message(toxes[0], 0, TOX_MESSAGE_TYPE_NORMAL, message, sizeof(message), &err);
ck_assert(err == TOX_ERR_FRIEND_SEND_MESSAGE_OK);
ck_assert(await_message(toxes));
for (uint32_t i = 0; i < 2; ++i) {
tox_kill(toxes[i]);
}
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_tox_events();
return 0;
}

View File

@ -0,0 +1,263 @@
/* Auto Tests: Many TCP.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
/* The Travis-CI container responds poorly to ::1 as a localhost address
* You're encouraged to -D FORCE_TESTS_IPV6 on a local test */
#ifdef FORCE_TESTS_IPV6
#define TOX_LOCALHOST "::1"
#else
#define TOX_LOCALHOST "127.0.0.1"
#endif
static bool enable_broken_tests = false;
static void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536) {
return;
}
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_friend_add_norequest(m, public_key, nullptr);
}
}
#define NUM_FRIENDS 50
#define NUM_TOXES_TCP 40
static uint16_t tcp_relay_port = 33448;
static void test_many_clients_tcp(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
long long unsigned int cur_time = time(nullptr);
Tox *toxes[NUM_TOXES_TCP];
uint32_t index[NUM_TOXES_TCP];
uint32_t i, j;
uint32_t to_comp = 974536;
for (i = 0; i < NUM_TOXES_TCP; ++i) {
struct Tox_Options *opts = tox_options_new(nullptr);
if (i == 0) {
tox_options_set_tcp_port(opts, tcp_relay_port);
} else {
tox_options_set_udp_enabled(opts, false);
}
index[i] = i + 1;
Tox_Err_New err;
toxes[i] = tox_new_log(opts, &err, &index[i]);
if (i == 0 && err == TOX_ERR_NEW_PORT_ALLOC) {
ck_assert(toxes[i] == nullptr);
--i;
++tcp_relay_port;
tox_options_free(opts);
continue;
}
ck_assert_msg(toxes[i] != nullptr, "Failed to create tox instances %u", i);
tox_callback_friend_request(toxes[i], accept_friend_request);
uint8_t dpk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(toxes[0], dpk);
Tox_Err_Bootstrap error = TOX_ERR_BOOTSTRAP_OK;
ck_assert_msg(tox_add_tcp_relay(toxes[i], TOX_LOCALHOST, tcp_relay_port, dpk, &error), "add relay error, %u, %d", i,
error);
uint16_t first_port = tox_self_get_udp_port(toxes[0], nullptr);
ck_assert_msg(tox_bootstrap(toxes[i], TOX_LOCALHOST, first_port, dpk, nullptr), "Bootstrap error");
tox_options_free(opts);
}
struct {
uint16_t tox1;
uint16_t tox2;
} pairs[NUM_FRIENDS];
uint8_t address[TOX_ADDRESS_SIZE];
for (i = 0; i < NUM_FRIENDS; ++i) {
loop_top:
pairs[i].tox1 = random_u32(rng) % NUM_TOXES_TCP;
pairs[i].tox2 = (pairs[i].tox1 + random_u32(rng) % (NUM_TOXES_TCP - 1) + 1) % NUM_TOXES_TCP;
for (j = 0; j < i; ++j) {
if (pairs[j].tox2 == pairs[i].tox1 && pairs[j].tox1 == pairs[i].tox2) {
goto loop_top;
}
}
tox_self_get_address(toxes[pairs[i].tox1], address);
Tox_Err_Friend_Add test;
uint32_t num = tox_friend_add(toxes[pairs[i].tox2], address, (const uint8_t *)"Gentoo", 7, &test);
if (test == TOX_ERR_FRIEND_ADD_ALREADY_SENT) {
goto loop_top;
}
ck_assert_msg(num != UINT32_MAX && test == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend error code: %i", test);
}
while (true) {
uint16_t counter = 0;
for (i = 0; i < NUM_TOXES_TCP; ++i) {
for (j = 0; j < tox_self_get_friend_list_size(toxes[i]); ++j) {
if (tox_friend_get_connection_status(toxes[i], j, nullptr) == TOX_CONNECTION_TCP) {
++counter;
}
}
}
if (counter == NUM_FRIENDS * 2) {
break;
}
for (i = 0; i < NUM_TOXES_TCP; ++i) {
tox_iterate(toxes[i], &to_comp);
}
c_sleep(50);
}
for (i = 0; i < NUM_TOXES_TCP; ++i) {
tox_kill(toxes[i]);
}
printf("test_many_clients_tcp succeeded, took %llu seconds\n", time(nullptr) - cur_time);
}
#define NUM_TCP_RELAYS 3
static void test_many_clients_tcp_b(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
long long unsigned int cur_time = time(nullptr);
Tox *toxes[NUM_TOXES_TCP];
uint32_t index[NUM_TOXES_TCP];
uint32_t i, j;
uint32_t to_comp = 974536;
for (i = 0; i < NUM_TOXES_TCP; ++i) {
struct Tox_Options *opts = tox_options_new(nullptr);
if (i < NUM_TCP_RELAYS) {
tox_options_set_tcp_port(opts, tcp_relay_port + i);
} else {
tox_options_set_udp_enabled(opts, 0);
}
index[i] = i + 1;
toxes[i] = tox_new_log(opts, nullptr, &index[i]);
ck_assert_msg(toxes[i] != nullptr, "Failed to create tox instances %u", i);
tox_callback_friend_request(toxes[i], accept_friend_request);
uint8_t dpk[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(toxes[(i % NUM_TCP_RELAYS)], dpk);
ck_assert_msg(tox_add_tcp_relay(toxes[i], TOX_LOCALHOST, tcp_relay_port + (i % NUM_TCP_RELAYS), dpk, nullptr),
"add relay error");
tox_self_get_dht_id(toxes[0], dpk);
uint16_t first_port = tox_self_get_udp_port(toxes[0], nullptr);
ck_assert_msg(tox_bootstrap(toxes[i], TOX_LOCALHOST, first_port, dpk, nullptr), "Bootstrap error");
tox_options_free(opts);
}
struct {
uint16_t tox1;
uint16_t tox2;
} pairs[NUM_FRIENDS];
uint8_t address[TOX_ADDRESS_SIZE];
for (i = 0; i < NUM_FRIENDS; ++i) {
loop_top:
pairs[i].tox1 = random_u32(rng) % NUM_TOXES_TCP;
pairs[i].tox2 = (pairs[i].tox1 + random_u32(rng) % (NUM_TOXES_TCP - 1) + 1) % NUM_TOXES_TCP;
for (j = 0; j < i; ++j) {
if (pairs[j].tox2 == pairs[i].tox1 && pairs[j].tox1 == pairs[i].tox2) {
goto loop_top;
}
}
tox_self_get_address(toxes[pairs[i].tox1], address);
Tox_Err_Friend_Add test;
uint32_t num = tox_friend_add(toxes[pairs[i].tox2], address, (const uint8_t *)"Gentoo", 7, &test);
if (test == TOX_ERR_FRIEND_ADD_ALREADY_SENT) {
goto loop_top;
}
ck_assert_msg(num != UINT32_MAX && test == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend error code: %i", test);
}
uint16_t last_count = 0;
while (true) {
uint16_t counter = 0;
for (i = 0; i < NUM_TOXES_TCP; ++i) {
for (j = 0; j < tox_self_get_friend_list_size(toxes[i]); ++j) {
if (tox_friend_get_connection_status(toxes[i], j, nullptr) == TOX_CONNECTION_TCP) {
++counter;
}
}
}
if (counter != last_count) {
printf("many_clients_tcp_b got to %u\n", counter);
last_count = counter;
}
if (counter == NUM_FRIENDS * 2) {
break;
}
for (i = 0; i < NUM_TOXES_TCP; ++i) {
tox_iterate(toxes[i], &to_comp);
}
c_sleep(30);
}
for (i = 0; i < NUM_TOXES_TCP; ++i) {
tox_kill(toxes[i]);
}
printf("test_many_clients_tcp_b succeeded, took %llu seconds\n", time(nullptr) - cur_time);
}
static void tox_suite(void)
{
/* Each tox connects to a single tox TCP */
test_many_clients_tcp();
if (enable_broken_tests) {
/* Try to make a connection to each "older sibling" tox instance via TCP */
/* Currently this test intermittently fails for unknown reasons. */
test_many_clients_tcp_b();
}
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
tox_suite();
return 0;
}

View File

@ -0,0 +1,134 @@
/* Auto Tests: Many clients.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
static void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_friend_add_norequest(m, public_key, nullptr);
}
}
#define TCP_TEST_NUM_TOXES 90
#define TCP_TEST_NUM_FRIENDS 50
static void test_many_clients(void)
{
const Random *rng = system_random();
ck_assert(rng != nullptr);
time_t cur_time = time(nullptr);
Tox *toxes[TCP_TEST_NUM_TOXES];
uint32_t index[TCP_TEST_NUM_TOXES];
for (uint32_t i = 0; i < TCP_TEST_NUM_TOXES; ++i) {
index[i] = i + 1;
toxes[i] = tox_new_log(nullptr, nullptr, &index[i]);
ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
tox_callback_friend_request(toxes[i], accept_friend_request);
}
struct {
uint16_t tox1;
uint16_t tox2;
} pairs[TCP_TEST_NUM_FRIENDS];
uint8_t address[TOX_ADDRESS_SIZE];
uint32_t num_f = 0;
for (uint32_t i = 0; i < TCP_TEST_NUM_TOXES; ++i) {
num_f += tox_self_get_friend_list_size(toxes[i]);
}
ck_assert_msg(num_f == 0, "bad num friends: %u", num_f);
for (uint32_t i = 0; i < TCP_TEST_NUM_FRIENDS; ++i) {
loop_top:
pairs[i].tox1 = random_u32(rng) % TCP_TEST_NUM_TOXES;
pairs[i].tox2 = (pairs[i].tox1 + random_u32(rng) % (TCP_TEST_NUM_TOXES - 1) + 1) % TCP_TEST_NUM_TOXES;
for (uint32_t j = 0; j < i; ++j) {
if (pairs[j].tox2 == pairs[i].tox1 && pairs[j].tox1 == pairs[i].tox2) {
goto loop_top;
}
}
tox_self_get_address(toxes[pairs[i].tox1], address);
Tox_Err_Friend_Add test;
uint32_t num = tox_friend_add(toxes[pairs[i].tox2], address, (const uint8_t *)"Gentoo", 7, &test);
if (test == TOX_ERR_FRIEND_ADD_ALREADY_SENT) {
goto loop_top;
}
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(toxes[pairs[i].tox1], dht_key);
const uint16_t dht_port = tox_self_get_udp_port(toxes[pairs[i].tox1], nullptr);
tox_bootstrap(toxes[pairs[i].tox2], "localhost", dht_port, dht_key, nullptr);
ck_assert_msg(num != UINT32_MAX && test == TOX_ERR_FRIEND_ADD_OK, "failed to add friend error code: %i", test);
}
for (uint32_t i = 0; i < TCP_TEST_NUM_TOXES; ++i) {
num_f += tox_self_get_friend_list_size(toxes[i]);
}
ck_assert_msg(num_f == TCP_TEST_NUM_FRIENDS, "bad num friends: %u", num_f);
uint16_t last_count = 0;
while (true) {
uint16_t counter = 0;
for (uint32_t i = 0; i < TCP_TEST_NUM_TOXES; ++i) {
for (uint32_t j = 0; j < tox_self_get_friend_list_size(toxes[i]); ++j) {
if (tox_friend_get_connection_status(toxes[i], j, nullptr) == TOX_CONNECTION_UDP) {
++counter;
}
}
}
if (counter != last_count) {
printf("many_clients got to %u\n", counter);
last_count = counter;
}
if (counter == TCP_TEST_NUM_FRIENDS * 2) {
break;
}
for (uint32_t i = 0; i < TCP_TEST_NUM_TOXES; ++i) {
tox_iterate(toxes[i], nullptr);
}
c_sleep(50);
}
for (uint32_t i = 0; i < TCP_TEST_NUM_TOXES; ++i) {
tox_kill(toxes[i]);
}
printf("test_many_clients succeeded, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_many_clients();
return 0;
}

View File

@ -0,0 +1,165 @@
#include <stdlib.h>
#include <string.h>
#include "../testing/misc_tools.h"
#include "check_compat.h"
typedef enum {
NEGATIVE,
ZERO,
POSITIVE
} Comparison;
static const char *Comparison_Str[] = { "NEGATIVE", "ZERO", "POSITIVE" };
static void verify(const char *s1, const char *s2, size_t n, Comparison expected)
{
int r = tox_strncasecmp(s1, s2, n);
Comparison actual = r < 0 ? NEGATIVE : r == 0 ? ZERO : POSITIVE;
ck_assert_msg(actual == expected,
"tox_strncasecmp(\"%s\", \"%s\", %u) == %s, but expected %s.",
s1, s2, (unsigned)n, Comparison_Str[actual], Comparison_Str[expected]);
}
static void test_general(void)
{
// empty strings are equal
verify("", "", 100, ZERO);
verify("", "", -1, ZERO);
// ====== Same Case Test Cases ======
// equal strings with n=0 are equal
verify("", "", 0, ZERO);
verify("AAA", "AAA", 0, ZERO);
// unequal strings with n=0 are equal
verify("A", "B", 0, ZERO);
verify("AAA", "BBB", 0, ZERO);
verify("AAA", "BBBBBB", 0, ZERO);
verify("AAAAAA", "BBB", 0, ZERO);
// equal strings are equal
verify("AAA", "AAA", 0, ZERO);
verify("AAA", "AAA", 1, ZERO);
verify("AAA", "AAA", 2, ZERO);
verify("AAA", "AAA", 3, ZERO);
verify("AAA", "AAA", 4, ZERO);
verify("AAA", "AAA", 5, ZERO);
verify("AAA", "AAA", -1, ZERO);
verify("AAA", "AAAAAA", 0, ZERO);
verify("AAA", "AAAAAA", 1, ZERO);
verify("AAA", "AAAAAA", 2, ZERO);
verify("AAA", "AAAAAA", 3, ZERO);
verify("AAA", "AAAAAA", 4, NEGATIVE);
verify("AAA", "AAAAAA", 5, NEGATIVE);
verify("AAA", "AAAAAA", -1, NEGATIVE);
verify("AAAAAA", "AAA", 0, ZERO);
verify("AAAAAA", "AAA", 1, ZERO);
verify("AAAAAA", "AAA", 2, ZERO);
verify("AAAAAA", "AAA", 3, ZERO);
verify("AAAAAA", "AAA", 4, POSITIVE);
verify("AAAAAA", "AAA", 5, POSITIVE);
verify("AAAAAA", "AAA", -1, POSITIVE);
verify("I'm eating wafers and drinking tea.", "I'm eating wafers and drinking tea.", -1, ZERO);
// unequal strings are equal only up to n
verify("AAAB", "AAAA", 0, ZERO);
verify("AAAB", "AAAA", 1, ZERO);
verify("AAAB", "AAAA", 2, ZERO);
verify("AAAB", "AAAA", 3, ZERO);
verify("AAAB", "AAAA", 4, POSITIVE);
verify("AAAB", "AAAA", 5, POSITIVE);
verify("AAAB", "AAAA", -1, POSITIVE);
verify("AAAA", "AAAB", 0, ZERO);
verify("AAAA", "AAAB", 1, ZERO);
verify("AAAA", "AAAB", 2, ZERO);
verify("AAAA", "AAAB", 3, ZERO);
verify("AAAA", "AAAB", 4, NEGATIVE);
verify("AAAA", "AAAB", 5, NEGATIVE);
verify("AAAA", "AAAB", -1, NEGATIVE);
verify("The wafers are salty.", "The wafers are sweet.", 16, ZERO);
verify("The wafers are salty.", "The wafers are sweet.", 17, NEGATIVE);
verify("The wafers are salty.", "The wafers are sweet.", -1, NEGATIVE);
// the comparison should stop at first mismatch
verify("AAABA", "AAAAB", -1, POSITIVE);
verify("AAAAB", "AAABA", -1, NEGATIVE);
// ====== Different Case Test Cases ======
// equal strings with n=0 are equal
verify("", "", 0, ZERO);
verify("aaa", "AAA", 0, ZERO);
// unequal strings with n=0 are equal
verify("a", "B", 0, ZERO);
verify("aaa", "BBB", 0, ZERO);
verify("aaa", "BBBBBB", 0, ZERO);
verify("aaaaaa", "BBB", 0, ZERO);
// equal strings are equal
verify("aaa", "AAA", 0, ZERO);
verify("AAA", "aaa", 1, ZERO);
verify("aaa", "AAA", 2, ZERO);
verify("aaa", "AAA", 3, ZERO);
verify("AAA", "aaa", 4, ZERO);
verify("AAA", "aaa", 5, ZERO);
verify("AAA", "aaa", -1, ZERO);
verify("aaa", "AAAAAA", 0, ZERO);
verify("AAA", "AAAaaa", 1, ZERO);
verify("aaA", "aaaAAA", 2, ZERO);
verify("AaA", "aAAAAA", 3, ZERO);
verify("AAA", "AAAAAA", 4, NEGATIVE);
verify("Aaa", "AAaaAA", 5, NEGATIVE);
verify("AAA", "AAAAAa", -1, NEGATIVE);
verify("AAAAAA", "aaa", 0, ZERO);
verify("AAAaaa", "AAA", 1, ZERO);
verify("aaaAAA", "aaA", 2, ZERO);
verify("aAAAAA", "AaA", 3, ZERO);
verify("AAAAAA", "AAA", 4, POSITIVE);
verify("AAaaAA", "Aaa", 5, POSITIVE);
verify("AAAAAa", "AAA", -1, POSITIVE);
verify("I'm Eating Wafers And Drinking Tea.", "I'm eating wafers and drinking tea.", -1, ZERO);
// unequal strings are equal only up to n
verify("aaaB", "AAAA", 0, ZERO);
verify("AaAB", "aAAA", 1, ZERO);
verify("aAAB", "AaAA", 2, ZERO);
verify("AAAB", "AAaA", 3, ZERO);
verify("AAAB", "AAAA", 4, POSITIVE);
verify("AAAb", "AAAA", 5, POSITIVE);
verify("AAAB", "AAAa", -1, POSITIVE);
verify("AAAA", "aaaB", 0, ZERO);
verify("aAAA", "AaAB", 1, ZERO);
verify("AaAA", "aAAB", 2, ZERO);
verify("AAaA", "AAAB", 3, ZERO);
verify("AAAA", "AAAB", 4, NEGATIVE);
verify("AAAA", "AAAb", 5, NEGATIVE);
verify("AAAa", "AAAB", -1, NEGATIVE);
verify("The Wafers Are Salty.", "The wafers are sweet.", 16, ZERO);
verify("The Wafers Are Salty.", "The wafers are sweet.", 17, NEGATIVE);
verify("The Wafers Are Salty.", "The wafers are sweet.", -1, NEGATIVE);
// the comparison should stop at first mismatch
verify("aAaBA", "AAAAb", -1, POSITIVE);
verify("AAAAb", "aAaBA", -1, NEGATIVE);
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_general();
return 0;
}

View File

@ -0,0 +1,520 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <vpx/vpx_image.h>
#include "../testing/misc_tools.h"
#include "../toxav/toxav.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/logger.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
#define TEST_REGULAR_AV 1
#define TEST_REGULAR_A 1
#define TEST_REGULAR_V 1
#define TEST_REJECT 1
#define TEST_CANCEL 1
#define TEST_MUTE_UNMUTE 1
#define TEST_STOP_RESUME_PAYLOAD 1
#define TEST_PAUSE_RESUME_SEND 1
#define ck_assert_call_control(a, b, c) do { \
Toxav_Err_Call_Control cc_err; \
bool ok = toxav_call_control(a, b, c, &cc_err); \
if (!ok) { \
printf("toxav_call_control returned error %d\n", cc_err); \
} \
ck_assert(ok); \
ck_assert(cc_err == TOXAV_ERR_CALL_CONTROL_OK); \
} while (0)
typedef struct {
bool incoming;
uint32_t state;
} CallControl;
static void clear_call_control(CallControl *cc)
{
const CallControl empty = {0};
*cc = empty;
}
/**
* Callbacks
*/
static void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
{
printf("Handling CALL callback\n");
((CallControl *)user_data)->incoming = true;
}
static void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
{
printf("Handling CALL STATE callback: %u\n", state);
((CallControl *)user_data)->state = state;
}
static void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
printf("Received video payload\n");
}
static void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
int16_t const *pcm,
size_t sample_count,
uint8_t channels,
uint32_t sampling_rate,
void *user_data)
{
printf("Received audio payload\n");
}
static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length,
void *userdata)
{
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
ck_assert(tox_friend_add_norequest(m, public_key, nullptr) != (uint32_t) -1);
}
}
/**
* Iterate helper
*/
static void iterate_tox(Tox *bootstrap, Tox *Alice, Tox *Bob)
{
c_sleep(100);
tox_iterate(bootstrap, nullptr);
tox_iterate(Alice, nullptr);
tox_iterate(Bob, nullptr);
}
static bool toxav_audio_send_frame_helper(ToxAV *av, uint32_t friend_number, Toxav_Err_Send_Frame *error)
{
static const int16_t PCM[960] = {0};
return toxav_audio_send_frame(av, 0, PCM, 960, 1, 48000, nullptr);
}
static void regular_call_flow(
Tox *Alice, Tox *Bob, Tox *bootstrap,
ToxAV *AliceAV, ToxAV *BobAV,
CallControl *AliceCC, CallControl *BobCC,
int a_br, int v_br)
{
clear_call_control(AliceCC);
clear_call_control(BobCC);
Toxav_Err_Call call_err;
toxav_call(AliceAV, 0, a_br, v_br, &call_err);
ck_assert_msg(call_err == TOXAV_ERR_CALL_OK, "toxav_call failed: %d\n", call_err);
const time_t start_time = time(nullptr);
do {
if (BobCC->incoming) {
Toxav_Err_Answer answer_err;
toxav_answer(BobAV, 0, a_br, v_br, &answer_err);
ck_assert_msg(answer_err == TOXAV_ERR_ANSWER_OK, "toxav_answer failed: %d\n", answer_err);
BobCC->incoming = false;
} else { /* TODO(mannol): rtp */
if (time(nullptr) - start_time >= 1) {
Toxav_Err_Call_Control cc_err;
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &cc_err);
ck_assert_msg(cc_err == TOXAV_ERR_CALL_CONTROL_OK, "toxav_call_control failed: %d\n", cc_err);
}
}
iterate_tox(bootstrap, Alice, Bob);
} while (BobCC->state != TOXAV_FRIEND_CALL_STATE_FINISHED);
printf("Success!\n");
}
static void test_av_flows(void)
{
Tox *Alice, *Bob, *bootstrap;
ToxAV *AliceAV, *BobAV;
uint32_t index[] = { 1, 2, 3 };
CallControl AliceCC, BobCC;
{
Tox_Err_New error;
bootstrap = tox_new_log(nullptr, &error, &index[0]);
ck_assert(error == TOX_ERR_NEW_OK);
Alice = tox_new_log(nullptr, &error, &index[1]);
ck_assert(error == TOX_ERR_NEW_OK);
Bob = tox_new_log(nullptr, &error, &index[2]);
ck_assert(error == TOX_ERR_NEW_OK);
}
printf("Created 3 instances of Tox\n");
printf("Preparing network...\n");
long long unsigned int cur_time = time(nullptr);
uint8_t address[TOX_ADDRESS_SIZE];
tox_callback_friend_request(Alice, t_accept_friend_request_cb);
tox_self_get_address(Alice, address);
printf("bootstrapping Alice and Bob off a third bootstrap node\n");
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(bootstrap, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(bootstrap, nullptr);
tox_bootstrap(Alice, "localhost", dht_port, dht_key, nullptr);
tox_bootstrap(Bob, "localhost", dht_port, dht_key, nullptr);
ck_assert(tox_friend_add(Bob, address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
uint8_t off = 1;
while (true) {
iterate_tox(bootstrap, Alice, Bob);
if (tox_self_get_connection_status(bootstrap) &&
tox_self_get_connection_status(Alice) &&
tox_self_get_connection_status(Bob) && off) {
printf("Toxes are online, took %llu seconds\n", time(nullptr) - cur_time);
off = 0;
}
if (tox_friend_get_connection_status(Alice, 0, nullptr) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Bob, 0, nullptr) == TOX_CONNECTION_UDP) {
break;
}
c_sleep(20);
}
{
Toxav_Err_New error;
AliceAV = toxav_new(Alice, &error);
ck_assert(error == TOXAV_ERR_NEW_OK);
BobAV = toxav_new(Bob, &error);
ck_assert(error == TOXAV_ERR_NEW_OK);
}
toxav_callback_call(AliceAV, t_toxav_call_cb, &AliceCC);
toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, &AliceCC);
toxav_callback_video_receive_frame(AliceAV, t_toxav_receive_video_frame_cb, &AliceCC);
toxav_callback_audio_receive_frame(AliceAV, t_toxav_receive_audio_frame_cb, &AliceCC);
toxav_callback_call(BobAV, t_toxav_call_cb, &BobCC);
toxav_callback_call_state(BobAV, t_toxav_call_state_cb, &BobCC);
toxav_callback_video_receive_frame(BobAV, t_toxav_receive_video_frame_cb, &BobCC);
toxav_callback_audio_receive_frame(BobAV, t_toxav_receive_audio_frame_cb, &BobCC);
printf("Created 2 instances of ToxAV\n");
printf("All set after %llu seconds!\n", time(nullptr) - cur_time);
if (TEST_REGULAR_AV) {
printf("\nTrying regular call (Audio and Video)...\n");
regular_call_flow(Alice, Bob, bootstrap, AliceAV, BobAV, &AliceCC, &BobCC,
48, 4000);
}
if (TEST_REGULAR_A) {
printf("\nTrying regular call (Audio only)...\n");
regular_call_flow(Alice, Bob, bootstrap, AliceAV, BobAV, &AliceCC, &BobCC,
48, 0);
}
if (TEST_REGULAR_V) {
printf("\nTrying regular call (Video only)...\n");
regular_call_flow(Alice, Bob, bootstrap, AliceAV, BobAV, &AliceCC, &BobCC,
0, 4000);
}
if (TEST_REJECT) { /* Alice calls; Bob rejects */
printf("\nTrying reject flow...\n");
clear_call_control(&AliceCC);
clear_call_control(&BobCC);
{
Toxav_Err_Call rc;
toxav_call(AliceAV, 0, 48, 0, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_OK, "toxav_call failed: %d\n", rc);
}
do {
iterate_tox(bootstrap, Alice, Bob);
} while (!BobCC.incoming);
/* Reject */
{
Toxav_Err_Call_Control rc;
toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_CONTROL_OK, "toxav_call_control failed: %d\n", rc);
}
do {
iterate_tox(bootstrap, Alice, Bob);
} while (AliceCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED);
printf("Success!\n");
}
if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */
printf("\nTrying cancel (while ringing) flow...\n");
clear_call_control(&AliceCC);
clear_call_control(&BobCC);
{
Toxav_Err_Call rc;
toxav_call(AliceAV, 0, 48, 0, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_OK, "toxav_call failed: %d\n", rc);
}
do {
iterate_tox(bootstrap, Alice, Bob);
} while (!BobCC.incoming);
/* Cancel */
{
Toxav_Err_Call_Control rc;
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_CONTROL_OK, "toxav_call_control failed: %d\n", rc);
}
/* Alice will not receive end state */
do {
iterate_tox(bootstrap, Alice, Bob);
} while (BobCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED);
printf("Success!\n");
}
if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */
printf("\nTrying mute functionality...\n");
clear_call_control(&AliceCC);
clear_call_control(&BobCC);
/* Assume sending audio and video */
{
Toxav_Err_Call rc;
toxav_call(AliceAV, 0, 48, 1000, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_OK, "toxav_call failed: %d\n", rc);
}
do {
iterate_tox(bootstrap, Alice, Bob);
} while (!BobCC.incoming);
/* At first try all stuff while in invalid state */
ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, nullptr));
ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, nullptr));
ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, nullptr));
ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, nullptr));
ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO, nullptr));
ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, nullptr));
{
Toxav_Err_Answer rc;
toxav_answer(BobAV, 0, 48, 4000, &rc);
ck_assert_msg(rc == TOXAV_ERR_ANSWER_OK, "toxav_answer failed: %d\n", rc);
}
iterate_tox(bootstrap, Alice, Bob);
/* Pause and Resume */
printf("Pause and Resume\n");
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state == 0);
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state & (TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V));
/* Mute/Unmute single */
printf("Mute/Unmute single\n");
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state ^ TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
/* Mute/Unmute both */
printf("Mute/Unmute both\n");
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state ^ TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state ^ TOXAV_FRIEND_CALL_STATE_ACCEPTING_V);
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V);
{
Toxav_Err_Call_Control rc;
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_CONTROL_OK, "toxav_call_control failed: %d\n", rc);
}
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
printf("Success!\n");
}
if (TEST_STOP_RESUME_PAYLOAD) { /* Stop and resume audio/video payload */
printf("\nTrying stop/resume functionality...\n");
clear_call_control(&AliceCC);
clear_call_control(&BobCC);
/* Assume sending audio and video */
{
Toxav_Err_Call rc;
toxav_call(AliceAV, 0, 48, 0, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_OK, "toxav_call failed: %d\n", rc);
}
do {
iterate_tox(bootstrap, Alice, Bob);
} while (!BobCC.incoming);
{
Toxav_Err_Answer rc;
toxav_answer(BobAV, 0, 48, 0, &rc);
ck_assert_msg(rc == TOXAV_ERR_ANSWER_OK, "toxav_answer failed: %d\n", rc);
}
iterate_tox(bootstrap, Alice, Bob);
printf("Call started as audio only\n");
printf("Turning on video for Alice...\n");
ck_assert(toxav_video_set_bit_rate(AliceAV, 0, 1000, nullptr));
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V);
printf("Turning off video for Alice...\n");
ck_assert(toxav_video_set_bit_rate(AliceAV, 0, 0, nullptr));
iterate_tox(bootstrap, Alice, Bob);
ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V));
printf("Turning off audio for Alice...\n");
ck_assert(toxav_audio_set_bit_rate(AliceAV, 0, 0, nullptr));
iterate_tox(bootstrap, Alice, Bob);
ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A));
{
Toxav_Err_Call_Control rc;
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_CONTROL_OK, "toxav_call_control failed: %d\n", rc);
}
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
printf("Success!\n");
}
if (TEST_PAUSE_RESUME_SEND) { /* Stop and resume audio/video payload and test send options */
printf("\nTrying stop/resume functionality...\n");
clear_call_control(&AliceCC);
clear_call_control(&BobCC);
/* Assume sending audio and video */
{
Toxav_Err_Call rc;
toxav_call(AliceAV, 0, 48, 0, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_OK, "toxav_call failed: %d\n", rc);
}
do {
iterate_tox(bootstrap, Alice, Bob);
} while (!BobCC.incoming);
{
Toxav_Err_Answer rc;
toxav_answer(BobAV, 0, 48, 0, &rc);
ck_assert_msg(rc == TOXAV_ERR_ANSWER_OK, "toxav_answer failed: %d\n", rc);
}
iterate_tox(bootstrap, Alice, Bob);
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(!toxav_audio_send_frame_helper(AliceAV, 0, nullptr));
ck_assert(!toxav_audio_send_frame_helper(BobAV, 0, nullptr));
ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME);
iterate_tox(bootstrap, Alice, Bob);
ck_assert(toxav_audio_send_frame_helper(AliceAV, 0, nullptr));
ck_assert(toxav_audio_send_frame_helper(BobAV, 0, nullptr));
iterate_tox(bootstrap, Alice, Bob);
{
Toxav_Err_Call_Control rc;
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
ck_assert_msg(rc == TOXAV_ERR_CALL_CONTROL_OK, "toxav_call_control failed: %d\n", rc);
}
iterate_tox(bootstrap, Alice, Bob);
ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
printf("Success!\n");
}
toxav_kill(BobAV);
toxav_kill(AliceAV);
tox_kill(Bob);
tox_kill(Alice);
tox_kill(bootstrap);
printf("\nTest successful!\n");
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_av_flows();
return 0;
}

View File

@ -0,0 +1,381 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32)
#include <pthread.h>
#endif
#include <vpx/vpx_image.h>
#include "../testing/misc_tools.h"
#include "../toxav/toxav.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/logger.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_struct.h"
#include "../toxcore/util.h"
#include "auto_test_support.h"
#include "check_compat.h"
typedef struct {
bool incoming;
uint32_t state;
} CallControl;
typedef struct {
ToxAV *AliceAV;
ToxAV *BobAV;
CallControl *AliceCC;
CallControl *BobCC;
uint32_t friend_number;
} thread_data;
/**
* Callbacks
*/
static void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
{
printf("Handling CALL callback\n");
((CallControl *)user_data)[friend_number].incoming = true;
}
static void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
{
printf("Handling CALL STATE callback: %u %p\n", state, (void *)av);
((CallControl *)user_data)[friend_number].state = state;
}
static void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
}
static void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
int16_t const *pcm,
size_t sample_count,
uint8_t channels,
uint32_t sampling_rate,
void *user_data)
{
}
static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length,
void *userdata)
{
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
ck_assert(tox_friend_add_norequest(m, public_key, nullptr) != (uint32_t) -1);
}
}
/**
* Iterate helper
*/
static ToxAV *setup_av_instance(Tox *tox, CallControl *CC)
{
Toxav_Err_New error;
ToxAV *av = toxav_new(tox, &error);
ck_assert(error == TOXAV_ERR_NEW_OK);
toxav_callback_call(av, t_toxav_call_cb, CC);
toxav_callback_call_state(av, t_toxav_call_state_cb, CC);
toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, CC);
toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, CC);
return av;
}
static void *call_thread(void *pd)
{
ToxAV *AliceAV = ((thread_data *) pd)->AliceAV;
ToxAV *BobAV = ((thread_data *) pd)->BobAV;
uint32_t friend_number = ((thread_data *) pd)->friend_number;
int16_t *PCM = (int16_t *)calloc(960, sizeof(int16_t));
uint8_t *video_y = (uint8_t *)calloc(800 * 600, sizeof(uint8_t));
uint8_t *video_u = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t));
uint8_t *video_v = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t));
time_t start_time = time(nullptr);
do {
toxav_iterate(AliceAV);
toxav_iterate(BobAV);
toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, nullptr);
toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, nullptr);
toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, nullptr);
toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, nullptr);
c_sleep(10);
} while (time(nullptr) - start_time < 4);
free(PCM);
free(video_y);
free(video_u);
free(video_v);
printf("Closing thread\n");
pthread_exit(nullptr);
return nullptr;
}
typedef struct Time_Data {
pthread_mutex_t lock;
uint64_t clock;
} Time_Data;
static uint64_t get_state_clock_callback(void *user_data)
{
Time_Data *time_data = (Time_Data *)user_data;
pthread_mutex_lock(&time_data->lock);
uint64_t clock = time_data->clock;
pthread_mutex_unlock(&time_data->lock);
return clock;
}
static void increment_clock(Time_Data *time_data, uint64_t count)
{
pthread_mutex_lock(&time_data->lock);
time_data->clock += count;
pthread_mutex_unlock(&time_data->lock);
}
static void set_current_time_callback(Tox *tox, Time_Data *time_data)
{
Mono_Time *mono_time = tox->mono_time;
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, time_data);
}
static void test_av_three_calls(void)
{
uint32_t index[] = { 1, 2, 3, 4, 5 };
Tox *Alice, *bootstrap, *Bobs[3];
ToxAV *AliceAV, *BobsAV[3];
void *retval;
CallControl AliceCC[3], BobsCC[3];
Time_Data time_data;
pthread_mutex_init(&time_data.lock, nullptr);
{
Tox_Err_New error;
bootstrap = tox_new_log(nullptr, &error, &index[0]);
ck_assert(error == TOX_ERR_NEW_OK);
time_data.clock = current_time_monotonic(bootstrap->mono_time);
set_current_time_callback(bootstrap, &time_data);
Alice = tox_new_log(nullptr, &error, &index[1]);
ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(Alice, &time_data);
Bobs[0] = tox_new_log(nullptr, &error, &index[2]);
ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(Bobs[0], &time_data);
Bobs[1] = tox_new_log(nullptr, &error, &index[3]);
ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(Bobs[1], &time_data);
Bobs[2] = tox_new_log(nullptr, &error, &index[4]);
ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(Bobs[2], &time_data);
}
printf("Created 5 instances of Tox\n");
printf("Preparing network...\n");
time_t cur_time = time(nullptr);
uint8_t address[TOX_ADDRESS_SIZE];
tox_callback_friend_request(Alice, t_accept_friend_request_cb);
tox_self_get_address(Alice, address);
printf("bootstrapping Alice and the %u Bobs off a third bootstrap node\n",
(unsigned)(sizeof(Bobs) / sizeof(Bobs[0])));
uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
tox_self_get_dht_id(bootstrap, dht_key);
const uint16_t dht_port = tox_self_get_udp_port(bootstrap, nullptr);
tox_bootstrap(Alice, "localhost", dht_port, dht_key, nullptr);
tox_bootstrap(Bobs[0], "localhost", dht_port, dht_key, nullptr);
tox_bootstrap(Bobs[1], "localhost", dht_port, dht_key, nullptr);
tox_bootstrap(Bobs[2], "localhost", dht_port, dht_key, nullptr);
ck_assert(tox_friend_add(Bobs[0], address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
ck_assert(tox_friend_add(Bobs[1], address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
ck_assert(tox_friend_add(Bobs[2], address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
uint8_t off = 1;
while (true) {
tox_iterate(bootstrap, nullptr);
tox_iterate(Alice, nullptr);
tox_iterate(Bobs[0], nullptr);
tox_iterate(Bobs[1], nullptr);
tox_iterate(Bobs[2], nullptr);
if (tox_self_get_connection_status(bootstrap) &&
tox_self_get_connection_status(Alice) &&
tox_self_get_connection_status(Bobs[0]) &&
tox_self_get_connection_status(Bobs[1]) &&
tox_self_get_connection_status(Bobs[2]) && off) {
printf("Toxes are online, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
off = 0;
}
if (tox_friend_get_connection_status(Alice, 0, nullptr) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Alice, 1, nullptr) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Alice, 2, nullptr) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Bobs[0], 0, nullptr) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Bobs[1], 0, nullptr) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Bobs[2], 0, nullptr) == TOX_CONNECTION_UDP) {
break;
}
increment_clock(&time_data, 200);
c_sleep(5);
}
AliceAV = setup_av_instance(Alice, AliceCC);
BobsAV[0] = setup_av_instance(Bobs[0], &BobsCC[0]);
BobsAV[1] = setup_av_instance(Bobs[1], &BobsCC[1]);
BobsAV[2] = setup_av_instance(Bobs[2], &BobsCC[2]);
printf("Created 4 instances of ToxAV\n");
printf("All set after %lu seconds!\n", (unsigned long)(time(nullptr) - cur_time));
thread_data tds[3];
for (size_t i = 0; i < 3; i++) {
tds[i].AliceAV = AliceAV;
tds[i].BobAV = BobsAV[i];
tds[i].AliceCC = &AliceCC[i];
tds[i].BobCC = &BobsCC[i];
tds[i].friend_number = i;
memset(tds[i].AliceCC, 0, sizeof(CallControl));
memset(tds[i].BobCC, 0, sizeof(CallControl));
}
pthread_t tids[3];
for (size_t i = 0; i < 3; i++) {
(void) pthread_create(&tids[i], nullptr, call_thread, &tds[i]);
}
time_t start_time = time(nullptr);
do {
tox_iterate(bootstrap, nullptr);
tox_iterate(Alice, nullptr);
tox_iterate(Bobs[0], nullptr);
tox_iterate(Bobs[1], nullptr);
tox_iterate(Bobs[2], nullptr);
increment_clock(&time_data, 100);
c_sleep(5);
} while (time(nullptr) - start_time < 1);
/* Call */
for (size_t i = 0; i < 3; i++) {
Toxav_Err_Call rc;
toxav_call(AliceAV, tds[i].friend_number, 48, 3000, &rc);
if (rc != TOXAV_ERR_CALL_OK) {
printf("toxav_call failed: %d\n", rc);
ck_assert(0);
}
}
do {
tox_iterate(bootstrap, nullptr);
tox_iterate(Alice, nullptr);
tox_iterate(Bobs[0], nullptr);
tox_iterate(Bobs[1], nullptr);
tox_iterate(Bobs[2], nullptr);
for (size_t i = 0; i < 3; i++) {
if (BobsCC[i].incoming) {
/* Answer */
Toxav_Err_Answer rc;
toxav_answer(BobsAV[i], 0, 8, 500, &rc);
if (rc != TOXAV_ERR_ANSWER_OK) {
printf("toxav_answer failed: %d\n", rc);
ck_assert(0);
}
BobsCC[i].incoming = false;
}
}
increment_clock(&time_data, 100);
c_sleep(5);
} while (time(nullptr) - start_time < 3);
/* Hangup */
for (size_t i = 0; i < 3; i++) {
Toxav_Err_Call_Control rc;
toxav_call_control(AliceAV, i, TOXAV_CALL_CONTROL_CANCEL, &rc);
if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
printf("toxav_call_control failed: %d %p %p\n", rc, (void *)AliceAV, (void *)&BobsAV[i]);
}
}
do {
tox_iterate(bootstrap, nullptr);
tox_iterate(Alice, nullptr);
tox_iterate(Bobs[0], nullptr);
tox_iterate(Bobs[1], nullptr);
tox_iterate(Bobs[2], nullptr);
increment_clock(&time_data, 100);
c_sleep(5);
} while (time(nullptr) - start_time < 5);
ck_assert(pthread_join(tids[0], &retval) == 0);
ck_assert(retval == nullptr);
ck_assert(pthread_join(tids[1], &retval) == 0);
ck_assert(retval == nullptr);
ck_assert(pthread_join(tids[2], &retval) == 0);
ck_assert(retval == nullptr);
printf("Killing all instances\n");
toxav_kill(BobsAV[2]);
toxav_kill(BobsAV[1]);
toxav_kill(BobsAV[0]);
toxav_kill(AliceAV);
tox_kill(Bobs[2]);
tox_kill(Bobs[1]);
tox_kill(Bobs[0]);
tox_kill(Alice);
tox_kill(bootstrap);
pthread_mutex_destroy(&time_data.lock);
printf("\nTest successful!\n");
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
test_av_three_calls();
return 0;
}

View File

@ -0,0 +1,63 @@
/* Tests that our typing notifications work.
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "../testing/misc_tools.h"
#include "../toxcore/ccompat.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "check_compat.h"
typedef struct State {
bool friend_is_typing;
} State;
#include "auto_test_support.h"
static void typing_callback(Tox *m, uint32_t friendnumber, bool typing, void *user_data)
{
const AutoTox *autotox = (AutoTox *)user_data;
State *state = (State *)autotox->state;
state->friend_is_typing = typing;
}
static void test_typing(AutoTox *autotoxes)
{
time_t cur_time = time(nullptr);
tox_callback_friend_typing(autotoxes[1].tox, &typing_callback);
tox_self_set_typing(autotoxes[0].tox, 0, true, nullptr);
do {
iterate_all_wait(autotoxes, 2, 200);
} while (!((State *)autotoxes[1].state)->friend_is_typing);
ck_assert_msg(tox_friend_get_typing(autotoxes[1].tox, 0, nullptr) == 1,
"tox_friend_get_typing should have returned true, but it didn't");
tox_self_set_typing(autotoxes[0].tox, 0, false, nullptr);
do {
iterate_all_wait(autotoxes, 2, 200);
} while (((State *)autotoxes[1].state)->friend_is_typing);
Tox_Err_Friend_Query err_t;
ck_assert_msg(tox_friend_get_typing(autotoxes[1].tox, 0, &err_t) == 0,
"tox_friend_get_typing should have returned false, but it didn't");
ck_assert_msg(err_t == TOX_ERR_FRIEND_QUERY_OK, "tox_friend_get_typing call did not return correct error");
printf("test_typing succeeded, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options options = default_run_auto_options();
options.graph = GRAPH_LINEAR;
run_auto_test(nullptr, 2, test_typing, sizeof(State), &options);
return 0;
}

View File

@ -0,0 +1,96 @@
#include "../toxcore/tox.h"
#include "check_compat.h"
#define check(major, minor, patch, expected) \
do_check(TOX_VERSION_MAJOR, TOX_VERSION_MINOR, TOX_VERSION_PATCH, \
major, minor, patch, \
TOX_VERSION_IS_API_COMPATIBLE(major, minor, patch), expected)
static void do_check(int lib_major, int lib_minor, int lib_patch,
int cli_major, int cli_minor, int cli_patch,
bool actual, bool expected)
{
ck_assert_msg(actual == expected,
"Client version %d.%d.%d is%s compatible with library version %d.%d.%d, but it should%s be\n",
cli_major, cli_minor, cli_patch, actual ? "" : " not",
lib_major, lib_minor, lib_patch, expected ? "" : " not");
}
#undef TOX_VERSION_MAJOR
#undef TOX_VERSION_MINOR
#undef TOX_VERSION_PATCH
int main(void)
{
#define TOX_VERSION_MAJOR 0
#define TOX_VERSION_MINOR 0
#define TOX_VERSION_PATCH 4
// Tox versions from 0.0.* are only compatible with themselves.
check(0, 0, 0, false);
check(0, 0, 3, false);
check(0, 0, 4, true);
check(0, 0, 5, false);
check(1, 0, 4, false);
#undef TOX_VERSION_MAJOR
#undef TOX_VERSION_MINOR
#undef TOX_VERSION_PATCH
#define TOX_VERSION_MAJOR 0
#define TOX_VERSION_MINOR 1
#define TOX_VERSION_PATCH 4
// Tox versions from 0.1.* are only compatible with themselves or 0.1.<*
check(0, 0, 0, false);
check(0, 0, 4, false);
check(0, 0, 5, false);
check(0, 1, 0, true);
check(0, 1, 4, true);
check(0, 1, 5, false);
check(0, 2, 0, false);
check(0, 2, 4, false);
check(0, 2, 5, false);
check(1, 0, 0, false);
check(1, 0, 4, false);
check(1, 0, 5, false);
check(1, 1, 4, false);
#undef TOX_VERSION_MAJOR
#undef TOX_VERSION_MINOR
#undef TOX_VERSION_PATCH
#define TOX_VERSION_MAJOR 1
#define TOX_VERSION_MINOR 0
#define TOX_VERSION_PATCH 4
// Beyond 0.*.* Tox is comfortable with any lower version within their major
check(0, 0, 4, false);
check(1, 0, 0, true);
check(1, 0, 1, true);
check(1, 0, 4, true);
check(1, 0, 5, false);
check(1, 1, 0, false);
check(2, 0, 0, false);
check(2, 0, 4, false);
#undef TOX_VERSION_MAJOR
#undef TOX_VERSION_MINOR
#undef TOX_VERSION_PATCH
#define TOX_VERSION_MAJOR 1
#define TOX_VERSION_MINOR 1
#define TOX_VERSION_PATCH 4
check(0, 0, 4, false);
check(1, 0, 0, true);
check(1, 0, 4, true);
check(1, 0, 5, true);
check(1, 1, 0, true);
check(1, 1, 1, true);
check(1, 1, 4, true);
check(1, 1, 5, false);
check(1, 2, 0, false);
check(1, 2, 4, false);
check(1, 2, 5, false);
check(2, 0, 0, false);
check(2, 1, 4, false);
#undef TOX_VERSION_MAJOR
#undef TOX_VERSION_MINOR
#undef TOX_VERSION_PATCH
return 0;
}