Squashed 'external/toxcore/c-toxcore/' content from commit 67badf69
git-subtree-dir: external/toxcore/c-toxcore git-subtree-split: 67badf69416a74e74f6d7eb51dd96f37282b8455
This commit is contained in:
commit
227425b90e
39
.circleci/cmake-asan
Executable file
39
.circleci/cmake-asan
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
CACHEDIR="$HOME/cache"
|
||||
|
||||
. ".github/scripts/flags-$CC.sh"
|
||||
add_flag -Werror
|
||||
add_flag -fdiagnostics-color=always
|
||||
add_flag -fno-omit-frame-pointer
|
||||
add_flag -fsanitize=address
|
||||
cmake -B_build -H. -GNinja \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$PWD/_install" \
|
||||
-DCMAKE_UNITY_BUILD=ON \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=ON \
|
||||
-DSTRICT_ABI=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=120 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON \
|
||||
-DBUILD_MISC_TESTS=ON \
|
||||
-DBUILD_FUN_UTILS=ON
|
||||
|
||||
cd _build
|
||||
|
||||
ninja install -j"$(nproc)"
|
||||
|
||||
export ASAN_OPTIONS="color=always"
|
||||
export ASAN_OPTIONS="$ASAN_OPTIONS,detect_invalid_pointer_pairs=1"
|
||||
export ASAN_OPTIONS="$ASAN_OPTIONS,detect_stack_use_after_return=1"
|
||||
export ASAN_OPTIONS="$ASAN_OPTIONS,strict_init_order=1"
|
||||
export ASAN_OPTIONS="$ASAN_OPTIONS,strict_string_checks=1"
|
||||
export ASAN_OPTIONS="$ASAN_OPTIONS,symbolize=1"
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6
|
35
.circleci/cmake-tsan
Executable file
35
.circleci/cmake-tsan
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
CACHEDIR="$HOME/cache"
|
||||
|
||||
. ".github/scripts/flags-$CC.sh"
|
||||
add_flag -Werror
|
||||
add_flag -fdiagnostics-color=always
|
||||
add_flag -fno-omit-frame-pointer
|
||||
add_flag -fsanitize=thread
|
||||
cmake -B_build -H. -GNinja \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$PWD/_install" \
|
||||
-DCMAKE_UNITY_BUILD=ON \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=ON \
|
||||
-DSTRICT_ABI=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=120 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON
|
||||
|
||||
cd _build
|
||||
|
||||
ninja install -j"$(nproc)"
|
||||
|
||||
export TSAN_OPTIONS="color=always"
|
||||
export TSAN_OPTIONS="$TSAN_OPTIONS,halt_on_error=1"
|
||||
export TSAN_OPTIONS="$TSAN_OPTIONS,second_deadlock_stack=1"
|
||||
export TSAN_OPTIONS="$TSAN_OPTIONS,symbolize=1"
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6
|
40
.circleci/cmake-ubsan
Executable file
40
.circleci/cmake-ubsan
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
CACHEDIR="$HOME/cache"
|
||||
|
||||
. ".github/scripts/flags-$CC.sh"
|
||||
add_flag -Werror
|
||||
add_flag -fdiagnostics-color=always
|
||||
add_flag -fno-omit-frame-pointer
|
||||
add_flag -fno-sanitize-recover=all
|
||||
add_flag -fsanitize=undefined,nullability,local-bounds,float-divide-by-zero,integer
|
||||
add_flag -fno-sanitize=implicit-conversion,unsigned-integer-overflow
|
||||
# Enable extra checks. We only do this on ubsan because it shows useful error
|
||||
# messages for the kind of bugs this catches (mostly incorrect nullability
|
||||
# annotations). Other builds will segfault, ubsan will show a stack trace.
|
||||
add_flag -D_DEBUG
|
||||
cmake -B_build -H. -GNinja \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$PWD/_install" \
|
||||
-DCMAKE_UNITY_BUILD=ON \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=ON \
|
||||
-DSTRICT_ABI=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=120 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON
|
||||
|
||||
cd _build
|
||||
|
||||
ninja install -j"$(nproc)"
|
||||
|
||||
export UBSAN_OPTIONS="color=always"
|
||||
export UBSAN_OPTIONS="$UBSAN_OPTIONS,print_stacktrace=1"
|
||||
export UBSAN_OPTIONS="$UBSAN_OPTIONS,symbolize=1"
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6
|
170
.circleci/config.yml
Normal file
170
.circleci/config.yml
Normal file
@ -0,0 +1,170 @@
|
||||
---
|
||||
version: 2
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
program-analysis:
|
||||
jobs:
|
||||
# Dynamic analysis
|
||||
- asan
|
||||
- tsan
|
||||
- msan
|
||||
- ubsan
|
||||
# Static analysis
|
||||
- clang-analyze
|
||||
- clang-tidy
|
||||
- cpplint
|
||||
- infer
|
||||
- static-analysis
|
||||
|
||||
jobs:
|
||||
asan:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: &apt_install
|
||||
apt-get update &&
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
apt-get install -y --no-install-recommends
|
||||
ca-certificates
|
||||
clang
|
||||
cmake
|
||||
git
|
||||
libconfig-dev
|
||||
libgtest-dev
|
||||
libopus-dev
|
||||
libsodium-dev
|
||||
libvpx-dev
|
||||
llvm-dev
|
||||
ninja-build
|
||||
pkg-config
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: CC=clang .circleci/cmake-asan
|
||||
|
||||
tsan:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: CC=clang .circleci/cmake-tsan
|
||||
|
||||
ubsan:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: CC=clang .circleci/cmake-ubsan
|
||||
|
||||
msan:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: toxchat/toktok-stack:latest-msan
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: rm -rf /src/workspace/c-toxcore/* && mv * /src/workspace/c-toxcore/
|
||||
- run:
|
||||
cd /src/workspace && bazel test
|
||||
//c-toxcore/auto_tests:lossless_packet_test
|
||||
//c-toxcore/toxav/...
|
||||
//c-toxcore/toxcore/...
|
||||
|
||||
infer:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: toxchat/infer
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: infer --no-progress-bar -- cc
|
||||
auto_tests/auto_test_support.c
|
||||
auto_tests/lossless_packet_test.c
|
||||
testing/misc_tools.c
|
||||
toxav/*.c
|
||||
toxcore/*.c
|
||||
toxcore/*/*.c
|
||||
toxencryptsave/*.c
|
||||
third_party/cmp/*.c
|
||||
-lpthread
|
||||
$(pkg-config --cflags --libs libsodium opus vpx)
|
||||
|
||||
static-analysis:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- run:
|
||||
apt-get install -y --no-install-recommends
|
||||
ca-certificates
|
||||
cppcheck
|
||||
g++
|
||||
llvm-dev
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: other/analysis/check_includes
|
||||
- run: other/analysis/check_logger_levels
|
||||
- run: other/analysis/run-clang
|
||||
- run: other/analysis/run-cppcheck
|
||||
- run: other/analysis/run-gcc
|
||||
|
||||
clang-analyze:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: other/analysis/run-clang-analyze
|
||||
|
||||
clang-tidy:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- run:
|
||||
apt-get install -y --no-install-recommends
|
||||
ca-certificates
|
||||
clang-tidy-12
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: cmake . -B_build -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
- run:
|
||||
other/analysis/run-clang-tidy ||
|
||||
other/analysis/run-clang-tidy ||
|
||||
other/analysis/run-clang-tidy
|
||||
|
||||
cpplint:
|
||||
working_directory: ~/work
|
||||
docker:
|
||||
- image: ubuntu
|
||||
|
||||
steps:
|
||||
- run: *apt_install
|
||||
- run:
|
||||
apt-get install -y --no-install-recommends
|
||||
ca-certificates
|
||||
python3-pip
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: pip install cpplint
|
||||
- run: other/analysis/run-cpplint
|
114
.cirrus.yml
Normal file
114
.cirrus.yml
Normal file
@ -0,0 +1,114 @@
|
||||
---
|
||||
bazel-opt_task:
|
||||
container:
|
||||
image: toxchat/toktok-stack:latest-release
|
||||
cpu: 2
|
||||
memory: 2G
|
||||
configure_script:
|
||||
- git submodule update --init --recursive
|
||||
- /src/workspace/tools/inject-repo c-toxcore
|
||||
test_all_script:
|
||||
- cd /src/workspace && bazel test -k
|
||||
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
--build_tag_filters=-haskell
|
||||
--test_tag_filters=-haskell
|
||||
--remote_download_minimal
|
||||
--
|
||||
//c-toxcore/...
|
||||
-//c-toxcore/auto_tests:tcp_relay_test # TODO(robinlinden): Why does this pass locally but not in Cirrus?
|
||||
|
||||
bazel-dbg_task:
|
||||
container:
|
||||
image: toxchat/toktok-stack:latest-debug
|
||||
cpu: 2
|
||||
memory: 2G
|
||||
configure_script:
|
||||
- git submodule update --init --recursive
|
||||
- /src/workspace/tools/inject-repo c-toxcore
|
||||
test_all_script:
|
||||
- cd /src/workspace && bazel test -k
|
||||
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
--build_tag_filters=-haskell
|
||||
--test_tag_filters=-haskell
|
||||
--remote_download_minimal
|
||||
--
|
||||
//c-toxcore/...
|
||||
-//c-toxcore/auto_tests:tcp_relay_test # TODO(robinlinden): Why does this pass locally but not in Cirrus?
|
||||
|
||||
bazel-asan_task:
|
||||
container:
|
||||
image: toxchat/toktok-stack:latest-asan
|
||||
cpu: 2
|
||||
memory: 4G
|
||||
configure_script:
|
||||
- git submodule update --init --recursive
|
||||
- /src/workspace/tools/inject-repo c-toxcore
|
||||
test_all_script:
|
||||
- cd /src/workspace && bazel test -k
|
||||
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
--build_tag_filters=-haskell
|
||||
--test_tag_filters=-haskell
|
||||
--remote_download_minimal
|
||||
--
|
||||
//c-toxcore/...
|
||||
-//c-toxcore/auto_tests:tcp_relay_test # TODO(robinlinden): Why does this pass locally but not in Cirrus?
|
||||
|
||||
# TODO(iphydf): Enable once this works properly.
|
||||
#bazel-msan_task:
|
||||
# container:
|
||||
# image: toxchat/toktok-stack:latest-msan
|
||||
# cpu: 2
|
||||
# memory: 4G
|
||||
# configure_script:
|
||||
# - git submodule update --init --recursive
|
||||
# - /src/workspace/tools/inject-repo c-toxcore
|
||||
# test_all_script:
|
||||
# - cd /src/workspace && bazel test -k
|
||||
# --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
# --build_tag_filters=-haskell
|
||||
# --test_tag_filters=-haskell
|
||||
# --remote_download_minimal
|
||||
# --
|
||||
# //c-toxcore/...
|
||||
# -//c-toxcore/auto_tests:tcp_relay_test # TODO(robinlinden): Why does this pass locally but not in Cirrus?
|
||||
|
||||
# TODO(iphydf): Fix test timeouts.
|
||||
bazel-tsan_task:
|
||||
container:
|
||||
image: toxchat/toktok-stack:latest-tsan
|
||||
cpu: 2
|
||||
memory: 4G
|
||||
configure_script:
|
||||
- git submodule update --init --recursive
|
||||
- /src/workspace/tools/inject-repo c-toxcore
|
||||
test_all_script:
|
||||
- cd /src/workspace && bazel test -k
|
||||
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
--build_tag_filters=-haskell
|
||||
--test_tag_filters=-haskell
|
||||
--remote_download_minimal
|
||||
--
|
||||
//c-toxcore/...
|
||||
-//c-toxcore/auto_tests:conference_av_test
|
||||
-//c-toxcore/auto_tests:conference_test
|
||||
-//c-toxcore/auto_tests:file_transfer_test
|
||||
-//c-toxcore/auto_tests:group_tcp_test
|
||||
-//c-toxcore/auto_tests:onion_test
|
||||
-//c-toxcore/auto_tests:tcp_relay_test
|
||||
-//c-toxcore/auto_tests:tox_many_test
|
||||
|
||||
cimple_task:
|
||||
container:
|
||||
image: toxchat/toktok-stack:latest-release
|
||||
cpu: 2
|
||||
memory: 4G
|
||||
configure_script:
|
||||
- git submodule update --init --recursive
|
||||
- /src/workspace/tools/inject-repo c-toxcore
|
||||
test_all_script:
|
||||
- cd /src/workspace && bazel test -k
|
||||
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
--build_tag_filters=haskell
|
||||
--test_tag_filters=haskell
|
||||
--
|
||||
//c-toxcore/...
|
22
.clang-format
Normal file
22
.clang-format
Normal file
@ -0,0 +1,22 @@
|
||||
BasedOnStyle: WebKit
|
||||
ColumnLimit: 100
|
||||
PointerAlignment: Right
|
||||
SpacesBeforeTrailingComments: 2
|
||||
AlignConsecutiveMacros: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
Cpp11BracedListStyle: true
|
||||
|
||||
IncludeIsMainRegex: '([-_](test|fuzz_test))?$'
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
26
.clusterfuzzlite/Dockerfile
Normal file
26
.clusterfuzzlite/Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
# c-toxcore Clusterfuzzlite build environment
|
||||
|
||||
# We want to use the latest tools always
|
||||
FROM gcr.io/oss-fuzz-base/base-builder:latest
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y install --no-install-suggests --no-install-recommends \
|
||||
cmake libtool autoconf automake pkg-config \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Static builds of dependencies
|
||||
|
||||
# libsodium
|
||||
RUN git clone --depth 1 --branch 1.0.18 https://github.com/jedisct1/libsodium libsodium
|
||||
WORKDIR $SRC/libsodium
|
||||
RUN ./autogen.sh && ./configure --enable-shared=no && make install
|
||||
WORKDIR $SRC
|
||||
|
||||
# Copy your project's source code.
|
||||
COPY . $SRC/c-toxcore
|
||||
# Working directory for build.sh.
|
||||
WORKDIR $SRC/c-toxcore
|
||||
RUN git submodule update --init --recursive
|
||||
# Copy build.sh into $SRC dir.
|
||||
COPY ./.clusterfuzzlite/build.sh $SRC/
|
25
.clusterfuzzlite/build.sh
Normal file
25
.clusterfuzzlite/build.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
FUZZ_TARGETS="bootstrap_fuzzer toxsave_fuzzer"
|
||||
|
||||
# out of tree build
|
||||
cd "$WORK"
|
||||
|
||||
ls /usr/local/lib/
|
||||
|
||||
# Debug build for asserts
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER="$CC" \
|
||||
-DCMAKE_CXX_COMPILER="$CXX" \
|
||||
-DCMAKE_C_FLAGS="$CFLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXXFLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LIB_FUZZING_ENGINE" \
|
||||
-DBUILD_TOXAV=OFF -DENABLE_SHARED=NO -DBUILD_FUZZ_TESTS=ON \
|
||||
-DDHT_BOOTSTRAP=OFF -DBOOTSTRAP_DAEMON=OFF "$SRC"/c-toxcore
|
||||
|
||||
for TARGET in $FUZZ_TARGETS; do
|
||||
# build fuzzer target
|
||||
cmake --build ./ --target "$TARGET"
|
||||
|
||||
# copy to output files
|
||||
cp "$WORK/testing/fuzzing/$TARGET" "$OUT"/
|
||||
done
|
22
.editorconfig
Normal file
22
.editorconfig
Normal file
@ -0,0 +1,22 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yml,sh,cmake}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[CMakeLists.txt]
|
||||
indent_style = space
|
||||
indent_size = 2
|
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
/.github/ @TokTok/admins
|
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "11:00"
|
||||
open-pull-requests-limit: 2
|
27
.github/scripts/autotools-linux
vendored
Executable file
27
.github/scripts/autotools-linux
vendored
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
NPROC=$(nproc)
|
||||
|
||||
. ".github/scripts/flags-$CC.sh"
|
||||
|
||||
add_ld_flag -Wl,-z,defs
|
||||
|
||||
# Make compilation error on a warning
|
||||
add_flag -Werror
|
||||
|
||||
add_config_flag --with-nacl-libs="$CACHEDIR/lib/amd64"
|
||||
add_config_flag --with-nacl-headers="$CACHEDIR/include/amd64"
|
||||
add_config_flag --disable-ipv6
|
||||
add_config_flag --enable-nacl
|
||||
add_config_flag --enable-daemon
|
||||
add_config_flag --with-log-level=TRACE
|
||||
|
||||
autoreconf -fi
|
||||
mkdir -p _build
|
||||
cd _build # pushd
|
||||
../configure "${CONFIG_FLAGS[@]}" || (cat config.log && false)
|
||||
make "-j$NPROC" -k CFLAGS="$C_FLAGS" LDFLAGS="$LD_FLAGS"
|
||||
make -j50 -k distcheck DISTCHECK_CONFIGURE_FLAGS="${CONFIG_FLAGS[*]}" || (cat tox-*/_build/build/test-suite.log && false)
|
||||
cd - # popd
|
65
.github/scripts/cmake-android
vendored
Executable file
65
.github/scripts/cmake-android
vendored
Executable file
@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
# Set up environment
|
||||
NDK=$ANDROID_NDK_HOME
|
||||
|
||||
ABI=${1:-"armeabi-v7a"}
|
||||
|
||||
case $ABI in
|
||||
armeabi-v7a)
|
||||
TARGET=armv7a-linux-androideabi
|
||||
NDK_API=16
|
||||
;;
|
||||
arm64-v8a)
|
||||
TARGET=aarch64-linux-android
|
||||
NDK_API=21
|
||||
;;
|
||||
x86)
|
||||
TARGET=i686-linux-android
|
||||
NDK_API=16
|
||||
;;
|
||||
x86_64)
|
||||
TARGET=x86_64-linux-android
|
||||
NDK_API=21
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -rf _android_prefix
|
||||
mkdir -p _android_prefix
|
||||
PREFIX=$PWD/_android_prefix
|
||||
|
||||
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
|
||||
SYSROOT=$TOOLCHAIN/sysroot
|
||||
|
||||
export CC="$TOOLCHAIN/bin/$TARGET$NDK_API"-clang
|
||||
export LDFLAGS=-static-libstdc++
|
||||
export PKG_CONFIG_PATH="$PREFIX"/lib/pkgconfig
|
||||
|
||||
# Build libsodium
|
||||
if [ ! -d libsodium ]; then
|
||||
git clone --branch=1.0.18 https://github.com/jedisct1/libsodium.git
|
||||
fi
|
||||
cd libsodium
|
||||
git clean -ffdx
|
||||
autoreconf -fi
|
||||
./configure --prefix="$PREFIX" --host="$TARGET" --with-sysroot="$SYSROOT" --disable-shared
|
||||
make -j"$(nproc)" install
|
||||
cd ..
|
||||
|
||||
# Build c-toxcore
|
||||
rm -rf _build
|
||||
mkdir -p _build
|
||||
cd _build
|
||||
cmake .. \
|
||||
-DBUILD_TOXAV=OFF \
|
||||
-DBOOTSTRAP_DAEMON=OFF \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$NDK/build/cmake/android.toolchain.cmake" \
|
||||
-DANDROID_ABI="$ABI" \
|
||||
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
|
||||
-DCMAKE_PREFIX_PATH="$PREFIX"
|
||||
cmake --build .
|
52
.github/scripts/cmake-freebsd-stage2
vendored
Executable file
52
.github/scripts/cmake-freebsd-stage2
vendored
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Copyright (C) 2018-2021 nurupo
|
||||
|
||||
# Toxcore building
|
||||
|
||||
set -eux
|
||||
|
||||
if [ "$PWD" != "/work" ]; then
|
||||
cd ..
|
||||
mv c-toxcore /
|
||||
mkdir c-toxcore
|
||||
cd /work
|
||||
fi
|
||||
|
||||
. cmake-freebsd-run.sh
|
||||
|
||||
# === Get VM ready to build the code ===
|
||||
|
||||
gunzip "$IMAGE_NAME.gz"
|
||||
|
||||
start_vm
|
||||
|
||||
# Copy over toxcore code from host to qemu
|
||||
scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -P "$SSH_PORT" -r /c-toxcore root@localhost:~
|
||||
|
||||
RUN ls -lh
|
||||
|
||||
cd /c-toxcore
|
||||
. ".github/scripts/flags-clang.sh"
|
||||
|
||||
add_ld_flag -Wl,-z,defs
|
||||
|
||||
# Make compilation error on a warning
|
||||
add_flag -Werror
|
||||
|
||||
RUN 'cmake -B_build -Hc-toxcore \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="_install" \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=ON \
|
||||
-DSTRICT_ABI=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=90 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON'
|
||||
|
||||
# We created the VM with the same number of cores as the host, so the host-ran `nproc` here is fine
|
||||
RUN 'gmake "-j$NPROC" -k install -C_build'
|
||||
RUN 'gmake "-j$NPROC" test ARGS="-j50" -C_build || true'
|
45
.github/scripts/cmake-osx
vendored
Executable file
45
.github/scripts/cmake-osx
vendored
Executable file
@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
NPROC=$(sysctl -n hw.physicalcpu)
|
||||
|
||||
# Workaround for bug in Homebrew where it only finds an old Ruby version.
|
||||
brew update
|
||||
|
||||
brew install \
|
||||
libconfig \
|
||||
libsodium \
|
||||
libvpx \
|
||||
opus
|
||||
|
||||
. ".github/scripts/flags-clang.sh"
|
||||
|
||||
add_ld_flag -undefined error
|
||||
|
||||
# Make compilation error on a warning
|
||||
add_flag -Werror
|
||||
|
||||
# Allow _Static_assert. Supported C11 extensions are fine, since we have several
|
||||
# C99-only compilers we test against anyway. Anything that passes all the
|
||||
# compilers we use is fine.
|
||||
add_c_flag -Wno-c11-extensions
|
||||
|
||||
cmake -B_build -H. \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$PWD/_install" \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=120 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON
|
||||
|
||||
cd _build # pushd
|
||||
make "-j$NPROC" -k install
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6 ||
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6
|
||||
cd - # popd
|
7
.github/scripts/cmake-win32
vendored
Executable file
7
.github/scripts/cmake-win32
vendored
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
i686=true
|
||||
x86_64=false
|
||||
WINDOWS_ARCH=win32
|
||||
|
||||
. .github/scripts/cmake-windows.sh
|
7
.github/scripts/cmake-win64
vendored
Executable file
7
.github/scripts/cmake-win64
vendored
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
i686=false
|
||||
x86_64=true
|
||||
WINDOWS_ARCH=win64
|
||||
|
||||
. .github/scripts/cmake-windows.sh
|
29
.github/scripts/cmake-windows.sh
vendored
Normal file
29
.github/scripts/cmake-windows.sh
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
NPROC=$(nproc)
|
||||
|
||||
. ".github/scripts/flags-gcc.sh"
|
||||
|
||||
# Allows wine to display source code file names and line numbers on crash in
|
||||
# its backtrace.
|
||||
add_flag -gdwarf-2
|
||||
|
||||
# Fix invalid register for .seh_savexmm error
|
||||
add_flag -fno-asynchronous-unwind-tables
|
||||
|
||||
docker run \
|
||||
-e ALLOW_TEST_FAILURE=true \
|
||||
-e ENABLE_ARCH_i686="$i686" \
|
||||
-e ENABLE_ARCH_x86_64="$x86_64" \
|
||||
-e ENABLE_TEST=true \
|
||||
-e EXTRA_CMAKE_FLAGS="-DBOOTSTRAP_DAEMON=OFF -DMIN_LOGGER_LEVEL=DEBUG -DTEST_TIMEOUT_SECONDS=90 -DAUTOTEST=ON" \
|
||||
-e CMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-e CMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-e CMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-e CMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-v "$PWD:/toxcore" \
|
||||
-v "$PWD/result:/prefix" \
|
||||
--rm \
|
||||
"toxchat/windows:$WINDOWS_ARCH"
|
61
.github/scripts/coverage-linux
vendored
Executable file
61
.github/scripts/coverage-linux
vendored
Executable file
@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
NPROC=$(nproc)
|
||||
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libgtest-dev \
|
||||
libopus-dev \
|
||||
libsodium-dev \
|
||||
libvpx-dev \
|
||||
llvm-11 \
|
||||
ninja-build
|
||||
git clone --depth=1 https://github.com/ralight/mallocfail /tmp/mallocfail
|
||||
cd /tmp/mallocfail # pushd
|
||||
make
|
||||
sudo make install
|
||||
cd - # popd
|
||||
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
|
||||
sudo install other/docker/coverage/run_mallocfail /usr/local/bin/run_mallocfail
|
||||
(cd other/proxy && go build)
|
||||
other/proxy/proxy &
|
||||
|
||||
. ".github/scripts/flags-coverage.sh"
|
||||
|
||||
cmake -B_build -H. -GNinja \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$PWD/_install" \
|
||||
-DENABLE_SHARED=OFF \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=OFF \
|
||||
-DSTRICT_ABI=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=120 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON \
|
||||
-DPROXY_TEST=ON
|
||||
|
||||
cmake --build _build --parallel "$NPROC" --target install -- -k 0
|
||||
|
||||
cd _build # pushd
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6 ||
|
||||
ctest -j50 --output-on-failure --rerun-failed --repeat until-pass:6
|
||||
|
||||
export PYTHONUNBUFFERED=1
|
||||
run_mallocfail --ctest=2 --jobs=8
|
||||
cd - # popd
|
||||
|
||||
#coveralls \
|
||||
# --exclude auto_tests \
|
||||
# --exclude other \
|
||||
# --exclude testing \
|
||||
# --gcov-options '\-lp'
|
||||
|
||||
bash <(curl -s https://codecov.io/bash) -x "llvm-cov-11 gcov"
|
71
.github/scripts/flags-clang.sh
vendored
Normal file
71
.github/scripts/flags-clang.sh
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
|
||||
. .github/scripts/flags.sh
|
||||
|
||||
# Add all warning flags we can.
|
||||
add_flag -Wall
|
||||
add_flag -Wextra
|
||||
add_flag -Weverything
|
||||
|
||||
# Disable specific warning flags for both C and C++.
|
||||
|
||||
# Very verbose, not very useful. This warns about things like int -> uint
|
||||
# conversions that change sign without a cast and narrowing conversions.
|
||||
add_flag -Wno-conversion
|
||||
# TODO(iphydf): Check enum values when received from the user, then assume
|
||||
# correctness and remove this suppression.
|
||||
add_flag -Wno-covered-switch-default
|
||||
# We use C99, so declarations can come after statements.
|
||||
add_flag -Wno-declaration-after-statement
|
||||
# Due to clang's tolower() macro being recursive
|
||||
# https://github.com/TokTok/c-toxcore/pull/481
|
||||
add_flag -Wno-disabled-macro-expansion
|
||||
# We don't put __attribute__ on the public API.
|
||||
add_flag -Wno-documentation-deprecated-sync
|
||||
# Bootstrap daemon does this.
|
||||
add_flag -Wno-format-nonliteral
|
||||
# struct Foo foo = {0}; is a common idiom. Missing braces means we'd need to
|
||||
# write {{{0}}} in some cases, which is ugly and a maintenance burden.
|
||||
add_flag -Wno-missing-braces
|
||||
add_flag -Wno-missing-field-initializers
|
||||
# We don't use this attribute. It appears in the non-NDEBUG stderr logger.
|
||||
add_flag -Wno-missing-noreturn
|
||||
# Useful sometimes, but we accept padding in structs for clarity.
|
||||
# Reordering fields to avoid padding will reduce readability.
|
||||
add_flag -Wno-padded
|
||||
# This warns on things like _XOPEN_SOURCE, which we currently need (we
|
||||
# probably won't need these in the future).
|
||||
add_flag -Wno-reserved-id-macro
|
||||
# TODO(iphydf): Clean these up. They are likely not bugs, but still
|
||||
# potential issues and probably confusing.
|
||||
add_flag -Wno-sign-compare
|
||||
# __attribute__((nonnull)) causes this warning on defensive null checks.
|
||||
add_flag -Wno-tautological-pointer-compare
|
||||
# Our use of mutexes results in a false positive, see 1bbe446.
|
||||
add_flag -Wno-thread-safety-analysis
|
||||
# File transfer code has this.
|
||||
add_flag -Wno-type-limits
|
||||
# Callbacks often don't use all their parameters.
|
||||
add_flag -Wno-unused-parameter
|
||||
# cimple does this better
|
||||
add_flag -Wno-unused-function
|
||||
# libvpx uses __attribute__((unused)) for "potentially unused" static
|
||||
# functions to avoid unused static function warnings.
|
||||
add_flag -Wno-used-but-marked-unused
|
||||
# We use variable length arrays a lot.
|
||||
add_flag -Wno-vla
|
||||
# Disable warnings about unknown Doxygen commands
|
||||
add_flag -Wno-documentation-unknown-command
|
||||
|
||||
# Disable specific warning flags for C++.
|
||||
|
||||
# Comma at end of enum is supported everywhere we run.
|
||||
add_cxx_flag -Wno-c++98-compat-pedantic
|
||||
# TODO(iphydf): Stop using flexible array members.
|
||||
add_cxx_flag -Wno-c99-extensions
|
||||
# We're C-compatible, so use C style casts.
|
||||
add_cxx_flag -Wno-old-style-cast
|
||||
|
||||
# Downgrade to warning so we still see it.
|
||||
add_flag -Wno-error=unreachable-code
|
||||
add_flag -Wno-error=unused-variable
|
27
.github/scripts/flags-coverage.sh
vendored
Normal file
27
.github/scripts/flags-coverage.sh
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
. .github/scripts/flags-clang.sh
|
||||
|
||||
add_ld_flag -Wl,-z,defs
|
||||
|
||||
# Make compilation error on a warning
|
||||
add_flag -Werror
|
||||
|
||||
# Coverage flags.
|
||||
add_flag --coverage
|
||||
|
||||
# Optimisation, but keep stack traces useful.
|
||||
add_c_flag -fno-inline -fno-omit-frame-pointer
|
||||
|
||||
# Show useful stack traces on crash.
|
||||
add_flag -fsanitize=undefined -fno-sanitize-recover=all
|
||||
|
||||
# In test code (_test.cc and libgtest), throw away all debug information.
|
||||
# We only care about stack frames inside toxcore (which is C). Without this,
|
||||
# mallocfail will spend a lot of time finding all the ways in which gtest can
|
||||
# fail to allocate memory, which is not interesting to us.
|
||||
add_cxx_flag -g0
|
||||
|
||||
# Continue executing code in error paths so we can see it cleanly exit (and the
|
||||
# test code itself may abort).
|
||||
add_flag -DABORT_ON_LOG_ERROR=false
|
63
.github/scripts/flags-gcc.sh
vendored
Normal file
63
.github/scripts/flags-gcc.sh
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
|
||||
. .github/scripts/flags.sh
|
||||
|
||||
# Add all warning flags we can.
|
||||
add_flag -Wall
|
||||
add_flag -Wextra
|
||||
|
||||
# Some additional warning flags not enabled by any of the above.
|
||||
add_flag -Wbool-compare
|
||||
add_flag -Wcast-align
|
||||
add_flag -Wcast-qual
|
||||
add_flag -Wchar-subscripts
|
||||
add_flag -Wdouble-promotion
|
||||
add_flag -Wduplicated-cond
|
||||
add_flag -Wempty-body
|
||||
add_flag -Wenum-compare
|
||||
add_flag -Wfloat-equal
|
||||
add_flag -Wformat=2
|
||||
add_flag -Wframe-address
|
||||
add_flag -Wframe-larger-than=9000
|
||||
add_flag -Wignored-attributes
|
||||
add_flag -Wignored-qualifiers
|
||||
add_flag -Winit-self
|
||||
add_flag -Winline
|
||||
add_flag -Wlarger-than=530000
|
||||
add_flag -Wmaybe-uninitialized
|
||||
add_flag -Wmemset-transposed-args
|
||||
add_flag -Wmisleading-indentation
|
||||
add_flag -Wmissing-declarations
|
||||
add_flag -Wnonnull
|
||||
add_flag -Wnull-dereference
|
||||
add_flag -Wodr
|
||||
add_flag -Wredundant-decls
|
||||
add_flag -Wreturn-type
|
||||
add_flag -Wshadow
|
||||
add_flag -Wsuggest-attribute=format
|
||||
add_flag -Wundef
|
||||
add_flag -Wunsafe-loop-optimizations
|
||||
add_flag -Wunused-but-set-parameter
|
||||
add_flag -Wunused-but-set-variable
|
||||
add_flag -Wunused-label
|
||||
add_flag -Wunused-local-typedefs
|
||||
add_flag -Wunused-value
|
||||
|
||||
# Disable specific warning flags for both C and C++.
|
||||
|
||||
# struct Foo foo = {0}; is a common idiom.
|
||||
add_flag -Wno-missing-field-initializers
|
||||
# TODO(iphydf): Clean these up. They are likely not bugs, but still
|
||||
# potential issues and probably confusing.
|
||||
add_flag -Wno-sign-compare
|
||||
# File transfer code has this.
|
||||
add_flag -Wno-type-limits
|
||||
# Callbacks often don't use all their parameters.
|
||||
add_flag -Wno-unused-parameter
|
||||
# cimple does this better
|
||||
add_flag -Wno-unused-function
|
||||
# struct Foo foo = {0}; is a common idiom. Missing braces means we'd need to
|
||||
# write {{{0}}} in some cases, which is ugly and a maintenance burden.
|
||||
add_flag -Wno-missing-braces
|
||||
# __attribute__((nonnull)) causes this warning on defensive null checks.
|
||||
add_flag -Wno-nonnull-compare
|
35
.github/scripts/flags.sh
vendored
Normal file
35
.github/scripts/flags.sh
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
add_config_flag() { CONFIG_FLAGS+=("$@"); }
|
||||
add_c_flag() { C_FLAGS="$C_FLAGS $@"; }
|
||||
add_cxx_flag() { CXX_FLAGS="$CXX_FLAGS $@"; }
|
||||
add_ld_flag() { LD_FLAGS="$LD_FLAGS $@"; }
|
||||
add_flag() {
|
||||
add_c_flag "$@"
|
||||
add_cxx_flag "$@"
|
||||
}
|
||||
|
||||
# Our own flags which we can insert in the correct place. We don't use CFLAGS
|
||||
# and friends here (we unset them below), because they influence config tests
|
||||
# such as ./configure and cmake tests. Our warning flags break those tests, so
|
||||
# we can't add them globally here.
|
||||
CONFIG_FLAGS=()
|
||||
C_FLAGS=""
|
||||
CXX_FLAGS=""
|
||||
LD_FLAGS=""
|
||||
|
||||
unset CFLAGS
|
||||
unset CXXFLAGS
|
||||
unset CPPFLAGS
|
||||
unset LDFLAGS
|
||||
|
||||
# Optimisation flags.
|
||||
add_flag -O3 -march=native
|
||||
|
||||
# Warn on non-ISO C.
|
||||
add_c_flag -pedantic
|
||||
add_c_flag -std=c99
|
||||
add_cxx_flag -std=c++11
|
||||
|
||||
add_flag -g3
|
||||
add_flag -ftrapv
|
7
.github/scripts/sonar-build
vendored
Executable file
7
.github/scripts/sonar-build
vendored
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
. ".github/scripts/flags-gcc.sh"
|
||||
|
||||
cmake --build _build --parallel "$(nproc)" --target install -- -k 0
|
32
.github/scripts/sonar-prepare
vendored
Executable file
32
.github/scripts/sonar-prepare
vendored
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libconfig-dev \
|
||||
libopus-dev \
|
||||
libsodium-dev \
|
||||
libvpx-dev \
|
||||
ninja-build
|
||||
|
||||
. ".github/scripts/flags-gcc.sh"
|
||||
|
||||
add_ld_flag -Wl,-z,defs
|
||||
|
||||
# Make compilation error on a warning
|
||||
add_flag -Werror
|
||||
|
||||
cmake -B_build -H. -GNinja \
|
||||
-DCMAKE_C_FLAGS="$C_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LD_FLAGS" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$PWD/_install" \
|
||||
-DMIN_LOGGER_LEVEL=TRACE \
|
||||
-DMUST_BUILD_TOXAV=ON \
|
||||
-DNON_HERMETIC_TESTS=OFF \
|
||||
-DSTRICT_ABI=ON \
|
||||
-DTEST_TIMEOUT_SECONDS=120 \
|
||||
-DUSE_IPV6=OFF \
|
||||
-DAUTOTEST=ON \
|
||||
-DENABLE_SHARED=OFF
|
77
.github/scripts/tox-bootstrapd-docker
vendored
Executable file
77
.github/scripts/tox-bootstrapd-docker
vendored
Executable file
@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -exu
|
||||
|
||||
LOCAL="${1:-}"
|
||||
|
||||
readarray -t FILES <<<"$(git ls-files)"
|
||||
|
||||
tar c "${FILES[@]}" | docker build -f other/bootstrap_daemon/docker/Dockerfile -t toxchat/bootstrap-node -
|
||||
docker tag toxchat/bootstrap-node:latest toxchat/bootstrap-node:"$(other/print-version)"
|
||||
|
||||
sudo useradd \
|
||||
--home-dir /var/lib/tox-bootstrapd \
|
||||
--create-home \
|
||||
--system \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Account to run Tox's DHT bootstrap daemon" \
|
||||
--user-group tox-bootstrapd
|
||||
sudo chmod 700 /var/lib/tox-bootstrapd
|
||||
docker run -d --name tox-bootstrapd \
|
||||
--user "$(id -u tox-bootstrapd):$(id -g tox-bootstrapd)" \
|
||||
-v /var/lib/tox-bootstrapd/:/var/lib/tox-bootstrapd/ \
|
||||
--ulimit nofile=32768:32768 \
|
||||
-p 443:443 \
|
||||
-p 3389:3389 \
|
||||
-p 33445:33445 \
|
||||
-p 33445:33445/udp \
|
||||
toxchat/bootstrap-node
|
||||
|
||||
sudo ls -lbh /var/lib/tox-bootstrapd
|
||||
|
||||
if sudo [ ! -f /var/lib/tox-bootstrapd/keys ]; then
|
||||
echo "Error: File /var/lib/tox-bootstrapd/keys doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$LOCAL" != "local" ]; then
|
||||
COUNTER=0
|
||||
COUNTER_END=120
|
||||
while [ "$COUNTER" -lt "$COUNTER_END" ]; do
|
||||
if docker logs tox-bootstrapd | grep -q "Connected to another bootstrap node successfully"; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
COUNTER=$(($COUNTER + 1))
|
||||
done
|
||||
|
||||
docker logs tox-bootstrapd
|
||||
|
||||
if [ "$COUNTER" = "$COUNTER_END" ]; then
|
||||
echo "Error: Didn't connect to any nodes"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
docker logs tox-bootstrapd
|
||||
fi
|
||||
|
||||
# Wait a bit before testing if the container is still running
|
||||
sleep 30
|
||||
|
||||
docker ps -a
|
||||
|
||||
if [ "$(docker inspect -f {{.State.Running}} tox-bootstrapd)" != "true" ]; then
|
||||
echo "Error: Container is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat /proc/"$(pidof tox-bootstrapd)"/limits
|
||||
if ! grep -P '^Max open files(\s+)32768(\s+)32768(\s+)files' /proc/"$(pidof tox-bootstrapd)"/limits; then
|
||||
echo "Error: ulimit is not set to the expected value"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! other/fun/bootstrap_node_info.py ipv4 localhost 33445; then
|
||||
echo "Error: Unable to get bootstrap node info"
|
||||
exit 1
|
||||
fi
|
70
.github/settings.yml
vendored
Normal file
70
.github/settings.yml
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
_extends: .github
|
||||
|
||||
repository:
|
||||
name: c-toxcore
|
||||
description: The future of online communications.
|
||||
homepage: https://tox.chat/
|
||||
topics: toxcore, network, p2p, security, encryption, cryptography
|
||||
|
||||
branches:
|
||||
- name: "master"
|
||||
protection:
|
||||
required_status_checks:
|
||||
contexts:
|
||||
- "bazel-asan"
|
||||
- "bazel-dbg"
|
||||
- "bazel-opt"
|
||||
- "bazel-tsan"
|
||||
- "build-compcert"
|
||||
- "build-macos"
|
||||
- "build-nacl"
|
||||
- "build-tcc"
|
||||
- "build-win32"
|
||||
- "build-win64"
|
||||
- "CodeFactor"
|
||||
- "common / buildifier"
|
||||
- "coverage-linux"
|
||||
- "ci/circleci: asan"
|
||||
- "ci/circleci: clang-analyze"
|
||||
- "ci/circleci: clang-tidy"
|
||||
- "ci/circleci: cpplint"
|
||||
- "ci/circleci: infer"
|
||||
- "ci/circleci: msan"
|
||||
- "ci/circleci: static-analysis"
|
||||
- "ci/circleci: tsan"
|
||||
- "ci/circleci: ubsan"
|
||||
- "cimple"
|
||||
- "code-review/reviewable"
|
||||
- "continuous-integration/appveyor/pr"
|
||||
- "docker-bootstrap-node"
|
||||
- "docker-bootstrap-node-websocket"
|
||||
- "docker-toxcore-js"
|
||||
- "mypy"
|
||||
- "sonar-scan"
|
||||
|
||||
# Labels specific to c-toxcore.
|
||||
labels:
|
||||
- name: "bootstrap"
|
||||
color: "#01707f"
|
||||
description: "Bootstrap"
|
||||
|
||||
- name: "crypto"
|
||||
color: "#1d76db"
|
||||
description: "Crypto"
|
||||
|
||||
- name: "file transfers"
|
||||
color: "#e02abf"
|
||||
description: "File Transfers"
|
||||
|
||||
- name: "messenger"
|
||||
color: "#d93f0b"
|
||||
description: "Messenger"
|
||||
|
||||
- name: "network"
|
||||
color: "#d4c5f9"
|
||||
description: "Network"
|
||||
|
||||
- name: "toxav"
|
||||
color: "#0052cc"
|
||||
description: "Audio/video"
|
37
.github/workflows/cflite_batch.yml
vendored
Normal file
37
.github/workflows/cflite_batch.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# Derived from: https://google.github.io/clusterfuzzlite/running-clusterfuzzlite/github-actions/
|
||||
|
||||
name: ClusterFuzzLite batch fuzzing
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6,8 * * *' # Run twice a day at low activity times
|
||||
workflow_dispatch: # Manual trigger for testing
|
||||
permissions: read-all
|
||||
jobs:
|
||||
BatchFuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer:
|
||||
- address
|
||||
- undefined
|
||||
- memory
|
||||
steps:
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
|
||||
with:
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||
id: run
|
||||
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fuzz-seconds: 3600 # 60min
|
||||
mode: 'batch'
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
# Optional but recommended: For storing certain artifacts from fuzzing.
|
||||
# See later section on "Git repo for storage".
|
||||
storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/TokTok/toktok-fuzzer.git
|
||||
storage-repo-branch: master # Optional. Defaults to "main"
|
||||
storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages".
|
50
.github/workflows/cflite_cron.yml
vendored
Normal file
50
.github/workflows/cflite_cron.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# Derived from: https://google.github.io/clusterfuzzlite/running-clusterfuzzlite/github-actions/
|
||||
|
||||
name: ClusterFuzzLite cron tasks
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once a day, after fuzzing run
|
||||
workflow_dispatch: # Manual trigger for testing
|
||||
|
||||
permissions: read-all
|
||||
jobs:
|
||||
Pruning:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
|
||||
- name: Run Fuzzers
|
||||
id: run
|
||||
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fuzz-seconds: 600
|
||||
mode: 'prune'
|
||||
# Optional but recommended.
|
||||
# See later section on "Git repo for storage".
|
||||
storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/TokTok/toktok-fuzzer.git
|
||||
storage-repo-branch: master # Optional. Defaults to "main"
|
||||
storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages".
|
||||
Coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
|
||||
with:
|
||||
sanitizer: coverage
|
||||
- name: Run Fuzzers
|
||||
id: run
|
||||
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fuzz-seconds: 600
|
||||
mode: 'coverage'
|
||||
sanitizer: 'coverage'
|
||||
# Optional but recommended.
|
||||
# See later section on "Git repo for storage".
|
||||
storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/TokTok/toktok-fuzzer.git
|
||||
storage-repo-branch: master # Optional. Defaults to "main"
|
||||
storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages".
|
||||
|
212
.github/workflows/ci.yml
vendored
Normal file
212
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
# Cancel old PR builds when pushing new commits.
|
||||
concurrency:
|
||||
group: build-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
common:
|
||||
uses: TokTok/ci-tools/.github/workflows/common-ci.yml@master
|
||||
|
||||
mypy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install mypy
|
||||
run: pip install mypy
|
||||
- name: Run mypy
|
||||
run: |
|
||||
(find . -name "*.py" -and -not -name "conanfile.py"; grep -lR '^#!.*python') \
|
||||
| xargs -n1 -P8 mypy --strict
|
||||
|
||||
doxygen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Docker Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/docker/doxygen/Dockerfile
|
||||
|
||||
tokstyle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Docker Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/docker/tokstyle/Dockerfile
|
||||
|
||||
misra:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Docker Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/docker/misra/Dockerfile
|
||||
|
||||
cimplefmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Run cimplefmt
|
||||
run: other/docker/cimplefmt/run -u $(find tox* -name "*.[ch]")
|
||||
|
||||
build-nacl:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Docker Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/docker/autotools/Dockerfile
|
||||
|
||||
build-win32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Cross compilation
|
||||
run: .github/scripts/cmake-win32 script
|
||||
|
||||
build-win64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Cross compilation
|
||||
run: .github/scripts/cmake-win64 script
|
||||
|
||||
build-freebsd:
|
||||
runs-on: ubuntu-latest
|
||||
container: toxchat/freebsd
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build on FreeBSD
|
||||
run: .github/scripts/cmake-freebsd-stage2
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build and test
|
||||
run: .github/scripts/cmake-osx
|
||||
|
||||
coverage-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build, test, and upload coverage
|
||||
run: .github/scripts/coverage-linux
|
||||
|
||||
build-tcc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install dependencies
|
||||
run:
|
||||
sudo apt-get install -y --no-install-recommends
|
||||
tcc
|
||||
libconfig-dev
|
||||
libopus-dev
|
||||
libsodium-dev
|
||||
libvpx-dev
|
||||
- name: Build with TCC
|
||||
run:
|
||||
tcc
|
||||
-Dinline=static
|
||||
-o send_message_test
|
||||
-Wall -Werror
|
||||
-bench -g
|
||||
auto_tests/auto_test_support.c
|
||||
auto_tests/send_message_test.c
|
||||
testing/misc_tools.c
|
||||
toxav/*.c
|
||||
toxcore/*.c
|
||||
toxcore/*/*.c
|
||||
toxencryptsave/*.c
|
||||
third_party/cmp/*.c
|
||||
$(pkg-config --cflags --libs libsodium opus vpx)
|
||||
- name: Run the test
|
||||
run: "./send_message_test | grep 'tox clients connected'"
|
||||
- name: Build amalgamation file with TCC
|
||||
run:
|
||||
other/make_single_file
|
||||
auto_tests/auto_test_support.c
|
||||
auto_tests/send_message_test.c
|
||||
testing/misc_tools.c |
|
||||
tcc -
|
||||
-o send_message_test
|
||||
-Wall -Werror
|
||||
-bench -g
|
||||
$(pkg-config --cflags --libs libsodium opus vpx)
|
||||
- name: Run the test again
|
||||
run: "./send_message_test | grep 'tox clients connected'"
|
||||
|
||||
build-compcert:
|
||||
runs-on: ubuntu-latest
|
||||
container: toxchat/compcert
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build with CompCert
|
||||
run:
|
||||
ccomp
|
||||
-o send_message_test
|
||||
-Wall -Werror
|
||||
-Wno-c11-extensions
|
||||
-Wno-unknown-pragmas
|
||||
-Wno-unused-variable
|
||||
-fstruct-passing -fno-unprototyped -g
|
||||
auto_tests/auto_test_support.c
|
||||
auto_tests/send_message_test.c
|
||||
testing/misc_tools.c
|
||||
toxav/*.c
|
||||
toxcore/*.c
|
||||
toxcore/*/*.c
|
||||
toxencryptsave/*.c
|
||||
third_party/cmp/*.c
|
||||
-D__COMPCERT__ -DDISABLE_VLA -Dinline=
|
||||
-lpthread $(pkg-config --cflags --libs libsodium opus vpx)
|
||||
- name: Run the test
|
||||
run: "./send_message_test | grep 'tox clients connected'"
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- run: .github/scripts/cmake-android armeabi-v7a
|
||||
- run: .github/scripts/cmake-android arm64-v8a
|
||||
- run: .github/scripts/cmake-android x86
|
||||
- run: .github/scripts/cmake-android x86_64
|
51
.github/workflows/coverity-scan.yml
vendored
Normal file
51
.github/workflows/coverity-scan.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: coverity-scan
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once a day
|
||||
|
||||
jobs:
|
||||
latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install libraries
|
||||
run:
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install -y --no-install-recommends
|
||||
libopus-dev
|
||||
libsodium-dev
|
||||
libvpx-dev
|
||||
|
||||
- name: Download Coverity Build Tool
|
||||
run: |
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=TokTok/c-toxcore" -O cov-analysis-linux64.tar.gz
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
|
||||
- name: Run autoreconf
|
||||
run: autoreconf -fi
|
||||
|
||||
- name: Configure
|
||||
run: ./configure
|
||||
|
||||
- name: Build with cov-build
|
||||
run: cov-analysis-linux64/bin/cov-build --dir cov-int make
|
||||
|
||||
- name: Submit the result to Coverity Scan
|
||||
run:
|
||||
tar czvf c-toxcore.tgz cov-int &&
|
||||
curl
|
||||
--form project=TokTok/c-toxcore
|
||||
--form token=$TOKEN
|
||||
--form email=iphydf@gmail.com
|
||||
--form file=@c-toxcore.tgz
|
||||
--form version="$(git rev-list --count HEAD)"
|
||||
--form description="CI build of $(git rev-parse --abbrev-ref HEAD) branch"
|
||||
https://scan.coverity.com/builds
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
187
.github/workflows/docker.yml
vendored
Normal file
187
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
name: docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
docker-bootstrap-node:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Docker Build
|
||||
run: .github/scripts/tox-bootstrapd-docker local
|
||||
- name: Push latest image to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: docker push toxchat/bootstrap-node:latest
|
||||
- name: Push versioned image to DockerHub
|
||||
if: ${{ github.event_name == 'push' && contains(github.ref, 'refs/tags/') }}
|
||||
run: docker push toxchat/bootstrap-node:"$(other/print-version)"
|
||||
|
||||
docker-bootstrap-node-websocket:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [docker-bootstrap-node]
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: "{{defaultContext}}:other/bootstrap_daemon/websocket"
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/bootstrap-node:latest-websocket
|
||||
cache-from: type=registry,ref=toxchat/bootstrap-node:latest-websocket
|
||||
cache-to: type=inline
|
||||
|
||||
docker-clusterfuzz:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: "."
|
||||
file: .clusterfuzzlite/Dockerfile
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/c-toxcore:clusterfuzz
|
||||
cache-from: type=registry,ref=toxchat/c-toxcore:clusterfuzz
|
||||
cache-to: type=inline
|
||||
|
||||
docker-fuzzer:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: testing/Dockerfile
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/c-toxcore:fuzzer
|
||||
cache-from: type=registry,ref=toxchat/c-toxcore:fuzzer
|
||||
cache-to: type=inline
|
||||
|
||||
docker-toxcore-js:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/emscripten/Dockerfile
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/c-toxcore:wasm
|
||||
cache-from: type=registry,ref=toxchat/c-toxcore:wasm
|
||||
cache-to: type=inline
|
||||
|
||||
docker-esp32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver: docker
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build toxchat/c-toxcore:sources
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/docker/sources/Dockerfile
|
||||
tags: toxchat/c-toxcore:sources
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: other/docker/esp32/Dockerfile
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/c-toxcore:esp32
|
||||
cache-from: type=registry,ref=toxchat/c-toxcore:esp32
|
||||
cache-to: type=inline
|
||||
|
||||
docker-win32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: "{{defaultContext}}:other/docker/windows"
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/windows:win32
|
||||
cache-from: type=registry,ref=toxchat/windows:win32
|
||||
cache-to: type=inline
|
||||
build-args: |
|
||||
SUPPORT_ARCH_i686=true
|
||||
SUPPORT_ARCH_x86_64=false
|
||||
SUPPORT_TEST=true
|
||||
|
||||
docker-win64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: "{{defaultContext}}:other/docker/windows"
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: toxchat/windows:win64
|
||||
cache-from: type=registry,ref=toxchat/windows:win64
|
||||
cache-to: type=inline
|
||||
build-args: |
|
||||
SUPPORT_ARCH_i686=false
|
||||
SUPPORT_ARCH_x86_64=true
|
||||
SUPPORT_TEST=true
|
51
.github/workflows/sonar-scan.yml
vendored
Normal file
51
.github/workflows/sonar-scan.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: sonar-scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
sonar-scan:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SONAR_SCANNER_VERSION: 4.4.0.2170
|
||||
SONAR_SERVER_URL: "https://sonarcloud.io"
|
||||
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
submodules: recursive
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Download and set up sonar-scanner
|
||||
env:
|
||||
SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip
|
||||
run: |
|
||||
mkdir -p $HOME/.sonar
|
||||
curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }}
|
||||
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
|
||||
echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH
|
||||
- name: Download and set up build-wrapper
|
||||
env:
|
||||
BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip
|
||||
run: |
|
||||
curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }}
|
||||
unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/
|
||||
echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH
|
||||
- name: Install dependencies and prepare build
|
||||
run: |
|
||||
.github/scripts/sonar-prepare
|
||||
- name: Run build-wrapper
|
||||
run: |
|
||||
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} .github/scripts/sonar-build
|
||||
- name: Run sonar-scanner
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: 'sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}"'
|
94
.gitignore
vendored
Normal file
94
.gitignore
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
# OS files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.mypy_cache
|
||||
.Spotlight-V100
|
||||
.Trash*
|
||||
Icon?
|
||||
ethumbs.db
|
||||
Thumbs.db
|
||||
*.tmp
|
||||
|
||||
# Make
|
||||
/_build
|
||||
/_install
|
||||
/tox-0.0.0*
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
Makefile
|
||||
!/other/rpm/Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
tags
|
||||
Makefile.in
|
||||
CMakeLists.txt.user
|
||||
DartConfiguration.tcl
|
||||
CTestTestfile.cmake
|
||||
*.pc
|
||||
|
||||
# Testing
|
||||
/amalgamation.*
|
||||
testing/data
|
||||
*~
|
||||
|
||||
# Vim
|
||||
*.swp
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.lo
|
||||
*.a
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.la
|
||||
|
||||
# Libraries
|
||||
*.so
|
||||
|
||||
# Misc (?)
|
||||
m4/*
|
||||
configure
|
||||
configure_aux
|
||||
!m4/pkg.m4
|
||||
aclocal.m4
|
||||
config.h*
|
||||
config.log
|
||||
config.status
|
||||
stamp-h1
|
||||
autom4te.cache
|
||||
libtool
|
||||
.deps
|
||||
.libs
|
||||
.dirstamp
|
||||
build/
|
||||
*.nvim*
|
||||
*.vim*
|
||||
|
||||
#kdevelop
|
||||
.kdev/
|
||||
*.kdev*
|
||||
|
||||
# VScode
|
||||
.vscode/
|
||||
|
||||
# Netbeans
|
||||
nbproject
|
||||
|
||||
# astyle
|
||||
*.orig
|
||||
|
||||
# Android buildscript
|
||||
android-toolchain-*
|
||||
toxcore-android-*
|
||||
|
||||
# cscope files list
|
||||
cscope.files
|
||||
|
||||
# rpm
|
||||
tox.spec
|
||||
|
||||
.idea/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "third_party/cmp"]
|
||||
path = third_party/cmp
|
||||
url = https://github.com/camgunz/cmp
|
13
.hadolint.yaml
Normal file
13
.hadolint.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
ignored:
|
||||
# "cd" is sometimes useful when you want to run one command in one directory
|
||||
# and then another command in another directory, but they should be executed
|
||||
# in the same RUN instruction.
|
||||
- DL3003
|
||||
- DL3007
|
||||
- DL3008
|
||||
- DL3013
|
||||
- DL3018
|
||||
- DL3059
|
||||
# $(pkg-config ...) needs this
|
||||
- SC2046
|
22
.restyled.yaml
Normal file
22
.restyled.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
exclude:
|
||||
- "**/*.api.h"
|
||||
# shfmt doesn't support this file
|
||||
- "other/analysis/run-clang-tidy"
|
||||
|
||||
restylers:
|
||||
- astyle:
|
||||
arguments: ["--options=other/astyle/astylerc"]
|
||||
include:
|
||||
- "!**/*.cc"
|
||||
- autopep8
|
||||
- black
|
||||
- clang-format:
|
||||
image: restyled/restyler-clang-format:13.0.1
|
||||
include:
|
||||
- "**/*.cc"
|
||||
- prettier-yaml
|
||||
- reorder-python-imports
|
||||
- shellharden
|
||||
- shfmt
|
||||
- yapf
|
38
BUILD.bazel
Normal file
38
BUILD.bazel
Normal file
@ -0,0 +1,38 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
load("//tools/project:build_defs.bzl", "project")
|
||||
|
||||
package(features = ["layering_check"])
|
||||
|
||||
project()
|
||||
|
||||
genrule(
|
||||
name = "public_headers",
|
||||
srcs = [
|
||||
"//c-toxcore/toxav:toxav.h",
|
||||
"//c-toxcore/toxcore:tox.h",
|
||||
"//c-toxcore/toxencryptsave:toxencryptsave.h",
|
||||
],
|
||||
outs = [
|
||||
"tox/toxav.h",
|
||||
"tox/tox.h",
|
||||
"tox/toxencryptsave.h",
|
||||
],
|
||||
cmd = """
|
||||
cp $(location //c-toxcore/toxav:toxav.h) $(GENDIR)/c-toxcore/tox/toxav.h
|
||||
cp $(location //c-toxcore/toxcore:tox.h) $(GENDIR)/c-toxcore/tox/tox.h
|
||||
cp $(location //c-toxcore/toxencryptsave:toxencryptsave.h) $(GENDIR)/c-toxcore/tox/toxencryptsave.h
|
||||
""",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "c-toxcore",
|
||||
hdrs = [":public_headers"],
|
||||
includes = ["."],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//c-toxcore/toxav",
|
||||
"//c-toxcore/toxcore",
|
||||
"//c-toxcore/toxencryptsave",
|
||||
],
|
||||
)
|
1669
CHANGELOG.md
Normal file
1669
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
501
CMakeLists.txt
Normal file
501
CMakeLists.txt
Normal file
@ -0,0 +1,501 @@
|
||||
################################################################################
|
||||
#
|
||||
# The main toxcore CMake build file.
|
||||
#
|
||||
# This file when processed with cmake produces:
|
||||
# - A number of small libraries (.a/.so/...) containing independent components
|
||||
# of toxcore. E.g. the DHT has its own library, and the system/network
|
||||
# abstractions are in their own library as well. These libraries are not
|
||||
# installed on `make install`. The toxav, and toxencryptsave libraries are
|
||||
# also not installed.
|
||||
# - A number of small programs, statically linked if possible.
|
||||
# - One big library containing all of the toxcore, toxav, and toxencryptsave
|
||||
# code.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
cmake_policy(VERSION 2.8.12)
|
||||
project(toxcore)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${toxcore_SOURCE_DIR}/cmake)
|
||||
|
||||
option(FLAT_OUTPUT_STRUCTURE "Whether to produce output artifacts in ${CMAKE_BINARY_DIR}/{bin,lib}" OFF)
|
||||
if(FLAT_OUTPUT_STRUCTURE)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
endif()
|
||||
|
||||
set_source_files_properties(
|
||||
toxcore/mono_time.c
|
||||
toxcore/network.c
|
||||
toxcore/tox.c
|
||||
toxcore/util.c
|
||||
PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Version management
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# This version is for the entire project. All libraries (core, av, ...) move in
|
||||
# versions in a synchronised way.
|
||||
set(PROJECT_VERSION_MAJOR "0")
|
||||
set(PROJECT_VERSION_MINOR "2")
|
||||
set(PROJECT_VERSION_PATCH "18")
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
||||
# set .so library version / following libtool scheme
|
||||
# https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
|
||||
file(STRINGS ${toxcore_SOURCE_DIR}/so.version SOVERSION_CURRENT REGEX "^CURRENT=[0-9]+$")
|
||||
string(SUBSTRING "${SOVERSION_CURRENT}" 8 -1 SOVERSION_CURRENT)
|
||||
file(STRINGS ${toxcore_SOURCE_DIR}/so.version SOVERSION_REVISION REGEX "^REVISION=[0-9]+$")
|
||||
string(SUBSTRING "${SOVERSION_REVISION}" 9 -1 SOVERSION_REVISION)
|
||||
file(STRINGS ${toxcore_SOURCE_DIR}/so.version SOVERSION_AGE REGEX "^AGE=[0-9]+$")
|
||||
string(SUBSTRING "${SOVERSION_AGE}" 4 -1 SOVERSION_AGE)
|
||||
# account for some libtool magic, see other/version-sync script for details
|
||||
math(EXPR SOVERSION_MAJOR ${SOVERSION_CURRENT}-${SOVERSION_AGE})
|
||||
set(SOVERSION "${SOVERSION_MAJOR}.${SOVERSION_AGE}.${SOVERSION_REVISION}")
|
||||
message("SOVERSION: ${SOVERSION}")
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Dependencies and configuration
|
||||
#
|
||||
################################################################################
|
||||
|
||||
include(CTest)
|
||||
include(ModulePackage)
|
||||
include(StrictAbi)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(APPLE)
|
||||
include(MacRpath)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
set(CMAKE_MACOSX_RPATH ON)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.1.0")
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
endif()
|
||||
else()
|
||||
# Set standard version for compiler.
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
message(STATUS "Supported C compiler features = ${CMAKE_C_COMPILE_FEATURES}")
|
||||
message(STATUS "Supported C++ compiler features = ${CMAKE_CXX_COMPILE_FEATURES}")
|
||||
endif()
|
||||
|
||||
set(MIN_LOGGER_LEVEL "" CACHE STRING "Logging level to use (TRACE, DEBUG, INFO, WARNING, ERROR)")
|
||||
if(MIN_LOGGER_LEVEL)
|
||||
if(("${MIN_LOGGER_LEVEL}" STREQUAL "TRACE") OR
|
||||
("${MIN_LOGGER_LEVEL}" STREQUAL "DEBUG") OR
|
||||
("${MIN_LOGGER_LEVEL}" STREQUAL "INFO") OR
|
||||
("${MIN_LOGGER_LEVEL}" STREQUAL "WARNING") OR
|
||||
("${MIN_LOGGER_LEVEL}" STREQUAL "ERROR"))
|
||||
add_definitions(-DMIN_LOGGER_LEVEL=LOGGER_LEVEL_${MIN_LOGGER_LEVEL})
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown value provided for MIN_LOGGER_LEVEL: \"${MIN_LOGGER_LEVEL}\", must be one of TRACE, DEBUG, INFO, WARNING or ERROR")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(USE_IPV6 "Use IPv6 in tests" ON)
|
||||
if(NOT USE_IPV6)
|
||||
add_definitions(-DUSE_IPV6=0)
|
||||
endif()
|
||||
|
||||
option(USE_TEST_NETWORK "Use a separate test network with different packet IDs" OFF)
|
||||
if(USE_TEST_NETWORK)
|
||||
add_definitions(-DUSE_TEST_NETWORK=1)
|
||||
endif()
|
||||
|
||||
option(BUILD_MISC_TESTS "Build additional tests and utilities" OFF)
|
||||
option(BUILD_FUN_UTILS "Build additional just for fun utilities" OFF)
|
||||
|
||||
option(AUTOTEST "Enable autotests (mainly for CI)" OFF)
|
||||
if (AUTOTEST)
|
||||
option(NON_HERMETIC_TESTS "Whether to build and run tests that depend on an internet connection" OFF)
|
||||
option(PROXY_TEST "Enable proxy test (needs HTTP/SOCKS5 proxy on port 8080/8081)" OFF)
|
||||
endif()
|
||||
|
||||
option(BUILD_TOXAV "Whether to build the tox AV library" ON)
|
||||
option(MUST_BUILD_TOXAV "Fail the build if toxav cannot be built" OFF)
|
||||
|
||||
option(DHT_BOOTSTRAP "Enable building of DHT_bootstrap" ON)
|
||||
option(BOOTSTRAP_DAEMON "Enable building of tox-bootstrapd" ON)
|
||||
if(BOOTSTRAP_DAEMON AND WIN32)
|
||||
message(WARNING "Building tox-bootstrapd for Windows is not supported, disabling")
|
||||
set(BOOTSTRAP_DAEMON OFF)
|
||||
endif()
|
||||
|
||||
# Enabling this breaks all other tests and no network connections will be possible
|
||||
option(BUILD_FUZZ_TESTS "Build fuzzing harnesses" OFF)
|
||||
if(BUILD_FUZZ_TESTS)
|
||||
message(STATUS "Building in fuzz testing mode, no network connection will be possible")
|
||||
# Disable everything we can
|
||||
set(AUTOTEST OFF)
|
||||
set(BUILD_MISC_TESTS OFF)
|
||||
set(BUILD_FUN_UTILS OFF)
|
||||
set(ENABLE_SHARED OFF)
|
||||
set(MUST_BUILD_TOXAV OFF)
|
||||
set(BUILD_TOXAV OFF)
|
||||
set(BOOTSTRAP_DAEMON OFF)
|
||||
set(DHT_BOOTSTRAP OFF)
|
||||
endif()
|
||||
|
||||
|
||||
if(MSVC)
|
||||
option(MSVC_STATIC_SODIUM "Whether to link libsodium statically for MSVC" OFF)
|
||||
if(MSVC_STATIC_SODIUM)
|
||||
add_definitions(-DSODIUM_STATIC=1 -DSODIUM_EXPORT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(Dependencies)
|
||||
|
||||
if(MUST_BUILD_TOXAV)
|
||||
set(NO_TOXAV_ERROR_TYPE SEND_ERROR)
|
||||
else()
|
||||
set(NO_TOXAV_ERROR_TYPE WARNING)
|
||||
endif()
|
||||
|
||||
if(BUILD_TOXAV)
|
||||
if(NOT OPUS_FOUND)
|
||||
message(${NO_TOXAV_ERROR_TYPE} "Option BUILD_TOXAV is enabled but required library OPUS was not found.")
|
||||
set(BUILD_TOXAV OFF)
|
||||
endif()
|
||||
if(NOT VPX_FOUND)
|
||||
message(${NO_TOXAV_ERROR_TYPE} "Option BUILD_TOXAV is enabled but required library VPX was not found.")
|
||||
set(BUILD_TOXAV OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Disable float/double packing in CMP (C MessagePack library).
|
||||
# We don't transfer floats over the network, so we disable this functionality.
|
||||
add_definitions(-DCMP_NO_FLOAT=1)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Tox Core Library
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# toxcore_PKGCONFIG_LIBS is what's added to the Libs: line in toxcore.pc. It
|
||||
# needs to contain all the libraries a program using toxcore should link against
|
||||
# if it's statically linked. If it's dynamically linked, there is no need to
|
||||
# explicitly link against all the dependencies, but it doesn't harm much(*)
|
||||
# either.
|
||||
#
|
||||
# (*) It allows client code to use symbols from our dependencies without
|
||||
# explicitly linking against them.
|
||||
set(toxcore_PKGCONFIG_LIBS)
|
||||
# Requires: in pkg-config file.
|
||||
set(toxcore_PKGCONFIG_REQUIRES)
|
||||
|
||||
set(toxcore_SOURCES
|
||||
third_party/cmp/cmp.c
|
||||
third_party/cmp/cmp.h
|
||||
toxcore/announce.c
|
||||
toxcore/announce.h
|
||||
toxcore/bin_pack.c
|
||||
toxcore/bin_pack.h
|
||||
toxcore/bin_unpack.c
|
||||
toxcore/bin_unpack.h
|
||||
toxcore/ccompat.c
|
||||
toxcore/ccompat.h
|
||||
toxcore/crypto_core.c
|
||||
toxcore/crypto_core.h
|
||||
toxcore/DHT.c
|
||||
toxcore/DHT.h
|
||||
toxcore/events/conference_connected.c
|
||||
toxcore/events/conference_invite.c
|
||||
toxcore/events/conference_message.c
|
||||
toxcore/events/conference_peer_list_changed.c
|
||||
toxcore/events/conference_peer_name.c
|
||||
toxcore/events/conference_title.c
|
||||
toxcore/events/events_alloc.c
|
||||
toxcore/events/events_alloc.h
|
||||
toxcore/events/file_chunk_request.c
|
||||
toxcore/events/file_recv.c
|
||||
toxcore/events/file_recv_chunk.c
|
||||
toxcore/events/file_recv_control.c
|
||||
toxcore/events/friend_connection_status.c
|
||||
toxcore/events/friend_lossless_packet.c
|
||||
toxcore/events/friend_lossy_packet.c
|
||||
toxcore/events/friend_message.c
|
||||
toxcore/events/friend_name.c
|
||||
toxcore/events/friend_read_receipt.c
|
||||
toxcore/events/friend_request.c
|
||||
toxcore/events/friend_status.c
|
||||
toxcore/events/friend_status_message.c
|
||||
toxcore/events/friend_typing.c
|
||||
toxcore/events/self_connection_status.c
|
||||
toxcore/events/group_custom_packet.c
|
||||
toxcore/events/group_custom_private_packet.c
|
||||
toxcore/events/group_invite.c
|
||||
toxcore/events/group_join_fail.c
|
||||
toxcore/events/group_message.c
|
||||
toxcore/events/group_moderation.c
|
||||
toxcore/events/group_password.c
|
||||
toxcore/events/group_peer_exit.c
|
||||
toxcore/events/group_peer_join.c
|
||||
toxcore/events/group_peer_limit.c
|
||||
toxcore/events/group_peer_name.c
|
||||
toxcore/events/group_peer_status.c
|
||||
toxcore/events/group_privacy_state.c
|
||||
toxcore/events/group_private_message.c
|
||||
toxcore/events/group_self_join.c
|
||||
toxcore/events/group_topic.c
|
||||
toxcore/events/group_topic_lock.c
|
||||
toxcore/events/group_voice_state.c
|
||||
toxcore/forwarding.c
|
||||
toxcore/forwarding.h
|
||||
toxcore/friend_connection.c
|
||||
toxcore/friend_connection.h
|
||||
toxcore/friend_requests.c
|
||||
toxcore/friend_requests.h
|
||||
toxcore/group.c
|
||||
toxcore/group_chats.c
|
||||
toxcore/group_chats.h
|
||||
toxcore/group_common.h
|
||||
toxcore/group_connection.c
|
||||
toxcore/group_connection.h
|
||||
toxcore/group.h
|
||||
toxcore/group_announce.c
|
||||
toxcore/group_announce.h
|
||||
toxcore/group_moderation.c
|
||||
toxcore/group_moderation.h
|
||||
toxcore/group_onion_announce.c
|
||||
toxcore/group_onion_announce.h
|
||||
toxcore/group_pack.c
|
||||
toxcore/group_pack.h
|
||||
toxcore/LAN_discovery.c
|
||||
toxcore/LAN_discovery.h
|
||||
toxcore/list.c
|
||||
toxcore/list.h
|
||||
toxcore/logger.c
|
||||
toxcore/logger.h
|
||||
toxcore/Messenger.c
|
||||
toxcore/Messenger.h
|
||||
toxcore/mono_time.c
|
||||
toxcore/mono_time.h
|
||||
toxcore/net_crypto.c
|
||||
toxcore/net_crypto.h
|
||||
toxcore/network.c
|
||||
toxcore/network.h
|
||||
toxcore/onion_announce.c
|
||||
toxcore/onion_announce.h
|
||||
toxcore/onion.c
|
||||
toxcore/onion_client.c
|
||||
toxcore/onion_client.h
|
||||
toxcore/onion.h
|
||||
toxcore/ping_array.c
|
||||
toxcore/ping_array.h
|
||||
toxcore/ping.c
|
||||
toxcore/ping.h
|
||||
toxcore/shared_key_cache.c
|
||||
toxcore/shared_key_cache.h
|
||||
toxcore/state.c
|
||||
toxcore/state.h
|
||||
toxcore/TCP_client.c
|
||||
toxcore/TCP_client.h
|
||||
toxcore/TCP_common.c
|
||||
toxcore/TCP_common.h
|
||||
toxcore/TCP_connection.c
|
||||
toxcore/TCP_connection.h
|
||||
toxcore/TCP_server.c
|
||||
toxcore/TCP_server.h
|
||||
toxcore/timed_auth.c
|
||||
toxcore/timed_auth.h
|
||||
toxcore/tox_api.c
|
||||
toxcore/tox.c
|
||||
toxcore/tox_dispatch.c
|
||||
toxcore/tox_dispatch.h
|
||||
toxcore/tox_events.c
|
||||
toxcore/tox_events.h
|
||||
toxcore/tox.h
|
||||
toxcore/tox_private.c
|
||||
toxcore/tox_private.h
|
||||
toxcore/tox_unpack.c
|
||||
toxcore/tox_unpack.h
|
||||
toxcore/util.c
|
||||
toxcore/util.h)
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ${LIBSODIUM_LIBRARIES})
|
||||
set(toxcore_PKGCONFIG_REQUIRES ${toxcore_PKGCONFIG_REQUIRES} libsodium)
|
||||
set(toxcore_API_HEADERS
|
||||
${toxcore_SOURCE_DIR}/toxcore/tox.h^tox
|
||||
${toxcore_SOURCE_DIR}/toxcore/tox_events.h^tox
|
||||
${toxcore_SOURCE_DIR}/toxcore/tox_dispatch.h^tox)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Audio/Video Library
|
||||
#
|
||||
################################################################################
|
||||
|
||||
if(BUILD_TOXAV)
|
||||
set(toxcore_SOURCES ${toxcore_SOURCES}
|
||||
toxav/audio.c
|
||||
toxav/audio.h
|
||||
toxav/bwcontroller.c
|
||||
toxav/bwcontroller.h
|
||||
toxav/groupav.c
|
||||
toxav/groupav.h
|
||||
toxav/msi.c
|
||||
toxav/msi.h
|
||||
toxav/ring_buffer.c
|
||||
toxav/ring_buffer.h
|
||||
toxav/rtp.c
|
||||
toxav/rtp.h
|
||||
toxav/toxav.c
|
||||
toxav/toxav.h
|
||||
toxav/toxav_old.c
|
||||
toxav/video.c
|
||||
toxav/video.h)
|
||||
set(toxcore_API_HEADERS ${toxcore_API_HEADERS}
|
||||
${toxcore_SOURCE_DIR}/toxav/toxav.h^toxav)
|
||||
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ${OPUS_LIBRARIES} ${VPX_LIBRARIES})
|
||||
set(toxcore_PKGCONFIG_REQUIRES ${toxcore_PKGCONFIG_REQUIRES} opus vpx)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Block encryption libraries
|
||||
#
|
||||
################################################################################
|
||||
|
||||
set(toxcore_SOURCES ${toxcore_SOURCES}
|
||||
toxencryptsave/toxencryptsave.c
|
||||
toxencryptsave/toxencryptsave.h)
|
||||
set(toxcore_API_HEADERS ${toxcore_API_HEADERS}
|
||||
${toxcore_SOURCE_DIR}/toxencryptsave/toxencryptsave.h^tox)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: System dependencies
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# These need to come after other dependencies, since e.g. libvpx may depend on
|
||||
# pthread, but doesn't list it in VPX_LIBRARIES. We're adding it here, after
|
||||
# any potential libvpx linking.
|
||||
message("CMAKE_THREAD_LIBS_INIT: ${CMAKE_THREAD_LIBS_INIT}")
|
||||
if(CMAKE_THREAD_LIBS_INIT)
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
|
||||
if(NSL_LIBRARIES)
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ${NSL_LIBRARIES})
|
||||
set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} -lnsl)
|
||||
endif()
|
||||
|
||||
if(RT_LIBRARIES)
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ${RT_LIBRARIES})
|
||||
set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} -lrt)
|
||||
endif()
|
||||
|
||||
if(SOCKET_LIBRARIES)
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ${SOCKET_LIBRARIES})
|
||||
set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} -lsocket)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(toxcore_LINK_MODULES ${toxcore_LINK_MODULES} ws2_32 iphlpapi)
|
||||
set(toxcore_PKGCONFIG_LIBS ${toxcore_PKGCONFIG_LIBS} -lws2_32 -liphlpapi)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: All layers together in one library for ease of use
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# Create combined library from all the sources.
|
||||
add_module(toxcore ${toxcore_SOURCES})
|
||||
|
||||
# Link it to all dependencies.
|
||||
target_link_modules(toxcore ${toxcore_LINK_MODULES})
|
||||
|
||||
# Make version script (on systems that support it) to limit symbol visibility.
|
||||
make_version_script(toxcore ${toxcore_API_HEADERS})
|
||||
|
||||
# Generate pkg-config file, install library to "${CMAKE_INSTALL_LIBDIR}" and install headers to
|
||||
# "${CMAKE_INSTALL_INCLUDEDIR}/tox".
|
||||
install_module(toxcore DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tox)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Unit tests: no networking, just pure function calls.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
include(CompileGTest)
|
||||
|
||||
# The actual unit tests follow.
|
||||
#
|
||||
unit_test(toxav ring_buffer)
|
||||
unit_test(toxav rtp)
|
||||
unit_test(toxcore DHT)
|
||||
unit_test(toxcore bin_pack)
|
||||
unit_test(toxcore crypto_core)
|
||||
unit_test(toxcore group_announce)
|
||||
unit_test(toxcore group_moderation)
|
||||
unit_test(toxcore mono_time)
|
||||
unit_test(toxcore ping_array)
|
||||
unit_test(toxcore tox)
|
||||
unit_test(toxcore util)
|
||||
|
||||
add_subdirectory(testing)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Automated regression tests: create a tox network and run integration tests
|
||||
#
|
||||
################################################################################
|
||||
|
||||
if(AUTOTEST)
|
||||
add_subdirectory(auto_tests)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# :: Bootstrap daemon
|
||||
#
|
||||
################################################################################
|
||||
|
||||
if(DHT_BOOTSTRAP)
|
||||
add_executable(DHT_bootstrap
|
||||
other/DHT_bootstrap.c
|
||||
other/bootstrap_node_packets.c)
|
||||
target_link_modules(DHT_bootstrap toxcore misc_tools)
|
||||
install(TARGETS DHT_bootstrap RUNTIME DESTINATION bin)
|
||||
endif()
|
||||
|
||||
if(BOOTSTRAP_DAEMON)
|
||||
if(LIBCONFIG_FOUND)
|
||||
add_subdirectory(other/bootstrap_daemon)
|
||||
else()
|
||||
message(WARNING "Option BOOTSTRAP_DAEMON is enabled but required library LIBCONFIG was not found.")
|
||||
set(BOOTSTRAP_DAEMON OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_FUN_UTILS)
|
||||
add_subdirectory(other/fun)
|
||||
endif()
|
||||
|
||||
if (BUILD_FUZZ_TESTS)
|
||||
add_subdirectory(testing/fuzzing)
|
||||
endif()
|
7
DONATORS
Normal file
7
DONATORS
Normal file
@ -0,0 +1,7 @@
|
||||
Minnesota > Florida
|
||||
vdo <vdo@greyfaze.net>
|
||||
Spitfire is best technicolor horse.
|
||||
if bad people don't hate you, you're doing something wrong
|
||||
Pinkie Pie is best pony.
|
||||
JRS was here
|
||||
qTox is so bloated it doesn't even fit in a 64-bit address space
|
297
INSTALL.md
Normal file
297
INSTALL.md
Normal file
@ -0,0 +1,297 @@
|
||||
# Installation instructions
|
||||
|
||||
These instructions will guide you through the process of building and installing the toxcore library and its components, as well as getting already pre-built binaries.
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Components](#components)
|
||||
- [Main](#main)
|
||||
- [Secondary](#secondary)
|
||||
- [Building](#building)
|
||||
- [Requirements](#requirements)
|
||||
- [Library dependencies](#library-dependencies)
|
||||
- [Compiler requirements](#compiler-requirements)
|
||||
- [Build system requirements](#build-system-requirements)
|
||||
- [CMake options](#cmake-options)
|
||||
- [Build process](#build-process)
|
||||
- [Unix-like](#unix-like)
|
||||
- [Windows](#windows)
|
||||
- [Building on Windows host](#building-on-windows-host)
|
||||
- [Microsoft Visual Studio's Developer Command Prompt](#microsoft-visual-studios-developer-command-prompt)
|
||||
- [MSYS/Cygwin](#msyscygwin)
|
||||
- [Cross-compiling from Linux](#cross-compiling-from-linux)
|
||||
- [Pre-built binaries](#pre-built-binaries)
|
||||
- [Linux](#linux)
|
||||
|
||||
## Overview
|
||||
|
||||
### Components
|
||||
|
||||
#### Main
|
||||
|
||||
This repository, although called `toxcore`, in fact contains several libraries besides `toxcore` which complement it, as well as several executables. However, note that although these are separate libraries, at the moment, when building the libraries, they are all merged into a single `toxcore` library. Here is the full list of the main components that can be built using the CMake, their dependencies and descriptions.
|
||||
|
||||
| Name | Type | Dependencies | Platform | Description |
|
||||
|------------------|------------|-----------------------------------------------|----------------|----------------------------------------------------------------------------|
|
||||
| `toxcore` | Library | libnacl or libsodium, libm, libpthread, librt | Cross-platform | The main Tox library that provides the messenger functionality. |
|
||||
| `toxav` | Library | libtoxcore, libopus, libvpx | Cross-platform | Provides audio/video functionality. |
|
||||
| `toxencryptsave` | Library | libtoxcore, libnacl or libsodium | Cross-platform | Provides encryption of Tox profiles (savedata), as well as arbitrary data. |
|
||||
| `DHT_bootstrap` | Executable | libtoxcore | Cross-platform | A simple DHT bootstrap node. |
|
||||
| `tox-bootstrapd` | Executable | libtoxcore, libconfig | Unix-like | Highly configurable DHT bootstrap node daemon (systemd, SysVinit, Docker). |
|
||||
| `cmp` | Library | | Cross-platform | C implementation of the MessagePack serialization format. [https://github.com/camgunz/cmp](https://github.com/camgunz/cmp) |
|
||||
|
||||
#### Secondary
|
||||
|
||||
There are some programs that are not built by default which you might find interesting. You need to pass `-DBUILD_FUN_UTILS=ON` to cmake to build them.
|
||||
|
||||
##### Vanity key generators
|
||||
|
||||
Can be used to generate vanity Tox Ids or DHT bootstrap node public keys.
|
||||
|
||||
| Name | Type | Dependencies | Platform | Description |
|
||||
|---------------------------|------------|-----------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `cracker` | Executable | libsodium, OpenMP | Cross-platform | Tries to find a curve25519 key pair, hex representation of the public key of which starts with a specified byte sequence. Multi-threaded. |
|
||||
| `cracker_simple` | Executable | libsodium | Cross-platform | Tries to find a curve25519 key pair, hex representation of the public key of which starts with a specified byte sequence. Single-threaded. |
|
||||
| `strkey` | Executable | libsodium | Cross-platform | Tries to find a curve25519 key pair, hex representation of the public key of which contains a specified byte sequence at a specified or any position at all. Single-threaded. |
|
||||
|
||||
##### Key file generators
|
||||
|
||||
Useful for generating Tox profiles from the output of the vanity key generators, as well as generating random Tox profiles.
|
||||
|
||||
| Name | Type | Dependencies | Platform | Description |
|
||||
|---------------------------|------------|-----------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `make-funny-savefile` | Script | python | Cross-platform | Generates a Tox profile file (savedata file) with the provided key pair. |
|
||||
| `create_bootstrap_keys` | Executable | libsodium | Cross-platform | Generates a keys file for tox-bootstrapd with either the provided or a random key pair. |
|
||||
| `create_minimal_savedata` | Executable | libsodium | Cross-platform | Generates a minimal Tox profile file (savedata file) with either the provided or a random key pair, printing the generated Tox Id and secret & public key information. |
|
||||
| `create_savedata` | Executable | libsodium, libtoxcore | Cross-platform | Generates a Tox profile file (savedata file) with either the provided or a random key pair using libtoxcore, printing the generated Tox Id and secret & public key information. |
|
||||
| `save-generator` | Executable | libtoxcore | Cross-platform | Generates a Tox profile file (savedata file) with a random key pair using libtoxcore, setting the specified user name, going online and adding specified Tox Ids as friends. |
|
||||
|
||||
##### Other
|
||||
|
||||
| Name | Type | Dependencies | Platform | Description |
|
||||
|---------------------------|------------|-----------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `bootstrap_node_info` | Script | python3 | Cross-platform | Prints version and Message Of The Day (MOTD) information of the specified DHT bootstrap node, given the node doesn't have those disabled. |
|
||||
| `sign` | Executable | libsodium | Cross-platform | Signs a file with a ed25519 key. |
|
||||
|
||||
## Building
|
||||
|
||||
### Requirements
|
||||
|
||||
#### Library dependencies
|
||||
|
||||
Library dependencies are listed in the [components](#components) table. The dependencies need to be satisfied for the components to be built. Note that if you don't have a dependency for some component, e.g. you don't have `libopus` installed required for building `toxav` component, building of that component is silently disabled.
|
||||
|
||||
|
||||
Be advised that due to the addition of `cmp` as a submodule, you now also need to initialize the git submodules required by toxcore. This can be done by cloning the repo with the addition of `--recurse-submodules` or by running `git submodule update --init` in the root directory of the repo.
|
||||
|
||||
#### Compiler requirements
|
||||
|
||||
The supported compilers are GCC, Clang and MinGW.
|
||||
|
||||
In theory, any compiler that fully supports C99 and accepts GCC flags should work.
|
||||
|
||||
There is a partial and experimental support of Microsoft Visual C++ compiler. We welcome any patches that help improve it.
|
||||
|
||||
You should have a C99 compatible compiler in order to build the main components. The secondary components might require the compiler to support GNU extensions.
|
||||
|
||||
#### Build system requirements
|
||||
|
||||
To build the main components you need to have CMake of at least 2.8.6 version installed. You also need to have pkg-config installed, the build system uses it to find dependency libraries.
|
||||
|
||||
There is some experimental accommodation for building natively on Windows, i.e. without having to use MSYS/Cygwin and pkg-config, but it uses exact hardcoded paths for finding libraries and supports building only of some of toxcore components, so your mileage might vary.
|
||||
|
||||
### CMake options
|
||||
|
||||
There are some options that are available to configure the build.
|
||||
|
||||
| Name | Description | Expected Value | Default Value |
|
||||
|------------------------|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------|
|
||||
| `AUTOTEST` | Enable autotests (mainly for CI). | ON or OFF | OFF |
|
||||
| `BOOTSTRAP_DAEMON` | Enable building of tox-bootstrapd, the DHT bootstrap node daemon. For Unix-like systems only. | ON or OFF | ON |
|
||||
| `BUILD_FUZZ_TESTS` | Build fuzzing harnesses. | ON or OFF | OFF |
|
||||
| `BUILD_MISC_TESTS` | Build additional tests. | ON or OFF | OFF |
|
||||
| `BUILD_FUN_UTILS` | Build additional funny utilities. | ON or OFF | OFF |
|
||||
| `BUILD_TOXAV` | Whether to build the toxav library. | ON or OFF | ON |
|
||||
| `CMAKE_INSTALL_PREFIX` | Path to where everything should be installed. | Directory path. | Platform-dependent. Refer to CMake documentation. |
|
||||
| `CMAKE_BUILD_TYPE` | Specifies the build type on single-configuration generators (e.g. make or ninja). | Debug, Release, RelWithDebInfo, MinSizeRel | Empty string. |
|
||||
| `DHT_BOOTSTRAP` | Enable building of `DHT_bootstrap` | ON or OFF | ON |
|
||||
| `ENABLE_SHARED` | Build shared (dynamic) libraries for all modules. | ON or OFF | ON |
|
||||
| `ENABLE_STATIC` | Build static libraries for all modules. | ON or OFF | ON |
|
||||
| `EXECUTION_TRACE` | Print a function trace during execution (for debugging). | ON or OFF | OFF |
|
||||
| `FULLY_STATIC` | Build fully static executables. | ON or OFF | OFF |
|
||||
| `MIN_LOGGER_LEVEL` | Logging level to use. | TRACE, DEBUG, INFO, WARNING, ERROR or nothing (empty string) for default. | Empty string. |
|
||||
| `MSVC_STATIC_SODIUM` | Whether to link libsodium statically for MSVC. | ON or OFF | OFF |
|
||||
| `MUST_BUILD_TOXAV` | Fail the build if toxav cannot be built. | ON or OFF | OFF |
|
||||
| `NON_HERMETIC_TESTS` | Whether to build and run tests that depend on an internet connection. | ON or OFF | OFF |
|
||||
| `STRICT_ABI` | Enforce strict ABI export in dynamic libraries. | ON or OFF | OFF |
|
||||
| `TEST_TIMEOUT_SECONDS` | Limit runtime of each test to the number of seconds specified. | Positive number or nothing (empty string). | Empty string. |
|
||||
| `USE_IPV6` | Use IPv6 in tests. | ON or OFF | ON |
|
||||
|
||||
You can get this list of option using the following commands
|
||||
|
||||
```sh
|
||||
grep "option(" CMakeLists.txt cmake/*
|
||||
grep "set(.* CACHE" CMakeLists.txt cmake/*
|
||||
```
|
||||
|
||||
Note that some options might be considered only if other options are enabled.
|
||||
|
||||
Example of calling cmake with options
|
||||
|
||||
```sh
|
||||
cmake \
|
||||
-D ENABLE_STATIC=OFF \
|
||||
-D ENABLE_SHARED=ON \
|
||||
-D CMAKE_INSTALL_PREFIX="${PWD}/prefix" \
|
||||
-D CMAKE_BUILD_TYPE=Release \
|
||||
-D TEST_TIMEOUT_SECONDS=120 \
|
||||
..
|
||||
```
|
||||
|
||||
### Building tests
|
||||
In addition to the integration tests ("autotests") and miscellaneous tests
|
||||
enabled by cmake variables described above, there are unit tests which will be
|
||||
built if the source distribution of gtest (the Google Unit Test framework) is
|
||||
found by cmake in `c-toxcore/third_party`. This can be achieved by running
|
||||
'git clone https://github.com/google/googletest` from that directory.
|
||||
|
||||
### Build process
|
||||
|
||||
#### Unix-like
|
||||
|
||||
Assuming all the [requirements](#requirements) are met, just run
|
||||
|
||||
```sh
|
||||
mkdir _build
|
||||
cd _build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
##### Building on Windows host
|
||||
|
||||
###### Microsoft Visual Studio's Developer Command Prompt
|
||||
|
||||
In addition to meeting the [requirements](#requirements), you need a version of Visual Studio (the [community edition](https://www.visualstudio.com/vs/visual-studio-express/) is enough) and a CMake version that's compatible with the Visual Studio version you're using.
|
||||
|
||||
You must also ensure that the msvc versions of dependencies you're using are placed in the correct folders.
|
||||
|
||||
For libsodium that is `c-toxcore/third_party/libsodium`, and for pthreads-w32, it's `c-toxcore/third_party/pthreads-win32`
|
||||
|
||||
Once all of this is done, from the **Developer Command Prompt for VS**, simply run
|
||||
|
||||
```
|
||||
mkdir _build
|
||||
cd _build
|
||||
cmake ..
|
||||
msbuild ALL_BUILD.vcxproj
|
||||
```
|
||||
|
||||
###### MSYS/Cygwin
|
||||
|
||||
Download Cygwin ([32-bit](https://cygwin.com/setup-x86.exe)/[64-bit](https://cygwin.com/setup-x86_64.exe))
|
||||
|
||||
Search and select exactly these packages in Devel category:
|
||||
|
||||
- mingw64-i686-gcc-core (32-bit) / mingw64-x86_64-gcc-core (64-bit)
|
||||
- mingw64-i686-gcc-g++ (32-bit) / mingw64-x86_64-gcc-g++ (64-bit)
|
||||
- make
|
||||
- cmake
|
||||
- libtool
|
||||
- autoconf
|
||||
- automake
|
||||
- tree
|
||||
- curl
|
||||
- perl
|
||||
- yasm
|
||||
- pkg-config
|
||||
|
||||
To handle Windows EOL correctly run the following in the Cygwin Terminal:
|
||||
|
||||
```sh
|
||||
echo '
|
||||
export SHELLOPTS
|
||||
set -o igncr
|
||||
' > ~/.bash_profile
|
||||
```
|
||||
|
||||
Download toxcore source code and extract it to a folder.
|
||||
|
||||
Open Cygwin Terminal in the toxcore folder and run `./other/windows_build_script_toxcore.sh` to start the build process.
|
||||
|
||||
Toxcore build result files will appear in `/root/prefix/` relatively to Cygwin folder (default `C:\cygwin64`).
|
||||
|
||||
Dependency versions can be customized in `./other/windows_build_script_toxcore.sh` and described in the section below.
|
||||
|
||||
|
||||
##### Cross-compiling from Linux
|
||||
|
||||
These cross-compilation instructions were tested on and written for 64-bit Ubuntu 16.04. You could generalize them for any Linux system, the only requirements are that you have Docker version of >= 1.9.0 and you are running 64-bit system.
|
||||
|
||||
The cross-compilation is fully automated by a parameterized [Dockerfile](/other/docker/windows/Dockerfile).
|
||||
|
||||
Install Docker
|
||||
|
||||
```sh
|
||||
apt-get update
|
||||
apt-get install docker.io
|
||||
```
|
||||
|
||||
Get the toxcore source code and navigate to `other/docker/windows`.
|
||||
|
||||
Build the container image based on the Dockerfile. The following options are available to customize the building of the container image.
|
||||
|
||||
| Name | Description | Expected Value | Default Value |
|
||||
|-----------------------|----------------------------------------------------------------|-------------------------------------|---------------|
|
||||
| `SUPPORT_ARCH_i686` | Support building 32-bit toxcore. | "true" or "false" (case sensitive). | true |
|
||||
| `SUPPORT_ARCH_x86_64` | Support building 64-bit toxcore. | "true" or "false" (case sensitive). | true |
|
||||
| `SUPPORT_TEST` | Support running toxcore automated tests. | "true" or "false" (case sensitive). | false |
|
||||
| `CROSS_COMPILE` | Cross-compiling. True for Docker, false for Cygwin. | "true" or "false" (case sensitive). | true |
|
||||
| `VERSION_OPUS` | Version of libopus to build toxcore with. | Numeric version number. | 1.3.1 |
|
||||
| `VERSION_SODIUM` | Version of libsodium to build toxcore with. | Numeric version number. | 1.0.18 |
|
||||
| `VERSION_VPX` | Version of libvpx to build toxcore with. | Numeric version number. | 1.11.0 |
|
||||
|
||||
Example of building a container image with options
|
||||
|
||||
```sh
|
||||
cd other/docker/windows
|
||||
docker build \
|
||||
--build-arg SUPPORT_TEST=true \
|
||||
-t toxcore \
|
||||
.
|
||||
```
|
||||
|
||||
Run the container to build toxcore. The following options are available to customize the running of the container image.
|
||||
|
||||
| Name | Description | Expected Value | Default Value |
|
||||
|----------------------|--------------------------------------------------------------------------------------------|-------------------------------------|--------------------------------------------------------------------|
|
||||
| `ALLOW_TEST_FAILURE` | Don't stop if a test suite fails. | "true" or "false" (case sensitive). | `false` |
|
||||
| `ENABLE_ARCH_i686` | Build 32-bit toxcore. The image should have been built with `SUPPORT_ARCH_i686` enabled. | "true" or "false" (case sensitive). | `true` |
|
||||
| `ENABLE_ARCH_x86_64` | Build 64-bit toxcore. The image should have been built with `SUPPORT_ARCH_x86_64` enabled. | "true" or "false" (case sensitive). | `true` |
|
||||
| `ENABLE_TEST` | Run the test suite. The image should have been built with `SUPPORT_TEST` enabled. | "true" or "false" (case sensitive). | `false` |
|
||||
| `EXTRA_CMAKE_FLAGS` | Extra arguments to pass to the CMake command when building toxcore. | CMake options. | `-DTEST_TIMEOUT_SECONDS=90` |
|
||||
| `CROSS_COMPILE` | Cross-compiling. True for Docker, false for Cygwin. | "true" or "false" (case sensitive). | `true` |
|
||||
|
||||
Example of running the container with options
|
||||
|
||||
```sh
|
||||
docker run \
|
||||
-e ENABLE_TEST=true \
|
||||
-e ALLOW_TEST_FAILURE=true \
|
||||
-v /path/to/toxcore/sourcecode:/toxcore \
|
||||
-v /path/to/where/output/build/result:/prefix \
|
||||
--rm \
|
||||
toxcore
|
||||
```
|
||||
|
||||
After the build succeeds, you should see the built toxcore libraries in `/path/to/where/output/build/result`.
|
||||
|
||||
## Pre-built binaries
|
||||
|
||||
### Linux
|
||||
|
||||
Toxcore is packaged by at least by the following distributions: ALT Linux, [Arch Linux](https://www.archlinux.org/packages/?q=toxcore), [Fedora](https://apps.fedoraproject.org/packages/toxcore), Mageia, openSUSE, PCLinuxOS, ROSA and Slackware, [according to the information from pkgs.org](https://pkgs.org/download/toxcore). Note that this list might be incomplete and some other distributions might package it too.
|
675
LICENSE
Normal file
675
LICENSE
Normal file
@ -0,0 +1,675 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
33
Makefile.am
Normal file
33
Makefile.am
Normal file
@ -0,0 +1,33 @@
|
||||
SUBDIRS = build
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
|
||||
|
||||
pkgconfig_DATA = $(top_builddir)/libtoxcore.pc
|
||||
BUILT_SOURCES = $(top_builddir)/libtoxcore.pc
|
||||
CLEANFILES = $(top_builddir)/libtoxcore.pc
|
||||
|
||||
|
||||
EXTRA_DIST = \
|
||||
README.md \
|
||||
libtoxcore.pc.in \
|
||||
tox.spec \
|
||||
so.version \
|
||||
$(top_srcdir)/docs/updates/Crypto.md \
|
||||
$(top_srcdir)/docs/updates/Spam-Prevention.md \
|
||||
$(top_srcdir)/docs/updates/Symmetric-NAT-Transversal.md \
|
||||
$(top_srcdir)/other/astyle/README.md \
|
||||
$(top_srcdir)/other/astyle/astylerc \
|
||||
$(top_srcdir)/other/astyle/pre-commit
|
||||
|
||||
if BUILD_AV
|
||||
|
||||
pkgconfig_DATA += $(top_builddir)/libtoxav.pc
|
||||
BUILT_SOURCES += $(top_builddir)/libtoxav.pc
|
||||
CLEANFILES += $(top_builddir)/libtoxav.pc
|
||||
|
||||
EXTRA_DIST += libtoxav.pc.in
|
||||
|
||||
endif
|
175
README.md
Normal file
175
README.md
Normal file
@ -0,0 +1,175 @@
|
||||
# ![Project Tox](https://raw.github.com/TokTok/c-toxcore/master/other/tox.png "Project Tox")
|
||||
|
||||
**Current Coverage:** [![coverage](https://codecov.io/gh/TokTok/c-toxcore/branch/master/graph/badge.svg?token=BRfCKo02De)](https://codecov.io/gh/TokTok/c-toxcore)
|
||||
|
||||
[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://tox.chat/download.html) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md)
|
||||
|
||||
**IRC Channels:** Users: [#tox@libera.chat](https://web.libera.chat/#tox), Developers: [#toktok@libera.chat](https://web.libera.chat/#toktok)
|
||||
|
||||
## What is Tox
|
||||
|
||||
Tox is a peer to peer (serverless) instant messenger aimed at making security
|
||||
and privacy easy to obtain for regular users. It uses
|
||||
[NaCl](https://nacl.cr.yp.to/) for its encryption and authentication.
|
||||
|
||||
## IMPORTANT!
|
||||
|
||||
### ![Danger: Experimental](other/tox-warning.png)
|
||||
|
||||
This is an **experimental** cryptographic network library. It has not been
|
||||
formally audited by an independent third party that specializes in
|
||||
cryptography or cryptanalysis. **Use this library at your own risk.**
|
||||
|
||||
The underlying crypto library [NaCl](https://nacl.cr.yp.to/install.html)
|
||||
provides reliable encryption, but the security model has not yet been fully
|
||||
specified. See [issue 210](https://github.com/TokTok/c-toxcore/issues/210) for
|
||||
a discussion on developing a threat model. See other issues for known
|
||||
weaknesses (e.g. [issue 426](https://github.com/TokTok/c-toxcore/issues/426)
|
||||
describes what can happen if your secret key is stolen).
|
||||
|
||||
## Toxcore Development Roadmap
|
||||
|
||||
The roadmap and changelog are generated from GitHub issues. You may view them
|
||||
on the website, where they are updated at least once every 24 hours:
|
||||
|
||||
- Changelog: https://toktok.ltd/changelog/c-toxcore
|
||||
- Roadmap: https://toktok.ltd/roadmap/c-toxcore
|
||||
|
||||
## Installing toxcore
|
||||
|
||||
Detailed installation instructions can be found in [INSTALL.md](INSTALL.md).
|
||||
|
||||
Be advised that due to the addition of `cmp` as a submodule, you now also need to initialize the git submodules required by toxcore. This can be done by cloning the repo with the following command: `git clone --recurse-submodules https://github.com/Toktok/c-toxcore` or by running `git submodule update --init` in the root directory of the repo.
|
||||
|
||||
In a nutshell, if you have [libsodium](https://github.com/jedisct1/libsodium)
|
||||
installed, run:
|
||||
|
||||
```sh
|
||||
mkdir _build && cd _build
|
||||
cmake ..
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
If you have [libvpx](https://github.com/webmproject/libvpx) and
|
||||
[opus](https://github.com/xiph/opus) installed, the above will also build the
|
||||
A/V library for multimedia chats.
|
||||
|
||||
## Using toxcore
|
||||
|
||||
The simplest "hello world" example could be an echo bot. Here we will walk
|
||||
through the implementation of a simple bot.
|
||||
|
||||
### Creating the tox instance
|
||||
|
||||
All toxcore API functions work with error parameters. They are enums with one
|
||||
`OK` value and several error codes that describe the different situations in
|
||||
which the function might fail.
|
||||
|
||||
```c
|
||||
TOX_ERR_NEW err_new;
|
||||
Tox *tox = tox_new(NULL, &err_new);
|
||||
if (err_new != TOX_ERR_NEW_OK) {
|
||||
fprintf(stderr, "tox_new failed with error code %d\n", err_new);
|
||||
exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
Here, we simply exit the program, but in a real client you will probably want
|
||||
to do some error handling and proper error reporting to the user. The `NULL`
|
||||
argument given to the first parameter of `tox_new` is the `Tox_Options`. It
|
||||
contains various write-once network settings and allows you to load a
|
||||
previously serialised instance. See [toxcore/tox.h](tox.h) for details.
|
||||
|
||||
### Setting up callbacks
|
||||
|
||||
Toxcore works with callbacks that you can register to listen for certain
|
||||
events. Examples of such events are "friend request received" or "friend sent
|
||||
a message". Search the API for `tox_callback_*` to find all of them.
|
||||
|
||||
Here, we will set up callbacks for receiving friend requests and receiving
|
||||
messages. We will always accept any friend request (because we're a bot), and
|
||||
when we receive a message, we send it back to the sender.
|
||||
|
||||
```c
|
||||
tox_callback_friend_request(tox, handle_friend_request);
|
||||
tox_callback_friend_message(tox, handle_friend_message);
|
||||
```
|
||||
|
||||
These two function calls set up the callbacks. Now we also need to implement
|
||||
these "handle" functions.
|
||||
|
||||
### Handle friend requests
|
||||
|
||||
```c
|
||||
static void handle_friend_request(
|
||||
Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length,
|
||||
void *user_data) {
|
||||
// Accept the friend request:
|
||||
TOX_ERR_FRIEND_ADD err_friend_add;
|
||||
tox_friend_add_norequest(tox, public_key, &err_friend_add);
|
||||
if (err_friend_add != TOX_ERR_FRIEND_ADD_OK) {
|
||||
fprintf(stderr, "unable to add friend: %d\n", err_friend_add);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `tox_friend_add_norequest` function adds the friend without sending them a
|
||||
friend request. Since we already got a friend request, this is the right thing
|
||||
to do. If you wanted to send a friend request yourself, you would use
|
||||
`tox_friend_add`, which has an extra parameter for the message.
|
||||
|
||||
### Handle messages
|
||||
|
||||
Now, when the friend sends us a message, we want to respond to them by sending
|
||||
them the same message back. This will be our "echo".
|
||||
|
||||
```c
|
||||
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) {
|
||||
TOX_ERR_FRIEND_SEND_MESSAGE err_send;
|
||||
tox_friend_send_message(tox, friend_number, type, message, length,
|
||||
&err_send);
|
||||
if (err_send != TOX_ERR_FRIEND_SEND_MESSAGE_OK) {
|
||||
fprintf(stderr, "unable to send message back to friend %d: %d\n",
|
||||
friend_number, err_send);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's it for the setup. Now we want to actually run the bot.
|
||||
|
||||
### Main event loop
|
||||
|
||||
Toxcore works with a main event loop function `tox_iterate` that you need to
|
||||
call at a certain frequency dictated by `tox_iteration_interval`. This is a
|
||||
polling function that receives new network messages and processes them.
|
||||
|
||||
```c
|
||||
while (true) {
|
||||
usleep(1000 * tox_iteration_interval(tox));
|
||||
tox_iterate(tox, NULL);
|
||||
}
|
||||
```
|
||||
|
||||
That's it! Now you have a working echo bot. The only problem is that since Tox
|
||||
works with public keys, and you can't really guess your bot's public key, you
|
||||
can't add it as a friend in your client. For this, we need to call another API
|
||||
function: `tox_self_get_address(tox, address)`. This will fill the 38 byte
|
||||
friend address into the `address` buffer. You can then display that binary
|
||||
string as hex and input it into your client. Writing a `bin2hex` function is
|
||||
left as exercise for the reader.
|
||||
|
||||
We glossed over a lot of details, such as the user data which we passed to
|
||||
`tox_iterate` (passing `NULL`), bootstrapping into an actual network (this bot
|
||||
will work in the LAN, but not on an internet server) and the fact that we now
|
||||
have no clean way of stopping the bot (`while (true)`). If you want to write a
|
||||
real bot, you will probably want to read up on all the API functions. Consult
|
||||
the API documentation in [toxcore/tox.h](toxcore/tox.h) for more information.
|
||||
|
||||
### Other resources
|
||||
|
||||
- [Another echo bot](https://wiki.tox.chat/developers/client_examples/echo_bot)
|
||||
- [minitox](https://github.com/hqwrong/minitox) (A minimal tox client)
|
32
appveyor.yml
Normal file
32
appveyor.yml
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
image: Visual Studio 2019
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.conan -> conanfile.py'
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- job_name: static
|
||||
- job_name: shared
|
||||
|
||||
install:
|
||||
- set PATH=C:\Python311-x64\Scripts;%PATH%
|
||||
- py -3 -m pip install conan==1.59.0
|
||||
- git submodule update --init --recursive
|
||||
|
||||
for:
|
||||
- matrix:
|
||||
only:
|
||||
- job_name: static
|
||||
before_build:
|
||||
- conan install -if _build -o with_tests=False .
|
||||
- matrix:
|
||||
only:
|
||||
- job_name: shared
|
||||
before_build:
|
||||
- conan install -if _build -o with_tests=False -o shared=True .
|
||||
|
||||
build_script:
|
||||
- set CONAN_CPU_COUNT=50
|
||||
- set CTEST_OUTPUT_ON_FAILURE=1
|
||||
- conan build -bf _build -if _build .
|
72
auto_tests/BUILD.bazel
Normal file
72
auto_tests/BUILD.bazel
Normal 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"])]
|
86
auto_tests/CMakeLists.txt
Normal file
86
auto_tests/CMakeLists.txt
Normal 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()
|
248
auto_tests/Makefile.inc
Normal file
248
auto_tests/Makefile.inc
Normal 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
|
902
auto_tests/TCP_test.c
Normal file
902
auto_tests/TCP_test.c
Normal 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;
|
||||
}
|
123
auto_tests/announce_test.c
Normal file
123
auto_tests/announce_test.c
Normal 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;
|
||||
}
|
465
auto_tests/auto_test_support.c
Normal file
465
auto_tests/auto_test_support.c
Normal 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);
|
||||
}
|
||||
|
65
auto_tests/auto_test_support.h
Normal file
65
auto_tests/auto_test_support.h
Normal 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
|
34
auto_tests/bootstrap_test.c
Normal file
34
auto_tests/bootstrap_test.c
Normal 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;
|
||||
}
|
32
auto_tests/check_compat.h
Normal file
32
auto_tests/check_compat.h
Normal 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
|
464
auto_tests/conference_av_test.c
Normal file
464
auto_tests/conference_av_test.c
Normal 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;
|
||||
}
|
90
auto_tests/conference_double_invite_test.c
Normal file
90
auto_tests/conference_double_invite_test.c
Normal 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;
|
||||
}
|
179
auto_tests/conference_invite_merge_test.c
Normal file
179
auto_tests/conference_invite_merge_test.c
Normal 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;
|
||||
}
|
137
auto_tests/conference_peer_nick_test.c
Normal file
137
auto_tests/conference_peer_nick_test.c
Normal 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;
|
||||
}
|
259
auto_tests/conference_simple_test.c
Normal file
259
auto_tests/conference_simple_test.c
Normal 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;
|
||||
}
|
441
auto_tests/conference_test.c
Normal file
441
auto_tests/conference_test.c
Normal 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;
|
||||
}
|
27
auto_tests/conference_two_test.c
Normal file
27
auto_tests/conference_two_test.c
Normal 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;
|
||||
}
|
354
auto_tests/crypto_test.c
Normal file
354
auto_tests/crypto_test.c
Normal 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;
|
||||
}
|
BIN
auto_tests/data/save.tox
Normal file
BIN
auto_tests/data/save.tox
Normal file
Binary file not shown.
152
auto_tests/dht_getnodes_api_test.c
Normal file
152
auto_tests/dht_getnodes_api_test.c
Normal 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
|
240
auto_tests/encryptsave_test.c
Normal file
240
auto_tests/encryptsave_test.c
Normal 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
|
121
auto_tests/file_saving_test.c
Normal file
121
auto_tests/file_saving_test.c
Normal 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;
|
||||
}
|
367
auto_tests/file_transfer_test.c
Normal file
367
auto_tests/file_transfer_test.c
Normal 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;
|
||||
}
|
334
auto_tests/forwarding_test.c
Normal file
334
auto_tests/forwarding_test.c
Normal 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;
|
||||
}
|
26
auto_tests/friend_connection_test.c
Normal file
26
auto_tests/friend_connection_test.c
Normal 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;
|
||||
}
|
71
auto_tests/friend_request_spam_test.c
Normal file
71
auto_tests/friend_request_spam_test.c
Normal 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;
|
||||
}
|
84
auto_tests/friend_request_test.c
Normal file
84
auto_tests/friend_request_test.c
Normal 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;
|
||||
}
|
437
auto_tests/group_general_test.c
Normal file
437
auto_tests/group_general_test.c
Normal 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
|
283
auto_tests/group_invite_test.c
Normal file
283
auto_tests/group_invite_test.c
Normal 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
|
546
auto_tests/group_message_test.c
Normal file
546
auto_tests/group_message_test.c
Normal 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
|
653
auto_tests/group_moderation_test.c
Normal file
653
auto_tests/group_moderation_test.c
Normal 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
|
300
auto_tests/group_save_test.c
Normal file
300
auto_tests/group_save_test.c
Normal 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
|
345
auto_tests/group_state_test.c
Normal file
345
auto_tests/group_state_test.c
Normal 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
|
464
auto_tests/group_sync_test.c
Normal file
464
auto_tests/group_sync_test.c
Normal 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
|
255
auto_tests/group_tcp_test.c
Normal file
255
auto_tests/group_tcp_test.c
Normal 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
|
345
auto_tests/group_topic_test.c
Normal file
345
auto_tests/group_topic_test.c
Normal 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
|
48
auto_tests/invalid_tcp_proxy_test.c
Normal file
48
auto_tests/invalid_tcp_proxy_test.c
Normal 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;
|
||||
}
|
48
auto_tests/invalid_udp_proxy_test.c
Normal file
48
auto_tests/invalid_udp_proxy_test.c
Normal 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;
|
||||
}
|
53
auto_tests/lan_discovery_test.c
Normal file
53
auto_tests/lan_discovery_test.c
Normal 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;
|
||||
}
|
67
auto_tests/lossless_packet_test.c
Normal file
67
auto_tests/lossless_packet_test.c
Normal 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;
|
||||
}
|
67
auto_tests/lossy_packet_test.c
Normal file
67
auto_tests/lossy_packet_test.c
Normal 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;
|
||||
}
|
173
auto_tests/network_test.c
Normal file
173
auto_tests/network_test.c
Normal 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;
|
||||
}
|
668
auto_tests/onion_test.c
Normal file
668
auto_tests/onion_test.c
Normal 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;
|
||||
}
|
64
auto_tests/overflow_recvq_test.c
Normal file
64
auto_tests/overflow_recvq_test.c
Normal 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;
|
||||
}
|
46
auto_tests/overflow_sendq_test.c
Normal file
46
auto_tests/overflow_sendq_test.c
Normal 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;
|
||||
}
|
129
auto_tests/proxy_test.c
Normal file
129
auto_tests/proxy_test.c
Normal 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;
|
||||
}
|
102
auto_tests/reconnect_test.c
Normal file
102
auto_tests/reconnect_test.c
Normal 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;
|
||||
}
|
161
auto_tests/save_compatibility_test.c
Normal file
161
auto_tests/save_compatibility_test.c
Normal 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;
|
||||
}
|
159
auto_tests/save_friend_test.c
Normal file
159
auto_tests/save_friend_test.c
Normal 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;
|
||||
}
|
268
auto_tests/save_load_test.c
Normal file
268
auto_tests/save_load_test.c
Normal 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;
|
||||
}
|
79
auto_tests/send_message_test.c
Normal file
79
auto_tests/send_message_test.c
Normal 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;
|
||||
}
|
100
auto_tests/set_name_test.c
Normal file
100
auto_tests/set_name_test.c
Normal 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;
|
||||
}
|
105
auto_tests/set_status_message_test.c
Normal file
105
auto_tests/set_status_message_test.c
Normal 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;
|
||||
}
|
37
auto_tests/tcp_relay_test.c
Normal file
37
auto_tests/tcp_relay_test.c
Normal 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;
|
||||
}
|
164
auto_tests/tox_dispatch_test.c
Normal file
164
auto_tests/tox_dispatch_test.c
Normal 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;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user