From aa72c47125a3c7cbbeaeeddbe2ccfe5bd6f69976 Mon Sep 17 00:00:00 2001 From: iphydf Date: Thu, 24 Feb 2022 15:51:18 +0000 Subject: [PATCH] test: Add test coverage docker build for local tests. This uses mallocfail to further increase coverage using the existing tests. Also: * Moved the non-auto "tox_one_test" to gtest. This should be split into smaller tests later. * Changed `hole_punching` to `bool`. --- CMakeLists.txt | 2 +- auto_tests/Makefile.inc | 5 - auto_tests/send_message_test.c | 11 +- auto_tests/tox_one_test.c | 141 ------------------ .../docker/tox-bootstrapd.sha256 | 2 +- other/docker/coverage/Dockerfile | 61 ++++++++ other/docker/coverage/run | 7 + other/docker/coverage/run_mallocfail | 76 ++++++++++ other/docker/sources/Dockerfile | 14 ++ toxcore/BUILD.bazel | 12 ++ toxcore/DHT.c | 6 +- toxcore/DHT.h | 4 +- toxcore/mono_time_test.cc | 3 + toxcore/tox_test.cc | 132 ++++++++++++++++ 14 files changed, 322 insertions(+), 154 deletions(-) delete mode 100644 auto_tests/tox_one_test.c create mode 100644 other/docker/coverage/Dockerfile create mode 100755 other/docker/coverage/run create mode 100755 other/docker/coverage/run_mallocfail create mode 100644 other/docker/sources/Dockerfile create mode 100644 toxcore/tox_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 393fbe5e..1c9cc444 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,7 @@ unit_test(toxcore DHT) unit_test(toxcore crypto_core) unit_test(toxcore mono_time) unit_test(toxcore ping_array) +unit_test(toxcore tox) unit_test(toxcore util) ################################################################################ @@ -478,7 +479,6 @@ auto_test(tox_dispatch) auto_test(tox_events) auto_test(tox_many) auto_test(tox_many_tcp) -auto_test(tox_one) auto_test(tox_strncasecmp) auto_test(typing) auto_test(version) diff --git a/auto_tests/Makefile.inc b/auto_tests/Makefile.inc index 8ff1fef6..b3bc82fb 100644 --- a/auto_tests/Makefile.inc +++ b/auto_tests/Makefile.inc @@ -38,7 +38,6 @@ TESTS = \ tox_dispatch_test \ tox_many_tcp_test \ tox_many_test \ - tox_one_test \ tox_strncasecmp_test \ typing_test \ version_test @@ -212,10 +211,6 @@ tox_many_test_SOURCES = ../auto_tests/tox_many_test.c tox_many_test_CFLAGS = $(AUTOTEST_CFLAGS) tox_many_test_LDADD = $(AUTOTEST_LDADD) -tox_one_test_SOURCES = ../auto_tests/tox_one_test.c -tox_one_test_CFLAGS = $(AUTOTEST_CFLAGS) -tox_one_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) diff --git a/auto_tests/send_message_test.c b/auto_tests/send_message_test.c index ac63b687..84a8c3b3 100644 --- a/auto_tests/send_message_test.c +++ b/auto_tests/send_message_test.c @@ -55,9 +55,18 @@ 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; - run_auto_test(nullptr, 2, send_message_test, sizeof(State), &options); + 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; } diff --git a/auto_tests/tox_one_test.c b/auto_tests/tox_one_test.c deleted file mode 100644 index 3a14d1e3..00000000 --- a/auto_tests/tox_one_test.c +++ /dev/null @@ -1,141 +0,0 @@ -/* Auto Tests: One instance. - */ - -#include -#include - -#include "../testing/misc_tools.h" -#include "../toxcore/ccompat.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 set_random_name_and_status_message(Tox *tox, uint8_t *name, uint8_t *status_message) -{ - int i; - - for (i = 0; i < TOX_MAX_NAME_LENGTH; ++i) { - name[i] = random_u08(); - } - - for (i = 0; i < TOX_MAX_STATUS_MESSAGE_LENGTH; ++i) { - status_message[i] = random_u08(); - } -} - -static void test_one(void) -{ - uint8_t name[TOX_MAX_NAME_LENGTH]; - uint8_t status_message[TOX_MAX_STATUS_MESSAGE_LENGTH]; - - uint8_t name2[TOX_MAX_NAME_LENGTH]; - uint8_t status_message2[TOX_MAX_STATUS_MESSAGE_LENGTH]; - - uint32_t index[] = { 1, 2 }; - Tox *tox1 = tox_new_log(nullptr, nullptr, &index[0]); - ck_assert(tox1 != nullptr); - set_random_name_and_status_message(tox1, name, status_message); - Tox *tox2 = tox_new_log(nullptr, nullptr, &index[1]); - ck_assert(tox2 != nullptr); - set_random_name_and_status_message(tox2, name2, status_message2); - - uint8_t address[TOX_ADDRESS_SIZE]; - tox_self_get_address(tox1, address); - Tox_Err_Friend_Add error; - uint32_t ret = tox_friend_add(tox1, address, (const uint8_t *)"m", 1, &error); - ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_OWN_KEY, "Adding own address worked."); - - tox_self_get_address(tox2, address); - uint8_t message[TOX_MAX_FRIEND_REQUEST_LENGTH + 1] = {0}; - ret = tox_friend_add(tox1, address, nullptr, 0, &error); - ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_NULL, "Sending request with no message worked."); - ret = tox_friend_add(tox1, address, message, 0, &error); - ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_NO_MESSAGE, "Sending request with no message worked."); - ret = tox_friend_add(tox1, address, message, sizeof(message), &error); - ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_TOO_LONG, - "TOX_MAX_FRIEND_REQUEST_LENGTH is too big."); - - address[0]++; - ret = tox_friend_add(tox1, address, (const uint8_t *)"m", 1, &error); - ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM, - "Adding address with bad checksum worked."); - - tox_self_get_address(tox2, address); - ret = tox_friend_add(tox1, address, message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error); - ck_assert_msg(ret == 0 && error == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend."); - ret = tox_friend_add(tox1, address, message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error); - ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_ALREADY_SENT, "Adding friend twice worked."); - - tox_self_set_name(tox1, name, sizeof(name), nullptr); - ck_assert_msg(tox_self_get_name_size(tox1) == sizeof(name), "Can't set name of TOX_MAX_NAME_LENGTH"); - - tox_self_set_status_message(tox1, status_message, sizeof(status_message), nullptr); - ck_assert_msg(tox_self_get_status_message_size(tox1) == sizeof(status_message), - "Can't set status message of TOX_MAX_STATUS_MESSAGE_LENGTH"); - - tox_self_get_address(tox1, address); - size_t save_size = tox_get_savedata_size(tox1); - uint8_t *data = (uint8_t *)malloc(save_size); - tox_get_savedata(tox1, data); - - tox_kill(tox2); - Tox_Err_New err_n; - - 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, data, save_size); - tox2 = tox_new_log(options, &err_n, &index[1]); - ck_assert_msg(err_n == TOX_ERR_NEW_OK, "Load failed"); - - ck_assert_msg(tox_self_get_name_size(tox2) == sizeof name, "Wrong name size."); - ck_assert_msg(tox_self_get_status_message_size(tox2) == sizeof status_message, "Wrong status message size"); - - uint8_t name_loaded[TOX_MAX_NAME_LENGTH] = { 0 }; - tox_self_get_name(tox2, name_loaded); - ck_assert_msg(!memcmp(name, name_loaded, sizeof name), "Wrong name."); - - uint8_t status_message_loaded[TOX_MAX_STATUS_MESSAGE_LENGTH] = { 0 }; - tox_self_get_status_message(tox2, status_message_loaded); - ck_assert_msg(!memcmp(status_message, status_message_loaded, sizeof status_message_loaded), "Wrong status message."); - - uint8_t address2[TOX_ADDRESS_SIZE] = { 0 }; - tox_self_get_address(tox2, address2); - ck_assert_msg(memcmp(address2, address, TOX_ADDRESS_SIZE) == 0, "Wrong address."); - uint8_t new_name[TOX_MAX_NAME_LENGTH] = { 0 }; - tox_self_get_name(tox2, new_name); - ck_assert_msg(memcmp(name, new_name, TOX_MAX_NAME_LENGTH) == 0, "Wrong name"); - - uint8_t sk[TOX_SECRET_KEY_SIZE]; - tox_self_get_secret_key(tox2, sk); - tox_kill(tox2); - - tox_options_default(options); - tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_SECRET_KEY); - tox_options_set_savedata_data(options, sk, sizeof(sk)); - tox2 = tox_new_log(options, &err_n, &index[1]); - ck_assert_msg(err_n == TOX_ERR_NEW_OK, "Load failed"); - uint8_t address3[TOX_ADDRESS_SIZE]; - tox_self_get_address(tox2, address3); - ck_assert_msg(memcmp(address3, address, TOX_PUBLIC_KEY_SIZE) == 0, "Wrong public key."); - uint8_t pk[TOX_PUBLIC_KEY_SIZE]; - tox_self_get_public_key(tox2, pk); - ck_assert_msg(memcmp(pk, address, TOX_PUBLIC_KEY_SIZE) == 0, "Wrong public key."); - - tox_options_free(options); - tox_kill(tox1); - tox_kill(tox2); - free(data); -} - - -int main(void) -{ - setvbuf(stdout, nullptr, _IONBF, 0); - - test_one(); - - return 0; -} diff --git a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 index 1d5112d2..560abdb7 100644 --- a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 +++ b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 @@ -1 +1 @@ -b899ee4325e854deafa435b697c0e37430222f4f87537f4b12021eb40d12857d /usr/local/bin/tox-bootstrapd +3b7df7850212de052071b23f6b8085852467e227c232f80c97a76e4cf28d0327 /usr/local/bin/tox-bootstrapd diff --git a/other/docker/coverage/Dockerfile b/other/docker/coverage/Dockerfile new file mode 100644 index 00000000..21e64dce --- /dev/null +++ b/other/docker/coverage/Dockerfile @@ -0,0 +1,61 @@ +FROM toxchat/c-toxcore:sources AS src +FROM ubuntu:20.04 AS build + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + libconfig-dev \ + libgtest-dev \ + libmsgpack-dev \ + libopus-dev \ + libsodium-dev \ + libvpx-dev \ + llvm-dev \ + ninja-build \ + pkg-config \ + python3-pip \ + python3-pygments \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && pip3 install --no-cache-dir gcovr + +WORKDIR /work/mallocfail +RUN ["git", "clone", "--depth=1", "https://github.com/ralight/mallocfail", "/work/mallocfail"] +RUN ["make", "install"] + +WORKDIR /work +COPY --from=src /src/ /work/ +RUN ["cmake", "-B_build", "-H.", "-GNinja", \ + "-DCMAKE_C_FLAGS=-fsanitize=undefined -fno-sanitize-recover=all -fprofile-arcs -ftest-coverage -O2 -fno-omit-frame-pointer -fno-inline -g", \ + "-DENABLE_SHARED=OFF", \ + "-DMIN_LOGGER_LEVEL=TRACE", \ + "-DMUST_BUILD_TOXAV=ON", \ + "-DNON_HERMETIC_TESTS=ON", \ + "-DSTRICT_ABI=ON", \ + "-DAUTOTEST=ON"] +RUN ["cmake", "--build", "_build", "--parallel", "8", "--target", "install"] + +WORKDIR /work/_build +RUN ["ctest", "-j50", "--output-on-failure", "--rerun-failed", "--repeat", "until-pass:6"] +COPY run_mallocfail /usr/local/bin/ +ENV PYTHONUNBUFFERED=1 \ + UBSAN_OPTIONS=color=always,print_stacktrace=1 +RUN run_mallocfail ./unit_*_test +RUN run_mallocfail ./auto_send_message_test +RUN ["gcovr", \ + "--sort-percentage", \ + "--gcov-executable=gcov", \ + "--html-details=html/", \ + "--html-self-contained", \ + "--root=..", \ + "--exclude=CMakeFiles/", \ + "--exclude=_deps/", \ + "--exclude=(.+/)?auto_tests/", \ + "--exclude=(.+/)?other/", \ + "--exclude=(.+/)?testing/"] + +FROM nginx:alpine +COPY --from=build /work/_build/html/coverage_details.html /usr/share/nginx/html/index.html +COPY --from=build /work/_build/html/ /usr/share/nginx/html/ diff --git a/other/docker/coverage/run b/other/docker/coverage/run new file mode 100755 index 00000000..cfb57691 --- /dev/null +++ b/other/docker/coverage/run @@ -0,0 +1,7 @@ +#!/bin/sh + +set -eux + +docker build -t toxchat/c-toxcore:sources -f other/docker/sources/Dockerfile . +docker build -t toxchat/c-toxcore:coverage other/docker/coverage +docker run --rm -it -p "28192:80" toxchat/c-toxcore:coverage diff --git a/other/docker/coverage/run_mallocfail b/other/docker/coverage/run_mallocfail new file mode 100755 index 00000000..280397cf --- /dev/null +++ b/other/docker/coverage/run_mallocfail @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Run a test repeatedly with mallocfail. + +Usage: run_mallocfail + +This runs the program with mallocfail until there are no more additional stack +hashes for mallocfail to try out. + +You need to build mallocfail (https://github.com/ralight/mallocfail) and install +it to /usr/local/lib/mallocfail. Change _MALLOCFAIL_SO below if you want it +elsewhere. +""" +import os +import shutil +import subprocess +import sys +from typing import List + +_MALLOCFAIL_SO = "/usr/local/lib/mallocfail.so" +_HASHES = "mallocfail_hashes" +_HASHES_PREV = "mallocfail_hashes.prev" +_TIMEOUT = 3 + + +def run_mallocfail(exe: str, iteration: int) -> bool: + """Run a program with mallocfail.""" + print(f"\x1b[1;33mmallocfail '{exe}' run #{iteration}\x1b[0m") + if os.path.exists(_HASHES): + shutil.copy(_HASHES, _HASHES_PREV) + try: + proc = subprocess.run([exe], + timeout=_TIMEOUT, + env={"LD_PRELOAD": _MALLOCFAIL_SO}) + except subprocess.TimeoutExpired: + print(f"\x1b[1;34mProgram {exe} timed out\x1b[0m") + return True + finally: + assert os.path.exists(_HASHES) + if os.path.exists(_HASHES_PREV): + with open(_HASHES_PREV, "r") as prev: + with open(_HASHES, "r") as cur: + if prev.read() == cur.read(): + # Done: no new stack hashes. + return False + + if proc.returncode >= 0: + # Process exited cleanly (success or failure). + pass + elif proc.returncode == -6: + # Assertion failed. + pass + elif proc.returncode == -14: + print(f"\x1b[0;34mProgram '{exe}' timed out\x1b[0m") + else: + print( + f"\x1b[1;34mProgram '{exe}' failed to handle OOM situation cleanly\x1b[0m" + ) + sys.exit(1) + + return True + + +def main(args: List[str]) -> None: + """Run a program repeatedly under mallocfail.""" + if len(args) == 1: + print(f"Usage: {args[0]} ") + sys.exit(1) + + for exe in args[1:]: + i = 1 + while run_mallocfail(exe, i): + i += 1 + + +if __name__ == "__main__": + main(sys.argv) diff --git a/other/docker/sources/Dockerfile b/other/docker/sources/Dockerfile new file mode 100644 index 00000000..743a86e7 --- /dev/null +++ b/other/docker/sources/Dockerfile @@ -0,0 +1,14 @@ +FROM scratch + +# Roughly in order of change frequency. +COPY cmake/ /src/cmake/ +COPY other/bootstrap_daemon/ /src/other/bootstrap_daemon/ +COPY other/pkgconfig/ /src/other/pkgconfig/ +COPY other/rpm/ /src/other/rpm/ +COPY other/*.[ch] /src/other/ +COPY CMakeLists.txt so.version /src/ +COPY toxencryptsave/ /src/toxencryptsave/ +COPY testing/ /src/testing/ +COPY toxav/ /src/toxav/ +COPY toxcore/ /src/toxcore/ +COPY auto_tests/ /src/auto_tests/ diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 5620dbf1..ff76d50f 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -491,6 +491,18 @@ cc_library( ], ) +cc_test( + name = "tox_test", + size = "small", + srcs = ["tox_test.cc"], + deps = [ + ":crypto_core", + ":tox", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "tox_unpack", srcs = ["tox_unpack.c"], diff --git a/toxcore/DHT.c b/toxcore/DHT.c index 2e579ba0..90050f52 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -2200,7 +2200,7 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so if (packet[0] == NAT_PING_RESPONSE) { if (dht_friend->nat.nat_ping_id == ping_id) { dht_friend->nat.nat_ping_id = random_u64(); - dht_friend->nat.hole_punching = 1; + dht_friend->nat.hole_punching = true; return 0; } } @@ -2340,7 +2340,7 @@ static void do_NAT(DHT *dht) dht->friends_list[i].nat.nat_ping_timestamp = temp_time; } - if (dht->friends_list[i].nat.hole_punching == 1 && + if (dht->friends_list[i].nat.hole_punching && dht->friends_list[i].nat.punching_timestamp + PUNCH_INTERVAL < temp_time && dht->friends_list[i].nat.recv_nat_ping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) { @@ -2361,7 +2361,7 @@ static void do_NAT(DHT *dht) punch_holes(dht, &ip, port_list, numports, i); dht->friends_list[i].nat.punching_timestamp = temp_time; - dht->friends_list[i].nat.hole_punching = 0; + dht->friends_list[i].nat.hole_punching = false; } } } diff --git a/toxcore/DHT.h b/toxcore/DHT.h index c44dc154..0a51209e 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -111,8 +111,8 @@ typedef struct Client_data { /*----------------------------------------------------------------------------------*/ typedef struct NAT { - /* 1 if currently hole punching, otherwise 0 */ - uint8_t hole_punching; + /* true if currently hole punching */ + bool hole_punching; uint32_t punching_index; uint32_t tries; uint32_t punching_index2; diff --git a/toxcore/mono_time_test.cc b/toxcore/mono_time_test.cc index 2f95a36e..87216c4a 100644 --- a/toxcore/mono_time_test.cc +++ b/toxcore/mono_time_test.cc @@ -7,6 +7,7 @@ namespace { TEST(MonoTime, UnixTimeIncreasesOverTime) { Mono_Time *mono_time = mono_time_new(); + ASSERT_NE(mono_time, nullptr); mono_time_update(mono_time); uint64_t const start = mono_time_get(mono_time); @@ -24,6 +25,7 @@ TEST(MonoTime, UnixTimeIncreasesOverTime) TEST(MonoTime, IsTimeout) { Mono_Time *mono_time = mono_time_new(); + ASSERT_NE(mono_time, nullptr); uint64_t const start = mono_time_get(mono_time); EXPECT_FALSE(mono_time_is_timeout(mono_time, start, 1)); @@ -45,6 +47,7 @@ uint64_t test_current_time_callback(Mono_Time *mono_time, void *user_data) TEST(MonoTime, CustomTime) { Mono_Time *mono_time = mono_time_new(); + ASSERT_NE(mono_time, nullptr); uint64_t test_time = current_time_monotonic(mono_time) + 42137; diff --git a/toxcore/tox_test.cc b/toxcore/tox_test.cc new file mode 100644 index 00000000..1c713f29 --- /dev/null +++ b/toxcore/tox_test.cc @@ -0,0 +1,132 @@ +#include "tox.h" + +#include + +#include "crypto_core.h" + +namespace { + +static void set_random_name_and_status_message(Tox *tox, uint8_t *name, uint8_t *status_message) +{ + for (uint16_t i = 0; i < TOX_MAX_NAME_LENGTH; ++i) { + name[i] = random_u08(); + } + + for (uint16_t i = 0; i < TOX_MAX_STATUS_MESSAGE_LENGTH; ++i) { + status_message[i] = random_u08(); + } +} + +TEST(Tox, OneTest) +{ + std::array name; + std::array status_message; + + std::array name2; + std::array status_message2; + + Tox *tox1 = tox_new(nullptr, nullptr); + ASSERT_NE(tox1, nullptr); + set_random_name_and_status_message(tox1, name.data(), status_message.data()); + Tox *tox2 = tox_new(nullptr, nullptr); + ASSERT_NE(tox2, nullptr); + set_random_name_and_status_message(tox2, name2.data(), status_message2.data()); + + std::array address; + tox_self_get_address(tox1, address.data()); + Tox_Err_Friend_Add error; + uint32_t ret + = tox_friend_add(tox1, address.data(), reinterpret_cast("m"), 1, &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_OWN_KEY) << "Adding own address worked."; + EXPECT_EQ(ret, UINT32_MAX); + + tox_self_get_address(tox2, address.data()); + uint8_t message[TOX_MAX_FRIEND_REQUEST_LENGTH + 1] = {0}; + ret = tox_friend_add(tox1, address.data(), nullptr, 0, &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_NULL) << "Sending request with no message worked."; + EXPECT_EQ(ret, UINT32_MAX); + ret = tox_friend_add(tox1, address.data(), message, 0, &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_NO_MESSAGE) << "Sending request with no message worked."; + EXPECT_EQ(ret, UINT32_MAX); + ret = tox_friend_add(tox1, address.data(), message, sizeof(message), &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_TOO_LONG) << "TOX_MAX_FRIEND_REQUEST_LENGTH is too big."; + EXPECT_EQ(ret, UINT32_MAX); + + address[0]++; + ret = tox_friend_add(tox1, address.data(), reinterpret_cast("m"), 1, &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_BAD_CHECKSUM) << "Adding address with bad checksum worked."; + EXPECT_EQ(ret, UINT32_MAX); + + tox_self_get_address(tox2, address.data()); + ret = tox_friend_add(tox1, address.data(), message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_OK) << "Failed to add friend."; + EXPECT_EQ(ret, 0); + ret = tox_friend_add(tox1, address.data(), message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error); + EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_ALREADY_SENT) << "Adding friend twice worked."; + EXPECT_EQ(ret, UINT32_MAX); + + tox_self_set_name(tox1, name.data(), name.size(), nullptr); + EXPECT_EQ(tox_self_get_name_size(tox1), name.size()) << "Can't set name of TOX_MAX_NAME_LENGTH"; + + tox_self_set_status_message(tox1, status_message.data(), status_message.size(), nullptr); + EXPECT_EQ(tox_self_get_status_message_size(tox1), status_message.size()) + << "Can't set status message of TOX_MAX_STATUS_MESSAGE_LENGTH"; + + tox_self_get_address(tox1, address.data()); + std::vector data(tox_get_savedata_size(tox1)); + tox_get_savedata(tox1, data.data()); + + tox_kill(tox2); + Tox_Err_New err_n; + + struct Tox_Options *options = tox_options_new(nullptr); + ASSERT_NE(options, nullptr); + tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE); + tox_options_set_savedata_data(options, data.data(), data.size()); + tox2 = tox_new(options, &err_n); + EXPECT_EQ(err_n, TOX_ERR_NEW_OK) << "Load failed"; + + EXPECT_EQ(tox_self_get_name_size(tox2), sizeof name) << "Wrong name size."; + EXPECT_EQ(tox_self_get_status_message_size(tox2), sizeof status_message) + << "Wrong status message size"; + + std::array name_loaded{}; + tox_self_get_name(tox2, name_loaded.data()); + EXPECT_EQ(name, name_loaded) << "Wrong name."; + + std::array status_message_loaded{}; + tox_self_get_status_message(tox2, status_message_loaded.data()); + EXPECT_EQ(status_message, status_message_loaded) << "Wrong status message."; + + std::array address2{}; + tox_self_get_address(tox2, address2.data()); + EXPECT_EQ(address2, address) << "Wrong address."; + std::array new_name{}; + tox_self_get_name(tox2, new_name.data()); + EXPECT_EQ(name, new_name) << "Wrong name"; + + std::array sk; + tox_self_get_secret_key(tox2, sk.data()); + tox_kill(tox2); + + tox_options_default(options); + tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_SECRET_KEY); + tox_options_set_savedata_data(options, sk.data(), sk.size()); + tox2 = tox_new(options, &err_n); + ASSERT_EQ(err_n, TOX_ERR_NEW_OK) << "Load failed"; + tox_self_set_nospam(tox2, tox_self_get_nospam(tox1)); + std::array address3; + tox_self_get_address(tox2, address3.data()); + EXPECT_EQ(address3, address) << "Wrong public key."; + std::array pk; + tox_self_get_public_key(tox2, pk.data()); + std::array pk_from_addr; + std::copy(address.begin(), address.begin() + TOX_PUBLIC_KEY_SIZE, pk_from_addr.begin()); + EXPECT_EQ(pk, pk_from_addr) << "Wrong public key."; + + tox_options_free(options); + tox_kill(tox1); + tox_kill(tox2); +} + +} // namespace