diff --git a/.cirrus.yml b/.cirrus.yml index ec26c95f2..bba9843cf 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,8 +7,4 @@ bazel-opt_task: configure_script: - /src/workspace/tools/inject-repo jvm-toxcore-c test_all_script: - - TEST="bazel test -k - --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST - --config=release - //jvm-toxcore-c/..." - - cd /src/workspace && $TEST || $TEST || $TEST || $TEST + - cd /src/workspace && bazel test -k //jvm-toxcore-c/... diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..ee1bee2bc --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +ColumnLimit: 100 diff --git a/.github/settings.yml b/.github/settings.yml deleted file mode 100644 index 19973be34..000000000 --- a/.github/settings.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -_extends: .github - -repository: - name: jvm-toxcore-c - description: JVM (Java/Scala/Kotlin) bindings to toxcore - topics: tox, java, scala, toxcore - has_issues: true - -branches: - - name: "master" - protection: - required_status_checks: - contexts: - - Codacy Static Code Analysis - - code-review/reviewable diff --git a/BUILD.bazel b/BUILD.bazel index 20e6c1769..1df6cee04 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,22 +1,11 @@ -load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library", "scala_test") -load("@io_bazel_rules_scala//scala_proto:scala_proto.bzl", "scalapb_proto_library") load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_proto_library") -load("@rules_java//java:defs.bzl", "java_test") +load("@rules_java//java:defs.bzl", "java_proto_library", "java_test") +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library", "kt_jvm_test") load("@rules_proto//proto:defs.bzl", "proto_library") load("//tools/project:build_defs.bzl", "project") project() -# If you are on Windows, the relative path below doesn't work, so you'll need -# to use an absolute path like below. If you find a way to make this work with -# relative paths, please send a pull request. -USER = "iphydf" - -JVM_FLAGS = select({ - "//tools/config:windows": ["-Djava.library.path=C:/Users/%s/source/repos/toktok-stack/bazel-bin/jvm-toxcore-c" % USER], - "//conditions:default": ["-Djava.library.path=jvm-toxcore-c"], -}) - genrule( name = "copy_link_jni_md_header", srcs = select({ @@ -50,8 +39,8 @@ cc_proto_library( deps = [":jni_proto"], ) -scalapb_proto_library( - name = "jni_scala_proto", +java_proto_library( + name = "jni_java_proto", deps = [":jni_proto"], ) @@ -85,266 +74,37 @@ cc_binary( ], ) -genrule( - name = "native_macho", - srcs = [":libtox4j-c.so"], - outs = ["libtox4j-c.dylib"], - cmd = "cp $< $@", -) - -genrule( - name = "native_windows", - srcs = [":libtox4j-c.so"], - outs = ["tox4j-c.dll"], - cmd = "cp $< $@", -) - -filegroup( - name = "native", - srcs = select({ - "//tools/config:freebsd": [":libtox4j-c.so"], - "//tools/config:linux": [":libtox4j-c.so"], - "//tools/config:osx": [":libtox4j-c.dylib"], - "//tools/config:windows": [":tox4j-c.dll"], - }), - visibility = ["//visibility:public"], -) - -scala_library( +kt_jvm_library( name = "jvm-toxcore-c", srcs = glob([ "src/main/java/**/*.java", - "src/main/java/**/*.scala", + "src/main/java/**/*.kt", ]), - visibility = ["//visibility:public"], - deps = [ - ":jni_scala_proto", - "//jvm-macros", - "//jvm-toxcore-api", - "@maven//:com_google_guava_guava", - "@maven//:com_typesafe_scala_logging_scala_logging_2_11", - "@maven//:org_jetbrains_annotations", - "@maven//:org_slf4j_slf4j_api", - ], -) - -scala_library( - name = "codegen_lib", - testonly = True, - srcs = glob([ - "src/test/java/gnieh/**/*.scala", - "src/test/java/im/tox/tox4j/impl/jni/codegen/**/*.scala", - ]) + [ - "src/test/java/im/tox/tox4j/impl/jni/MethodMap.scala", - ], - deps = [ - ":jni_scala_proto", - ":jvm-toxcore-c", - "//jvm-macros", - "//jvm-toxcore-api", - "@maven//:com_google_guava_guava", - "@maven//:org_apache_commons_commons_lang3", - "@maven//:org_jetbrains_annotations", - ], -) - -scala_library( - name = "test_lib", - testonly = True, - srcs = [ - "src/test/java/im/tox/core/random/RandomCore.scala", - "src/test/java/im/tox/tox4j/ConnectedListener.scala", - "src/test/java/im/tox/tox4j/DhtNode.scala", - "src/test/java/im/tox/tox4j/DhtNodeSelector.scala", - "src/test/java/im/tox/tox4j/SocksServer.scala", - "src/test/java/im/tox/tox4j/TestConstants.scala", - "src/test/java/im/tox/tox4j/ToxCoreTestBase.scala", - "src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerator.scala", - "src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerators.scala", - "src/test/java/im/tox/tox4j/av/callbacks/audio/AudioPlayback.scala", - "src/test/java/im/tox/tox4j/av/callbacks/video/ArithmeticVideoGenerator.scala", - "src/test/java/im/tox/tox4j/av/callbacks/video/RgbVideoGenerator.scala", - "src/test/java/im/tox/tox4j/av/callbacks/video/TextImageGenerator.scala", - "src/test/java/im/tox/tox4j/av/callbacks/video/VideoConversions.scala", - "src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerator.scala", - "src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerators.scala", - "src/test/java/im/tox/tox4j/core/SmallNat.scala", - "src/test/java/im/tox/tox4j/core/ToxCoreFactory.scala", - "src/test/java/im/tox/tox4j/core/ToxList.scala", - "src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeTestBase.scala", - "src/test/java/im/tox/tox4j/core/callbacks/InvokeTest.scala", - "src/test/java/im/tox/tox4j/crypto/ToxCryptoTest.scala", - "src/test/java/im/tox/tox4j/impl/jni/NamingConventionsTest.scala", - "src/test/java/im/tox/tox4j/impl/jni/ToxAvImplFactory.scala", - "src/test/java/im/tox/tox4j/impl/jni/ToxCoreImplFactory.scala", - "src/test/java/im/tox/tox4j/testing/GetDisjunction.scala", - "src/test/java/im/tox/tox4j/testing/ToxExceptionChecks.scala", - "src/test/java/im/tox/tox4j/testing/ToxTestMixin.scala", - "src/test/java/im/tox/tox4j/testing/autotest/AliceBobTest.scala", - "src/test/java/im/tox/tox4j/testing/autotest/AliceBobTestBase.scala", - "src/test/java/im/tox/tox4j/testing/autotest/AutoTest.scala", - "src/test/java/im/tox/tox4j/testing/autotest/AutoTestSuite.scala", - "src/test/java/im/tox/tox4j/testing/autotest/ChatClient.scala", - ], - deps = [ - ":codegen_lib", - ":jvm-toxcore-c", - "//jvm-macros", - "//jvm-toxcore-api", - "@io_bazel_rules_scala//scala/scalatest", - "@maven//:com_chuusai_shapeless_2_11", - "@maven//:com_typesafe_scala_logging_scala_logging_2_11", - "@maven//:org_jetbrains_annotations", - "@maven//:org_scalacheck_scalacheck_2_11", - "@maven//:org_scalatestplus_scalacheck_1_14_2_11", - "@maven//:org_slf4j_slf4j_api", - ], + data = ["libtox4j-c.so"], + deps = [":jni_java_proto"], ) -[scala_test( - name = src[src.rindex("/") + 1:-6], +kt_jvm_test( + name = "ToxCoreTest", size = "small", - srcs = [src], - data = [":native"], - jvm_flags = JVM_FLAGS, - resources = glob([ - "src/test/resources/**/*", - ]), - runtime_deps = [ - "@maven//:log4j_log4j", - "@maven//:org_slf4j_slf4j_log4j12", - ], + srcs = ["src/test/java/im/tox/tox4j/core/ToxCoreTest.kt"], + jvm_flags = ["-Djava.library.path=jvm-toxcore-c"], + test_class = "im.tox.tox4j.core.ToxCoreTest", deps = [ - ":codegen_lib", - ":jni_scala_proto", ":jvm-toxcore-c", - ":test_lib", - "//jvm-macros", - "//jvm-toxcore-api", - "@maven//:com_chuusai_shapeless_2_11", - "@maven//:com_google_guava_guava", - "@maven//:com_typesafe_scala_logging_scala_logging_2_11", - "@maven//:org_apache_commons_commons_lang3", - "@maven//:org_jetbrains_annotations", - "@maven//:org_scalacheck_scalacheck_2_11", - "@maven//:org_scalatestplus_scalacheck_1_14_2_11", - "@maven//:org_slf4j_slf4j_api", - ], -) for src in glob(["src/test/java/**/*Test.scala"])] - -[java_test( - name = src[src.rindex("/") + 1:-5], - size = "small", - srcs = [src], - data = [":native"], - jvm_flags = JVM_FLAGS, - resources = glob([ - "src/test/resources/**/*", - ]), - runtime_deps = [ - "@maven//:log4j_log4j", - "@maven//:org_slf4j_slf4j_log4j12", - ], - deps = [ - ":jvm-toxcore-c", - "//jvm-toxcore-api", - "@io_bazel_rules_scala//scala/scalatest", - "@maven//:junit_junit", - "@maven//:org_scalatestplus_junit_4_13_2_11", - "@maven//:org_slf4j_slf4j_api", - ], -) for src in glob(["src/test/java/**/*.java"])] - -[scala_binary( - name = src[src.rindex("/") + 1:-6], - testonly = True, - srcs = [src], - main_class = "im.tox.tox4j.impl.jni.codegen." + src[src.rindex("/") + 1:-6], - resources = glob([ - "src/test/resources/**/*", - ]), - deps = [ - ":codegen_lib", - ":jni_scala_proto", - ":jvm-toxcore-c", - "//jvm-toxcore-api", - "@maven//:com_google_guava_guava", - "@maven//:org_jetbrains_annotations", - ], -) for src in glob(["src/test/java/im/tox/tox4j/impl/jni/codegen/Jni*.scala"])] - -scala_binary( - name = "AudioPlaybackShow", - testonly = True, - srcs = [ - "src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerator.scala", - "src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerators.scala", - "src/test/java/im/tox/tox4j/av/callbacks/audio/AudioPlayback.scala", - "src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioPlaybackShow.scala", - ], - main_class = "im.tox.tox4j.av.callbacks.audio.AudioPlaybackShow", - resources = glob([ - "src/test/resources/**/*", - ]), - deps = [ - "//jvm-toxcore-api", + "@maven//:org_jetbrains_kotlin_kotlin_test_junit", + "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", ], ) -scala_test( - name = "AudioReceiveFrameCallbackShow", +java_test( + name = "ToxCoreJavaTest", size = "small", - srcs = ["src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackShow.scala"], - data = [":native"], - #jvm_flags = JVM_FLAGS, - resources = glob([ - "src/test/resources/**/*", - ]), - tags = ["manual"], - runtime_deps = [ - "@maven//:log4j_log4j", - "@maven//:org_slf4j_slf4j_log4j12", - ], + srcs = ["src/test/java/im/tox/tox4j/core/ToxCoreJavaTest.java"], + jvm_flags = ["-Djava.library.path=jvm-toxcore-c"], + test_class = "im.tox.tox4j.core.ToxCoreJavaTest", deps = [ ":jvm-toxcore-c", - ":test_lib", - "//jvm-toxcore-api", - "@io_bazel_rules_scala//scala/scalatest", - "@maven//:com_typesafe_scala_logging_scala_logging_2_11", - "@maven//:org_jetbrains_annotations", - "@maven//:org_slf4j_slf4j_api", - ], -) - -scala_test( - name = "VideoReceiveFrameCallbackShow", - size = "small", - srcs = [ - "src/tools/java/im/tox/tox4j/av/callbacks/video/ConsoleVideoDisplay.scala", - "src/tools/java/im/tox/tox4j/av/callbacks/video/GuiVideoDisplay.scala", - "src/tools/java/im/tox/tox4j/av/callbacks/video/RgbVideoDisplay.scala", - "src/tools/java/im/tox/tox4j/av/callbacks/video/VideoDisplay.scala", - "src/tools/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackShow.scala", - ], - data = [":native"], - jvm_flags = JVM_FLAGS, - resources = glob([ - "src/test/resources/**/*", - ]), - tags = ["manual"], - runtime_deps = [ - "@maven//:log4j_log4j", - "@maven//:org_slf4j_slf4j_log4j12", - ], - deps = [ - ":jvm-toxcore-c", - ":test_lib", - "//jvm-toxcore-api", - "@io_bazel_rules_scala//scala/scalatest", - "@maven//:com_chuusai_shapeless_2_11", - "@maven//:com_typesafe_scala_logging_scala_logging_2_11", - "@maven//:org_jetbrains_annotations", - "@maven//:org_scala_lang_modules_scala_swing_2_11", - "@maven//:org_slf4j_slf4j_api", + "@maven//:junit_junit", ], ) diff --git a/Makefile b/Makefile deleted file mode 100644 index bca84ccaf..000000000 --- a/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -heroku: - $(MAKE) -f scripts/build-host diff --git a/Procfile b/Procfile deleted file mode 100644 index 7699a0705..000000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: java -Xmx256m -Djava.library.path=. -cp tox4j_2.11-0.1-SNAPSHOT.jar im.tox.client.TestClient -P $PORT diff --git a/build.sbt b/build.sbt deleted file mode 100644 index 4729da14e..000000000 --- a/build.sbt +++ /dev/null @@ -1,61 +0,0 @@ -// General settings. -organization := "org.toktok" -name := "tox4j-c" -version := "0.2.3" -scalaVersion := "2.11.12" - -bintrayVcsUrl := Some("https://github.com/TokTok/jvm-toxcore-c") - -/****************************************************************************** - * Dependencies - ******************************************************************************/ - -// Snapshot and linter repository. -resolvers += Resolver.sonatypeRepo("snapshots") - -// Build dependencies. -libraryDependencies ++= Seq( - "org.toktok" %% "tox4j-api" % version.value, - "org.toktok" %% "macros" % "0.1.1", - "com.chuusai" %% "shapeless" % "2.3.3", - "com.trueaccord.scalapb" %% "scalapb-runtime-grpc" % "0.5.43", - "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2" -) - -// Test dependencies. -libraryDependencies ++= Seq( - "com.storm-enroute" %% "scalameter" % "0.19", - "org.jline" % "jline" % "3.14.0", - "junit" % "junit" % "4.13", - "org.scalacheck" %% "scalacheck" % "1.13.5", - "org.scalatest" %% "scalatest" % "3.0.1", - "org.scalaz" %% "scalaz-concurrent" % "7.3.0-M27", - "org.slf4j" % "slf4j-log4j12" % "1.7.30" -) map (_ % Test) - -// Add ScalaMeter as test framework. -testFrameworks += new TestFramework("org.scalameter.ScalaMeterFramework") - -// Disable parallel test execution, as network tests become flaky that way. -parallelExecution in Test := false - -/****************************************************************************** - * Other settings and plugin configuration. - ******************************************************************************/ - -// TODO(iphydf): Require less test coverage for now, until ToxAv is tested. -import scoverage.ScoverageKeys._ -coverageMinimum := 20 -coverageExcludedPackages := ".*\\.proto\\..*" - -import im.tox.sbt.Scalastyle -Scalastyle.projectSettings - -// Mixed project. -compileOrder := CompileOrder.Mixed -scalaSource in Compile := (javaSource in Compile).value -scalaSource in Test := (javaSource in Test ).value - -// Override Scalastyle configuration for test. -scalastyleConfigUrl in Test := None -scalastyleConfig in Test := (scalaSource in Test).value / "scalastyle-config.xml" diff --git a/checkstyle-config.xml b/checkstyle-config.xml deleted file mode 100644 index 76aa2e4b3..000000000 --- a/checkstyle-config.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index c52494f23..8c35ce8c5 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,9 +1,8 @@ project(tox4j-c) cmake_minimum_required(VERSION 2.8.7) -enable_testing() set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/cmake") -if (NOT DEFINED CMAKE_MACOSX_RPATH) +if(NOT DEFINED CMAKE_MACOSX_RPATH) set(CMAKE_MACOSX_RPATH true) endif() @@ -59,31 +58,37 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") include(CheckCXXSourceCompiles) # ::gets -check_cxx_source_compiles(" +check_cxx_source_compiles( + " #include using ::gets; int main() {} -" HAVE_GETS) +" + HAVE_GETS) if(HAVE_GETS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_GETS=1") endif() # std::make_unique -check_cxx_source_compiles(" +check_cxx_source_compiles( + " #include using std::make_unique; int main() {} -" HAVE_MAKE_UNIQUE) +" + HAVE_MAKE_UNIQUE) if(HAVE_MAKE_UNIQUE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_MAKE_UNIQUE=1") endif() # std::to_string -check_cxx_source_compiles(" +check_cxx_source_compiles( + " #include using std::to_string; int main() {} -" HAVE_TO_STRING) +" + HAVE_TO_STRING) if(HAVE_TO_STRING) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_TO_STRING=1") endif() @@ -92,13 +97,11 @@ endif() # Build # -PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS - src/ToxAv/Av.proto - src/ToxCore/Core.proto - src/util/ProtoLog.proto -) +protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS src/ToxAv/Av.proto + src/ToxCore/Core.proto src/util/ProtoLog.proto) -add_library(${PROJECT_NAME} SHARED +add_library( + ${PROJECT_NAME} SHARED ${ANDROID_CPU_FEATURES} ${PROTO_SRCS} ${PROTO_HDRS} @@ -167,32 +170,28 @@ add_library(${PROJECT_NAME} SHARED src/util/to_bytes.cpp src/util/to_bytes.h src/util/unused.h - src/util/wrap_void.h -) + src/util/wrap_void.h) if(ANDROID_CPU_FEATURES) target_compile_definitions(${PROJECT_NAME} PRIVATE -Dtypeof=__typeof__) endif() -target_link_libraries(${PROJECT_NAME} - ${PROTOBUF_LIBRARIES} -) +target_link_libraries(${PROJECT_NAME} ${PROTOBUF_LIBRARIES}) if(LIBTOXCORE_FOUND) - target_link_libraries(${PROJECT_NAME} - ${LIBTOXAV_STATIC_LIBRARIES} - ${LIBTOXCORE_STATIC_LIBRARIES} - ) + target_link_libraries(${PROJECT_NAME} ${LIBTOXAV_STATIC_LIBRARIES} + ${LIBTOXCORE_STATIC_LIBRARIES}) else() - target_link_libraries(${PROJECT_NAME} - ${TOXCORE_STATIC_LIBRARIES} - ) + target_link_libraries(${PROJECT_NAME} ${TOXCORE_STATIC_LIBRARIES}) endif() # Windows and OSX don't have this linker functionality. if(NOT WIN32 AND NOT APPLE) - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS - "-Wl,-z,defs -Wl,--version-script,${CMAKE_SOURCE_DIR}/src/libtox4j-c.ld") + set_target_properties( + ${PROJECT_NAME} + PROPERTIES + LINK_FLAGS + "-Wl,-z,defs -Wl,--version-script,${CMAKE_SOURCE_DIR}/src/libtox4j-c.ld") endif() # @@ -200,33 +199,3 @@ endif() # install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION "lib") - -# -# Testing -# - -find_package(GTest) -if(GTEST_FOUND) - add_executable(tox4j-test - test/util/jni/ArrayFromJava_test.cpp - test/util/jni/ArrayToJava_test.cpp - test/util/jni/UTFChars_test.cpp - test/util/debug_log_test.cpp - test/util/exceptions_test.cpp - test/util/instance_manager_test.cpp - test/util/to_bytes_test.cpp - test/util/wrap_void_test.cpp - test/tox4j/ToxInstances_test.cpp - test/tox/common_test.cpp - test/main.cpp - test/mock_jni.cpp - test/mock_jni.h - ) - - target_include_directories(tox4j-test PUBLIC ${GTEST_INCLUDE_DIRS}) - - target_link_libraries(tox4j-test ${GTEST_BOTH_LIBRARIES}) - target_link_libraries(tox4j-test tox4j-c) - - add_test(tox4j-test tox4j-test) -endif() diff --git a/cpp/build.sbt b/cpp/build.sbt deleted file mode 100644 index a4a2f19d2..000000000 --- a/cpp/build.sbt +++ /dev/null @@ -1,13 +0,0 @@ -// General settings. -organization := "org.toktok" -name := "tox4j-c_" + sys.env("TOX4J_PLATFORM") -version := ("pkg-config --modversion toxcore" !!).trim - -// Pure Java project. -crossPaths := false -autoScalaLibrary := false - -// Bintray publishing settings. -licenses += (("AGPL-V3", url("http://opensource.org/licenses/AGPL-V3"))) -bintrayOrganization := Some("toktok") -bintrayVcsUrl := Some("https://github.com/TokTok/jvm-toxcore-c") diff --git a/cpp/project/.gitignore b/cpp/project/.gitignore deleted file mode 100644 index 3ec95a4e2..000000000 --- a/cpp/project/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/project -/target diff --git a/cpp/project/build.properties b/cpp/project/build.properties deleted file mode 100644 index 8e682c526..000000000 --- a/cpp/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=0.13.18 diff --git a/cpp/project/build.sbt b/cpp/project/build.sbt deleted file mode 100644 index 8dd913f98..000000000 --- a/cpp/project/build.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") diff --git a/cpp/src/uncrustify.cfg b/cpp/src/uncrustify.cfg deleted file mode 100644 index e27eb8bd2..000000000 --- a/cpp/src/uncrustify.cfg +++ /dev/null @@ -1,1693 +0,0 @@ -# Uncrustify 0.61 - -# -# General options -# - -# The type of line endings -newlines = auto # auto/lf/crlf/cr - -# The original size of tabs in the input -input_tab_size = 8 # number - -# The size of tabs in the output (only used if align_with_tabs=true) -output_tab_size = 8 # number - -# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) -string_escape_char = 92 # number - -# Alternate string escape char for Pawn. Only works right before the quote char. -string_escape_char2 = 0 # number - -# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. -# If true (default), 'assert(x<0 && y>=3)' will be broken. -# Improvements to template detection may make this option obsolete. -tok_split_gte = false # false/true - -# Control what to do with the UTF-8 BOM (recommend 'remove') -utf8_bom = remove # ignore/add/remove/force - -# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 -utf8_byte = true # false/true - -# Force the output encoding to UTF-8 -utf8_force = true # false/true - -# -# Indenting -# - -# The number of columns to indent per level. -# Usually 2, 3, 4, or 8. -indent_columns = 2 # number - -# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. -# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level -indent_continue = 2 # number - -# How to use tabs when indenting code -# 0=spaces only -# 1=indent with tabs to brace level, align with spaces -# 2=indent and align with tabs, using spaces when not on a tabstop -indent_with_tabs = 0 # number - -# Comments that are not a brace level are indented with tabs on a tabstop. -# Requires indent_with_tabs=2. If false, will use spaces. -indent_cmt_with_tabs = false # false/true - -# Whether to indent strings broken by '\' so that they line up -indent_align_string = false # false/true - -# The number of spaces to indent multi-line XML strings. -# Requires indent_align_string=True -indent_xml_string = 0 # number - -# Spaces to indent '{' from level -indent_brace = 2 # number - -# Whether braces are indented to the body level -indent_braces = false # false/true - -# Disabled indenting function braces if indent_braces is true -indent_braces_no_func = true # false/true - -# Disabled indenting class braces if indent_braces is true -indent_braces_no_class = false # false/true - -# Disabled indenting struct braces if indent_braces is true -indent_braces_no_struct = false # false/true - -# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. -indent_brace_parent = false # false/true - -# Indent based on the paren open instead of the brace open in '({\n', default is to indent by brace. -indent_paren_open_brace = true # false/true - -# Whether the 'namespace' body is indented -indent_namespace = true # false/true - -# Only indent one namespace and no sub-namepaces. -# Requires indent_namespace=true. -indent_namespace_single_indent = false # false/true - -# The number of spaces to indent a namespace block -indent_namespace_level = 0 # number - -# If the body of the namespace is longer than this number, it won't be indented. -# Requires indent_namespace=true. Default=0 (no limit) -indent_namespace_limit = 0 # number - -# Whether the 'extern "C"' body is indented -indent_extern = false # false/true - -# Whether the 'class' body is indented -indent_class = false # false/true - -# Whether to indent the stuff after a leading base class colon -indent_class_colon = false # false/true - -# Whether to indent the stuff after a leading class initializer colon -indent_constr_colon = false # false/true - -# Virtual indent from the ':' for member initializers. Default is 2 -indent_ctor_init_leading = 2 # number - -# Additional indenting for constructor initializer list -indent_ctor_init = 0 # number - -# False=treat 'else\nif' as 'else if' for indenting purposes -# True=indent the 'if' one level -indent_else_if = false # false/true - -# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute -indent_var_def_blk = 0 # number - -# Indent continued variable declarations instead of aligning. -indent_var_def_cont = false # false/true - -# True: force indentation of function definition to start in column 1 -# False: use the default behavior -indent_func_def_force_col1 = false # false/true - -# True: indent continued function call parameters one indent level -# False: align parameters under the open paren -indent_func_call_param = true # false/true - -# Same as indent_func_call_param, but for function defs -indent_func_def_param = false # false/true - -# Same as indent_func_call_param, but for function protos -indent_func_proto_param = false # false/true - -# Same as indent_func_call_param, but for class declarations -indent_func_class_param = false # false/true - -# Same as indent_func_call_param, but for class variable constructors -indent_func_ctor_var_param = false # false/true - -# Same as indent_func_call_param, but for templates -indent_template_param = false # false/true - -# Double the indent for indent_func_xxx_param options -indent_func_param_double = false # false/true - -# Indentation column for standalone 'const' function decl/proto qualifier -indent_func_const = 0 # number - -# Indentation column for standalone 'throw' function decl/proto qualifier -indent_func_throw = 0 # number - -# The number of spaces to indent a continued '->' or '.' -# Usually set to 0, 1, or indent_columns. -indent_member = 0 # number - -# Spaces to indent single line ('//') comments on lines before code -indent_sing_line_comments = 0 # number - -# If set, will indent trailing single line ('//') comments relative -# to the code instead of trying to keep the same absolute column -indent_relative_single_line_comments = false # false/true - -# Spaces to indent 'case' from 'switch' -# Usually 0 or indent_columns. -indent_switch_case = 0 # number - -# Spaces to shift the 'case' line, without affecting any other lines -# Usually 0. -indent_case_shift = 0 # number - -# Spaces to indent '{' from 'case'. -# By default, the brace will appear under the 'c' in case. -# Usually set to 0 or indent_columns. -indent_case_brace = 0 # number - -# Whether to indent comments found in first column -indent_col1_comment = false # false/true - -# How to indent goto labels -# >0 : absolute column where 1 is the leftmost column -# <=0 : subtract from brace indent -indent_label = 1 # number - -# Same as indent_label, but for access specifiers that are followed by a colon -indent_access_spec = 1 # number - -# Indent the code after an access specifier by one level. -# If set, this option forces 'indent_access_spec=0' -indent_access_spec_body = false # false/true - -# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) -indent_paren_nl = false # false/true - -# Controls the indent of a close paren after a newline. -# 0: Indent to body level -# 1: Align under the open paren -# 2: Indent to the brace level -indent_paren_close = 2 # number - -# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren -indent_comma_paren = false # false/true - -# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren -indent_bool_paren = false # false/true - -# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones -indent_first_bool_expr = false # false/true - -# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) -indent_square_nl = false # false/true - -# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies -indent_preserve_sql = false # false/true - -# Align continued statements at the '='. Default=True -# If FALSE or the '=' is followed by a newline, the next line is indent one tab. -indent_align_assign = true # false/true - -# Indent OC blocks at brace level instead of usual rules. -indent_oc_block = false # false/true - -# Indent OC blocks in a message relative to the parameter name. -# 0=use indent_oc_block rules, 1+=spaces to indent -indent_oc_block_msg = 0 # number - -# Minimum indent for subsequent parameters -indent_oc_msg_colon = 0 # number - -# If true, prioritize aligning with initial colon (and stripping spaces from lines, if necessary). -# Default is true. -indent_oc_msg_prioritize_first_colon = true # false/true - -# If indent_oc_block_msg and this option are on, blocks will be indented the way that Xcode does by default (from keyword if the parameter is on its own line; otherwise, from the previous indentation level). -indent_oc_block_msg_xcode_style = false # false/true - -# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg keyword. -indent_oc_block_msg_from_keyword = false # false/true - -# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg colon. -indent_oc_block_msg_from_colon = false # false/true - -# If indent_oc_block_msg and this option are on, blocks will be indented from where the block caret is. -indent_oc_block_msg_from_caret = false # false/true - -# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is. -indent_oc_block_msg_from_brace = false # false/true - -# -# Spacing options -# - -# Add or remove space around arithmetic operator '+', '-', '/', '*', etc -sp_arith = ignore # ignore/add/remove/force - -# Add or remove space around assignment operator '=', '+=', etc -sp_assign = ignore # ignore/add/remove/force - -# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign -sp_cpp_lambda_assign = remove # ignore/add/remove/force - -# Add or remove space after the capture specification in C++11 lambda. -sp_cpp_lambda_paren = add # ignore/add/remove/force - -# Add or remove space around assignment operator '=' in a prototype -sp_assign_default = ignore # ignore/add/remove/force - -# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. -sp_before_assign = ignore # ignore/add/remove/force - -# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. -sp_after_assign = ignore # ignore/add/remove/force - -# Add or remove space in 'NS_ENUM (' -sp_enum_paren = ignore # ignore/add/remove/force - -# Add or remove space around assignment '=' in enum -sp_enum_assign = ignore # ignore/add/remove/force - -# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. -sp_enum_before_assign = ignore # ignore/add/remove/force - -# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. -sp_enum_after_assign = ignore # ignore/add/remove/force - -# Add or remove space around preprocessor '##' concatenation operator. Default=Add -sp_pp_concat = add # ignore/add/remove/force - -# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. -sp_pp_stringify = ignore # ignore/add/remove/force - -# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. -sp_before_pp_stringify = ignore # ignore/add/remove/force - -# Add or remove space around boolean operators '&&' and '||' -sp_bool = ignore # ignore/add/remove/force - -# Add or remove space around compare operator '<', '>', '==', etc -sp_compare = ignore # ignore/add/remove/force - -# Add or remove space inside '(' and ')' -sp_inside_paren = ignore # ignore/add/remove/force - -# Add or remove space between nested parens: '((' vs ') )' -sp_paren_paren = ignore # ignore/add/remove/force - -# Add or remove space between back-to-back parens: ')(' vs ') (' -sp_cparen_oparen = ignore # ignore/add/remove/force - -# Whether to balance spaces inside nested parens -sp_balance_nested_parens = false # false/true - -# Add or remove space between ')' and '{' -sp_paren_brace = add # ignore/add/remove/force - -# Add or remove space before pointer star '*' -sp_before_ptr_star = ignore # ignore/add/remove/force - -# Add or remove space before pointer star '*' that isn't followed by a variable name -# If set to 'ignore', sp_before_ptr_star is used instead. -sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force - -# Add or remove space between pointer stars '*' -sp_between_ptr_star = ignore # ignore/add/remove/force - -# Add or remove space after pointer star '*', if followed by a word. -sp_after_ptr_star = ignore # ignore/add/remove/force - -# Add or remove space after pointer star '*', if followed by a qualifier. -sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force - -# Add or remove space after a pointer star '*', if followed by a func proto/def. -sp_after_ptr_star_func = ignore # ignore/add/remove/force - -# Add or remove space after a pointer star '*', if followed by an open paren (function types). -sp_ptr_star_paren = ignore # ignore/add/remove/force - -# Add or remove space before a pointer star '*', if followed by a func proto/def. -sp_before_ptr_star_func = ignore # ignore/add/remove/force - -# Add or remove space before a reference sign '&' -sp_before_byref = ignore # ignore/add/remove/force - -# Add or remove space before a reference sign '&' that isn't followed by a variable name -# If set to 'ignore', sp_before_byref is used instead. -sp_before_unnamed_byref = ignore # ignore/add/remove/force - -# Add or remove space after reference sign '&', if followed by a word. -sp_after_byref = ignore # ignore/add/remove/force - -# Add or remove space after a reference sign '&', if followed by a func proto/def. -sp_after_byref_func = ignore # ignore/add/remove/force - -# Add or remove space before a reference sign '&', if followed by a func proto/def. -sp_before_byref_func = ignore # ignore/add/remove/force - -# Add or remove space between type and word. Default=Force -sp_after_type = force # ignore/add/remove/force - -# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. -sp_before_template_paren = ignore # ignore/add/remove/force - -# Add or remove space in 'template <' vs 'template<'. -# If set to ignore, sp_before_angle is used. -sp_template_angle = ignore # ignore/add/remove/force - -# Add or remove space before '<>' -sp_before_angle = ignore # ignore/add/remove/force - -# Add or remove space inside '<' and '>' -sp_inside_angle = ignore # ignore/add/remove/force - -# Add or remove space after '<>' -sp_after_angle = ignore # ignore/add/remove/force - -# Add or remove space between '<>' and '(' as found in 'new List();' -sp_angle_paren = ignore # ignore/add/remove/force - -# Add or remove space between '<>' and a word as in 'List m;' -sp_angle_word = ignore # ignore/add/remove/force - -# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add -sp_angle_shift = remove # ignore/add/remove/force - -# Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False -# sp_angle_shift cannot remove the space without this option. -sp_permit_cpp11_shift = true # false/true - -# Add or remove space before '(' of 'if', 'for', 'switch', and 'while' -sp_before_sparen = add # ignore/add/remove/force - -# Add or remove space inside if-condition '(' and ')' -sp_inside_sparen = remove # ignore/add/remove/force - -# Add or remove space before if-condition ')'. Overrides sp_inside_sparen. -sp_inside_sparen_close = ignore # ignore/add/remove/force - -# Add or remove space before if-condition '('. Overrides sp_inside_sparen. -sp_inside_sparen_open = ignore # ignore/add/remove/force - -# Add or remove space after ')' of 'if', 'for', 'switch', and 'while' -sp_after_sparen = ignore # ignore/add/remove/force - -# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' -sp_sparen_brace = add # ignore/add/remove/force - -# Add or remove space between 'invariant' and '(' in the D language. -sp_invariant_paren = ignore # ignore/add/remove/force - -# Add or remove space after the ')' in 'invariant (C) c' in the D language. -sp_after_invariant_paren = ignore # ignore/add/remove/force - -# Add or remove space before empty statement ';' on 'if', 'for' and 'while' -sp_special_semi = ignore # ignore/add/remove/force - -# Add or remove space before ';'. Default=Remove -sp_before_semi = remove # ignore/add/remove/force - -# Add or remove space before ';' in non-empty 'for' statements -sp_before_semi_for = ignore # ignore/add/remove/force - -# Add or remove space before a semicolon of an empty part of a for statement. -sp_before_semi_for_empty = ignore # ignore/add/remove/force - -# Add or remove space after ';', except when followed by a comment. Default=Add -sp_after_semi = add # ignore/add/remove/force - -# Add or remove space after ';' in non-empty 'for' statements. Default=Force -sp_after_semi_for = force # ignore/add/remove/force - -# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). -sp_after_semi_for_empty = ignore # ignore/add/remove/force - -# Add or remove space before '[' (except '[]') -sp_before_square = ignore # ignore/add/remove/force - -# Add or remove space before '[]' -sp_before_squares = ignore # ignore/add/remove/force - -# Add or remove space inside a non-empty '[' and ']' -sp_inside_square = ignore # ignore/add/remove/force - -# Add or remove space after ',' -sp_after_comma = ignore # ignore/add/remove/force - -# Add or remove space before ',' -sp_before_comma = remove # ignore/add/remove/force - -# Add or remove space between an open paren and comma: '(,' vs '( ,' -sp_paren_comma = force # ignore/add/remove/force - -# Add or remove space before the variadic '...' when preceded by a non-punctuator -sp_before_ellipsis = ignore # ignore/add/remove/force - -# Add or remove space after class ':' -sp_after_class_colon = ignore # ignore/add/remove/force - -# Add or remove space before class ':' -sp_before_class_colon = ignore # ignore/add/remove/force - -# Add or remove space after class constructor ':' -sp_after_constr_colon = ignore # ignore/add/remove/force - -# Add or remove space before class constructor ':' -sp_before_constr_colon = ignore # ignore/add/remove/force - -# Add or remove space before case ':'. Default=Remove -sp_before_case_colon = remove # ignore/add/remove/force - -# Add or remove space between 'operator' and operator sign -sp_after_operator = ignore # ignore/add/remove/force - -# Add or remove space between the operator symbol and the open paren, as in 'operator ++(' -sp_after_operator_sym = ignore # ignore/add/remove/force - -# Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' -sp_after_cast = ignore # ignore/add/remove/force - -# Add or remove spaces inside cast parens -sp_inside_paren_cast = ignore # ignore/add/remove/force - -# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' -sp_cpp_cast_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'sizeof' and '(' -sp_sizeof_paren = ignore # ignore/add/remove/force - -# Add or remove space after the tag keyword (Pawn) -sp_after_tag = ignore # ignore/add/remove/force - -# Add or remove space inside enum '{' and '}' -sp_inside_braces_enum = add # ignore/add/remove/force - -# Add or remove space inside struct/union '{' and '}' -sp_inside_braces_struct = add # ignore/add/remove/force - -# Add or remove space inside '{' and '}' -sp_inside_braces = add # ignore/add/remove/force - -# Add or remove space inside '{}' -sp_inside_braces_empty = add # ignore/add/remove/force - -# Add or remove space between return type and function name -# A minimum of 1 is forced except for pointer return types. -sp_type_func = ignore # ignore/add/remove/force - -# Add or remove space between function name and '(' on function declaration -sp_func_proto_paren = ignore # ignore/add/remove/force - -# Add or remove space between function name and '(' on function definition -sp_func_def_paren = ignore # ignore/add/remove/force - -# Add or remove space inside empty function '()' -sp_inside_fparens = ignore # ignore/add/remove/force - -# Add or remove space inside function '(' and ')' -sp_inside_fparen = ignore # ignore/add/remove/force - -# Add or remove space inside the first parens in the function type: 'void (*x)(...)' -sp_inside_tparen = ignore # ignore/add/remove/force - -# Add or remove between the parens in the function type: 'void (*x)(...)' -sp_after_tparen_close = ignore # ignore/add/remove/force - -# Add or remove space between ']' and '(' when part of a function call. -sp_square_fparen = ignore # ignore/add/remove/force - -# Add or remove space between ')' and '{' of function -sp_fparen_brace = add # ignore/add/remove/force - -# Java: Add or remove space between ')' and '{{' of double brace initializer. -sp_fparen_dbrace = add # ignore/add/remove/force - -# Add or remove space between function name and '(' on function calls -sp_func_call_paren = ignore # ignore/add/remove/force - -# Add or remove space between function name and '()' on function calls without parameters. -# If set to 'ignore' (the default), sp_func_call_paren is used. -sp_func_call_paren_empty = ignore # ignore/add/remove/force - -# Add or remove space between the user function name and '(' on function calls -# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. -sp_func_call_user_paren = ignore # ignore/add/remove/force - -# Add or remove space between a constructor/destructor and the open paren -sp_func_class_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'return' and '(' -sp_return_paren = ignore # ignore/add/remove/force - -# Add or remove space between '__attribute__' and '(' -sp_attribute_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'defined' and '(' in '#if defined (FOO)' -sp_defined_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'throw' and '(' in 'throw (something)' -sp_throw_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' -sp_after_throw = ignore # ignore/add/remove/force - -# Add or remove space between 'catch' and '(' in 'catch (something) { }' -# If set to ignore, sp_before_sparen is used. -sp_catch_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'version' and '(' in 'version (something) { }' (D language) -# If set to ignore, sp_before_sparen is used. -sp_version_paren = ignore # ignore/add/remove/force - -# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) -# If set to ignore, sp_before_sparen is used. -sp_scope_paren = ignore # ignore/add/remove/force - -# Add or remove space between macro and value -sp_macro = ignore # ignore/add/remove/force - -# Add or remove space between macro function ')' and value -sp_macro_func = ignore # ignore/add/remove/force - -# Add or remove space between 'else' and '{' if on the same line -sp_else_brace = add # ignore/add/remove/force - -# Add or remove space between '}' and 'else' if on the same line -sp_brace_else = add # ignore/add/remove/force - -# Add or remove space between '}' and the name of a typedef on the same line -sp_brace_typedef = add # ignore/add/remove/force - -# Add or remove space between 'catch' and '{' if on the same line -sp_catch_brace = add # ignore/add/remove/force - -# Add or remove space between '}' and 'catch' if on the same line -sp_brace_catch = add # ignore/add/remove/force - -# Add or remove space between 'finally' and '{' if on the same line -sp_finally_brace = add # ignore/add/remove/force - -# Add or remove space between '}' and 'finally' if on the same line -sp_brace_finally = add # ignore/add/remove/force - -# Add or remove space between 'try' and '{' if on the same line -sp_try_brace = add # ignore/add/remove/force - -# Add or remove space between get/set and '{' if on the same line -sp_getset_brace = add # ignore/add/remove/force - -# Add or remove space between a variable and '{' for C++ uniform initialization -sp_word_brace = add # ignore/add/remove/force - -# Add or remove space between a variable and '{' for a namespace -sp_word_brace_ns = add # ignore/add/remove/force - -# Add or remove space before the '::' operator -sp_before_dc = ignore # ignore/add/remove/force - -# Add or remove space after the '::' operator -sp_after_dc = ignore # ignore/add/remove/force - -# Add or remove around the D named array initializer ':' operator -sp_d_array_colon = ignore # ignore/add/remove/force - -# Add or remove space after the '!' (not) operator. Default=Remove -sp_not = remove # ignore/add/remove/force - -# Add or remove space after the '~' (invert) operator. Default=Remove -sp_inv = remove # ignore/add/remove/force - -# Add or remove space after the '&' (address-of) operator. Default=Remove -# This does not affect the spacing after a '&' that is part of a type. -sp_addr = remove # ignore/add/remove/force - -# Add or remove space around the '.' or '->' operators. Default=Remove -sp_member = remove # ignore/add/remove/force - -# Add or remove space after the '*' (dereference) operator. Default=Remove -# This does not affect the spacing after a '*' that is part of a type. -sp_deref = remove # ignore/add/remove/force - -# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove -sp_sign = remove # ignore/add/remove/force - -# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove -sp_incdec = remove # ignore/add/remove/force - -# Add or remove space before a backslash-newline at the end of a line. Default=Add -sp_before_nl_cont = add # ignore/add/remove/force - -# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' -sp_after_oc_scope = ignore # ignore/add/remove/force - -# Add or remove space after the colon in message specs -# '-(int) f:(int) x;' vs '-(int) f: (int) x;' -sp_after_oc_colon = ignore # ignore/add/remove/force - -# Add or remove space before the colon in message specs -# '-(int) f: (int) x;' vs '-(int) f : (int) x;' -sp_before_oc_colon = ignore # ignore/add/remove/force - -# Add or remove space after the colon in immutable dictionary expression -# 'NSDictionary *test = @{@"foo" :@"bar"};' -sp_after_oc_dict_colon = ignore # ignore/add/remove/force - -# Add or remove space before the colon in immutable dictionary expression -# 'NSDictionary *test = @{@"foo" :@"bar"};' -sp_before_oc_dict_colon = ignore # ignore/add/remove/force - -# Add or remove space after the colon in message specs -# '[object setValue:1];' vs '[object setValue: 1];' -sp_after_send_oc_colon = ignore # ignore/add/remove/force - -# Add or remove space before the colon in message specs -# '[object setValue:1];' vs '[object setValue :1];' -sp_before_send_oc_colon = ignore # ignore/add/remove/force - -# Add or remove space after the (type) in message specs -# '-(int)f: (int) x;' vs '-(int)f: (int)x;' -sp_after_oc_type = ignore # ignore/add/remove/force - -# Add or remove space after the first (type) in message specs -# '-(int) f:(int)x;' vs '-(int)f:(int)x;' -sp_after_oc_return_type = ignore # ignore/add/remove/force - -# Add or remove space between '@selector' and '(' -# '@selector(msgName)' vs '@selector (msgName)' -# Also applies to @protocol() constructs -sp_after_oc_at_sel = ignore # ignore/add/remove/force - -# Add or remove space between '@selector(x)' and the following word -# '@selector(foo) a:' vs '@selector(foo)a:' -sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force - -# Add or remove space inside '@selector' parens -# '@selector(foo)' vs '@selector( foo )' -# Also applies to @protocol() constructs -sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force - -# Add or remove space before a block pointer caret -# '^int (int arg){...}' vs. ' ^int (int arg){...}' -sp_before_oc_block_caret = ignore # ignore/add/remove/force - -# Add or remove space after a block pointer caret -# '^int (int arg){...}' vs. '^ int (int arg){...}' -sp_after_oc_block_caret = ignore # ignore/add/remove/force - -# Add or remove space between the receiver and selector in a message. -# '[receiver selector ...]' -sp_after_oc_msg_receiver = ignore # ignore/add/remove/force - -# Add or remove space after @property. -sp_after_oc_property = ignore # ignore/add/remove/force - -# Add or remove space around the ':' in 'b ? t : f' -sp_cond_colon = ignore # ignore/add/remove/force - -# Add or remove space before the ':' in 'b ? t : f'. Overrides sp_cond_colon. -sp_cond_colon_before = ignore # ignore/add/remove/force - -# Add or remove space after the ':' in 'b ? t : f'. Overrides sp_cond_colon. -sp_cond_colon_after = ignore # ignore/add/remove/force - -# Add or remove space around the '?' in 'b ? t : f' -sp_cond_question = ignore # ignore/add/remove/force - -# Add or remove space before the '?' in 'b ? t : f'. Overrides sp_cond_question. -sp_cond_question_before = ignore # ignore/add/remove/force - -# Add or remove space after the '?' in 'b ? t : f'. Overrides sp_cond_question. -sp_cond_question_after = ignore # ignore/add/remove/force - -# In the abbreviated ternary form (a ?: b), add/remove space between ? and :.'. Overrides all other sp_cond_* options. -sp_cond_ternary_short = ignore # ignore/add/remove/force - -# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. -sp_case_label = ignore # ignore/add/remove/force - -# Control the space around the D '..' operator. -sp_range = ignore # ignore/add/remove/force - -# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) -sp_after_for_colon = ignore # ignore/add/remove/force - -# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) -sp_before_for_colon = ignore # ignore/add/remove/force - -# Control the spacing in 'extern (C)' (D) -sp_extern_paren = ignore # ignore/add/remove/force - -# Control the space after the opening of a C++ comment '// A' vs '//A' -sp_cmt_cpp_start = ignore # ignore/add/remove/force - -# Controls the spaces between #else or #endif and a trailing comment -sp_endif_cmt = ignore # ignore/add/remove/force - -# Controls the spaces after 'new', 'delete', and 'delete[]' -sp_after_new = ignore # ignore/add/remove/force - -# Controls the spaces before a trailing or embedded comment -sp_before_tr_emb_cmt = ignore # ignore/add/remove/force - -# Number of spaces before a trailing or embedded comment -sp_num_before_tr_emb_cmt = 0 # number - -# Control space between a Java annotation and the open paren. -sp_annotation_paren = ignore # ignore/add/remove/force - -# -# Code alignment (not left column spaces/tabs) -# - -# Whether to keep non-indenting tabs -align_keep_tabs = false # false/true - -# Whether to use tabs for aligning -align_with_tabs = false # false/true - -# Whether to bump out to the next tab when aligning -align_on_tabstop = false # false/true - -# Whether to left-align numbers -align_number_left = false # false/true - -# Whether to keep whitespace not required for alignment. -align_keep_extra_space = false # false/true - -# Align variable definitions in prototypes and functions -align_func_params = false # false/true - -# Align parameters in single-line functions that have the same name. -# The function names must already be aligned with each other. -align_same_func_call_params = false # false/true - -# The span for aligning variable definitions (0=don't align) -align_var_def_span = 0 # number - -# How to align the star in variable definitions. -# 0=Part of the type 'void * foo;' -# 1=Part of the variable 'void *foo;' -# 2=Dangling 'void *foo;' -align_var_def_star_style = 0 # number - -# How to align the '&' in variable definitions. -# 0=Part of the type -# 1=Part of the variable -# 2=Dangling -align_var_def_amp_style = 0 # number - -# The threshold for aligning variable definitions (0=no limit) -align_var_def_thresh = 0 # number - -# The gap for aligning variable definitions -align_var_def_gap = 0 # number - -# Whether to align the colon in struct bit fields -align_var_def_colon = false # false/true - -# Whether to align any attribute after the variable name -align_var_def_attribute = false # false/true - -# Whether to align inline struct/enum/union variable definitions -align_var_def_inline = false # false/true - -# The span for aligning on '=' in assignments (0=don't align) -align_assign_span = 0 # number - -# The threshold for aligning on '=' in assignments (0=no limit) -align_assign_thresh = 0 # number - -# The span for aligning on '=' in enums (0=don't align) -align_enum_equ_span = 0 # number - -# The threshold for aligning on '=' in enums (0=no limit) -align_enum_equ_thresh = 0 # number - -# The span for aligning struct/union (0=don't align) -align_var_struct_span = 0 # number - -# The threshold for aligning struct/union member definitions (0=no limit) -align_var_struct_thresh = 0 # number - -# The gap for aligning struct/union member definitions -align_var_struct_gap = 0 # number - -# The span for aligning struct initializer values (0=don't align) -align_struct_init_span = 0 # number - -# The minimum space between the type and the synonym of a typedef -align_typedef_gap = 0 # number - -# The span for aligning single-line typedefs (0=don't align) -align_typedef_span = 0 # number - -# How to align typedef'd functions with other typedefs -# 0: Don't mix them at all -# 1: align the open paren with the types -# 2: align the function type name with the other type names -align_typedef_func = 0 # number - -# Controls the positioning of the '*' in typedefs. Just try it. -# 0: Align on typedef type, ignore '*' -# 1: The '*' is part of type name: typedef int *pint; -# 2: The '*' is part of the type, but dangling: typedef int *pint; -align_typedef_star_style = 0 # number - -# Controls the positioning of the '&' in typedefs. Just try it. -# 0: Align on typedef type, ignore '&' -# 1: The '&' is part of type name: typedef int &pint; -# 2: The '&' is part of the type, but dangling: typedef int &pint; -align_typedef_amp_style = 0 # number - -# The span for aligning comments that end lines (0=don't align) -align_right_cmt_span = 0 # number - -# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment -align_right_cmt_mix = false # false/true - -# If a trailing comment is more than this number of columns away from the text it follows, -# it will qualify for being aligned. This has to be > 0 to do anything. -align_right_cmt_gap = 0 # number - -# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) -align_right_cmt_at_col = 0 # number - -# The span for aligning function prototypes (0=don't align) -align_func_proto_span = 0 # number - -# Minimum gap between the return type and the function name. -align_func_proto_gap = 0 # number - -# Align function protos on the 'operator' keyword instead of what follows -align_on_operator = false # false/true - -# Whether to mix aligning prototype and variable declarations. -# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. -align_mix_var_proto = false # false/true - -# Align single-line functions with function prototypes, uses align_func_proto_span -align_single_line_func = false # false/true - -# Aligning the open brace of single-line functions. -# Requires align_single_line_func=true, uses align_func_proto_span -align_single_line_brace = false # false/true - -# Gap for align_single_line_brace. -align_single_line_brace_gap = 0 # number - -# The span for aligning ObjC msg spec (0=don't align) -align_oc_msg_spec_span = 0 # number - -# Whether to align macros wrapped with a backslash and a newline. -# This will not work right if the macro contains a multi-line comment. -align_nl_cont = false # false/true - -# # Align macro functions and variables together -align_pp_define_together = false # false/true - -# The minimum space between label and value of a preprocessor define -align_pp_define_gap = 0 # number - -# The span for aligning on '#define' bodies (0=don't align, other=number of lines including comments between blocks) -align_pp_define_span = 0 # number - -# Align lines that start with '<<' with previous '<<'. Default=true -align_left_shift = true # false/true - -# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) -align_oc_msg_colon_span = 0 # number - -# If true, always align with the first parameter, even if it is too short. -align_oc_msg_colon_first = false # false/true - -# Aligning parameters in an Obj-C '+' or '-' declaration on the ':' -align_oc_decl_colon = false # false/true - -# -# Newline adding and removing options -# - -# Whether to collapse empty blocks between '{' and '}' -nl_collapse_empty_body = false # false/true - -# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' -nl_assign_leave_one_liners = false # false/true - -# Don't split one-line braced statements inside a class xx { } body -nl_class_leave_one_liners = false # false/true - -# Don't split one-line enums: 'enum foo { BAR = 15 };' -nl_enum_leave_one_liners = false # false/true - -# Don't split one-line get or set functions -nl_getset_leave_one_liners = false # false/true - -# Don't split one-line function definitions - 'int foo() { return 0; }' -nl_func_leave_one_liners = false # false/true - -# Don't split one-line C++11 lambdas - '[]() { return 0; }' -nl_cpp_lambda_leave_one_liners = true # false/true - -# Don't split one-line if/else statements - 'if(a) b++;' -nl_if_leave_one_liners = false # false/true - -# Don't split one-line OC messages -nl_oc_msg_leave_one_liner = false # false/true - -# Add or remove newlines at the start of the file -nl_start_of_file = ignore # ignore/add/remove/force - -# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' -nl_start_of_file_min = 0 # number - -# Add or remove newline at the end of the file -nl_end_of_file = ignore # ignore/add/remove/force - -# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') -nl_end_of_file_min = 0 # number - -# Add or remove newline between '=' and '{' -nl_assign_brace = ignore # ignore/add/remove/force - -# Add or remove newline between '=' and '[' (D only) -nl_assign_square = ignore # ignore/add/remove/force - -# Add or remove newline after '= [' (D only). Will also affect the newline before the ']' -nl_after_square_assign = ignore # ignore/add/remove/force - -# The number of blank lines after a block of variable definitions at the top of a function body -# 0 = No change (default) -nl_func_var_def_blk = 0 # number - -# The number of newlines before a block of typedefs -# 0 = No change (default) -nl_typedef_blk_start = 0 # number - -# The number of newlines after a block of typedefs -# 0 = No change (default) -nl_typedef_blk_end = 0 # number - -# The maximum consecutive newlines within a block of typedefs -# 0 = No change (default) -nl_typedef_blk_in = 0 # number - -# The number of newlines before a block of variable definitions not at the top of a function body -# 0 = No change (default) -nl_var_def_blk_start = 0 # number - -# The number of newlines after a block of variable definitions not at the top of a function body -# 0 = No change (default) -nl_var_def_blk_end = 0 # number - -# The maximum consecutive newlines within a block of variable definitions -# 0 = No change (default) -nl_var_def_blk_in = 0 # number - -# Add or remove newline between a function call's ')' and '{', as in: -# list_for_each(item, &list) { } -nl_fcall_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'enum' and '{' -nl_enum_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'struct and '{' -nl_struct_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'union' and '{' -nl_union_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'if' and '{' -nl_if_brace = ignore # ignore/add/remove/force - -# Add or remove newline between '}' and 'else' -nl_brace_else = ignore # ignore/add/remove/force - -# Add or remove newline between 'else if' and '{' -# If set to ignore, nl_if_brace is used instead -nl_elseif_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'else' and '{' -nl_else_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'else' and 'if' -nl_else_if = ignore # ignore/add/remove/force - -# Add or remove newline between '}' and 'finally' -nl_brace_finally = ignore # ignore/add/remove/force - -# Add or remove newline between 'finally' and '{' -nl_finally_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'try' and '{' -nl_try_brace = ignore # ignore/add/remove/force - -# Add or remove newline between get/set and '{' -nl_getset_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'for' and '{' -nl_for_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'catch' and '{' -nl_catch_brace = ignore # ignore/add/remove/force - -# Add or remove newline between '}' and 'catch' -nl_brace_catch = ignore # ignore/add/remove/force - -# Add or remove newline between '}' and ']' -nl_brace_square = ignore # ignore/add/remove/force - -# Add or remove newline between '}' and ')' in a function invocation -nl_brace_fparen = ignore # ignore/add/remove/force - -# Add or remove newline between 'while' and '{' -nl_while_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'scope (x)' and '{' (D) -nl_scope_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'unittest' and '{' (D) -nl_unittest_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'version (x)' and '{' (D) -nl_version_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'using' and '{' -nl_using_brace = ignore # ignore/add/remove/force - -# Add or remove newline between two open or close braces. -# Due to general newline/brace handling, REMOVE may not work. -nl_brace_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'do' and '{' -nl_do_brace = ignore # ignore/add/remove/force - -# Add or remove newline between '}' and 'while' of 'do' statement -nl_brace_while = ignore # ignore/add/remove/force - -# Add or remove newline between 'switch' and '{' -nl_switch_brace = ignore # ignore/add/remove/force - -# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. -# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. -nl_multi_line_cond = false # false/true - -# Force a newline in a define after the macro name for multi-line defines. -nl_multi_line_define = false # false/true - -# Whether to put a newline before 'case' statement -nl_before_case = false # false/true - -# Add or remove newline between ')' and 'throw' -nl_before_throw = ignore # ignore/add/remove/force - -# Whether to put a newline after 'case' statement -nl_after_case = false # false/true - -# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. -nl_case_colon_brace = ignore # ignore/add/remove/force - -# Newline between namespace and { -nl_namespace_brace = ignore # ignore/add/remove/force - -# Add or remove newline between 'template<>' and whatever follows. -nl_template_class = ignore # ignore/add/remove/force - -# Add or remove newline between 'class' and '{' -nl_class_brace = ignore # ignore/add/remove/force - -# Add or remove newline after each ',' in the class base list -nl_class_init_args = ignore # ignore/add/remove/force - -# Add or remove newline after each ',' in the constructor member initialization -nl_constr_init_args = ignore # ignore/add/remove/force - -# Add or remove newline between return type and function name in a function definition -nl_func_type_name = ignore # ignore/add/remove/force - -# Add or remove newline between return type and function name inside a class {} -# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. -nl_func_type_name_class = ignore # ignore/add/remove/force - -# Add or remove newline between function scope and name in a definition -# Controls the newline after '::' in 'void A::f() { }' -nl_func_scope_name = ignore # ignore/add/remove/force - -# Add or remove newline between return type and function name in a prototype -nl_func_proto_type_name = ignore # ignore/add/remove/force - -# Add or remove newline between a function name and the opening '(' -nl_func_paren = ignore # ignore/add/remove/force - -# Add or remove newline between a function name and the opening '(' in the definition -nl_func_def_paren = ignore # ignore/add/remove/force - -# Add or remove newline after '(' in a function declaration -nl_func_decl_start = ignore # ignore/add/remove/force - -# Add or remove newline after '(' in a function definition -nl_func_def_start = ignore # ignore/add/remove/force - -# Overrides nl_func_decl_start when there is only one parameter. -nl_func_decl_start_single = ignore # ignore/add/remove/force - -# Overrides nl_func_def_start when there is only one parameter. -nl_func_def_start_single = ignore # ignore/add/remove/force - -# Add or remove newline after each ',' in a function declaration -nl_func_decl_args = ignore # ignore/add/remove/force - -# Add or remove newline after each ',' in a function definition -nl_func_def_args = ignore # ignore/add/remove/force - -# Add or remove newline before the ')' in a function declaration -nl_func_decl_end = ignore # ignore/add/remove/force - -# Add or remove newline before the ')' in a function definition -nl_func_def_end = ignore # ignore/add/remove/force - -# Overrides nl_func_decl_end when there is only one parameter. -nl_func_decl_end_single = ignore # ignore/add/remove/force - -# Overrides nl_func_def_end when there is only one parameter. -nl_func_def_end_single = ignore # ignore/add/remove/force - -# Add or remove newline between '()' in a function declaration. -nl_func_decl_empty = ignore # ignore/add/remove/force - -# Add or remove newline between '()' in a function definition. -nl_func_def_empty = ignore # ignore/add/remove/force - -# Whether to put each OC message parameter on a separate line -# See nl_oc_msg_leave_one_liner -nl_oc_msg_args = false # false/true - -# Add or remove newline between function signature and '{' -nl_fdef_brace = ignore # ignore/add/remove/force - -# Add or remove newline between C++11 lambda signature and '{' -nl_cpp_ldef_brace = add # ignore/add/remove/force - -# Add or remove a newline between the return keyword and return expression. -nl_return_expr = ignore # ignore/add/remove/force - -# Whether to put a newline after semicolons, except in 'for' statements -nl_after_semicolon = false # false/true - -# Java: Control the newline between the ')' and '{{' of the double brace initializer. -nl_paren_dbrace_open = ignore # ignore/add/remove/force - -# Whether to put a newline after brace open. -# This also adds a newline before the matching brace close. -nl_after_brace_open = false # false/true - -# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is -# placed between the open brace and a trailing single-line comment. -nl_after_brace_open_cmt = false # false/true - -# Whether to put a newline after a virtual brace open with a non-empty body. -# These occur in un-braced if/while/do/for statement bodies. -nl_after_vbrace_open = false # false/true - -# Whether to put a newline after a virtual brace open with an empty body. -# These occur in un-braced if/while/do/for statement bodies. -nl_after_vbrace_open_empty = false # false/true - -# Whether to put a newline after a brace close. -# Does not apply if followed by a necessary ';'. -nl_after_brace_close = false # false/true - -# Whether to put a newline after a virtual brace close. -# Would add a newline before return in: 'if (foo) a++; return;' -nl_after_vbrace_close = false # false/true - -# Control the newline between the close brace and 'b' in: 'struct { int a; } b;' -# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close -nl_brace_struct_var = ignore # ignore/add/remove/force - -# Whether to alter newlines in '#define' macros -nl_define_macro = false # false/true - -# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' -nl_squeeze_ifdef = false # false/true - -# Add or remove blank line before 'if' -nl_before_if = ignore # ignore/add/remove/force - -# Add or remove blank line after 'if' statement -nl_after_if = ignore # ignore/add/remove/force - -# Add or remove blank line before 'for' -nl_before_for = ignore # ignore/add/remove/force - -# Add or remove blank line after 'for' statement -nl_after_for = ignore # ignore/add/remove/force - -# Add or remove blank line before 'while' -nl_before_while = ignore # ignore/add/remove/force - -# Add or remove blank line after 'while' statement -nl_after_while = ignore # ignore/add/remove/force - -# Add or remove blank line before 'switch' -nl_before_switch = ignore # ignore/add/remove/force - -# Add or remove blank line after 'switch' statement -nl_after_switch = ignore # ignore/add/remove/force - -# Add or remove blank line before 'do' -nl_before_do = ignore # ignore/add/remove/force - -# Add or remove blank line after 'do/while' statement -nl_after_do = ignore # ignore/add/remove/force - -# Whether to double-space commented-entries in struct/enum -nl_ds_struct_enum_cmt = false # false/true - -# Whether to double-space before the close brace of a struct/union/enum -# (lower priority than 'eat_blanks_before_close_brace') -nl_ds_struct_enum_close_brace = false # false/true - -# Add or remove a newline around a class colon. -# Related to pos_class_colon, nl_class_init_args, and pos_class_comma. -nl_class_colon = ignore # ignore/add/remove/force - -# Add or remove a newline around a class constructor colon. -# Related to pos_constr_colon, nl_constr_init_args, and pos_constr_comma. -nl_constr_colon = ignore # ignore/add/remove/force - -# Change simple unbraced if statements into a one-liner -# 'if(b)\n i++;' => 'if(b) i++;' -nl_create_if_one_liner = false # false/true - -# Change simple unbraced for statements into a one-liner -# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' -nl_create_for_one_liner = false # false/true - -# Change simple unbraced while statements into a one-liner -# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' -nl_create_while_one_liner = false # false/true - -# -# Positioning options -# - -# The position of arithmetic operators in wrapped expressions -pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of assignment in wrapped expressions. -# Do not affect '=' followed by '{' -pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of boolean operators in wrapped expressions -pos_bool = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of comparison operators in wrapped expressions -pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of conditional (b ? t : f) operators in wrapped expressions -pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of the comma in wrapped expressions -pos_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of the comma in the class base list -pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of the comma in the constructor initialization list -pos_constr_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of colons between class and base class list -pos_class_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# The position of colons between constructor and member initialization -pos_constr_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force - -# -# Line Splitting options -# - -# Try to limit code width to N number of columns -code_width = 0 # number - -# Whether to fully split long 'for' statements at semi-colons -ls_for_split_full = false # false/true - -# Whether to fully split long function protos/calls at commas -ls_func_split_full = false # false/true - -# Whether to split lines as close to code_width as possible and ignore some groupings -ls_code_width = false # false/true - -# -# Blank line options -# - -# The maximum consecutive newlines -nl_max = 0 # number - -# The number of newlines after a function prototype, if followed by another function prototype -nl_after_func_proto = 0 # number - -# The number of newlines after a function prototype, if not followed by another function prototype -nl_after_func_proto_group = 0 # number - -# The number of newlines after '}' of a multi-line function body -nl_after_func_body = 0 # number - -# The number of newlines after '}' of a multi-line function body in a class declaration -nl_after_func_body_class = 0 # number - -# The number of newlines after '}' of a single line function body -nl_after_func_body_one_liner = 0 # number - -# The minimum number of newlines before a multi-line comment. -# Doesn't apply if after a brace open or another multi-line comment. -nl_before_block_comment = 0 # number - -# The minimum number of newlines before a single-line C comment. -# Doesn't apply if after a brace open or other single-line C comments. -nl_before_c_comment = 0 # number - -# The minimum number of newlines before a CPP comment. -# Doesn't apply if after a brace open or other CPP comments. -nl_before_cpp_comment = 0 # number - -# Whether to force a newline after a multi-line comment. -nl_after_multiline_comment = false # false/true - -# The number of newlines after '}' or ';' of a struct/enum/union definition -nl_after_struct = 0 # number - -# The number of newlines after '}' or ';' of a class definition -nl_after_class = 0 # number - -# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. -# Will not change the newline count if after a brace open. -# 0 = No change. -nl_before_access_spec = 0 # number - -# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. -# 0 = No change. -nl_after_access_spec = 0 # number - -# The number of newlines between a function def and the function comment. -# 0 = No change. -nl_comment_func_def = 0 # number - -# The number of newlines after a try-catch-finally block that isn't followed by a brace close. -# 0 = No change. -nl_after_try_catch_finally = 0 # number - -# The number of newlines before and after a property, indexer or event decl. -# 0 = No change. -nl_around_cs_property = 0 # number - -# The number of newlines between the get/set/add/remove handlers in C#. -# 0 = No change. -nl_between_get_set = 0 # number - -# Add or remove newline between C# property and the '{' -nl_property_brace = ignore # ignore/add/remove/force - -# Whether to remove blank lines after '{' -eat_blanks_after_open_brace = false # false/true - -# Whether to remove blank lines before '}' -eat_blanks_before_close_brace = false # false/true - -# How aggressively to remove extra newlines not in preproc. -# 0: No change -# 1: Remove most newlines not handled by other config -# 2: Remove all newlines and reformat completely by config -nl_remove_extra_newlines = 0 # number - -# Whether to put a blank line before 'return' statements, unless after an open brace. -nl_before_return = false # false/true - -# Whether to put a blank line after 'return' statements, unless followed by a close brace. -nl_after_return = false # false/true - -# Whether to put a newline after a Java annotation statement. -# Only affects annotations that are after a newline. -nl_after_annotation = ignore # ignore/add/remove/force - -# Controls the newline between two annotations. -nl_between_annotation = ignore # ignore/add/remove/force - -# -# Code modifying options (non-whitespace) -# - -# Add or remove braces on single-line 'do' statement -mod_full_brace_do = ignore # ignore/add/remove/force - -# Add or remove braces on single-line 'for' statement -mod_full_brace_for = ignore # ignore/add/remove/force - -# Add or remove braces on single-line function definitions. (Pawn) -mod_full_brace_function = ignore # ignore/add/remove/force - -# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. -mod_full_brace_if = ignore # ignore/add/remove/force - -# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. -# If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. -mod_full_brace_if_chain = false # false/true - -# Don't remove braces around statements that span N newlines -mod_full_brace_nl = 0 # number - -# Add or remove braces on single-line 'while' statement -mod_full_brace_while = ignore # ignore/add/remove/force - -# Add or remove braces on single-line 'using ()' statement -mod_full_brace_using = ignore # ignore/add/remove/force - -# Add or remove unnecessary paren on 'return' statement -mod_paren_on_return = ignore # ignore/add/remove/force - -# Whether to change optional semicolons to real semicolons -mod_pawn_semicolon = false # false/true - -# Add parens on 'while' and 'if' statement around bools -mod_full_paren_if_bool = false # false/true - -# Whether to remove superfluous semicolons -mod_remove_extra_semicolon = false # false/true - -# If a function body exceeds the specified number of newlines and doesn't have a comment after -# the close brace, a comment will be added. -mod_add_long_function_closebrace_comment = 0 # number - -# If a namespace body exceeds the specified number of newlines and doesn't have a comment after -# the close brace, a comment will be added. -mod_add_long_namespace_closebrace_comment = 0 # number - -# If a switch body exceeds the specified number of newlines and doesn't have a comment after -# the close brace, a comment will be added. -mod_add_long_switch_closebrace_comment = 0 # number - -# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after -# the #endif, a comment will be added. -mod_add_long_ifdef_endif_comment = 0 # number - -# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after -# the #else, a comment will be added. -mod_add_long_ifdef_else_comment = 0 # number - -# If TRUE, will sort consecutive single-line 'import' statements [Java, D] -mod_sort_import = false # false/true - -# If TRUE, will sort consecutive single-line 'using' statements [C#] -mod_sort_using = false # false/true - -# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] -# This is generally a bad idea, as it may break your code. -mod_sort_include = false # false/true - -# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. -mod_move_case_break = false # false/true - -# Will add or remove the braces around a fully braced case statement. -# Will only remove the braces if there are no variable declarations in the block. -mod_case_brace = ignore # ignore/add/remove/force - -# If TRUE, it will remove a void 'return;' that appears as the last statement in a function. -mod_remove_empty_return = false # false/true - -# -# Comment modifications -# - -# Try to wrap comments at cmt_width columns -cmt_width = 0 # number - -# Set the comment reflow mode (default: 0) -# 0: no reflowing (apart from the line wrapping due to cmt_width) -# 1: no touching at all -# 2: full reflow -cmt_reflow_mode = 0 # number - -# Whether to convert all tabs to spaces in comments. Default is to leave tabs inside comments alone, unless used for indenting. -cmt_convert_tab_to_spaces = false # false/true - -# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. -# Default is true. -cmt_indent_multi = true # false/true - -# Whether to group c-comments that look like they are in a block -cmt_c_group = false # false/true - -# Whether to put an empty '/*' on the first line of the combined c-comment -cmt_c_nl_start = false # false/true - -# Whether to put a newline before the closing '*/' of the combined c-comment -cmt_c_nl_end = false # false/true - -# Whether to group cpp-comments that look like they are in a block -cmt_cpp_group = false # false/true - -# Whether to put an empty '/*' on the first line of the combined cpp-comment -cmt_cpp_nl_start = false # false/true - -# Whether to put a newline before the closing '*/' of the combined cpp-comment -cmt_cpp_nl_end = false # false/true - -# Whether to change cpp-comments into c-comments -cmt_cpp_to_c = false # false/true - -# Whether to put a star on subsequent comment lines -cmt_star_cont = false # false/true - -# The number of spaces to insert at the start of subsequent comment lines -cmt_sp_before_star_cont = 0 # number - -# The number of spaces to insert after the star on subsequent comment lines -cmt_sp_after_star_cont = 0 # number - -# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of -# the comment are the same length. Default=True -cmt_multi_check_last = true # false/true - -# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. -# Will substitute $(filename) with the current file's name. -cmt_insert_file_header = "" # string - -# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. -# Will substitute $(filename) with the current file's name. -cmt_insert_file_footer = "" # string - -# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. -# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. -# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } -cmt_insert_func_header = "" # string - -# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. -# Will substitute $(class) with the class name. -cmt_insert_class_header = "" # string - -# The filename that contains text to insert before a Obj-C message specification if the method isn't preceded with a C/C++ comment. -# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. -cmt_insert_oc_msg_header = "" # string - -# If a preprocessor is encountered when stepping backwards from a function name, then -# this option decides whether the comment should be inserted. -# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. -cmt_insert_before_preproc = false # false/true - -# -# Preprocessor options -# - -# Control indent of preprocessors inside #if blocks at brace level 0 (file-level) -pp_indent = ignore # ignore/add/remove/force - -# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) -pp_indent_at_level = false # false/true - -# Specifies the number of columns to indent preprocessors per level at brace level 0 (file-level). -# If pp_indent_at_level=false, specifies the number of columns to indent preprocessors per level at brace level > 0 (function-level). -# Default=1. -pp_indent_count = 1 # number - -# Add or remove space after # based on pp_level of #if blocks -pp_space = ignore # ignore/add/remove/force - -# Sets the number of spaces added with pp_space -pp_space_count = 0 # number - -# The indent for #region and #endregion in C# and '#pragma region' in C/C++ -pp_indent_region = 0 # number - -# Whether to indent the code between #region and #endregion -pp_region_indent_code = false # false/true - -# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level. -# 0: indent preprocessors using output_tab_size. -# >0: column at which all preprocessors will be indented. -pp_indent_if = 0 # number - -# Control whether to indent the code between #if, #else and #endif. -pp_if_indent_code = false # false/true - -# Whether to indent '#define' at the brace level (true) or from column 1 (false) -pp_define_at_level = false # false/true - -# You can force a token to be a type with the 'type' option. -# Example: -# type myfoo1 myfoo2 -# -# You can create custom macro-based indentation using macro-open, -# macro-else and macro-close. -# Example: -# macro-open BEGIN_TEMPLATE_MESSAGE_MAP -# macro-open BEGIN_MESSAGE_MAP -# macro-close END_MESSAGE_MAP -# -# You can assign any keyword to any type with the set option. -# set func_call_user _ N_ -# -# The full syntax description of all custom definition config entries -# is shown below: -# -# define custom tokens as: -# - embed whitespace in token using '' escape character, or -# put token in quotes -# - these: ' " and ` are recognized as quote delimiters -# -# type token1 token2 token3 ... -# ^ optionally specify multiple tokens on a single line -# define def_token output_token -# ^ output_token is optional, then NULL is assumed -# macro-open token -# macro-close token -# macro-else token -# set id token1 token2 ... -# ^ optionally specify multiple tokens on a single line -# ^ id is one of the names in token_enum.h sans the CT_ prefix, -# e.g. PP_PRAGMA -# -# all tokens are separated by any mix of ',' commas, '=' equal signs -# and whitespace (space, tab) -# -# You can add support for other file extensions using the 'file_ext' command. -# The first arg is the language name used with the '-l' option. -# The remaining args are file extensions, matched with 'endswith'. -# file_ext CPP .ch .cxx .cpp.in -# diff --git a/cpp/src/util/instance_manager.h b/cpp/src/util/instance_manager.h index ceaa0a906..fc20da96a 100644 --- a/cpp/src/util/instance_manager.h +++ b/cpp/src/util/instance_manager.h @@ -10,7 +10,6 @@ #include "util/exceptions.h" #include "util/unused.h" - /** * The base instance_manager class. Contains functions to add, destroy, and * finalise pairs of objects. It manages an @ref Object and an @ref Events @@ -27,49 +26,43 @@ * * The @ref finalize() function may not be called before @ref kill(). */ -template -class instance_manager -{ -protected: +template +class instance_manager { + protected: typedef typename ObjectP::element_type Object; typedef typename EventsP::element_type Events; -private: + private: /** * Holds an object and an events pointer. The choice of putting this into the * instance_manager instead of just managing a single object was that this way, * the client code will never need to see unique_ptrs, and can simply operate * on object pointers and events references. */ - struct instance_pointers - { + struct instance_pointers { ObjectP object_p; EventsP events_p; - explicit operator bool () const - { + explicit operator bool() const { // These come and go hand in hand. - assert (!object_p == !events_p); + assert(!object_p == !events_p); return object_p != nullptr; } - /** * Contains the actual pointer/reference to the instance_pointers data. * This is used so we can unlock the instance_manager after getting the * instance and before calling the function in with_instance, which may take * a long time and would unnecessarily block the manager during its execution. */ - struct locked - { + struct locked { Object *object; Events *events; std::unique_lock lock; - explicit operator bool () const - { + explicit operator bool() const { // These come and go hand in hand. - assert (!object == !events); + assert(!object == !events); return object != nullptr; } }; @@ -79,13 +72,11 @@ class instance_manager * pointers or references into the @ref instance_pointers object, which may be * moved when its containing @ref instances vector reallocates. */ - locked - get (std::unique_lock lock) const - { + locked get(std::unique_lock lock) const { return { - object_p.get (), - events_p.get (), - std::move (lock), + object_p.get(), + events_p.get(), + std::move(lock), }; } }; @@ -103,7 +94,6 @@ class instance_manager // The global lock for the instance manager. std::mutex mutex; - /** * Check whether an instance number is currently valid within this instance manager. * @@ -124,133 +114,110 @@ class instance_manager * * @return Whether or not to proceed with the instance number. */ - bool - check_instance_number (JNIEnv *env, jint instanceNumber, bool allow_zero, - std::lock_guard const &lock) - { - unused (lock); - - if (instanceNumber < 0) - { - throw_illegal_state_exception (env, instanceNumber, - "instance number out of range" - ); - return false; - } + bool check_instance_number(JNIEnv *env, jint instanceNumber, bool allow_zero, + std::lock_guard const &lock) { + unused(lock); + + if (instanceNumber < 0) { + throw_illegal_state_exception(env, instanceNumber, "instance number out of range"); + return false; + } // This can happen when an exception is thrown from the constructor, giving this object // an invalid state, containing instanceNumber = 0. - if (instanceNumber == 0) - { - if (!allow_zero) - throw_illegal_state_exception (env, instanceNumber, - "function called on null instance" - ); - // Null instances are OK, but should still not be processed. - return false; - } + if (instanceNumber == 0) { + if (!allow_zero) + throw_illegal_state_exception(env, instanceNumber, "function called on null instance"); + // Null instances are OK, but should still not be processed. + return false; + } - if (static_cast (instanceNumber) > instances.size ()) - { - throw_illegal_state_exception (env, instanceNumber, - "function called on invalid instance" - ); - return false; - } + if (static_cast(instanceNumber) > instances.size()) { + throw_illegal_state_exception(env, instanceNumber, "function called on invalid instance"); + return false; + } // An instance should never be on this list twice. - if (std::find (freelist.begin (), freelist.end (), instanceNumber) != freelist.end ()) - { - throw_illegal_state_exception (env, instanceNumber, - "accessed instance thought to be garbage collected" - ); - return false; - } + if (std::find(freelist.begin(), freelist.end(), instanceNumber) != freelist.end()) { + throw_illegal_state_exception(env, instanceNumber, + "accessed instance thought to be garbage collected"); + return false; + } return true; } - -public: - instance_manager () = default; + public: + instance_manager() = default; // Non-copyable. - instance_manager (instance_manager const &) = delete; - instance_manager &operator = (instance_manager const &) = delete; - + instance_manager(instance_manager const &) = delete; + instance_manager &operator=(instance_manager const &) = delete; /** * Hands over management of the object and events pointers to the manager. The * returned instance number can be used to call with_instance for access and * kill/finalize for destruction/cleanup. */ - jint - add (JNIEnv *env, ObjectP object_p, EventsP events_p) - { + jint add(JNIEnv *env, ObjectP object_p, EventsP events_p) { // The manager is locked for the entire duration of this function. No access // or mutation may happen during an add, because we might reallocate and // move objects here. - std::lock_guard lock (mutex); + std::lock_guard lock(mutex); - tox4j_assert (object_p); - tox4j_assert (events_p); + tox4j_assert(object_p); + tox4j_assert(events_p); instance_pointers instance_p = { - std::move (object_p), - std::move (events_p), + std::move(object_p), + std::move(events_p), }; // If there are free objects we can reuse.. - if (!freelist.empty ()) - { - // ..use the last object that became unreachable (it will most likely be in cache). - jint instanceNumber = freelist.back (); - freelist.pop_back (); // Remove it from the free list. + if (!freelist.empty()) { + // ..use the last object that became unreachable (it will most likely be in cache). + jint instanceNumber = freelist.back(); + freelist.pop_back(); // Remove it from the free list. - // The null instance should never be on the freelist. - tox4j_assert (instanceNumber >= 1); - // All instances on the freelist should be empty. - tox4j_assert (!instances[instanceNumber - 1]); + // The null instance should never be on the freelist. + tox4j_assert(instanceNumber >= 1); + // All instances on the freelist should be empty. + tox4j_assert(!instances[instanceNumber - 1]); - instances[instanceNumber - 1] = std::move (instance_p); + instances[instanceNumber - 1] = std::move(instance_p); - return instanceNumber; - } + return instanceNumber; + } // Otherwise, add a new one. - instances.push_back (std::move (instance_p)); - locks.emplace_back (); + instances.push_back(std::move(instance_p)); + locks.emplace_back(); // Check invariant. - tox4j_assert (instances.size () == locks.size ()); - return instances.size (); + tox4j_assert(instances.size() == locks.size()); + return instances.size(); } - /** * Destroys an instance, deallocating both the object and the events. * * This function is idempotent as long as there was no finalize call with the * passed instance number in between. */ - void - kill (JNIEnv *env, jint instanceNumber) - { + void kill(JNIEnv *env, jint instanceNumber) { // Lock the manager, since we're going to access the lists while add() might // be modifying them. - std::lock_guard lock (mutex); + std::lock_guard lock(mutex); - if (!check_instance_number (env, instanceNumber, true, lock)) - return; + if (!check_instance_number(env, instanceNumber, true, lock)) return; // Lock before moving the pointers out. - std::lock_guard instance_lock (locks[instanceNumber - 1]); + std::lock_guard instance_lock(locks[instanceNumber - 1]); // The instance destructor is called inside the critical section entered above. - auto dying = std::move (instances[instanceNumber - 1]); + auto dying = std::move(instances[instanceNumber - 1]); } - /** * Add the instance number to the freelist, making it a candidate for reuse. * @@ -260,31 +227,25 @@ class instance_manager * called twice on the same instance number (given there was no add in between * that returned that instance number). */ - void - finalize (JNIEnv *env, jint instanceNumber) - { + void finalize(JNIEnv *env, jint instanceNumber) { // Lock the manager, since we're going to access the lists while add() might // be modifying them. - std::lock_guard lock (mutex); + std::lock_guard lock(mutex); // Don't throw on null instances, but also don't put it on the freelist. - if (!check_instance_number (env, instanceNumber, true, lock)) - return; + if (!check_instance_number(env, instanceNumber, true, lock)) return; // The C++ side should already have been killed. - if (instances[instanceNumber - 1]) - { - throw_illegal_state_exception (env, instanceNumber, - "Leaked Tox instance #" + std::to_string (instanceNumber) - ); - return; - } + if (instances[instanceNumber - 1]) { + throw_illegal_state_exception(env, instanceNumber, + "Leaked Tox instance #" + std::to_string(instanceNumber)); + return; + } - tox4j_assert (instanceNumber != 0); - freelist.push_back (instanceNumber); + tox4j_assert(instanceNumber != 0); + freelist.push_back(instanceNumber); } - /** * Access the instance identified by the instance number. Func must be a * callable type and accept an Object* and an Events&. The result of the @@ -295,38 +256,33 @@ class instance_manager * single instance are not possible, but multiple concurrent calls to different * instances is permitted. */ - template - auto - with_instance (JNIEnv *env, jint instanceNumber, Func func) - { - typedef typename std::result_of::type return_type; + template + auto with_instance(JNIEnv *env, jint instanceNumber, Func func) { + using return_type = std::invoke_result_t; // After this object is initialised, it is independent of the pointers' // memory location in the manager. - auto instance = [this, env, instanceNumber] - { - // Lock the instance manager only while accessing the list. - std::lock_guard lock (mutex); - - if (!check_instance_number (env, instanceNumber, false, lock)) - return typename instance_pointers::locked { }; - - // Lock the instance, get the pointers out of the instance vector, and - // unlock the manager (by lock_guard's destructor). - return instances[instanceNumber - 1].get (std::unique_lock (locks[instanceNumber - 1])); - } (); - - if (!instance) - { - if (!env->ExceptionCheck ()) - throw_tox_killed_exception (env, instanceNumber, - "function called on killed tox instance" - ); - return return_type (); - } + auto instance = [this, env, instanceNumber] { + // Lock the instance manager only while accessing the list. + std::lock_guard lock(mutex); + + if (!check_instance_number(env, instanceNumber, false, lock)) + return typename instance_pointers::locked{}; + + // Lock the instance, get the pointers out of the instance vector, and + // unlock the manager (by lock_guard's destructor). + return instances[instanceNumber - 1].get( + std::unique_lock(locks[instanceNumber - 1])); + }(); + + if (!instance) { + if (!env->ExceptionCheck()) + throw_tox_killed_exception(env, instanceNumber, "function called on killed tox instance"); + return return_type(); + } // After this function returns, the unique_lock in the instance object will // be destroyed, unlocking the instance. - return func (instance.object, *instance.events); + return func(instance.object, *instance.events); } }; diff --git a/cpp/src/util/wrap_void.h b/cpp/src/util/wrap_void.h index 4adcf95ec..d9454fa93 100644 --- a/cpp/src/util/wrap_void.h +++ b/cpp/src/util/wrap_void.h @@ -1,9 +1,8 @@ #pragma once -#include "cpp14compat.h" - #include +#include "cpp14compat.h" /** * Helper template to capture the return value of a function call so that @@ -16,45 +15,34 @@ * T must be movable or copyable for this to work. Also, all the arguments * must be copyable. */ -template -struct wrapped_value -{ +template +struct wrapped_value { T value; - T unwrap () { return std::move (value); } + T unwrap() { return std::move(value); } - template - static wrapped_value - wrap (FuncT func, Args &&...args) - { - return { func (std::forward (args)...) }; + template + static wrapped_value wrap(FuncT func, Args &&...args) { + return {func(std::forward(args)...)}; } }; +template <> +struct wrapped_value { + void unwrap() const {} -template<> -struct wrapped_value -{ - void unwrap () const { } - - template - static wrapped_value - wrap (FuncT func, Args &&...args) - { - func (std::forward (args)...); - return { }; + template + static wrapped_value wrap(FuncT func, Args &&...args) { + func(std::forward(args)...); + return {}; } }; - /** * Call this with the function and its arguments to wrap the return value in * a wrapped_value class. */ -template -wrapped_value::type> -wrap_void (FuncT func, Args &&...args) -{ - return wrapped_value< - typename std::result_of::type - >::wrap (func, std::forward (args)...); +template +wrapped_value> wrap_void(FuncT func, Args &&...args) { + return wrapped_value>::wrap(func, + std::forward(args)...); } diff --git a/cpp/test/main.cpp b/cpp/test/main.cpp deleted file mode 100644 index fda9e7298..000000000 --- a/cpp/test/main.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "util/logging.h" -#include - -// #include - -int -main (int argc, char **argv) -{ - google::InitGoogleLogging (argv[0]); - testing::InitGoogleTest (&argc, argv); - - google::LogToStderr (); - - int result = RUN_ALL_TESTS (); - - google::ShutdownGoogleLogging (); - // google::protobuf::ShutdownProtobufLibrary (); - - return result; -} diff --git a/cpp/test/mock_jni.cpp b/cpp/test/mock_jni.cpp deleted file mode 100644 index 646f9e272..000000000 --- a/cpp/test/mock_jni.cpp +++ /dev/null @@ -1,1853 +0,0 @@ -#pragma GCC diagnostic ignored "-Wunused-parameter" - -#include "mock_jni.h" -#include - - -jint -GetVersion (JNIEnv *env) -{ - ADD_FAILURE () << "GetVersion"; - return 0; -} - -jclass -DefineClass (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len) -{ - ADD_FAILURE () << "DefineClass"; - return 0; -} - -jmethodID -FromReflectedMethod (JNIEnv *env, jobject method) -{ - ADD_FAILURE () << "FromReflectedMethod"; - return 0; -} - -jfieldID -FromReflectedField (JNIEnv *env, jobject field) -{ - ADD_FAILURE () << "FromReflectedField"; - return 0; -} - -jobject -ToReflectedMethod (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic) -{ - ADD_FAILURE () << "ToReflectedMethod"; - return 0; -} - -jclass -GetSuperclass (JNIEnv *env, jclass sub) -{ - ADD_FAILURE () << "GetSuperclass"; - return 0; -} - -jboolean -IsAssignableFrom (JNIEnv *env, jclass sub, jclass sup) -{ - ADD_FAILURE () << "IsAssignableFrom"; - return 0; -} - -jobject -ToReflectedField (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic) -{ - ADD_FAILURE () << "ToReflectedField"; - return 0; -} - -jint -Throw (JNIEnv *env, jthrowable obj) -{ - ADD_FAILURE () << "Throw"; - return 0; -} - -jthrowable -ExceptionOccurred (JNIEnv *env) -{ - ADD_FAILURE () << "ExceptionOccurred"; - return 0; -} - -void -ExceptionDescribe (JNIEnv *env) -{ - ADD_FAILURE () << "ExceptionDescribe"; -} - -void -ExceptionClear (JNIEnv *env) -{ - ADD_FAILURE () << "ExceptionClear"; -} - -void -FatalError (JNIEnv *env, const char *msg) -{ - ADD_FAILURE () << "FatalError"; -} - -jint -PushLocalFrame (JNIEnv *env, jint capacity) -{ - ADD_FAILURE () << "PushLocalFrame"; - return 0; -} - -jobject -PopLocalFrame (JNIEnv *env, jobject result) -{ - ADD_FAILURE () << "PopLocalFrame"; - return 0; -} - -jobject -NewGlobalRef (JNIEnv *env, jobject lobj) -{ - ADD_FAILURE () << "NewGlobalRef"; - return 0; -} - -void -DeleteGlobalRef (JNIEnv *env, jobject gref) -{ - ADD_FAILURE () << "DeleteGlobalRef"; -} - -void -DeleteLocalRef (JNIEnv *env, jobject obj) -{ - ADD_FAILURE () << "DeleteLocalRef"; -} - -jboolean -IsSameObject (JNIEnv *env, jobject obj1, jobject obj2) -{ - ADD_FAILURE () << "IsSameObject"; - return 0; -} - -jobject -NewLocalRef (JNIEnv *env, jobject ref) -{ - ADD_FAILURE () << "NewLocalRef"; - return 0; -} - -jint -EnsureLocalCapacity (JNIEnv *env, jint capacity) -{ - ADD_FAILURE () << "EnsureLocalCapacity"; - return 0; -} - -jobject -AllocObject (JNIEnv *env, jclass clazz) -{ - ADD_FAILURE () << "AllocObject"; - return 0; -} - -jobject -NewObject (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "NewObject"; - return 0; -} - -jobject -NewObjectV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "NewObjectV"; - return 0; -} - -jobject -NewObjectA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "NewObjectA"; - return 0; -} - -jclass -GetObjectClass (JNIEnv *env, jobject obj) -{ - ADD_FAILURE () << "GetObjectClass"; - return 0; -} - -jboolean -IsInstanceOf (JNIEnv *env, jobject obj, jclass clazz) -{ - ADD_FAILURE () << "IsInstanceOf"; - return 0; -} - -jmethodID -GetMethodID (JNIEnv *env, jclass clazz, const char *name, const char *sig) -{ - ADD_FAILURE () << "GetMethodID"; - return 0; -} - -jobject -CallObjectMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallObjectMethod"; - return 0; -} - -jobject -CallObjectMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallObjectMethodV"; - return 0; -} - -jobject -CallObjectMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallObjectMethodA"; - return 0; -} - -jboolean -CallBooleanMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallBooleanMethod"; - return 0; -} - -jboolean -CallBooleanMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallBooleanMethodV"; - return 0; -} - -jboolean -CallBooleanMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallBooleanMethodA"; - return 0; -} - -jbyte -CallByteMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallByteMethod"; - return 0; -} - -jbyte -CallByteMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallByteMethodV"; - return 0; -} - -jbyte -CallByteMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallByteMethodA"; - return 0; -} - -jchar -CallCharMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallCharMethod"; - return 0; -} - -jchar -CallCharMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallCharMethodV"; - return 0; -} - -jchar -CallCharMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallCharMethodA"; - return 0; -} - -jshort -CallShortMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallShortMethod"; - return 0; -} - -jshort -CallShortMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallShortMethodV"; - return 0; -} - -jshort -CallShortMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallShortMethodA"; - return 0; -} - -jint -CallIntMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallIntMethod"; - return 0; -} - -jint -CallIntMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallIntMethodV"; - return 0; -} - -jint -CallIntMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallIntMethodA"; - return 0; -} - -jlong -CallLongMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallLongMethod"; - return 0; -} - -jlong -CallLongMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallLongMethodV"; - return 0; -} - -jlong -CallLongMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallLongMethodA"; - return 0; -} - -jfloat -CallFloatMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallFloatMethod"; - return 0; -} - -jfloat -CallFloatMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallFloatMethodV"; - return 0; -} - -jfloat -CallFloatMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallFloatMethodA"; - return 0; -} - -jdouble -CallDoubleMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallDoubleMethod"; - return 0; -} - -jdouble -CallDoubleMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallDoubleMethodV"; - return 0; -} - -jdouble -CallDoubleMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallDoubleMethodA"; - return 0; -} - -void -CallVoidMethod (JNIEnv *env, jobject obj, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallVoidMethod"; -} - -void -CallVoidMethodV (JNIEnv *env, jobject obj, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallVoidMethodV"; -} - -void -CallVoidMethodA (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallVoidMethodA"; -} - -jobject -CallNonvirtualObjectMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualObjectMethod"; - return 0; -} - -jobject -CallNonvirtualObjectMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualObjectMethodV"; - return 0; -} - -jobject -CallNonvirtualObjectMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualObjectMethodA"; - return 0; -} - -jboolean -CallNonvirtualBooleanMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualBooleanMethod"; - return 0; -} - -jboolean -CallNonvirtualBooleanMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualBooleanMethodV"; - return 0; -} - -jboolean -CallNonvirtualBooleanMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualBooleanMethodA"; - return 0; -} - -jbyte -CallNonvirtualByteMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualByteMethod"; - return 0; -} - -jbyte -CallNonvirtualByteMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualByteMethodV"; - return 0; -} - -jbyte -CallNonvirtualByteMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualByteMethodA"; - return 0; -} - -jchar -CallNonvirtualCharMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualCharMethod"; - return 0; -} - -jchar -CallNonvirtualCharMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualCharMethodV"; - return 0; -} - -jchar -CallNonvirtualCharMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualCharMethodA"; - return 0; -} - -jshort -CallNonvirtualShortMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualShortMethod"; - return 0; -} - -jshort -CallNonvirtualShortMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualShortMethodV"; - return 0; -} - -jshort -CallNonvirtualShortMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualShortMethodA"; - return 0; -} - -jint -CallNonvirtualIntMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualIntMethod"; - return 0; -} - -jint -CallNonvirtualIntMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualIntMethodV"; - return 0; -} - -jint -CallNonvirtualIntMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualIntMethodA"; - return 0; -} - -jlong -CallNonvirtualLongMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualLongMethod"; - return 0; -} - -jlong -CallNonvirtualLongMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualLongMethodV"; - return 0; -} - -jlong -CallNonvirtualLongMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualLongMethodA"; - return 0; -} - -jfloat -CallNonvirtualFloatMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualFloatMethod"; - return 0; -} - -jfloat -CallNonvirtualFloatMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualFloatMethodV"; - return 0; -} - -jfloat -CallNonvirtualFloatMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualFloatMethodA"; - return 0; -} - -jdouble -CallNonvirtualDoubleMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualDoubleMethod"; - return 0; -} - -jdouble -CallNonvirtualDoubleMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualDoubleMethodV"; - return 0; -} - -jdouble -CallNonvirtualDoubleMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualDoubleMethodA"; - return 0; -} - -void -CallNonvirtualVoidMethod (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallNonvirtualVoidMethod"; -} - -void -CallNonvirtualVoidMethodV (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallNonvirtualVoidMethodV"; -} - -void -CallNonvirtualVoidMethodA (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallNonvirtualVoidMethodA"; -} - -jfieldID -GetFieldID (JNIEnv *env, jclass clazz, const char *name, const char *sig) -{ - ADD_FAILURE () << "GetFieldID"; - return 0; -} - -jobject -GetObjectField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetObjectField"; - return 0; -} - -jboolean -GetBooleanField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetBooleanField"; - return 0; -} - -jbyte -GetByteField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetByteField"; - return 0; -} - -jchar -GetCharField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetCharField"; - return 0; -} - -jshort -GetShortField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetShortField"; - return 0; -} - -jint -GetIntField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetIntField"; - return 0; -} - -jlong -GetLongField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetLongField"; - return 0; -} - -jfloat -GetFloatField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetFloatField"; - return 0; -} - -jdouble -GetDoubleField (JNIEnv *env, jobject obj, jfieldID fieldID) -{ - ADD_FAILURE () << "GetDoubleField"; - return 0; -} - -void -SetObjectField (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val) -{ - ADD_FAILURE () << "SetObjectField"; -} - -void -SetBooleanField (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val) -{ - ADD_FAILURE () << "SetBooleanField"; -} - -void -SetByteField (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val) -{ - ADD_FAILURE () << "SetByteField"; -} - -void -SetCharField (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val) -{ - ADD_FAILURE () << "SetCharField"; -} - -void -SetShortField (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val) -{ - ADD_FAILURE () << "SetShortField"; -} - -void -SetIntField (JNIEnv *env, jobject obj, jfieldID fieldID, jint val) -{ - ADD_FAILURE () << "SetIntField"; -} - -void -SetLongField (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val) -{ - ADD_FAILURE () << "SetLongField"; -} - -void -SetFloatField (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val) -{ - ADD_FAILURE () << "SetFloatField"; -} - -void -SetDoubleField (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val) -{ - ADD_FAILURE () << "SetDoubleField"; -} - -jmethodID -GetStaticMethodID (JNIEnv *env, jclass clazz, const char *name, const char *sig) -{ - ADD_FAILURE () << "GetStaticMethodID"; - return 0; -} - -jobject -CallStaticObjectMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticObjectMethod"; - return 0; -} - -jobject -CallStaticObjectMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticObjectMethodV"; - return 0; -} - -jobject -CallStaticObjectMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticObjectMethodA"; - return 0; -} - -jboolean -CallStaticBooleanMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticBooleanMethod"; - return 0; -} - -jboolean -CallStaticBooleanMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticBooleanMethodV"; - return 0; -} - -jboolean -CallStaticBooleanMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticBooleanMethodA"; - return 0; -} - -jbyte -CallStaticByteMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticByteMethod"; - return 0; -} - -jbyte -CallStaticByteMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticByteMethodV"; - return 0; -} - -jbyte -CallStaticByteMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticByteMethodA"; - return 0; -} - -jchar -CallStaticCharMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticCharMethod"; - return 0; -} - -jchar -CallStaticCharMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticCharMethodV"; - return 0; -} - -jchar -CallStaticCharMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticCharMethodA"; - return 0; -} - -jshort -CallStaticShortMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticShortMethod"; - return 0; -} - -jshort -CallStaticShortMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticShortMethodV"; - return 0; -} - -jshort -CallStaticShortMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticShortMethodA"; - return 0; -} - -jint -CallStaticIntMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticIntMethod"; - return 0; -} - -jint -CallStaticIntMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticIntMethodV"; - return 0; -} - -jint -CallStaticIntMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticIntMethodA"; - return 0; -} - -jlong -CallStaticLongMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticLongMethod"; - return 0; -} - -jlong -CallStaticLongMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticLongMethodV"; - return 0; -} - -jlong -CallStaticLongMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticLongMethodA"; - return 0; -} - -jfloat -CallStaticFloatMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticFloatMethod"; - return 0; -} - -jfloat -CallStaticFloatMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticFloatMethodV"; - return 0; -} - -jfloat -CallStaticFloatMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticFloatMethodA"; - return 0; -} - -jdouble -CallStaticDoubleMethod (JNIEnv *env, jclass clazz, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticDoubleMethod"; - return 0; -} - -jdouble -CallStaticDoubleMethodV (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticDoubleMethodV"; - return 0; -} - -jdouble -CallStaticDoubleMethodA (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticDoubleMethodA"; - return 0; -} - -void -CallStaticVoidMethod (JNIEnv *env, jclass cls, jmethodID methodID, ...) -{ - ADD_FAILURE () << "CallStaticVoidMethod"; -} - -void -CallStaticVoidMethodV (JNIEnv *env, jclass cls, jmethodID methodID, va_list args) -{ - ADD_FAILURE () << "CallStaticVoidMethodV"; -} - -void -CallStaticVoidMethodA (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) -{ - ADD_FAILURE () << "CallStaticVoidMethodA"; -} - -jfieldID -GetStaticFieldID (JNIEnv *env, jclass clazz, const char *name, const char *sig) -{ - ADD_FAILURE () << "GetStaticFieldID"; - return 0; -} - -jobject -GetStaticObjectField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticObjectField"; - return 0; -} - -jboolean -GetStaticBooleanField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticBooleanField"; - return 0; -} - -jbyte -GetStaticByteField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticByteField"; - return 0; -} - -jchar -GetStaticCharField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticCharField"; - return 0; -} - -jshort -GetStaticShortField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticShortField"; - return 0; -} - -jint -GetStaticIntField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticIntField"; - return 0; -} - -jlong -GetStaticLongField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticLongField"; - return 0; -} - -jfloat -GetStaticFloatField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticFloatField"; - return 0; -} - -jdouble -GetStaticDoubleField (JNIEnv *env, jclass clazz, jfieldID fieldID) -{ - ADD_FAILURE () << "GetStaticDoubleField"; - return 0; -} - -void -SetStaticObjectField (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value) -{ - ADD_FAILURE () << "SetStaticObjectField"; -} - -void -SetStaticBooleanField (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value) -{ - ADD_FAILURE () << "SetStaticBooleanField"; -} - -void -SetStaticByteField (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value) -{ - ADD_FAILURE () << "SetStaticByteField"; -} - -void -SetStaticCharField (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value) -{ - ADD_FAILURE () << "SetStaticCharField"; -} - -void -SetStaticShortField (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value) -{ - ADD_FAILURE () << "SetStaticShortField"; -} - -void -SetStaticIntField (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value) -{ - ADD_FAILURE () << "SetStaticIntField"; -} - -void -SetStaticLongField (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value) -{ - ADD_FAILURE () << "SetStaticLongField"; -} - -void -SetStaticFloatField (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value) -{ - ADD_FAILURE () << "SetStaticFloatField"; -} - -void -SetStaticDoubleField (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value) -{ - ADD_FAILURE () << "SetStaticDoubleField"; -} - -jstring -NewString (JNIEnv *env, const jchar *unicode, jsize len) -{ - ADD_FAILURE () << "NewString"; - return 0; -} - -jsize -GetStringLength (JNIEnv *env, jstring str) -{ - ADD_FAILURE () << "GetStringLength"; - return 0; -} - -const jchar * -GetStringChars (JNIEnv *env, jstring str, jboolean *isCopy) -{ - ADD_FAILURE () << "GetStringChars"; - return 0; -} - -void -ReleaseStringChars (JNIEnv *env, jstring str, const jchar *chars) -{ - ADD_FAILURE () << "ReleaseStringChars"; -} - -jstring -NewStringUTF (JNIEnv *env, const char *utf) -{ - ADD_FAILURE () << "NewStringUTF"; - return 0; -} - -jsize -GetStringUTFLength (JNIEnv *env, jstring str) -{ - ADD_FAILURE () << "GetStringUTFLength"; - return 0; -} - -const char * -GetStringUTFChars (JNIEnv *env, jstring str, jboolean *isCopy) -{ - ADD_FAILURE () << "GetStringUTFChars"; - return 0; -} - -void -ReleaseStringUTFChars (JNIEnv *env, jstring str, const char *chars) -{ - ADD_FAILURE () << "ReleaseStringUTFChars"; -} - -jsize -GetArrayLength (JNIEnv *env, jarray array) -{ - ADD_FAILURE () << "GetArrayLength"; - return 0; -} - -jobjectArray -NewObjectArray (JNIEnv *env, jsize len, jclass clazz, jobject init) -{ - ADD_FAILURE () << "NewObjectArray"; - return 0; -} - -jobject -GetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index) -{ - ADD_FAILURE () << "GetObjectArrayElement"; - return 0; -} - -void -SetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index, jobject val) -{ - ADD_FAILURE () << "SetObjectArrayElement"; -} - -jbooleanArray -NewBooleanArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewBooleanArray"; - return 0; -} - -jbyteArray -NewByteArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewByteArray"; - return 0; -} - -jcharArray -NewCharArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewCharArray"; - return 0; -} - -jshortArray -NewShortArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewShortArray"; - return 0; -} - -jintArray -NewIntArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewIntArray"; - return 0; -} - -jlongArray -NewLongArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewLongArray"; - return 0; -} - -jfloatArray -NewFloatArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewFloatArray"; - return 0; -} - -jdoubleArray -NewDoubleArray (JNIEnv *env, jsize len) -{ - ADD_FAILURE () << "NewDoubleArray"; - return 0; -} - -jboolean * -GetBooleanArrayElements (JNIEnv *env, jbooleanArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetBooleanArrayElements"; - return 0; -} - -jbyte * -GetByteArrayElements (JNIEnv *env, jbyteArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetByteArrayElements"; - return 0; -} - -jchar * -GetCharArrayElements (JNIEnv *env, jcharArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetCharArrayElements"; - return 0; -} - -jshort * -GetShortArrayElements (JNIEnv *env, jshortArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetShortArrayElements"; - return 0; -} - -jint * -GetIntArrayElements (JNIEnv *env, jintArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetIntArrayElements"; - return 0; -} - -jlong * -GetLongArrayElements (JNIEnv *env, jlongArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetLongArrayElements"; - return 0; -} - -jfloat * -GetFloatArrayElements (JNIEnv *env, jfloatArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetFloatArrayElements"; - return 0; -} - -jdouble * -GetDoubleArrayElements (JNIEnv *env, jdoubleArray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetDoubleArrayElements"; - return 0; -} - -void -ReleaseBooleanArrayElements (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseBooleanArrayElements"; -} - -void -ReleaseByteArrayElements (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseByteArrayElements"; -} - -void -ReleaseCharArrayElements (JNIEnv *env, jcharArray array, jchar *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseCharArrayElements"; -} - -void -ReleaseShortArrayElements (JNIEnv *env, jshortArray array, jshort *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseShortArrayElements"; -} - -void -ReleaseIntArrayElements (JNIEnv *env, jintArray array, jint *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseIntArrayElements"; -} - -void -ReleaseLongArrayElements (JNIEnv *env, jlongArray array, jlong *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseLongArrayElements"; -} - -void -ReleaseFloatArrayElements (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseFloatArrayElements"; -} - -void -ReleaseDoubleArrayElements (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode) -{ - ADD_FAILURE () << "ReleaseDoubleArrayElements"; -} - -void -GetBooleanArrayRegion (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf) -{ - ADD_FAILURE () << "GetBooleanArrayRegion"; -} - -void -GetByteArrayRegion (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf) -{ - ADD_FAILURE () << "GetByteArrayRegion"; -} - -void -GetCharArrayRegion (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf) -{ - ADD_FAILURE () << "GetCharArrayRegion"; -} - -void -GetShortArrayRegion (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf) -{ - ADD_FAILURE () << "GetShortArrayRegion"; -} - -void -GetIntArrayRegion (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf) -{ - ADD_FAILURE () << "GetIntArrayRegion"; -} - -void -GetLongArrayRegion (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf) -{ - ADD_FAILURE () << "GetLongArrayRegion"; -} - -void -GetFloatArrayRegion (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf) -{ - ADD_FAILURE () << "GetFloatArrayRegion"; -} - -void -GetDoubleArrayRegion (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf) -{ - ADD_FAILURE () << "GetDoubleArrayRegion"; -} - -void -SetBooleanArrayRegion (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf) -{ - ADD_FAILURE () << "SetBooleanArrayRegion"; -} - -void -SetByteArrayRegion (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf) -{ - ADD_FAILURE () << "SetByteArrayRegion"; -} - -void -SetCharArrayRegion (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf) -{ - ADD_FAILURE () << "SetCharArrayRegion"; -} - -void -SetShortArrayRegion (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf) -{ - ADD_FAILURE () << "SetShortArrayRegion"; -} - -void -SetIntArrayRegion (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf) -{ - ADD_FAILURE () << "SetIntArrayRegion"; -} - -void -SetLongArrayRegion (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf) -{ - ADD_FAILURE () << "SetLongArrayRegion"; -} - -void -SetFloatArrayRegion (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf) -{ - ADD_FAILURE () << "SetFloatArrayRegion"; -} - -void -SetDoubleArrayRegion (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf) -{ - ADD_FAILURE () << "SetDoubleArrayRegion"; -} - -jint -RegisterNatives (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods) -{ - ADD_FAILURE () << "RegisterNatives"; - return 0; -} - -jint -UnregisterNatives (JNIEnv *env, jclass clazz) -{ - ADD_FAILURE () << "UnregisterNatives"; - return 0; -} - -jint -MonitorEnter (JNIEnv *env, jobject obj) -{ - ADD_FAILURE () << "MonitorEnter"; - return 0; -} - -jint -MonitorExit (JNIEnv *env, jobject obj) -{ - ADD_FAILURE () << "MonitorExit"; - return 0; -} - -jint -GetJavaVM (JNIEnv *env, JavaVM **vm) -{ - ADD_FAILURE () << "GetJavaVM"; - return 0; -} - -void -GetStringRegion (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf) -{ - ADD_FAILURE () << "GetStringRegion"; -} - -void -GetStringUTFRegion (JNIEnv *env, jstring str, jsize start, jsize len, char *buf) -{ - ADD_FAILURE () << "GetStringUTFRegion"; -} - -void * -GetPrimitiveArrayCritical (JNIEnv *env, jarray array, jboolean *isCopy) -{ - ADD_FAILURE () << "GetPrimitiveArrayCritical"; - return 0; -} - -void -ReleasePrimitiveArrayCritical (JNIEnv *env, jarray array, void *carray, jint mode) -{ - ADD_FAILURE () << "ReleasePrimitiveArrayCritical"; -} - -const jchar * -GetStringCritical (JNIEnv *env, jstring string, jboolean *isCopy) -{ - ADD_FAILURE () << "GetStringCritical"; - return 0; -} - -void -ReleaseStringCritical (JNIEnv *env, jstring string, const jchar *cstring) -{ - ADD_FAILURE () << "ReleaseStringCritical"; -} - -jweak -NewWeakGlobalRef (JNIEnv *env, jobject obj) -{ - ADD_FAILURE () << "NewWeakGlobalRef"; - return 0; -} - -void -DeleteWeakGlobalRef (JNIEnv *env, jweak ref) -{ - ADD_FAILURE () << "DeleteWeakGlobalRef"; -} - -jboolean -ExceptionCheck (JNIEnv *env) -{ - ADD_FAILURE () << "ExceptionCheck"; - return 0; -} - -jobject -NewDirectByteBuffer (JNIEnv *env, void *address, jlong capacity) -{ - ADD_FAILURE () << "NewDirectByteBuffer"; - return 0; -} - -void * -GetDirectBufferAddress (JNIEnv *env, jobject buf) -{ - ADD_FAILURE () << "GetDirectBufferAddress"; - return 0; -} - -jlong -GetDirectBufferCapacity (JNIEnv *env, jobject buf) -{ - ADD_FAILURE () << "GetDirectBufferCapacity"; - return 0; -} - -/* New JNI 1.6 Features */ - -jobjectRefType -GetObjectRefType (JNIEnv *env, jobject obj) -{ - ADD_FAILURE () << "GetObjectRefType"; - return { }; -} - - -static mock_jni & -self (JNIEnv *env) -{ - return *static_cast (env); -} - - -static JNINativeInterface_ const functions = { - nullptr, - nullptr, - nullptr, - nullptr, - - GetVersion, - - DefineClass, - [] (JNIEnv *env, const char *name) { return self (env).FindClass (name); }, - - FromReflectedMethod, - FromReflectedField, - - ToReflectedMethod, - - GetSuperclass, - IsAssignableFrom, - - ToReflectedField, - - Throw, - [] (JNIEnv *env, jclass clazz, const char *msg) { return self (env).ThrowNew (clazz, msg); }, - ExceptionOccurred, - ExceptionDescribe, - ExceptionClear, - FatalError, - - PushLocalFrame, - PopLocalFrame, - - NewGlobalRef, - DeleteGlobalRef, - DeleteLocalRef, - IsSameObject, - NewLocalRef, - EnsureLocalCapacity, - - AllocObject, - NewObject, - NewObjectV, - NewObjectA, - - GetObjectClass, - IsInstanceOf, - - GetMethodID, - - CallObjectMethod, - CallObjectMethodV, - CallObjectMethodA, - - CallBooleanMethod, - CallBooleanMethodV, - CallBooleanMethodA, - - CallByteMethod, - CallByteMethodV, - CallByteMethodA, - - CallCharMethod, - CallCharMethodV, - CallCharMethodA, - - CallShortMethod, - CallShortMethodV, - CallShortMethodA, - - CallIntMethod, - CallIntMethodV, - CallIntMethodA, - - CallLongMethod, - CallLongMethodV, - CallLongMethodA, - - CallFloatMethod, - CallFloatMethodV, - CallFloatMethodA, - - CallDoubleMethod, - CallDoubleMethodV, - CallDoubleMethodA, - - CallVoidMethod, - CallVoidMethodV, - CallVoidMethodA, - - CallNonvirtualObjectMethod, - CallNonvirtualObjectMethodV, - CallNonvirtualObjectMethodA, - - CallNonvirtualBooleanMethod, - CallNonvirtualBooleanMethodV, - CallNonvirtualBooleanMethodA, - - CallNonvirtualByteMethod, - CallNonvirtualByteMethodV, - CallNonvirtualByteMethodA, - - CallNonvirtualCharMethod, - CallNonvirtualCharMethodV, - CallNonvirtualCharMethodA, - - CallNonvirtualShortMethod, - CallNonvirtualShortMethodV, - CallNonvirtualShortMethodA, - - CallNonvirtualIntMethod, - CallNonvirtualIntMethodV, - CallNonvirtualIntMethodA, - - CallNonvirtualLongMethod, - CallNonvirtualLongMethodV, - CallNonvirtualLongMethodA, - - CallNonvirtualFloatMethod, - CallNonvirtualFloatMethodV, - CallNonvirtualFloatMethodA, - - CallNonvirtualDoubleMethod, - CallNonvirtualDoubleMethodV, - CallNonvirtualDoubleMethodA, - - CallNonvirtualVoidMethod, - CallNonvirtualVoidMethodV, - CallNonvirtualVoidMethodA, - - GetFieldID, - - GetObjectField, - GetBooleanField, - GetByteField, - GetCharField, - GetShortField, - GetIntField, - GetLongField, - GetFloatField, - GetDoubleField, - - SetObjectField, - SetBooleanField, - SetByteField, - SetCharField, - SetShortField, - SetIntField, - SetLongField, - SetFloatField, - SetDoubleField, - - GetStaticMethodID, - - CallStaticObjectMethod, - CallStaticObjectMethodV, - CallStaticObjectMethodA, - - CallStaticBooleanMethod, - CallStaticBooleanMethodV, - CallStaticBooleanMethodA, - - CallStaticByteMethod, - CallStaticByteMethodV, - CallStaticByteMethodA, - - CallStaticCharMethod, - CallStaticCharMethodV, - CallStaticCharMethodA, - - CallStaticShortMethod, - CallStaticShortMethodV, - CallStaticShortMethodA, - - CallStaticIntMethod, - CallStaticIntMethodV, - CallStaticIntMethodA, - - CallStaticLongMethod, - CallStaticLongMethodV, - CallStaticLongMethodA, - - CallStaticFloatMethod, - CallStaticFloatMethodV, - CallStaticFloatMethodA, - - CallStaticDoubleMethod, - CallStaticDoubleMethodV, - CallStaticDoubleMethodA, - - CallStaticVoidMethod, - CallStaticVoidMethodV, - CallStaticVoidMethodA, - - GetStaticFieldID, - GetStaticObjectField, - GetStaticBooleanField, - GetStaticByteField, - GetStaticCharField, - GetStaticShortField, - GetStaticIntField, - GetStaticLongField, - GetStaticFloatField, - GetStaticDoubleField, - - SetStaticObjectField, - SetStaticBooleanField, - SetStaticByteField, - SetStaticCharField, - SetStaticShortField, - SetStaticIntField, - SetStaticLongField, - SetStaticFloatField, - SetStaticDoubleField, - - NewString, - GetStringLength, - GetStringChars, - ReleaseStringChars, - - NewStringUTF, - GetStringUTFLength, - GetStringUTFChars, - ReleaseStringUTFChars, - - - GetArrayLength, - - NewObjectArray, - GetObjectArrayElement, - SetObjectArrayElement, - - NewBooleanArray, - NewByteArray, - NewCharArray, - NewShortArray, - NewIntArray, - NewLongArray, - NewFloatArray, - NewDoubleArray, - - GetBooleanArrayElements, - GetByteArrayElements, - GetCharArrayElements, - GetShortArrayElements, - GetIntArrayElements, - GetLongArrayElements, - GetFloatArrayElements, - GetDoubleArrayElements, - - ReleaseBooleanArrayElements, - ReleaseByteArrayElements, - ReleaseCharArrayElements, - ReleaseShortArrayElements, - ReleaseIntArrayElements, - ReleaseLongArrayElements, - ReleaseFloatArrayElements, - ReleaseDoubleArrayElements, - - GetBooleanArrayRegion, - GetByteArrayRegion, - GetCharArrayRegion, - GetShortArrayRegion, - GetIntArrayRegion, - GetLongArrayRegion, - GetFloatArrayRegion, - GetDoubleArrayRegion, - - SetBooleanArrayRegion, - SetByteArrayRegion, - SetCharArrayRegion, - SetShortArrayRegion, - SetIntArrayRegion, - SetLongArrayRegion, - SetFloatArrayRegion, - SetDoubleArrayRegion, - - RegisterNatives, - UnregisterNatives, - - MonitorEnter, - MonitorExit, - - GetJavaVM, - - GetStringRegion, - GetStringUTFRegion, - - GetPrimitiveArrayCritical, - ReleasePrimitiveArrayCritical, - - GetStringCritical, - ReleaseStringCritical, - - NewWeakGlobalRef, - DeleteWeakGlobalRef, - - ExceptionCheck, - - NewDirectByteBuffer, - GetDirectBufferAddress, - GetDirectBufferCapacity, - - /* New JNI 1.6 Features */ - - GetObjectRefType, -}; - - -mock_jni * -mock_jnienv () -{ - return new mock_jni { &functions }; -} diff --git a/cpp/test/mock_jni.h b/cpp/test/mock_jni.h deleted file mode 100644 index 8652c6344..000000000 --- a/cpp/test/mock_jni.h +++ /dev/null @@ -1,58 +0,0 @@ -#include - -#include -#include -#include - - -struct mock_jclass - : _jclass -{ - explicit mock_jclass (std::string name) - : name (name) - { } - - std::string name; -}; - - -struct mock_jthrowable - : _jthrowable -{ - mock_jthrowable (jclass clazz, std::string name) - : clazz (static_cast (clazz)) - , name (name) - { } - - mock_jclass *clazz; - std::string name; -}; - - -struct mock_jni - : JNIEnv -{ - mock_jni (JNINativeInterface_ const *functions) - : JNIEnv { functions } - { } - - - jclass - FindClass (const char *name) - { - return new mock_jclass (name); - } - - jint - ThrowNew (jclass clazz, const char *msg) - { - exn = new mock_jthrowable (clazz, msg); - return 0; - } - - - mock_jthrowable *exn = nullptr; -}; - - -mock_jni *mock_jnienv (); diff --git a/cpp/test/tox/common_test.cpp b/cpp/test/tox/common_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/tox4j/ToxInstances_test.cpp b/cpp/test/tox4j/ToxInstances_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/util/debug_log_test.cpp b/cpp/test/util/debug_log_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/util/exceptions_test.cpp b/cpp/test/util/exceptions_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/util/instance_manager_test.cpp b/cpp/test/util/instance_manager_test.cpp deleted file mode 100644 index e7e66d800..000000000 --- a/cpp/test/util/instance_manager_test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "util/instance_manager.h" - -#include "util/logging.h" -#include - -#include "../mock_jni.h" - - -static std::unique_ptr -make_int (int i) -{ - return std::unique_ptr (new int (i)); -} - - -typedef instance_manager, std::unique_ptr> int_manager; - - -TEST (InstanceManager, Add) { - mock_jni *env = mock_jnienv (); - - int_manager mgr; - jint id = mgr.add (env, make_int (1), make_int (2)); - ASSERT_GT (id, 0); -} - - -TEST (InstanceManager, Kill) { - mock_jni *env = mock_jnienv (); - - int_manager mgr; - jint id = mgr.add (env, make_int (1), make_int (2)); - mgr.kill (env, id); -} - - -TEST (InstanceManager, FinalizeWithoutKill) { - mock_jni *env = mock_jnienv (); - - int_manager mgr; - jint id = mgr.add (env, make_int (1), make_int (2)); - mgr.finalize (env, id); - ASSERT_TRUE (env->exn != nullptr); -} - - -TEST (InstanceManager, Finalize) { - mock_jni *env = mock_jnienv (); - - int_manager mgr; - jint id = mgr.add (env, make_int (1), make_int (2)); - mgr.kill (env, id); - mgr.finalize (env, id); -} - - -TEST (InstanceManager, WithInstance) { - mock_jni *env = mock_jnienv (); - - int_manager mgr; - jint id = mgr.add (env, make_int (1), make_int (2)); - mgr.with_instance (env, id, - [] (int *a, int &b) - { - ASSERT_EQ (*a, 1); - ASSERT_EQ (b, 2); - } - ); -} diff --git a/cpp/test/util/jni/ArrayFromJava_test.cpp b/cpp/test/util/jni/ArrayFromJava_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/util/jni/ArrayToJava_test.cpp b/cpp/test/util/jni/ArrayToJava_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/util/jni/UTFChars_test.cpp b/cpp/test/util/jni/UTFChars_test.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/cpp/test/util/to_bytes_test.cpp b/cpp/test/util/to_bytes_test.cpp deleted file mode 100644 index 4cf6ccc62..000000000 --- a/cpp/test/util/to_bytes_test.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "util/to_bytes.h" - -#include "util/logging.h" -#include - -#include "../mock_jni.h" - - -template -std::string -str (char const (&bytes)[N]) -{ - return std::string (bytes, N - 1); -} - - -TEST (ToBytes, NegativeValues) { - std::cout << "\033[1;31mbold red text\033[0m\n"; - int16_t values[] = { -1, 0, 1, -32768, -32767, 32767 }; - std::string out; - to_bytes (values, values + sizeof values / sizeof *values, out); - ASSERT_EQ ( - str( - "\xFF\xFF" // -1 - "\x00\x00" // 0 - "\x00\x01" // 1 - "\x80\x00" // -32768 - "\x80\x01" // -32767 - "\x7F\xFF" // 32767 - ), - out - ); -} diff --git a/cpp/test/util/wrap_void_test.cpp b/cpp/test/util/wrap_void_test.cpp deleted file mode 100644 index f4aae7e76..000000000 --- a/cpp/test/util/wrap_void_test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "util/wrap_void.h" - -#include "util/logging.h" -#include - -#include - - -static std::unique_ptr -make_int (int i) -{ - return make_unique (i); -} - - -TEST (WrapVoid, MovableType1) { - auto wrapped = wrap_void (make_int, 4); - ASSERT_EQ (4, *wrapped.value); - ASSERT_EQ (4, *wrapped.value); -} - -TEST (WrapVoid, MovableType2) { - auto wrapped = wrap_void (make_int, 4); - ASSERT_EQ (4, *wrapped.unwrap ()); - ASSERT_EQ (nullptr, wrapped.unwrap ()); -} diff --git a/project/build.properties b/project/build.properties deleted file mode 100644 index 8e682c526..000000000 --- a/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=0.13.18 diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index 9f28ff443..000000000 --- a/project/plugins.sbt +++ /dev/null @@ -1,12 +0,0 @@ -// Common tox4j build rules. -addSbtPlugin("org.toktok" % "sbt-plugins" % "0.1.6") - -// Compiler version for additional plugins in this project. -scalaVersion := "2.10.7" - -// Build dependencies. -libraryDependencies ++= Seq( - "com.github.os72" % "protoc-jar" % "3.4.0" -) - -addSbtPlugin("com.trueaccord.scalapb" % "sbt-scalapb" % "0.5.43") diff --git a/project/src/main/scala/im/tox/sbt/MakeScripts.scala b/project/src/main/scala/im/tox/sbt/MakeScripts.scala deleted file mode 100644 index 18706db4b..000000000 --- a/project/src/main/scala/im/tox/sbt/MakeScripts.scala +++ /dev/null @@ -1,57 +0,0 @@ -package im.tox.sbt - -import sbt.Keys._ -import sbt._ - -@SuppressWarnings(Array( - "org.wartremover.warts.Equals" -)) -object MakeScripts extends AutoPlugin { - - override def trigger: PluginTrigger = allRequirements - - object Keys { - val makeScripts: TaskKey[Unit] = TaskKey[Unit]("makeScripts") - } - - import Keys._ - - private def classBaseName(s: String): String = { - val lastDot = s.lastIndexOf('.') - if (lastDot == -1) { - s - } else { - s.substring(lastDot + 1) - } - } - - private def makeScriptsTask(base: File, cp: Classpath, mains: Seq[String]) = { - val template = - """#!/usr/bin/env perl - |my @CLASSPATH = ( - | "%s" - |); - |exec "java", - | "-classpath", (join ":", @CLASSPATH), - | "%s", @ARGV - |""".stripMargin - for (main <- mains) { - val contents = template.format( - cp.files.get.mkString("\",\n \""), - main - ) - val out = base / "bin" / classBaseName(main) - IO.write(out, contents) - out.setExecutable(true) - } - } - - override val projectSettings: Seq[Setting[_]] = { - makeScripts := makeScriptsTask( - baseDirectory.value, - (fullClasspath in Test).value, - (discoveredMainClasses in Test).value - ) - } - -} diff --git a/project/src/main/scala/im/tox/sbt/ProtobufScalaPlugin.scala b/project/src/main/scala/im/tox/sbt/ProtobufScalaPlugin.scala deleted file mode 100644 index 5b4eb846a..000000000 --- a/project/src/main/scala/im/tox/sbt/ProtobufScalaPlugin.scala +++ /dev/null @@ -1,28 +0,0 @@ -package im.tox.sbt - -import com.github.os72.protocjar.Protoc -import com.trueaccord.scalapb.ScalaPbPlugin._ -import sbt.Keys._ -import sbt._ -import sbt.plugins.{ IvyPlugin, JvmPlugin } - -object ProtobufScalaPlugin extends AutoPlugin { - - override def trigger: PluginTrigger = allRequirements - override def requires: Plugins = IvyPlugin && JvmPlugin - - override def projectSettings: Seq[Setting[_]] = protobufSettings ++ inConfig(protobufConfig)(Seq( - runProtoc in protobufConfig := { (args: Seq[String]) => - Protoc.runProtoc(args.toArray) - }, - - javaSource := (sourceManaged in Compile).value, - scalaSource := (sourceManaged in Compile).value, - - version := "3.4.0", - javaConversions := true, - flatPackage := true, - grpc := true - )) - -} diff --git a/src/bench/java/im/tox/tox4j/av/bench/AvProfiling.scala b/src/bench/java/im/tox/tox4j/av/bench/AvProfiling.scala deleted file mode 100644 index 8859861cc..000000000 --- a/src/bench/java/im/tox/tox4j/av/bench/AvProfiling.scala +++ /dev/null @@ -1,52 +0,0 @@ -package im.tox.tox4j.av.bench - -import im.tox.tox4j.av.callbacks.ToxAvEventAdapter -import im.tox.tox4j.av.data.{ AudioChannels, SamplingRate } -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.impl.jni.{ ToxAvImplFactory, ToxCoreImplFactory } - -import scala.collection.mutable -import scala.util.Random - -@SuppressWarnings(Array("org.wartremover.warts.While")) -object AvProfiling extends App { - - ToxCoreImplFactory.withTox(ToxOptions()) { tox => - ToxAvImplFactory.withToxAv(tox) { toxAv => - val friendNumber = ToxFriendNumber.fromInt(1).get - val pcm = Array.ofDim[Short](288000) - - val random = new Random - pcm.indices.foreach { i => - pcm(i) = random.nextInt().toShort - } - - val channels = AudioChannels.Mono - val samplingRate = SamplingRate.Rate8k - - val eventListener = new ToxAvEventAdapter[Unit] - - val times = new mutable.Queue[Long] - while (true) { - val start = System.currentTimeMillis() - var i = 0 - while (i < 100) { - toxAv.invokeAudioReceiveFrame(friendNumber, pcm, channels, samplingRate) - toxAv.iterate(eventListener)(()) - i += 1 - } - val end = System.currentTimeMillis() - times.enqueue(end - start) - if (times.length > 20) { - times.dequeue() - } - - System.out.print("\u001b[2J\u001b[H") - System.out.println(times.sum / times.length) - Thread.sleep(500) - } - } - } - -} diff --git a/src/bench/java/im/tox/tox4j/av/callbacks/audio/AudioGeneratorTimingBench.scala b/src/bench/java/im/tox/tox4j/av/callbacks/audio/AudioGeneratorTimingBench.scala deleted file mode 100644 index e31ffb7e0..000000000 --- a/src/bench/java/im/tox/tox4j/av/callbacks/audio/AudioGeneratorTimingBench.scala +++ /dev/null @@ -1,40 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import im.tox.tox4j.av.data.{ AudioLength, SamplingRate } -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.bench.{ Confidence, TimingReport } - -final class AudioGeneratorTimingBench extends TimingReport { - - protected override def confidence = Confidence.normal - - private val audioLength = AudioLength.Length60 - private val audioSamplingRate = SamplingRate.Rate8k - - private val frames1k = range("frames")(1000) - - @SuppressWarnings(Array("org.wartremover.warts.While")) - def performanceTest(name: String, generator: AudioGenerator): Unit = { - performance of name in { - using(frames1k) in { frames => - var t = 0 - while (t < frames) { - generator.nextFrame16(audioLength, audioSamplingRate, t) - t += 1 - } - } - } - } - - timing.of[AudioGenerator] { - - performanceTest("ItCrowd", AudioGenerators.ItCrowd) - performanceTest("MortalKombat", AudioGenerators.MortalKombat) - performanceTest("Sine1", AudioGenerators.Sine1) - performanceTest("Sine2", AudioGenerators.Sine2) - performanceTest("Sine3", AudioGenerators.Sine3) - performanceTest("SongOfStorms", AudioGenerators.SongOfStorms) - - } - -} diff --git a/src/bench/java/im/tox/tox4j/av/callbacks/video/VideoGeneratorTimingBench.scala b/src/bench/java/im/tox/tox4j/av/callbacks/video/VideoGeneratorTimingBench.scala deleted file mode 100644 index bb6d4fe7f..000000000 --- a/src/bench/java/im/tox/tox4j/av/callbacks/video/VideoGeneratorTimingBench.scala +++ /dev/null @@ -1,56 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import im.tox.tox4j.av.data.{ Height, Width } -import im.tox.tox4j.bench.{ Confidence, TimingReport } -import im.tox.tox4j.bench.ToxBenchBase._ -import org.scalameter.api.Gen - -@SuppressWarnings(Array("org.wartremover.warts.While")) -final class VideoGeneratorTimingBench extends TimingReport { - - protected override def confidence = Confidence.normal - - val frames100k: Gen[Int] = range("frames")(500) - - timing.of[VideoGenerator] { - - performance of "Xor5" in { - - using(frames100k) in { frames => - val generator = VideoGenerators - .Xor5(VideoGenerators.DefaultWidth, VideoGenerators.DefaultHeight) - - var i = 0 - while (i < frames) { - generator.yuv(i) - i += 1 - } - } - } - - performance of "Xor5.resize(401, 401)" in { - - using(frames100k) in { frames => - val generator = VideoGenerators - .Xor5(VideoGenerators.DefaultWidth, VideoGenerators.DefaultHeight) - .resize(Width(401), Height(401)) - - var i = 0 - while (i < frames) { - generator.yuv(i) - i += 1 - } - } - } - - /* - performance of "Zero" in { - using(frames100k) in { frames => - (0 until frames) foreach VideoGenerators.ByteMax.yuv - } - } - */ - - } - -} diff --git a/src/bench/java/im/tox/tox4j/bench/Confidence.scala b/src/bench/java/im/tox/tox4j/bench/Confidence.scala deleted file mode 100644 index aa3536f7f..000000000 --- a/src/bench/java/im/tox/tox4j/bench/Confidence.scala +++ /dev/null @@ -1,98 +0,0 @@ -package im.tox.tox4j.bench - -import org.scalameter.KeyValue -import org.scalameter.api._ - -/** - * Contains some predefined confidence levels for benchmarks. - * - * VM invocation overhead numbers are experimental estimates based on running it on an i7-3632QM CPU @ 2.20GHz. It - * includes VM warm-up time. - */ -object Confidence { - /** - * This is the minimal number (2) of benchmark runs to gather a number. Use this as a one-time run to see how long a - * single run takes. The statistics from this confidence level are almost certainly useless. - * - * VM invocation overhead: 1 second. - */ - val lowest: Seq[KeyValue] = Seq[KeyValue]( - exec.benchRuns -> 2, - exec.independentSamples -> 1 - ) - - /** - * Choose if you don't yet know how long your benchmark will run and you want to get a feeling for what performance - * characteristics it exhibits. - * - * VM invocation overhead: 1 second. - */ - val low: Seq[KeyValue] = Seq[KeyValue]( - exec.benchRuns -> 10, - exec.independentSamples -> 1 - ) - - /** - * The usual configuration used in benchmarks based on [[ToxBenchBase]]. Usually this is the right choice for - * production benchmarks. - * - * VM invocation overhead: 4 seconds. - */ - val normal: Seq[KeyValue] = Seq[KeyValue]( - exec.benchRuns -> 36, - exec.independentSamples -> 3 - ) - - /** - * Choose this if the function you're benchmarking has very flaky performance. This runs fewer tests per VM - * invocation, so if you run into GC issues, you may want to choose this configuration. For tests that are flaky for - * another reason, you may find that adding `config (exec.benchRuns -> 100)` and using the [[normal]] configuration - * has a better effect. - * - * VM invocation overhead: 17 seconds. - */ - val high: Seq[KeyValue] = Seq[KeyValue]( - exec.benchRuns -> 100, - exec.independentSamples -> 10 - ) - - /** - * Run 50 independent VMs with 20 runs each. Use this if you need very high confidence in the test results. This will - * likely take a long time, regardless of how fast the actual test is, simply because it needs to spawn 50 JVM - * instances. Don't use this for production benchmarks, only for local experiments. - * - * VM invocation overhead: 55 seconds. - */ - val extreme: Seq[KeyValue] = Seq[KeyValue]( - exec.benchRuns -> 1000, - exec.independentSamples -> 50 - ) - - /** - * This level runs the benchmarks 2000 times more often than [[low]] and 200 times more often than [[high]]. If you - * run a benchmark with this confidence level and it takes less than 5 minutes to complete, you're likely to get very - * poor results, but you'll be able to convince the benchmarking framework that they are very accurate. - * - * If you find yourself using this mode, your test is most likely very poorly parametrised and you would benefit more - * from making the snippet itself take more time by increasing the iteration count inside the snippet. - * - * For comparison, the iterationInterval test with [[ToxBenchBase.iterations10k]] takes 2 seconds on [[low]] - * and 7 minutes and 15 seconds on [[insane]]. That is 216 times slower. On [[normal]], it takes 4 seconds (108 times - * faster than [[insane]]). On [[high]], it takes 9 seconds (48 times faster), and on [[extreme]], it takes 57 seconds - * (7 times faster). Only on [[insane]], this test actually has accurate results. - * - * This shows that [[ToxBenchBase.iterations10k]] is the wrong parameter set for the iterationInterval test. - * Running with [[ToxBenchBase.iterations100k]] on [[normal]] takes 9 seconds and gives a linear graph with - * reasonable confidence. Increasing the number of [[exec.benchRuns]] to 100 produces a completely accurate linear - * graph with high confidence in 18 seconds. - * - * To summarise, this mode rarely adds value over [[high]] or [[extreme]]. Don't use this mode unless you want to - * stress-test your CPU (and for that there are better tools). If you use this mode, you're insane. - * - * VM invocation overhead: 2 minutes, 11 seconds. - */ - val insane: Seq[KeyValue] = Seq[KeyValue]( - exec.benchRuns -> 20000, - exec.independentSamples -> 100 - ) -} diff --git a/src/bench/java/im/tox/tox4j/bench/MemoryReport.scala b/src/bench/java/im/tox/tox4j/bench/MemoryReport.scala deleted file mode 100644 index 7ff98f212..000000000 --- a/src/bench/java/im/tox/tox4j/bench/MemoryReport.scala +++ /dev/null @@ -1,28 +0,0 @@ -package im.tox.tox4j.bench - -import org.scalameter.api._ - -import scala.reflect.ClassTag - -/** - * Base class for memory benchmarks. Use the `memory` method at the top level - * and any other DSL statements inside. Do not use `performance of` at the - * top level, as it will not use [[ToxBenchBase.defaultConfig]]. - */ -@SuppressWarnings(Array("org.wartremover.warts.LeakingSealed")) -abstract class MemoryReport extends ToxBenchBase { - - require(getClass.getSimpleName.endsWith("MemoryBench")) - - override def measurer: Measurer[Double] = new Executor.Measurer.MemoryFootprint - - object memory { // scalastyle:ignore object.name - def of(modulename: String): Scope = { - performance of (modulename + " (memory)") config defaultConfig - } - def of[T](block: => Unit)(implicit classTag: ClassTag[T]): Unit = { - of(classTag.runtimeClass.getSimpleName).in(block) - } - } - -} diff --git a/src/bench/java/im/tox/tox4j/bench/TimingReport.scala b/src/bench/java/im/tox/tox4j/bench/TimingReport.scala deleted file mode 100644 index b663c09fd..000000000 --- a/src/bench/java/im/tox/tox4j/bench/TimingReport.scala +++ /dev/null @@ -1,24 +0,0 @@ -package im.tox.tox4j.bench - -import scala.reflect.ClassTag - -/** - * Base class for timing benchmarks. Use the `timing` method at the top level - * and any other DSL statements inside. Do not use `performance of` at the - * top level, as it will not use [[ToxBenchBase.defaultConfig]]. - */ -@SuppressWarnings(Array("org.wartremover.warts.LeakingSealed")) -abstract class TimingReport extends ToxBenchBase { - - require(getClass.getSimpleName.endsWith("TimingBench")) - - object timing extends Serializable { // scalastyle:ignore object.name - def of(modulename: String): Scope = { - performance of modulename config defaultConfig - } - def of[T](block: => Unit)(implicit classTag: ClassTag[T]): Unit = { - of(classTag.runtimeClass.getSimpleName).in(block) - } - } - -} diff --git a/src/bench/java/im/tox/tox4j/bench/ToxBenchBase.scala b/src/bench/java/im/tox/tox4j/bench/ToxBenchBase.scala deleted file mode 100644 index 685cde127..000000000 --- a/src/bench/java/im/tox/tox4j/bench/ToxBenchBase.scala +++ /dev/null @@ -1,280 +0,0 @@ -package im.tox.tox4j.bench - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.bench.picklers.Implicits._ -import im.tox.tox4j.core._ -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.exceptions.ToxNewException -import im.tox.tox4j.core.options.{ SaveDataOptions, ToxOptions } -import im.tox.tox4j.impl.jni.{ ToxAvImpl, ToxCoreImpl, ToxCoreImplFactory } -import im.tox.tox4j.testing.GetDisjunction._ -import org.scalameter.api._ - -import scala.collection.immutable -import scala.util.Random - -/** - * The two test base classes [[MemoryReport]] and [[TimingReport]] are based on this common class. It provides some - * basic utility methods and the default base configuration. See the companion object for all the common dimensions used - * throughout the benchmarks. Don't inherit from this class directly. Use its two subclasses, instead. - * - * See [[Confidence]] for notes on confidence levels in the benchmarks. Our benchmarks generally have a test granularity - * of 10, meaning that we evenly divide the axis into 10 samples. For example for testing [[ToxCore.bootstrap]], we run - * it with 10, 20, 30, ..., 100 iterations. We aim for the smallest number to take more than 1ms and the largest to take - * less than 100ms. If the timing is on average somewhere between 10 and 100ms, you generally get useful results with - * [[Confidence.normal]]. - * - * Some methods are very fast and may need more iterations to give good results, since commodity computer timers often - * have flaky timing at high resolutions. Some methods may take too much memory when ran with high values of their - * dimensions. In such cases, consider using a higher [[Confidence]] configuration or simply increasing the value of - * [[exec.benchRuns]] for a specific test. - * - * We generally aim for a confidence level of 95% and a confidence interval of less than 5% in each direction. Higher - * confidence usually gives no better insight. Lower levels may turn out to be very flaky and hinder effective - * regression testing. - */ -@SuppressWarnings(Array("org.wartremover.warts.LeakingSealed")) -private[bench] abstract class ToxBenchBase extends Bench.OfflineRegressionReport { - - protected def confidence = Confidence.normal - - final override def defaultConfig: Context = Context.empty ++ Context( - verbose -> false, - reports.resultDir -> "target/benchmarks", - exec.jvmflags -> List( - // "-XX:+UnlockDiagnosticVMOptions", - // "-XX:CompileCommand=print *Xor1Opt$.yuv", - "-Djava.library.path=" + sys.props("java.library.path") - ), - exec.reinstantiation.frequency -> 1000, - exec.reinstantiation.fullGC -> true - ) ++ confidence - - /** - * Helper methods to run with tupled generators. Tupled [[Gen]]s run with a cross product of their arguments, so use - * these with care. You can easily run into combinatorial explosion. - */ - def using[A, B](a: Gen[A], b: Gen[B]): Using[(A, B)] = using(Gen.crossProduct(a, b)) - def using[A, B, C](a: Gen[A], b: Gen[B], c: Gen[C]): Using[(A, B, C)] = using(Gen.crossProduct(a, b, c)) - def using[A, B, C, D](a: Gen[A], b: Gen[B], c: Gen[C], d: Gen[D]): Using[(A, B, C, D)] = using(Gen.crossProduct(a, b, c, d)) - - /** - * The same as the above, but adding [[toxInstance]] as last element of the tuple. - */ - def usingTox[A](a: Gen[A]): Using[(A, ToxCore)] = using(a, toxInstance) - def usingTox[A, B](a: Gen[A], b: Gen[B]): Using[(A, B, ToxCore)] = using(a, b, toxInstance) - def usingTox[A, B, C](a: Gen[A], b: Gen[B], c: Gen[C]): Using[(A, B, C, ToxCore)] = using(a, b, c, toxInstance) - - /** - * The same as the above, but adding [[toxAvInstance]] as last element of the tuple. - */ - def usingToxAv[A](a: Gen[A]): Using[(A, ToxAv)] = using(a, toxAvInstance) - def usingToxAv[A, B](a: Gen[A], b: Gen[B]): Using[(A, B, ToxAv)] = using(a, b, toxAvInstance) - def usingToxAv[A, B, C](a: Gen[A], b: Gen[B], c: Gen[C]): Using[(A, B, C, ToxAv)] = using(a, b, c, toxAvInstance) - -} - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -object ToxBenchBase { - - /** - * We keep a private PRNG for various generators. - */ - private val random = new Random - - /** - * Set [[ToxOptions.startPort]] to a lower number so we have more ports to choose from. This reduces the chance of a - * [[ToxNewException.Code.PORT_ALLOC]] error occurring before all old [[ToxCore]] instances have been garbage - * collected. - */ - private val toxOptions = ToxOptions(startPort = 30000) - - /** - * Create a number of valid Tox Addresses. These addresses are guaranteed to pass checksum tests, but are very - * unlikely to be owned by actual people. - * - * @param sz The number of Tox Addresses to generate. - * @return A [[Seq]] of Tox Addresses in [[Byte]] arrays. - */ - def friendAddresses(sz: Int): Seq[ToxFriendAddress] = { - for (_ <- 0 until sz) yield { - ToxCoreImplFactory.withToxUnit(_.getAddress) - } - } - - /** - * Create a number of valid public keys. These pass the validity tests in toxcore, but may not have private key - * counterparts. - * - * @param sz The number of public keys to generate. - * @return A [[Seq]] containing public keys in [[Byte]] arrays. - */ - def friendKeys(sz: Int): Seq[ToxPublicKey] = { - for (_ <- 0 until sz) yield { - val key = Array.ofDim[Byte](ToxCoreConstants.PublicKeySize) - // noinspection SideEffectsInMonadicTransformation - random.nextBytes(key) - // Key needs the last byte to be 0 or toxcore will complain about checksums. - key(key.length - 1) = 0 - ToxPublicKey.unsafeFromValue(key) - } - } - - /** - * Make a [[ToxCore]] instance with the common [[toxOptions]], set a name and status message, and add a number of - * imaginary friends. - * - * @param friendCount The number of friends to add. - * @return A new [[ToxCore]] instance with a name, status message, and friendCount friends. - */ - def makeToxWithFriends(friendCount: Int): ToxCore = { - val tox = ToxCoreImplFactory(toxOptions) - tox.setName(ToxNickname.fromValue(Array.ofDim(ToxCoreConstants.MaxNameLength)).toOption.get) - tox.setStatusMessage(ToxStatusMessage.fromValue(Array.ofDim(ToxCoreConstants.MaxStatusMessageLength)).toOption.get) - friendKeys(friendCount) foreach tox.addFriendNorequest - tox - } - - /** - * The same as [[makeToxWithFriends]], but only adds a single friend. - * - * The [[toxInstance]] generator uses this to create its values. Use this if you have more specific needs than what - * the generator provides (e.g. if you need to mutate its state). - * - * @return A new [[ToxCore]] instance with a name, status message, and 1 friend. - */ - def makeTox(): ToxCore = { - makeToxWithFriends(1) - } - - // Base generators. - - /** - * Generator for a [[ToxCore]] instance. Note that this instance may be shared between many test runs, so your test - * should not mutate it. If it does, it needs to ensure that it returns to an equivalent state as before the test - * began. In particular, if you add friends, you need to ensure that you remove all but 1 friends on tearDown. - */ - val toxInstance: Gen[ToxCore] = Gen.single("tox")(classOf[ToxCoreImpl]).map(_ => makeTox()).cached - - /** - * Generator for a [[ToxAv]] instance. - */ - val toxAvInstance: Gen[ToxAv] = toxInstance.map(tox => new ToxAvImpl(tox.asInstanceOf[ToxCoreImpl]): ToxAv).cached - - /** - * Helper function to create a range axis evenly divided into 10 samples. The range starts with `upto / 10` and ends - * with `upto`. - * - * This method requires the end value to be divisible by 10. If you have specific needs that require an indivisible - * end number, use [[Gen.range]] directly. - * - * @param axisName The name of the dimension. - * @param upto The highest value this generator will produce. - * @return A generator from `upto / 10` to `upto`. - */ - def range(axisName: String)(upto: Int): Gen[Int] = { - require(upto % 10 == 0) - Gen.range(axisName)(upto / 10, upto, upto / 10) - } - - val nodes: Gen[Int] = range("nodes")(100) - val instances: Gen[Int] = range("instances")(100) - - def friends: Int => Gen[Int] = range("friends") - val friends1k: Gen[Int] = friends(1000) - val friends10k: Gen[Int] = friends(10000) - - def iterations: Int => Gen[Int] = range("iterations") - val iterations1k: Gen[Int] = iterations(1000) - val iterations10k: Gen[Int] = iterations(10000) - val iterations100k: Gen[Int] = iterations(100000) - val iterations1000k: Gen[Int] = iterations(1000000) - - val nameLengths: Gen[Int] = Gen.range("name length")(0, ToxCoreConstants.MaxNameLength, 8) - val statusMessageLengths: Gen[Int] = Gen.range("status message length")(0, ToxCoreConstants.MaxStatusMessageLength, 100) - - // Derived generators - - /** - * A caching function object to create a single Tox instance with a number of friends. The caching ensures that for - * each friend count, there is exactly one instance with that number of friends. - * - * Experiments have shown that this custom caching takes 2.3GB for a 10-step range of 1000-10000 friends instead of - * 2.7GB when using [[org.scalameter.Gen.cached]]. It is also about 15% faster. - * - * Do not mutate objects returned by this function. - */ - object toxWithFriends extends (Int => ToxCore) with Serializable { - /** - * [[immutable.HashMap]] was chosen here for its semantics, not efficiency. A [[Vector]][([[Int]], [[ToxCore]])] or - * an [[Array]] would possibly be faster, but the map is easier to use. - */ - @transient - private var toxesWithFriends = immutable.HashMap.empty[Int, ToxCore] - - override def apply(sz: Int): ToxCore = { - toxesWithFriends.get(sz) match { - case Some(tox) => - tox - case None => - val tox = makeToxWithFriends(sz) - toxesWithFriends = toxesWithFriends.updated(sz, tox) - tox - } - } - } - - val toxWithFriends1k = friends1k map toxWithFriends - val toxWithFriends10k = friends10k map toxWithFriends - - /** - * Extract a random friend list from a Tox instance. If limit is 0 or omitted, extract the entire friend list. If - * limit is non-zero, a slice of the friend list is taken with at most that size. - * - * The friend list is randomly shuffled before it is returned, so each time this function is called, you will get a - * different list. - * - * @param limit Maximum number of friends to return. To get the same number on every run, choose at most the minimum - * number of friends generated by your chosen generator. - * @param tox The Tox instance to extract the friends from. - * @return A pair containing the passed Tox instance and a random slice of the friend list. - */ - def toxAndFriendNumbers(limit: Int = 0)(tox: ToxCore): (Seq[ToxFriendNumber], ToxCore) = { - val friendList = random.shuffle(tox.getFriendNumbers.toSeq) - if (limit != 0) { - (friendList.slice(0, limit), tox) - } else { - (friendList, tox) - } - } - - /** - * The same as [[toxAndFriendNumbers]] but returns the friends' public keys instead of friend numbers. - * - * @param limit Maximum number of friends to return. To get the same number on every run, choose at most the minimum - * number of friends generated by your chosen generator. - * @param tox The Tox instance to extract the friends from. - * @return A pair containing the passed Tox instance and a random slice of the friend list. - */ - def toxAndFriendKeys(limit: Int)(tox: ToxCore): (Seq[ToxPublicKey], ToxCore) = { - toxAndFriendNumbers(limit)(tox) match { - case (friendList, _) => (friendList map tox.getFriendPublicKey, tox) - } - } - - /** - * Produces [[instances]] valid Tox save data arrays as produced by [[ToxCore.getSavedata]]. - */ - val toxSaves: Gen[Seq[ToxOptions]] = { - for (sz <- instances) yield { - for (_ <- 0 until sz) yield { - ToxOptions(saveData = SaveDataOptions.ToxSave(makeTox().getSavedata)) - } - } - } - - val names: Gen[ToxNickname] = nameLengths.map(Array.ofDim[Byte]).map(ToxNickname.fromValue(_).toOption.get) - val statusMessages: Gen[ToxStatusMessage] = statusMessageLengths.map(Array.ofDim[Byte]).map(ToxStatusMessage.fromValue(_).toOption.get) - -} diff --git a/src/bench/java/im/tox/tox4j/bench/TravisReport.scala b/src/bench/java/im/tox/tox4j/bench/TravisReport.scala deleted file mode 100644 index e7594d0df..000000000 --- a/src/bench/java/im/tox/tox4j/bench/TravisReport.scala +++ /dev/null @@ -1,9 +0,0 @@ -package im.tox.tox4j.bench - -@SuppressWarnings(Array( - "org.wartremover.warts.Equals", - "org.wartremover.warts.LeakingSealed" -)) -abstract class TravisReport extends ToxBenchBase { - require(getClass.getSimpleName == "TravisBenchSuite") -} diff --git a/src/bench/java/im/tox/tox4j/bench/picklers/ClassPickler.scala b/src/bench/java/im/tox/tox4j/bench/picklers/ClassPickler.scala deleted file mode 100644 index 3d0be1830..000000000 --- a/src/bench/java/im/tox/tox4j/bench/picklers/ClassPickler.scala +++ /dev/null @@ -1,18 +0,0 @@ -package im.tox.tox4j.bench.picklers - -import org.scalameter.picklers.Implicits._ -import org.scalameter.picklers.Pickler - -final class ClassPickler[T] extends Pickler[Class[T]] { - - override def pickle(x: Class[T]): Array[Byte] = { - implicitly[Pickler[String]].pickle(x.getCanonicalName) - } - - override def unpickle(a: Array[Byte], from: Int): (Class[T], Int) = { - val (className, next) = implicitly[Pickler[String]].unpickle(a, from) - val unpickledClass = Class.forName(className) - (unpickledClass.asInstanceOf[Class[T]], next) - } - -} diff --git a/src/bench/java/im/tox/tox4j/bench/picklers/Implicits.scala b/src/bench/java/im/tox/tox4j/bench/picklers/Implicits.scala deleted file mode 100644 index b8bfb7bf2..000000000 --- a/src/bench/java/im/tox/tox4j/bench/picklers/Implicits.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j.bench.picklers - -object Implicits { - implicit def classPickler[T]: ClassPickler[T] = new ClassPickler[T] -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/BenchWip.scala b/src/bench/java/im/tox/tox4j/core/bench/BenchWip.scala deleted file mode 100644 index fb4317dc3..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/BenchWip.scala +++ /dev/null @@ -1,42 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.bench.{ Confidence, TimingReport } -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter - -/** - * Work in progress benchmarks. - */ -final class BenchWip extends TimingReport { - - protected override def confidence = Confidence.normal - - private val eventListener = new ToxCoreEventAdapter[Unit] - - timing.of[ToxCore] { - - measure method "iterate+friends" in { - using(iterations1k, toxWithFriends1k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterate(eventListener)(())) - } - } - - } - - /** - * Benchmarks we're not currently working on. - */ - object HoldingPen { - - measure method "iterationInterval" in { - usingTox(iterations1k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterationInterval) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/CoreCallbackTimingBench.scala b/src/bench/java/im/tox/tox4j/core/bench/CoreCallbackTimingBench.scala deleted file mode 100644 index 0c882d6da..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/CoreCallbackTimingBench.scala +++ /dev/null @@ -1,108 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxNickname, ToxPublicKey } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxMessageType, ToxUserStatus } -import im.tox.tox4j.core.{ ToxCore, ToxCoreConstants } -import im.tox.tox4j.impl.jni.ToxCoreImpl -import im.tox.tox4j.testing.GetDisjunction._ - -final class CoreCallbackTimingBench extends TimingReport { - - private val eventListener = new ToxCoreEventAdapter[Unit] - - private val friendNumber = ToxFriendNumber.fromInt(1).get - private val publicKey = ToxPublicKey.fromValue(Array.ofDim[Byte](ToxCoreConstants.PublicKeySize)).toOption.get - private val nickname = ToxNickname.fromValue(Array.ofDim[Byte](ToxNickname.MaxSize)).toOption.get - private val data = Array.ofDim[Byte](ToxCoreConstants.MaxCustomPacketSize) - - def invokePerformance(method: String, f: ToxCoreImpl => Unit): Unit = { - performance of method in { - usingTox(iterations1k) in { - case (sz, tox: ToxCoreImpl) => - (0 until sz) foreach { _ => - f(tox) - tox.iterate(eventListener)(()) - } - } - } - } - - def invokeAllCallbacks(tox: ToxCoreImpl): Unit = { - tox.invokeFileChunkRequest(friendNumber, 2, 3, 4) - tox.invokeFileRecv(friendNumber, 2, 3, 4, data) - tox.invokeFileRecvChunk(friendNumber, 2, 3, data) - tox.invokeFileRecvControl(friendNumber, 2, ToxFileControl.PAUSE) - tox.invokeFriendConnectionStatus(friendNumber, ToxConnection.TCP) - tox.invokeFriendLosslessPacket(friendNumber, data) - tox.invokeFriendLossyPacket(friendNumber, data) - tox.invokeFriendMessage(friendNumber, ToxMessageType.NORMAL, 2, data) - tox.invokeFriendName(friendNumber, nickname) - tox.invokeFriendReadReceipt(friendNumber, 2) - tox.invokeFriendRequest(publicKey, 1, data) - tox.invokeFriendStatus(friendNumber, ToxUserStatus.AWAY) - tox.invokeFriendStatusMessage(friendNumber, data) - tox.invokeFriendTyping(friendNumber, isTyping = true) - tox.invokeSelfConnectionStatus(ToxConnection.TCP) - } - - timing.of[ToxCore] { - - measure method "iterate" in { - usingTox(iterations1k) in { - case (sz, tox) => - (0 until sz) foreach { _ => - tox.iterate(eventListener)(()) - } - } - } - - performance of "enqueuing a callback" in { - usingTox(iterations1k) in { - case (sz, tox: ToxCoreImpl) => - (0 until sz) foreach { _ => - tox.invokeFileChunkRequest(friendNumber, 2, 3, 4) - } - } - } - - performance of "enqueue all callbacks" in { - usingTox(iterations1k) in { - case (sz, tox: ToxCoreImpl) => - (0 until sz) foreach { _ => - invokeAllCallbacks(tox) - } - } - } - - performance of "call all callbacks" in { - usingTox(iterations1k) in { - case (sz, tox: ToxCoreImpl) => - (0 until sz) foreach { _ => - invokeAllCallbacks(tox) - tox.iterate(eventListener)(()) - } - } - } - - invokePerformance("invokeFileChunkRequest", _.invokeFileChunkRequest(friendNumber, 2, 3, 4)) - invokePerformance("invokeFileRecv", _.invokeFileRecv(friendNumber, 2, 3, 4, data)) - invokePerformance("invokeFileRecvChunk", _.invokeFileRecvChunk(friendNumber, 2, 3, data)) - invokePerformance("invokeFileRecvControl", _.invokeFileRecvControl(friendNumber, 2, ToxFileControl.PAUSE)) - invokePerformance("invokeFriendConnectionStatus", _.invokeFriendConnectionStatus(friendNumber, ToxConnection.TCP)) - invokePerformance("invokeFriendLosslessPacket", _.invokeFriendLosslessPacket(friendNumber, data)) - invokePerformance("invokeFriendLossyPacket", _.invokeFriendLossyPacket(friendNumber, data)) - invokePerformance("invokeFriendMessage", _.invokeFriendMessage(friendNumber, ToxMessageType.NORMAL, 2, data)) - invokePerformance("invokeFriendName", _.invokeFriendName(friendNumber, nickname)) - invokePerformance("invokeFriendReadReceipt", _.invokeFriendReadReceipt(friendNumber, 2)) - invokePerformance("invokeFriendRequest", _.invokeFriendRequest(publicKey, 1, data)) - invokePerformance("invokeFriendStatus", _.invokeFriendStatus(friendNumber, ToxUserStatus.AWAY)) - invokePerformance("invokeFriendStatusMessage", _.invokeFriendStatusMessage(friendNumber, data)) - invokePerformance("invokeFriendTyping", _.invokeFriendTyping(friendNumber, isTyping = true)) - invokePerformance("invokeSelfConnectionStatus", _.invokeSelfConnectionStatus(ToxConnection.TCP)) - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/FriendListTimingBench.scala b/src/bench/java/im/tox/tox4j/core/bench/FriendListTimingBench.scala deleted file mode 100644 index 2cf014f33..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/FriendListTimingBench.scala +++ /dev/null @@ -1,100 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.core.typesafe.Equals._ -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.bench.{ Confidence, TimingReport } -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendRequestMessage } -import im.tox.tox4j.testing.GetDisjunction._ -import org.scalameter.Gen -import org.scalameter.picklers.Implicits._ - -final class FriendListTimingBench extends TimingReport { - - /** - * Deletes all but 1 friends. - */ - private def clearFriendList(pair: (_, ToxCore)): Unit = { - val tox = pair._2 - tox.getFriendNumbers.tail foreach tox.deleteFriend - } - - /** - * Fills the friend list back up with the expected number of friends. - */ - private def refillFriendList(pair: (Seq[ToxFriendNumber], ToxCore)): Unit = { - val (friendList, tox) = pair - val missing = friendList.length - tox.getFriendList.length - assert(missing >= 0) - friendKeys(missing) foreach tox.addFriendNorequest - } - - timing.of[ToxCore] { - - measure method "addFriend" in { - using(friends(100) map friendAddresses, toxInstance.cached) tearDown clearFriendList in { - case (friendList, tox) => - friendList foreach (tox.addFriend(_, ToxFriendRequestMessage.fromValue(Array.ofDim(1)).toOption.get)) - } - } - - measure method "addFriendNoRequest" in { - using(friends(100) map friendKeys, toxInstance.cached) tearDown clearFriendList in { - case (friendList, tox) => - friendList foreach tox.addFriendNorequest - } - } - - performance of "deleting all friends" in { - using(friends1k.map(makeToxWithFriends).map(toxAndFriendNumbers())) tearDown refillFriendList config (Confidence.high: _*) in { - case (friendList, tox) => - assert(friendList.length >= 100) - assert(friendList.length <= 1000) - assert(friendList.length % 100 === 0) - friendList foreach tox.deleteFriend - } - } - - measure method "getFriendList" in { - using(toxWithFriends1k, Gen.single("iterations")(100)) in { - case (tox, sz) => - (0 until sz) foreach (_ => tox.getFriendList) - } - } - - measure method "friendExists" in { - using(toxWithFriends1k map toxAndFriendNumbers(100), Gen.single("iterations")(100000)) in { - case ((friendNumbers, tox), iterations) => - (0 until iterations / friendNumbers.length) foreach { _ => - friendNumbers foreach { i => - tox.friendExists(i) - } - } - } - } - - measure method "getFriendPublicKey" in { - using(toxWithFriends1k map toxAndFriendNumbers(100), Gen.single("iterations")(100000)) in { - case ((friendNumbers, tox), iterations) => - // Divide iterations by the number of friends we look up, so we do $iterations calls to getFriendPublicKey. - (0 until iterations / friendNumbers.length) foreach { _ => - // Look up a random 100 friends. The toxWithFriends1k generator produces at least 100 friends. - friendNumbers foreach { i => - tox.getFriendPublicKey(i) - } - } - } - } - - measure method "getFriendByPublicKey" in { - using(toxWithFriends1k map toxAndFriendKeys(limit = 100), Gen.single("iterations")(100)) in { - case ((friendList, tox), iterations) => - (0 until iterations) foreach { _ => - friendList foreach (key => tox.friendByPublicKey(key)) - } - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/GettersTimingBench.scala b/src/bench/java/im/tox/tox4j/core/bench/GettersTimingBench.scala deleted file mode 100644 index 5e7356f89..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/GettersTimingBench.scala +++ /dev/null @@ -1,97 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.ToxCore - -final class GettersTimingBench extends TimingReport { - - timing.of[ToxCore] { - - measure method "getAddress" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getAddress) - } - } - - measure method "getDhtId" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getDhtId) - } - } - - measure method "getNoSpam" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getNospam) - } - } - - measure method "name" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getName) - } - } - - measure method "getPublicKey" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getPublicKey) - } - } - - measure method "getSecretKey" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getSecretKey) - } - } - - measure method "getStatus" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getStatus) - } - } - - measure method "getStatusMessage" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getStatusMessage) - } - } - - measure method "getUdpPort" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getUdpPort) - } - } - - measure method "getNoSpam" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getNospam) - } - } - - measure method "getStatus" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getStatus) - } - } - - measure method "getUdpPort" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.getUdpPort) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/IterateMemoryBench.scala b/src/bench/java/im/tox/tox4j/core/bench/IterateMemoryBench.scala deleted file mode 100644 index 934077f7e..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/IterateMemoryBench.scala +++ /dev/null @@ -1,27 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.MemoryReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.ToxCore - -final class IterateMemoryBench extends MemoryReport { - - memory.of[ToxCore] { - - measure method "iterate" in { - usingTox(iterations1k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterate(null)(())) - } - } - - measure method "iterationInterval" in { - usingTox(iterations1k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterationInterval) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/IterateTimingBench.scala b/src/bench/java/im/tox/tox4j/core/bench/IterateTimingBench.scala deleted file mode 100644 index 75371d557..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/IterateTimingBench.scala +++ /dev/null @@ -1,41 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter -import org.scalameter.KeyValue -import org.scalameter.api._ - -final class IterateTimingBench extends TimingReport { - - protected override def confidence = Seq[KeyValue](exec.benchRuns -> 100) - - private val eventListener = new ToxCoreEventAdapter[Unit] - - timing.of[ToxCore] { - - measure method "iterate" in { - usingTox(iterations10k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterate(eventListener)(())) - } - } - - measure method "iterationInterval" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterationInterval) - } - } - - measure method "iterate+friends" in { - using(iterations1k, toxWithFriends1k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.iterate(eventListener)(())) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/SettersTimingBench.scala b/src/bench/java/im/tox/tox4j/core/bench/SettersTimingBench.scala deleted file mode 100644 index 614deed50..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/SettersTimingBench.scala +++ /dev/null @@ -1,52 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxUserStatus - -final class SettersTimingBench extends TimingReport { - - private val friendNumber = ToxFriendNumber.fromInt(0).get - - timing.of[ToxCore] { - - measure method "setNoSpam" in { - usingTox(iterations100k) in { - case (sz, tox) => - (0 until sz) foreach (i => tox.setNospam(i)) - } - } - - measure method "setName" in { - usingTox(names, iterations10k) in { - case (name, sz, tox) => - (0 until sz) foreach (_ => tox.setName(name)) - } - } - - measure method "setStatusMessage" in { - usingTox(statusMessages, iterations10k) in { - case (statusMessage, sz, tox) => - (0 until sz) foreach (_ => tox.setStatusMessage(statusMessage)) - } - } - - measure method "setStatus" in { - usingTox(iterations10k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.setStatus(ToxUserStatus.AWAY)) - } - } - - measure method "setTyping" in { - usingTox(iterations10k) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.setTyping(friendNumber, typing = true)) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/ToxCoreTimingBench.scala b/src/bench/java/im/tox/tox4j/core/bench/ToxCoreTimingBench.scala deleted file mode 100644 index 3ca3f74dd..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/ToxCoreTimingBench.scala +++ /dev/null @@ -1,33 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.core.network.Port -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.data.ToxPublicKey -import im.tox.tox4j.core.{ ToxCore, ToxCoreConstants } -import im.tox.tox4j.testing.GetDisjunction._ - -final class ToxCoreTimingBench extends TimingReport { - - private val port = Port.fromInt(8080).get - private val publicKey = ToxPublicKey.fromValue(Array.ofDim(ToxCoreConstants.PublicKeySize)).toOption.get - - timing.of[ToxCore] { - - measure method "bootstrap" in { - usingTox(nodes) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.bootstrap("localhost", port, publicKey)) - } - } - - measure method "addTcpRelay" in { - usingTox(nodes) in { - case (sz, tox) => - (0 until sz) foreach (_ => tox.addTcpRelay("localhost", port, publicKey)) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/core/bench/TravisBenchSuite.scala b/src/bench/java/im/tox/tox4j/core/bench/TravisBenchSuite.scala deleted file mode 100644 index f3c40d849..000000000 --- a/src/bench/java/im/tox/tox4j/core/bench/TravisBenchSuite.scala +++ /dev/null @@ -1,10 +0,0 @@ -package im.tox.tox4j.core.bench - -import im.tox.tox4j.bench.TravisReport - -final class TravisBenchSuite extends TravisReport { - - include[IterateTimingBench] - include[IterateMemoryBench] - -} diff --git a/src/bench/java/im/tox/tox4j/impl/jni/AudioReceiveFrameTimingBench.scala b/src/bench/java/im/tox/tox4j/impl/jni/AudioReceiveFrameTimingBench.scala deleted file mode 100644 index 32b182846..000000000 --- a/src/bench/java/im/tox/tox4j/impl/jni/AudioReceiveFrameTimingBench.scala +++ /dev/null @@ -1,38 +0,0 @@ -package im.tox.tox4j.impl.jni - -import java.nio.ByteBuffer - -import com.google.protobuf.ByteString -import im.tox.tox4j.av.callbacks.ToxAvEventAdapter -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.proto.{ AvEvents, AudioReceiveFrame } -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ - -final class AudioReceiveFrameTimingBench extends TimingReport { - - timing.of[AudioReceiveFrame] { - - val audioLength = AudioLength.Length60 - val channels = AudioChannels.Mono - val samplingRate = SamplingRate.Rate48k - - val frame = AudioReceiveFrame(0, ByteString.copyFrom(ByteBuffer.wrap(Array.ofDim[Byte]( - SampleCount(audioLength, samplingRate).value * channels.value * 2 - )))) - - val frames = range("frames")(10000).map { count => - AvEvents(audioReceiveFrame = (0 until count) map (_ => frame)).toByteArray - } - - val handler = new ToxAvEventAdapter[Unit] with Serializable - - performance of "60ms per frame at 48k" in { - using(frames) in { eventData => - ToxAvEventDispatch.dispatch(handler, eventData)(()) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/impl/jni/AvCallbackTimingBench.scala b/src/bench/java/im/tox/tox4j/impl/jni/AvCallbackTimingBench.scala deleted file mode 100644 index 197d61d5f..000000000 --- a/src/bench/java/im/tox/tox4j/impl/jni/AvCallbackTimingBench.scala +++ /dev/null @@ -1,52 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.callbacks.ToxAvEventAdapter -import im.tox.tox4j.av.data.{ AudioChannels, AudioLength, SampleCount, SamplingRate } -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.data.ToxFriendNumber - -final class AvCallbackTimingBench extends TimingReport { - - private val audioLength = AudioLength.Length60 - private val channels = AudioChannels.Mono - private val samplingRate = SamplingRate.Rate48k - - private val pcm = Array.ofDim[Short]( - SampleCount(audioLength, samplingRate).value * channels.value - ) - - private val friendNumber = ToxFriendNumber.fromInt(1).get - - object EventListener extends ToxAvEventAdapter[Unit] with Serializable - - timing of s"AudioReceiveFrame (${pcm.length} samples)" in { - - val invokeAll: (((Int, ToxAv)) => Unit) = { - case (frames, toxAv) => - // Invoke all the events beforehand. - (0 until frames) foreach { _ => - toxAv.invokeAudioReceiveFrame(friendNumber, pcm, channels, samplingRate) - } - } - - measure method "ToxAv.iterate" in { - usingToxAv(range("frames")(10000)) setUp invokeAll in { - case (frames, av) => - // Then fetch and process them in the timed section. - av.iterate(EventListener)(()) - } - } - - measure method "ToxAvJni.toxAvIterate" in { - usingToxAv(range("frames")(10000)) setUp invokeAll in { - case (frames, av: ToxAvImpl) => - // Then fetch them in the timed section. - ToxAvJni.toxavIterate(av.instanceNumber) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/impl/jni/AvInvokeTimingBench.scala b/src/bench/java/im/tox/tox4j/impl/jni/AvInvokeTimingBench.scala deleted file mode 100644 index 1a5b5ff87..000000000 --- a/src/bench/java/im/tox/tox4j/impl/jni/AvInvokeTimingBench.scala +++ /dev/null @@ -1,37 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.data.{ AudioChannels, AudioLength, SampleCount, SamplingRate } -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.data.ToxFriendNumber - -final class AvInvokeTimingBench extends TimingReport { - - private val audioLength = AudioLength.Length60 - private val channels = AudioChannels.Mono - private val samplingRate = SamplingRate.Rate48k - - private val pcm = Array.ofDim[Short]( - SampleCount(audioLength, samplingRate).value * channels.value - ) - - private val friendNumber = ToxFriendNumber.fromInt(1).get - - timing.of[ToxAv] { - - measure method "invokeAudioReceiveFrame" in { - usingToxAv(range("frames")(10000)) tearDown { - case (frames, av: ToxAvImpl) => - ToxAvJni.toxavIterate(av.instanceNumber) - } in { - case (frames, av) => - (0 until frames) foreach { _ => - av.invokeAudioReceiveFrame(friendNumber, pcm, channels, samplingRate) - } - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/impl/jni/VideoCallbackTimingBench.scala b/src/bench/java/im/tox/tox4j/impl/jni/VideoCallbackTimingBench.scala deleted file mode 100644 index b9d673968..000000000 --- a/src/bench/java/im/tox/tox4j/impl/jni/VideoCallbackTimingBench.scala +++ /dev/null @@ -1,56 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.callbacks.ToxAvEventAdapter -import im.tox.tox4j.av.callbacks.video.VideoGenerators -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.data.ToxFriendNumber - -final class VideoCallbackTimingBench extends TimingReport { - - private val VideoLength = 30 - - private val friendNumber = ToxFriendNumber.fromInt(1).get - - private val width = VideoGenerators.DefaultWidth - private val height = VideoGenerators.DefaultHeight - private val yStride = width.value + 80 - private val uStride = yStride / 2 - private val vStride = yStride / 2 - - private val y = Array.ofDim[Byte](yStride * height.value) - private val u = Array.ofDim[Byte](uStride * height.value / 2) - private val v = Array.ofDim[Byte](uStride * height.value / 2) - - object EventListener extends ToxAvEventAdapter[Unit] with Serializable - - timing of s"VideoReceiveFrame ${width}x$height" in { - - val invokeAll: (((Int, ToxAv)) => Unit) = { - case (frames, av) => - // Invoke all the events beforehand. - (0 until frames) foreach { _ => - av.invokeVideoReceiveFrame(friendNumber, width, height, y, u, v, yStride, uStride, vStride) - } - } - - measure method "ToxAv.iterate" in { - usingToxAv(range("frames")(VideoLength)) setUp invokeAll in { - case (frames, av) => - // Then fetch and process them in the timed section. - av.iterate(EventListener)(()) - } - } - - measure method "ToxAvJni.toxAvIterate" in { - usingToxAv(range("frames")(VideoLength)) setUp invokeAll in { - case (frames, av: ToxAvImpl) => - // Then fetch them in the timed section. - ToxAvJni.toxavIterate(av.instanceNumber) - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/impl/jni/VideoInvokeTimingBench.scala b/src/bench/java/im/tox/tox4j/impl/jni/VideoInvokeTimingBench.scala deleted file mode 100644 index b28c9fa0e..000000000 --- a/src/bench/java/im/tox/tox4j/impl/jni/VideoInvokeTimingBench.scala +++ /dev/null @@ -1,39 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.callbacks.video.VideoGenerators -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ -import im.tox.tox4j.core.data.ToxFriendNumber - -final class VideoInvokeTimingBench extends TimingReport { - - private val friendNumber = ToxFriendNumber.fromInt(1).get - - private val width = VideoGenerators.DefaultWidth - private val height = VideoGenerators.DefaultHeight - private val yStride = width.value + 80 - private val uStride = yStride / 2 - private val vStride = yStride / 2 - - private val y = Array.ofDim[Byte](yStride * height.value) - private val u = Array.ofDim[Byte](uStride * height.value / 2) - private val v = Array.ofDim[Byte](uStride * height.value / 2) - - timing.of[ToxAv] { - - measure method "invokeVideoReceiveFrame" in { - usingToxAv(range("frames")(100)) tearDown { - case (frames, av: ToxAvImpl) => - ToxAvJni.toxavIterate(av.instanceNumber) - } in { - case (frames, av) => - (0 until frames) foreach { _ => - av.invokeVideoReceiveFrame(friendNumber, width, height, y, u, v, yStride, uStride, vStride) - } - } - } - - } - -} diff --git a/src/bench/java/im/tox/tox4j/impl/jni/VideoReceiveFrameTimingBench.scala b/src/bench/java/im/tox/tox4j/impl/jni/VideoReceiveFrameTimingBench.scala deleted file mode 100644 index 03f302755..000000000 --- a/src/bench/java/im/tox/tox4j/impl/jni/VideoReceiveFrameTimingBench.scala +++ /dev/null @@ -1,57 +0,0 @@ -package im.tox.tox4j.impl.jni - -import com.google.protobuf.ByteString -import im.tox.tox4j.av.callbacks.ToxAvEventAdapter -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.proto.{ AvEvents, VideoReceiveFrame } -import im.tox.tox4j.bench.TimingReport -import im.tox.tox4j.bench.ToxBenchBase._ - -final class VideoReceiveFrameTimingBench extends TimingReport { - - timing.of[VideoReceiveFrame] { - - val width = 100 - val height = 100 - val y = ByteString.copyFrom(Array.ofDim[Byte](width * height)) - val uv = ByteString.copyFrom(Array.ofDim[Byte](width * height / 4)) - val frame = VideoReceiveFrame(0, width, height, y, uv, uv, width, width, width) - - val frames = range("frames")(10000).map { count => - AvEvents(videoReceiveFrame = (0 until count) map (_ => frame)).toByteArray - } - - val nonCachingHandler = new ToxAvEventAdapter[Unit] with Serializable - - val cachingHandler = new ToxAvEventAdapter[Unit] with Serializable { - private val cache = Some(( - Array.ofDim[Byte](width * height), - Array.ofDim[Byte](width * height / 4), - Array.ofDim[Byte](width * height / 4) - )) - - override def videoFrameCachedYUV( - height: Height, - yStride: Int, - uStride: Int, - vStride: Int - ): Option[(Array[Byte], Array[Byte], Array[Byte])] = { - cache - } - } - - performance of s"${width}x$height" in { - using(frames) in { eventData => - ToxAvEventDispatch.dispatch(nonCachingHandler, eventData)(()) - } - } - - performance of s"${width}x$height (cached)" in { - using(frames) in { eventData => - ToxAvEventDispatch.dispatch(cachingHandler, eventData)(()) - } - } - - } - -} diff --git a/src/main/java/im/tox/core/network/Port.kt b/src/main/java/im/tox/core/network/Port.kt new file mode 100644 index 000000000..0f9120b6f --- /dev/null +++ b/src/main/java/im/tox/core/network/Port.kt @@ -0,0 +1,4 @@ +package im.tox.core.network + +/** IP_Port stores an IP datastructure with a port. */ +@JvmInline value class Port constructor(val value: UShort) diff --git a/src/main/java/im/tox/tox4j/ToxEventListener.kt b/src/main/java/im/tox/tox4j/ToxEventListener.kt new file mode 100644 index 000000000..6e494f24a --- /dev/null +++ b/src/main/java/im/tox/tox4j/ToxEventListener.kt @@ -0,0 +1,7 @@ +package im.tox.tox4j + +import im.tox.tox4j.av.callbacks.ToxAvEventListener +import im.tox.tox4j.core.callbacks.ToxCoreEventListener + +interface ToxEventListener : + ToxCoreEventListener, ToxAvEventListener diff --git a/src/main/java/im/tox/tox4j/av/ToxAv.kt b/src/main/java/im/tox/tox4j/av/ToxAv.kt new file mode 100644 index 000000000..faca95f0d --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/ToxAv.kt @@ -0,0 +1,162 @@ +package im.tox.tox4j.av + +import im.tox.tox4j.av.callbacks.* +import im.tox.tox4j.av.data.* +import im.tox.tox4j.av.enums.ToxavCallControl +import im.tox.tox4j.av.exceptions.* +import im.tox.tox4j.core.ToxCore +import im.tox.tox4j.core.data.ToxFriendNumber +import java.io.Closeable + +/** + * Public audio/video API for Tox clients. + * + * This API can handle multiple calls. Each call has its state, in very rare occasions the library + * can change the state of the call without apps knowledge. + * + * Like the Core API, this API is fully thread-safe. The library will ensure the proper + * synchronisation of parallel calls. + * + * A common way to run ToxAv (multiple or single instance) is to have a thread, separate from tox + * instance thread, running a simple [[ToxAv#iterate]] loop, sleeping for + * [[ToxAv#iterationInterval]] * milliseconds on each iteration. + * + * Each ToxAv instance can be bound to only one Tox instance, and Tox instance can have only one + * ToxAv instance. One must make sure to close ToxAv instance prior to closing the Tox instance + * otherwise undefined behaviour occurs. Upon closing of ToxAv instance, all active calls will be + * forcibly terminated without notifying peers. + */ +interface ToxAv : Closeable { + + /** + * Start new A/V session. There can only be only one session per Tox instance. + * + * @param tox A compatible ToxCore implementation. + * @return the new A/V session. + */ + // @throws[ToxavNewException] + fun create(tox: ToxCore): ToxAv + + /** + * Releases all resources associated with the A/V session. + * + * If any calls were ongoing, these will be forcibly terminated without notifying peers. After + * calling this function, no other functions may be called and the av pointer becomes invalid. + */ + override fun close(): Unit + + /** Returns the interval in milliseconds when the next [[iterate]] call should be. */ + val iterationInterval: Int + + /** + * Main loop for the session. This function needs to be called in intervals of + * [[iterationInterval]] milliseconds. It is best called in the separate thread from + * [[ToxCore.iterate]]. + */ + fun iterate(handler: ToxAvEventListener, state: S): S + + /** + * Call a friend. This will start ringing the friend. + * + * It is the client's responsibility to stop ringing after a certain timeout, if such behaviour is + * desired. If the client does not stop ringing, the library will not stop until the friend is + * disconnected. + * + * @param friendNumber The friend number of the friend that should be called. + * @param audioBitRate Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. + * @param videoBitRate Video bit rate in Kb/sec. Set this to 0 to disable video sending. + */ + // @throws[ToxavCallException] + fun call(friendNumber: ToxFriendNumber, audioBitRate: BitRate, videoBitRate: BitRate): Unit + + /** + * Accept an incoming call. + * + * If answering fails for any reason, the call will still be pending and it is possible to try and + * answer it later. + * + * @param friendNumber The friend number of the friend that is calling. + * @param audioBitRate Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. + * @param videoBitRate Video bit rate in Kb/sec. Set this to 0 to disable video sending. + */ + // @throws[ToxavAnswerException] + fun answer(friendNumber: ToxFriendNumber, audioBitRate: BitRate, videoBitRate: BitRate): Unit + + /** + * Sends a call control command to a friend. + * + * @param friendNumber The friend number of the friend to send the call control to. + * @param control The control command to send. + */ + // @throws[ToxavCallControlException] + fun callControl(friendNumber: ToxFriendNumber, control: ToxavCallControl): Unit + + /** + * Set the audio bit rate to be used in subsequent audio frames. + * + * @param friendNumber The friend number of the friend for which to set the audio bit rate. + * @param audioBitRate The new audio bit rate in Kb/sec. Set to 0 to disable audio sending. Pass + * -1 to leave unchanged. + */ + // @throws[ToxavBitRateSetException] + fun setAudioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate): Unit + + /** + * Set the video bit rate to be used in subsequent audio frames. + * + * @param friendNumber The friend number of the friend for which to set the audio bit rate. + * @param videoBitRate The new video bit rate in Kb/sec. Set to 0 to disable video sending. Pass + * -1 to leave unchanged. + */ + // @throws[ToxavBitRateSetException] + fun setVideoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate): Unit + + /** + * Send an audio frame to a friend. + * + * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... Meaning: sample 1 + * for channel 1, sample 1 for channel 2, ... For mono audio, this has no meaning, every sample is + * subsequent. For stereo, this means the expected format is LRLRLR... with samples for left and + * right alternating. + * + * @param friendNumber The friend number of the friend to which to send an audio frame. + * @param pcm An array of audio samples. The size of this array must be sample_count * channels. + * @param sampleCount Number of samples in this frame in milliseconds. Valid numbers here are + * ((sample rate) * (audio length) / 1000), where audio length can be 2.5, 5, 10, 20, 40 or 60 + * milliseconds. + * @param channels Number of audio channels. Supported values are 1 and 2. + * @param samplingRate Audio sampling rate used in this frame in Hz. Valid sampling rates are + * 8000, 12000, 16000, 24000, or 48000. + */ + // @throws[ToxavSendFrameException] + fun audioSendFrame( + friendNumber: ToxFriendNumber, + pcm: ShortArray, + sampleCount: SampleCount, + channels: AudioChannels, + samplingRate: SamplingRate + ): Unit + + /** + * Send a video frame to a friend. + * + * Y - plane should be of size: height * width U - plane should be of size: (height/2) * (width/2) + * V - plane should be of size: (height/2) * (width/2) + * + * @param friendNumber The friend number of the friend to which to send a video frame. + * @param width Width of the frame in pixels. + * @param height Height of the frame in pixels. + * @param y Y (Luminance) plane data. + * @param u U (Chroma) plane data. + * @param v V (Chroma) plane data. + */ + // @throws[ToxavSendFrameException] + fun videoSendFrame( + friendNumber: ToxFriendNumber, + width: Int, + height: Int, + y: ByteArray, + u: ByteArray, + v: ByteArray + ): Unit +} diff --git a/src/main/java/im/tox/tox4j/av/callbacks/AudioBitRateCallback.kt b/src/main/java/im/tox/tox4j/av/callbacks/AudioBitRateCallback.kt new file mode 100644 index 000000000..f6c4b95dc --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/AudioBitRateCallback.kt @@ -0,0 +1,20 @@ +package im.tox.tox4j.av.callbacks + +import im.tox.tox4j.av.data.BitRate +import im.tox.tox4j.core.data.ToxFriendNumber + +/** + * The event is triggered when the network becomes too saturated for current bit rates at which + * point core suggests new bit rates. + */ +interface AudioBitRateCallback { + /** + * @param friendNumber The friend number of the friend for which to set the audio bit rate. + * @param audioBitRate Suggested maximum audio bit rate in Kb/sec. + */ + fun audioBitRate( + friendNumber: ToxFriendNumber, + audioBitRate: BitRate, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/av/callbacks/AudioReceiveFrameCallback.kt b/src/main/java/im/tox/tox4j/av/callbacks/AudioReceiveFrameCallback.kt new file mode 100644 index 000000000..bcd858a39 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/AudioReceiveFrameCallback.kt @@ -0,0 +1,22 @@ +package im.tox.tox4j.av.callbacks + +import im.tox.tox4j.av.data.AudioChannels +import im.tox.tox4j.av.data.SamplingRate +import im.tox.tox4j.core.data.ToxFriendNumber + +/** Called when an audio frame is received. */ +interface AudioReceiveFrameCallback { + /** + * @param friendNumber The friend number of the friend who sent an audio frame. + * @param pcm An array of audio samples (sample_count * channels elements). + * @param channels Number of audio channels. + * @param samplingRate Sampling rate used in this frame. + */ + fun audioReceiveFrame( + friendNumber: ToxFriendNumber, + pcm: ShortArray, + channels: AudioChannels, + samplingRate: SamplingRate, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/av/callbacks/CallCallback.kt b/src/main/java/im/tox/tox4j/av/callbacks/CallCallback.kt new file mode 100644 index 000000000..67ad040b5 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/CallCallback.kt @@ -0,0 +1,18 @@ +package im.tox.tox4j.av.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber + +/** Triggered when a friend calls us. */ +interface CallCallback { + /** + * @param friendNumber The friend number from which the call is incoming. + * @param audioEnabled True if friend is sending audio. + * @param videoEnabled True if friend is sending video. + */ + fun call( + friendNumber: ToxFriendNumber, + audioEnabled: Boolean, + videoEnabled: Boolean, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/av/callbacks/CallStateCallback.kt b/src/main/java/im/tox/tox4j/av/callbacks/CallStateCallback.kt new file mode 100644 index 000000000..047b451c8 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/CallStateCallback.kt @@ -0,0 +1,20 @@ +package im.tox.tox4j.av.callbacks + +import im.tox.tox4j.av.enums.ToxavFriendCallState +import im.tox.tox4j.core.data.ToxFriendNumber +import java.util.EnumSet + +/** Called when the call state changes. */ +interface CallStateCallback { + /** + * @param friendNumber The friend number this call state change is for. + * @param callState A set of ToxCallState values comprising the new call state. Although this is a + * Collection (therefore might actually be a List), this is effectively a Set. Any + * [[ToxavFriendCallState]] value is contained exactly 0 or 1 times. + */ + fun callState( + friendNumber: ToxFriendNumber, + callState: EnumSet, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/av/callbacks/ToxAvEventListener.kt b/src/main/java/im/tox/tox4j/av/callbacks/ToxAvEventListener.kt new file mode 100644 index 000000000..29815724d --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/ToxAvEventListener.kt @@ -0,0 +1,9 @@ +package im.tox.tox4j.av.callbacks + +interface ToxAvEventListener : + CallCallback, + CallStateCallback, + AudioBitRateCallback, + VideoBitRateCallback, + AudioReceiveFrameCallback, + VideoReceiveFrameCallback diff --git a/src/main/java/im/tox/tox4j/av/callbacks/VideoBitRateCallback.kt b/src/main/java/im/tox/tox4j/av/callbacks/VideoBitRateCallback.kt new file mode 100644 index 000000000..612fbb61f --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/VideoBitRateCallback.kt @@ -0,0 +1,20 @@ +package im.tox.tox4j.av.callbacks + +import im.tox.tox4j.av.data.BitRate +import im.tox.tox4j.core.data.ToxFriendNumber + +/** + * The event is triggered when the network becomes too saturated for current bit rates at which + * point core suggests new bit rates. + */ +interface VideoBitRateCallback { + /** + * @param friendNumber The friend number of the friend for which to set the video bit rate. + * @param videoBitRate Suggested maximum video bit rate in Kb/sec. + */ + fun videoBitRate( + friendNumber: ToxFriendNumber, + videoBitRate: BitRate, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/av/callbacks/VideoReceiveFrameCallback.kt b/src/main/java/im/tox/tox4j/av/callbacks/VideoReceiveFrameCallback.kt new file mode 100644 index 000000000..800d0c58f --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/callbacks/VideoReceiveFrameCallback.kt @@ -0,0 +1,49 @@ +package im.tox.tox4j.av.callbacks + +import im.tox.tox4j.av.data.Height +import im.tox.tox4j.av.data.Width +import im.tox.tox4j.core.data.ToxFriendNumber + +/** Triggered when a video frame is received. */ +interface VideoReceiveFrameCallback { + /** + * @param friendNumber The friend number of the friend who sent a video frame. + * @param width Width of the frame in pixels. + * @param height Height of the frame in pixels. + * @param y Y-plane. + * @param u U-plane. + * @param v V-plane. The size of plane data is derived from width and height where Y = max(width, + * abs(yStride)) * height U = max(width/2, abs(uStride)) * (height/2) V = max(width/2, + * abs(vStride)) * (height/2). + * @param yStride Stride length for Y-plane. + * @param uStride Stride length for U-plane. + * @param vStride Stride length for V-plane. Strides represent padding for each plane that may or + * may not be present. You must handle strides in your image processing code. Strides are + * negative if the image is bottom-up hence why you must abs() it when calculating plane buffer + * size. + */ + fun videoReceiveFrame( + friendNumber: ToxFriendNumber, + width: Width, + height: Height, + y: ByteArray, + u: ByteArray, + v: ByteArray, + yStride: Int, + uStride: Int, + vStride: Int, + state: ToxCoreState + ): ToxCoreState = state + + /** + * An implementation may choose to keep the arrays to copy the data to around as an optimisation. + * If this method does not return [[null]], the arrays in the return value are passed to + * [[videoReceiveFrame]] as y, u, and v. + */ + fun videoFrameCachedYUV( + height: Height, + yStride: Int, + uStride: Int, + vStride: Int + ): Triple? = null +} diff --git a/src/main/java/im/tox/tox4j/av/data/AudioChannels.kt b/src/main/java/im/tox/tox4j/av/data/AudioChannels.kt new file mode 100644 index 000000000..3984a89b9 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/AudioChannels.kt @@ -0,0 +1,6 @@ +package im.tox.tox4j.av.data + +enum class AudioChannels(val value: Int) { + Mono(1), + Stereo(2), +} diff --git a/src/main/java/im/tox/tox4j/av/data/AudioLength.kt b/src/main/java/im/tox/tox4j/av/data/AudioLength.kt new file mode 100644 index 000000000..a8f63c2ac --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/AudioLength.kt @@ -0,0 +1,13 @@ +package im.tox.tox4j.av.data + +/** Length in microseconds. */ +enum class AudioLength(val value: Int) { + Length2_5(2500), + Length5(5000), + Length10(10000), + Length20(20000), + Length40(40000), + Length60(60000); + + fun toMillis(): Double = value * 0.001 +} diff --git a/src/main/java/im/tox/tox4j/av/data/BitRate.kt b/src/main/java/im/tox/tox4j/av/data/BitRate.kt new file mode 100644 index 000000000..c021d6d99 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/BitRate.kt @@ -0,0 +1,9 @@ +package im.tox.tox4j.av.data + +@JvmInline +value class BitRate(val value: Int) { + companion object { + val Unchanged: BitRate = BitRate(-1) + val Disabled: BitRate = BitRate(0) + } +} diff --git a/src/main/java/im/tox/tox4j/av/data/Height.kt b/src/main/java/im/tox/tox4j/av/data/Height.kt new file mode 100644 index 000000000..722018e15 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/Height.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.av.data + +@JvmInline value class Height(val value: Int) diff --git a/src/main/java/im/tox/tox4j/av/data/SampleCount.kt b/src/main/java/im/tox/tox4j/av/data/SampleCount.kt new file mode 100644 index 000000000..c05fff8ce --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/SampleCount.kt @@ -0,0 +1,9 @@ +package im.tox.tox4j.av.data + +@JvmInline +value class SampleCount(val value: Int) { + constructor( + audioLength: AudioLength, + samplingRate: SamplingRate + ) : this((samplingRate.value / 1000 * audioLength.toMillis()).toInt()) +} diff --git a/src/main/java/im/tox/tox4j/av/data/SamplingRate.kt b/src/main/java/im/tox/tox4j/av/data/SamplingRate.kt new file mode 100644 index 000000000..4dab96e26 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/SamplingRate.kt @@ -0,0 +1,9 @@ +package im.tox.tox4j.av.data + +enum class SamplingRate(val value: Int) { + Rate8k(8000), + Rate12k(12000), + Rate16k(16000), + Rate24k(24000), + Rate48k(48000), +} diff --git a/src/main/java/im/tox/tox4j/av/data/Width.kt b/src/main/java/im/tox/tox4j/av/data/Width.kt new file mode 100644 index 000000000..b52b16d8a --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/data/Width.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.av.data + +@JvmInline value class Width(val value: Int) diff --git a/src/main/java/im/tox/tox4j/av/enums/ToxavCallControl.java b/src/main/java/im/tox/tox4j/av/enums/ToxavCallControl.java new file mode 100644 index 000000000..5c37d5732 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/enums/ToxavCallControl.java @@ -0,0 +1,50 @@ +package im.tox.tox4j.av.enums; + +/** + * Call control. + */ +public enum ToxavCallControl { + /** + * Resume a previously paused call. Only valid if the pause was caused by this + * client, if not, this control is ignored. Not valid before the call is + * accepted. + */ + RESUME, + + /** + * Put a call on hold. Not valid before the call is accepted. + */ + PAUSE, + + /** + * Reject a call if it was not answered, yet. Cancel a call after it was + * answered. + */ + CANCEL, + + /** + * Request that the friend stops sending audio. Regardless of the friend's + * compliance, this will cause the {@link + * im.tox.tox4j.av.callbacks.AudioReceiveFrameCallback} event to stop being + * triggered on receiving an audio frame from the friend. + */ + MUTE_AUDIO, + + /** + * Calling this control will notify client to start sending audio again. + */ + UNMUTE_AUDIO, + + /** + * Request that the friend stops sending video. Regardless of the friend's + * compliance, this will cause the {@link + * im.tox.tox4j.av.callbacks.VideoReceiveFrameCallback} event to stop being + * triggered on receiving an video frame from the friend. + */ + HIDE_VIDEO, + + /** + * Calling this control will notify client to start sending video again. + */ + SHOW_VIDEO, +} diff --git a/src/main/java/im/tox/tox4j/av/enums/ToxavFriendCallState.java b/src/main/java/im/tox/tox4j/av/enums/ToxavFriendCallState.java new file mode 100644 index 000000000..cf5de6e99 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/enums/ToxavFriendCallState.java @@ -0,0 +1,41 @@ +package im.tox.tox4j.av.enums; + +/** + * Call state graph. + */ +public enum ToxavFriendCallState { + /** + * Set by the AV core if an error occurred on the remote end or if friend + * timed out. This is the final state after which no more state + * transitions can occur for the call. This call state will never be triggered + * in combination with other call states. + */ + ERROR, + + /** + * The call has finished. This is the final state after which no more state + * transitions can occur for the call. This call state will never be + * triggered in combination with other call states. + */ + FINISHED, + + /** + * The flag that marks that friend is sending audio. + */ + SENDING_A, + + /** + * The flag that marks that friend is sending video. + */ + SENDING_V, + + /** + * The flag that marks that friend is receiving audio. + */ + ACCEPTING_A, + + /** + * The flag that marks that friend is receiving video. + */ + ACCEPTING_V, +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/Makefile b/src/main/java/im/tox/tox4j/av/exceptions/Makefile new file mode 100644 index 000000000..cbc0eac30 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/Makefile @@ -0,0 +1,2 @@ +all: ../../mkexceptions exceptions.json + $+ diff --git a/src/main/java/im/tox/tox4j/av/exceptions/ToxavAnswerException.java b/src/main/java/im/tox/tox4j/av/exceptions/ToxavAnswerException.java new file mode 100644 index 000000000..7fb0669dc --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/ToxavAnswerException.java @@ -0,0 +1,38 @@ +package im.tox.tox4j.av.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxavAnswerException extends ToxException { + public enum Code { + /** + * Failed to initialise codecs for call session. + */ + CODEC_INITIALIZATION, + /** + * The friend was valid, but they are not currently trying to initiate a + * call. This is also returned if this client is already in a call with the + * friend. + */ + FRIEND_NOT_CALLING, + /** + * The friend number did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * Audio or video bit rate is invalid. + */ + INVALID_BIT_RATE, + /** + * Synchronization error occurred. + */ + SYNC, + } + + public ToxavAnswerException(Code code) { + this(code, ""); + } + + public ToxavAnswerException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/ToxavBitRateSetException.java b/src/main/java/im/tox/tox4j/av/exceptions/ToxavBitRateSetException.java new file mode 100644 index 000000000..36b93da6c --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/ToxavBitRateSetException.java @@ -0,0 +1,32 @@ +package im.tox.tox4j.av.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxavBitRateSetException extends ToxException { + public enum Code { + /** + * The friend_number passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. + */ + FRIEND_NOT_IN_CALL, + /** + * The bit rate passed was not one of the supported values. + */ + INVALID_BIT_RATE, + /** + * Synchronization error occurred. + */ + SYNC, + } + + public ToxavBitRateSetException(Code code) { + this(code, ""); + } + + public ToxavBitRateSetException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/ToxavCallControlException.java b/src/main/java/im/tox/tox4j/av/exceptions/ToxavCallControlException.java new file mode 100644 index 000000000..25adbdc60 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/ToxavCallControlException.java @@ -0,0 +1,34 @@ +package im.tox.tox4j.av.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxavCallControlException extends ToxException { + public enum Code { + /** + * The friend number did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. Before the call + * is answered, only CANCEL is a valid control + */ + FRIEND_NOT_IN_CALL, + /** + * Happens if user tried to pause an already paused call or if trying to + * resume a call that is not paused. + */ + INVALID_TRANSITION, + /** + * Synchronization error occurred. + */ + SYNC, + } + + public ToxavCallControlException(Code code) { + this(code, ""); + } + + public ToxavCallControlException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/ToxavCallException.java b/src/main/java/im/tox/tox4j/av/exceptions/ToxavCallException.java new file mode 100644 index 000000000..8671e33ca --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/ToxavCallException.java @@ -0,0 +1,41 @@ +package im.tox.tox4j.av.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxavCallException extends ToxException { + public enum Code { + /** + * Attempted to call a friend while already in an audio or video call with + * them. + */ + FRIEND_ALREADY_IN_CALL, + /** + * The friend was valid, but not currently connected. + */ + FRIEND_NOT_CONNECTED, + /** + * The friend number did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * Audio or video bit rate is invalid. + */ + INVALID_BIT_RATE, + /** + * A memory allocation error occurred. + */ + MALLOC, + /** + * Synchronization error occurred. + */ + SYNC, + } + + public ToxavCallException(Code code) { + this(code, ""); + } + + public ToxavCallException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/ToxavNewException.java b/src/main/java/im/tox/tox4j/av/exceptions/ToxavNewException.java new file mode 100644 index 000000000..2debd0284 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/ToxavNewException.java @@ -0,0 +1,35 @@ +package im.tox.tox4j.av.exceptions; + +import im.tox.tox4j.exceptions.JavaOnly; +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxavNewException extends ToxException { + public enum Code { + /** + * The ToxCore implementation passed was not compatible with this ToxAv + * implementation. + */ + @JavaOnly INCOMPATIBLE, + /** + * Memory allocation failure while trying to allocate structures required + * for the A/V session. + */ + MALLOC, + /** + * Attempted to create a second session for the same Tox instance. + */ + MULTIPLE, + /** + * One of the arguments to the function was NULL when it was not expected. + */ + NULL, + } + + public ToxavNewException(Code code) { + this(code, ""); + } + + public ToxavNewException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/ToxavSendFrameException.java b/src/main/java/im/tox/tox4j/av/exceptions/ToxavSendFrameException.java new file mode 100644 index 000000000..8f83b78d6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/ToxavSendFrameException.java @@ -0,0 +1,47 @@ +package im.tox.tox4j.av.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxavSendFrameException extends ToxException { + public enum Code { + /** + * The friend number did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. + */ + FRIEND_NOT_IN_CALL, + /** + * One or more of the frame parameters was invalid. E.g. the resolution may + * be too small or too large, or the audio sampling rate may be unsupported. + */ + INVALID, + /** + * In case of video, one of Y, U, or V was NULL. In case of audio, the + * samples data pointer was NULL. + */ + NULL, + /** + * Either friend turned off audio or video receiving or we turned off + * sending for the said payload. + */ + PAYLOAD_TYPE_DISABLED, + /** + * Failed to push frame through rtp interface. + */ + RTP_FAILED, + /** + * Synchronization error occurred. + */ + SYNC, + } + + public ToxavSendFrameException(Code code) { + this(code, ""); + } + + public ToxavSendFrameException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/av/exceptions/exceptions.json b/src/main/java/im/tox/tox4j/av/exceptions/exceptions.json new file mode 100644 index 000000000..8e42a5c9d --- /dev/null +++ b/src/main/java/im/tox/tox4j/av/exceptions/exceptions.json @@ -0,0 +1,95 @@ +[ + "av", + "av", + { + "New": { + "NULL": [ + "One of the arguments to the function was NULL when it was not expected." + ], + "MALLOC": [ + "Memory allocation failure while trying to allocate structures required for", + "the A/V session." + ], + "MULTIPLE": [ + "Attempted to create a second session for the same Tox instance." + ], + "@JavaOnly INCOMPATIBLE": [ + "The ToxCore implementation passed was not compatible with this ToxAv implementation." + ] + }, + "Call": { + "SYNC": ["Synchronization error occurred."], + "MALLOC": ["A memory allocation error occurred."], + "FRIEND_NOT_FOUND": [ + "The friend number did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "The friend was valid, but not currently connected." + ], + "FRIEND_ALREADY_IN_CALL": [ + "Attempted to call a friend while already in an audio or video call with them." + ], + "INVALID_BIT_RATE": ["Audio or video bit rate is invalid."] + }, + "Answer": { + "SYNC": ["Synchronization error occurred."], + "CODEC_INITIALIZATION": ["Failed to initialise codecs for call session."], + "FRIEND_NOT_FOUND": [ + "The friend number did not designate a valid friend." + ], + "FRIEND_NOT_CALLING": [ + "The friend was valid, but they are not currently trying to initiate a call.", + "This is also returned if this client is already in a call with the friend." + ], + "INVALID_BIT_RATE": ["Audio or video bit rate is invalid."] + }, + "CallControl": { + "SYNC": ["Synchronization error occurred."], + "FRIEND_NOT_FOUND": [ + "The friend number did not designate a valid friend." + ], + "FRIEND_NOT_IN_CALL": [ + "This client is currently not in a call with the friend. Before the call is", + "answered, only CANCEL is a valid control" + ], + "INVALID_TRANSITION": [ + "Happens if user tried to pause an already paused call or if trying to", + "resume a call that is not paused." + ] + }, + "BitRateSet": { + "SYNC": ["Synchronization error occurred."], + "INVALID_BIT_RATE": [ + "The bit rate passed was not one of the supported values." + ], + "FRIEND_NOT_FOUND": [ + "The friend_number passed did not designate a valid friend." + ], + "FRIEND_NOT_IN_CALL": [ + "This client is currently not in a call with the friend." + ] + }, + "SendFrame": { + "SYNC": ["Synchronization error occurred."], + "NULL": [ + "In case of video, one of Y, U, or V was NULL. In case of audio, the samples", + "data pointer was NULL." + ], + "FRIEND_NOT_FOUND": [ + "The friend number did not designate a valid friend." + ], + "FRIEND_NOT_IN_CALL": [ + "This client is currently not in a call with the friend." + ], + "INVALID": [ + "One or more of the frame parameters was invalid. E.g. the resolution may be too", + "small or too large, or the audio sampling rate may be unsupported." + ], + "RTP_FAILED": ["Failed to push frame through rtp interface."], + "PAYLOAD_TYPE_DISABLED": [ + "Either friend turned off audio or video receiving or we turned off sending", + "for the said payload." + ] + } + } +] diff --git a/src/main/java/im/tox/tox4j/core/ToxCore.kt b/src/main/java/im/tox/tox4j/core/ToxCore.kt new file mode 100644 index 000000000..24af98bd3 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/ToxCore.kt @@ -0,0 +1,565 @@ +package im.tox.tox4j.core + +import im.tox.core.network.Port +import im.tox.tox4j.core.callbacks.* +import im.tox.tox4j.core.data.* +import im.tox.tox4j.core.enums.* +import im.tox.tox4j.core.exceptions.* +import im.tox.tox4j.core.options.ToxOptions +import java.io.Closeable + +/** + * Interface for a basic wrapper of tox chat functionality. + * + * This interface is designed to be thread-safe. However, once [[ToxCore.close]] has been called, + * all subsequent calls will result in [[im.tox.tox4j.exceptions.ToxKilledException]] being thrown. + * When one thread invokes [[ToxCore.close]], all other threads with pending calls will throw. The + * exception is unchecked, as it should not occur in a normal execution flow. To prevent it from + * occurring in a multi-threaded environment, all additional threads should be stopped or stop using + * the instance before one thread invokes [[ToxCore.close]] on it, or appropriate exception handlers + * should be installed in all threads. + */ +interface ToxCore : Closeable { + + /** + * Store all information associated with the tox instance to a byte array. + * + * The data in the byte array can be used to create a new instance with [[load]] by passing it to + * the [[ToxOptions]] constructor. The concrete format in this serialised instance is + * implementation-defined. Passing save data created by one class to a different class may not + * work. + * + * @return a byte array containing the serialised tox instance. + */ + val savedata: ByteArray + + /** + * Create a new [[ToxCore]] instance with different options. The implementation may choose to + * create an object of its own class or a different class. If the implementation was compatible + * with another subsystem implementation (e.g. [[im.tox.tox4j.av.ToxAv]]), then the new object + * must be compatible with the same implementation. + * + * This function will bring the instance into a valid state. Running the event loop with a new + * instance will operate correctly. + * + * If the [[ToxOptions.saveData]] field is not empty, this function will load the Tox instance + * from a byte array previously filled by [[savedata]]. + * + * If loading failed or succeeded only partially, an exception will be thrown. + * + * @return a new [[ToxCore]] instance. + */ + // @Throws(ToxNewException::class) + fun load(options: ToxOptions): ToxCore + + /** + * Shut down the tox instance. + * + * Releases all resources associated with the Tox instance and disconnects from the network. + * + * Once this method has been called, all other calls on this instance will throw + * [ [im.tox.tox4j.exceptions.ToxKilledException]]. A closed instance cannot be reused; a new + * instance must be created. + */ + override fun close(): Unit + + /** + * Bootstrap into the tox network. + * + * Sends a "get nodes" request to the given bootstrap node with IP, port, and public key to setup + * connections. + * + * This function will only attempt to connect to the node using UDP. If you want to additionally + * attempt to connect using TCP, use [[addTcpRelay]] together with this function. + * + * @param address the hostname, or an IPv4/IPv6 address of the node. + * @param port the port of the node. + * @param publicKey the public key of the node. + */ + // @Throws(ToxBootstrapException::class) + fun bootstrap(address: String, port: Port, publicKey: ToxPublicKey): Unit + fun bootstrap(address: String, port: Int, publicKey: ByteArray): Unit = + bootstrap(address, Port(port.toUShort()), ToxPublicKey(publicKey)) + + /** + * Connect to a TCP relay to forward traffic. + * + * This function can be used to initiate TCP connections to different ports on the same bootstrap + * node, or to add TCP relays without using them as bootstrap nodes. + * + * @param address the hostname, or an IPv4/IPv6 address of the node. + * @param port the TCP port the node is running a relay on. + * @param publicKey the public key of the node. + */ + // @Throws(ToxBootstrapException::class) + fun addTcpRelay(address: String, port: Port, publicKey: ToxPublicKey): Unit + fun addTcpRelay(address: String, port: Int, publicKey: ByteArray): Unit = + addTcpRelay(address, Port(port.toUShort()), ToxPublicKey(publicKey)) + + /** + * Get the UDP port this instance is bound to. + * + * @return a port number between 1 and 65535. + */ + // @Throws(ToxGetPortException::class) + val udpPort: Port + fun getUdpPort(): Int = udpPort.value.toInt() + + /** + * Return the TCP port this Tox instance is bound to. This is only relevant if the instance is + * acting as a TCP relay. + * + * @return a port number between 1 and 65535. + */ + // @Throws(ToxGetPortException::class) + val tcpPort: Port + fun getTcpPort(): Int = tcpPort.value.toInt() + + /** + * Writes the temporary DHT public key of this instance to a byte array. + * + * This can be used in combination with an externally accessible IP address and the bound port + * (from [[udpPort]]}) to run a temporary bootstrap node. + * + * Be aware that every time a new instance is created, the DHT public key changes, meaning this + * cannot be used to run a permanent bootstrap node. + * + * @return a byte array of size [[ToxCoreConstants.PublicKeySize]] + */ + val dhtId: ToxPublicKey + fun getDhtId(): ByteArray = dhtId.value + + /** + * Get the time in milliseconds until [[iterate]] should be called again for optimal performance. + * + * @return the time in milliseconds until [[iterate]] should be called again. + */ + val iterationInterval: Int + + /** + * The main loop. + * + * This should be invoked every [[iterationInterval]] milliseconds. + */ + fun iterate(handler: ToxCoreEventListener, state: S): S + + /** + * Copy the Tox Public Key (long term) from the Tox object. + * + * @return a byte array of size [[ToxCoreConstants.PublicKeySize]] + */ + val publicKey: ToxPublicKey + fun getPublicKey(): ByteArray = publicKey.value + + /** + * Copy the Tox Secret Key from the Tox object. + * + * @return a byte array of size [[ToxCoreConstants.SecretKeySize]] + */ + val secretKey: ToxSecretKey + fun getSecretKey(): ByteArray = secretKey.value + + /** + * The 4-byte nospam part of the address. + * + * Setting the nospam makes it impossible for others to send us friend requests that contained the + * old nospam number. + */ + var nospam: Int + + /** + * Get our current tox address to give to friends. + * + * The format is the following: [Public Key (32 bytes)][noSpam number (4 bytes)][checksum (2 + * bytes)]. After a call to [[setNospam]], the old address can no longer be used to send friend + * requests to this instance. + * + * Note that it is not in a human-readable format. To display it to users, it needs to be + * formatted. + * + * @return a byte array of size [[ToxCoreConstants.AddressSize]] + */ + val address: ToxFriendAddress + fun getAddress(): ByteArray = address.value + + /** + * The nickname for the Tox client. + * + * Cannot be longer than [[ToxCoreConstants.MaxNameLength]] bytes. Can be empty (zero-length). + */ + // @Throws(ToxSetInfoException::class) + var name: ToxNickname + fun getName(): ByteArray = name.value + fun setName(value: ByteArray) { name = ToxNickname(value) } + + /** + * Our status message. + * + * Cannot be longer than [[ToxCoreConstants.MaxStatusMessageLength]] bytes. + */ + // @Throws(ToxSetInfoException::class) + var statusMessage: ToxStatusMessage + fun getStatusMessage(): ByteArray = statusMessage.value + fun setStatusMessage(value: ByteArray) { statusMessage = ToxStatusMessage(value) } + + /** + * Our status. + */ + var status: ToxUserStatus + + /** + * Add a friend to the friend list and send a friend request. + * + * A friend request message must be at least 1 byte long and at most + * [ [ToxCoreConstants.MaxFriendRequestLength]]. + * + * Friend numbers are unique identifiers used in all functions that operate on friends. Once + * added, a friend number is stable for the lifetime of the Tox object. After saving the state and + * reloading it, the friend numbers may not be the same as before. Deleting a friend creates a gap + * in the friend number set, which is filled by the next adding of a friend. Any pattern in friend + * numbers should not be relied on. + * + * If more than [[Int.MaxValue]] friends are added, this function throws an exception. + * + * @param address the address to add as a friend ([[ToxCoreConstants.AddressSize]] bytes). + * + * ``` + * This is the byte array the friend got from their own [[getAddress]]. + * @param message + * ``` + * + * the message to send with the friend request (must not be empty). + * + * @return the new friend's friend number. + */ + // @Throws(ToxFriendAddException::class, IllegalArgumentException::class) + fun addFriend(address: ToxFriendAddress, message: ToxFriendRequestMessage): ToxFriendNumber + fun addFriend(address: ByteArray, message: ByteArray): Int = + addFriend(ToxFriendAddress(address), ToxFriendRequestMessage(message)).value + + /** + * Add a friend without sending a friend request. + * + * This function is used to add a friend in response to a friend request. If the client receives a + * friend request, it can be reasonably sure that the other client added this client as a friend, + * eliminating the need for a friend request. + * + * This function is also useful in a situation where both instances are controlled by the same + * entity, so that this entity can perform the mutual friend adding. In this case, there is no + * need for a friend request, either. + * + * @param publicKey the Public Key to add as a friend ([[ToxCoreConstants.PublicKeySize]] bytes). + * @return the new friend's friend number. + */ + // @Throws(ToxFriendAddException::class, IllegalArgumentException::class) + fun addFriendNorequest(publicKey: ToxPublicKey): ToxFriendNumber + fun addFriendNorequest(publicKey: ByteArray): Int = + addFriendNorequest(ToxPublicKey(publicKey)).value + + /** + * Remove a friend from the friend list. + * + * This does not notify the friend of their deletion. After calling this function, this client + * will appear offline to the friend and no communication can occur between the two. + * + * @param friendNumber the friend number to delete. + */ + // @Throws(ToxFriendDeleteException::class) + fun deleteFriend(friendNumber: ToxFriendNumber): Unit + fun deleteFriend(friendNumber: Int): Unit = + deleteFriend(ToxFriendNumber(friendNumber)) + + /** + * Gets the friend number for the specified Public Key. + * + * @param publicKey the Public Key. + * @return the friend number that is associated with the Public Key. + */ + // @Throws(ToxFriendByPublicKeyException::class) + fun friendByPublicKey(publicKey: ToxPublicKey): ToxFriendNumber + fun friendByPublicKey(publicKey: ByteArray): Int = + friendByPublicKey(ToxPublicKey(publicKey)).value + + /** + * Gets the Public Key for the specified friend number. + * + * @param friendNumber the friend number. + * @return the Public Key associated with the friend number. + */ + // @Throws(ToxFriendGetPublicKeyException::class) + fun getFriendPublicKey(friendNumber: ToxFriendNumber): ToxPublicKey + fun getFriendPublicKey(friendNumber: Int): ByteArray = + getFriendPublicKey(ToxFriendNumber(friendNumber)).value + + /** + * Checks whether a friend with the specified friend number exists. + * + * If this function returns true, the return value is valid until the friend is + * deleted. If false is returned, the return value is valid until either of + * [ [addFriend]] or [[addFriendNorequest]] is invoked. + * + * @param friendNumber the friend number to check. + * @return true if such a friend exists. + */ + fun friendExists(friendNumber: ToxFriendNumber): Boolean + fun friendExists(friendNumber: Int): Boolean = + friendExists(ToxFriendNumber(friendNumber)) + + /** + * Get an array of currently valid friend numbers. + * + * This list is valid until either of the following is invoked: [[deleteFriend]], [[addFriend]], + * [ [addFriendNorequest]]. + * + * @return an array containing the currently valid friend numbers, the empty int array if there + * are no friends. + */ + val friendList: IntArray + + /** + * Get an array of [[ToxFriendNumber]] objects with the same values as [[getFriendList]]. + * + * This method exists for Java compatibility, because [[getFriendList]] must return an int array. + * + * @return [[getFriendList]] mapped to [[ToxFriendNumber]]. + */ + val friendNumbers: List + get() = friendList.map { ToxFriendNumber(it) } + + /** + * Tell friend number whether or not we are currently typing. + * + * The client is responsible for turning it on or off. + * + * @param friendNumber the friend number to set typing status for. + * @param typing true if we are currently typing. + */ + // @Throws(ToxSetTypingException::class) + fun setTyping(friendNumber: ToxFriendNumber, typing: Boolean): Unit + fun setTyping(friendNumber: Int, typing: Boolean): Unit = + setTyping(ToxFriendNumber(friendNumber), typing) + + /** + * Send a text chat message to an online friend. + * + * This function creates a chat message packet and pushes it into the send queue. + * + * The message length may not exceed [[ToxCoreConstants.MaxMessageLength]]. Larger messages must + * be split by the client and sent as separate messages. Other clients can then reassemble the + * fragments. Messages may not be empty. + * + * The return value of this function is the message ID. If a read receipt is received, the + * triggered [[FriendReadReceiptCallback]] event will be passed this message ID. + * + * Message IDs are unique per friend per instance. The first message ID is 0. Message IDs are + * incremented by 1 each time a message is sent. If [[Int.MaxValue]] messages were sent, the next + * message ID is [[Int.MinValue]]. + * + * Message IDs are not stored in the array returned by [[savedata]]. + * + * @param friendNumber The friend number of the friend to send the message to. + * @param messageType Message type (normal, action, ...). + * @param timeDelta The time between composition (user created the message) and calling this + * function. + * @param message The message text + * @return the message ID. + */ + // @Throws(ToxFriendSendMessageException::class) + fun friendSendMessage( + friendNumber: ToxFriendNumber, + messageType: ToxMessageType, + timeDelta: Int, + message: ToxFriendMessage + ): Int + fun friendSendMessage( + friendNumber: Int, + messageType: ToxMessageType, + timeDelta: Int, + message: ByteArray + ): Int = + friendSendMessage(ToxFriendNumber(friendNumber), messageType, timeDelta, ToxFriendMessage(message)) + + /** + * Sends a file control command to a friend for a given file transfer. + * + * @param friendNumber The friend number of the friend the file is being transferred to or + * received from. + * @param fileNumber The friend-specific identifier for the file transfer. + * @param control The control command to send. + */ + // @Throws(ToxFileControlException::class) + fun fileControl(friendNumber: ToxFriendNumber, fileNumber: Int, control: ToxFileControl): Unit + + /** + * Sends a file seek control command to a friend for a given file transfer. + * + * This function can only be called to resume a file transfer right before + * [ [ToxFileControl.RESUME]] is sent. + * + * @param friendNumber The friend number of the friend the file is being received from. + * @param fileNumber The friend-specific identifier for the file transfer. + * @param position The position that the file should be seeked to. + */ + // @Throws(ToxFileSeekException::class) + fun fileSeek(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long): Unit + + /** + * Return the file id associated to the file transfer as a byte array. + * + * @param friendNumber The friend number of the friend the file is being transferred to or + * received from. + * @param fileNumber The friend-specific identifier for the file transfer. + */ + // @Throws(ToxFileGetException::class) + fun getFileFileId(friendNumber: ToxFriendNumber, fileNumber: Int): ToxFileId + + /** + * Send a file transmission request. + * + * Maximum filename length is [[ToxCoreConstants.MaxFilenameLength]] bytes. The filename should + * generally just be a file name, not a path with directory names. + * + * If a non-negative file size is provided, it can be used by both sides to determine the sending + * progress. File size can be set to a negative value for streaming data of unknown size. + * + * File transmission occurs in chunks, which are requested through the [[FileChunkRequestCallback] + * ] event. + * + * When a friend goes offline, all file transfers associated with the friend are purged from core. + * + * If the file contents change during a transfer, the behaviour is unspecified in general. What + * will actually happen depends on the mode in which the file was modified and how the client + * determines the file size. + * - If the file size was increased + * - and sending mode was streaming (fileSize = -1), the behaviour + * + * ``` + * will be as expected. + * ``` + * - and sending mode was file (fileSize != -1), the + * + * ``` + * [[FileChunkRequestCallback]] callback will receive length = 0 when Core thinks + * the file transfer has finished. If the client remembers the file size as + * it was when sending the request, it will terminate the transfer normally. + * If the client re-reads the size, it will think the friend cancelled the + * transfer. + * ``` + * - If the file size was decreased + * - and sending mode was streaming, the behaviour is as expected. + * - and sending mode was file, the callback will return 0 at the new + * + * ``` + * (earlier) end-of-file, signalling to the friend that the transfer was + * cancelled. + * ``` + * - If the file contents were modified + * - at a position before the current read, the two files (local and remote) + * + * ``` + * will differ after the transfer terminates. + * ``` + * - at a position after the current read, the file transfer will succeed as + * + * ``` + * expected. + * ``` + * - In either case, both sides will regard the transfer as complete and + * + * ``` + * successful. + * + * @param friendNumber + * ``` + * + * The friend number of the friend the file send request should be sent to. + * + * @param kind The meaning of the file to be sent. + * @param fileSize Size in bytes of the file the client wants to send, -1 if unknown or streaming. + * @param fileId A file identifier of length [[ToxCoreConstants.FileIdLength]] that can be used to + * + * ``` + * uniquely identify file transfers across core restarts. If empty, a random one will + * be generated by core. It can then be obtained by using [[getFileFileId]] + * @param filename + * ``` + * + * Name of the file. Does not need to be the actual name. This + * + * ``` + * name will be sent along with the file send request. + * @return + * ``` + * + * A file number used as an identifier in subsequent callbacks. This + * + * ``` + * number is per friend. File numbers are reused after a transfer terminates. + * Any pattern in file numbers should not be relied on. + * ``` + */ + // @Throws(ToxFileSendException::class) + fun fileSend( + friendNumber: ToxFriendNumber, + kind: Int, + fileSize: Long, + fileId: ToxFileId, + filename: ToxFilename + ): Int + + /** + * Send a chunk of file data to a friend. + * + * This function is called in response to the [[FileChunkRequestCallback]] callback. The length + * parameter should be equal to the one received though the callback. If it is zero, the transfer + * is assumed complete. For files with known size, Core will know that the transfer is complete + * after the last byte has been received, so it is not necessary (though not harmful) to send a + * zero-length chunk to terminate. For streams, core will know that the transfer is finished if a + * chunk with length less than the length requested in the callback is sent. + * + * @param friendNumber The friend number of the receiving friend for this file. + * @param fileNumber The file transfer identifier returned by [[fileSend]]. + * @param position The file or stream position from which the friend should continue writing. + * @param data The chunk data. + */ + // @Throws(ToxFileSendChunkException::class) + fun fileSendChunk( + friendNumber: ToxFriendNumber, + fileNumber: Int, + position: Long, + data: ByteArray + ): Unit + + /** + * Send a custom lossy packet to a friend. + * + * The first byte of data must be in the range 200-254. Maximum length of a custom packet is + * [ [ToxCoreConstants.MaxCustomPacketSize]]. + * + * Lossy packets behave like UDP packets, meaning they might never reach the other side or might + * arrive more than once (if someone is messing with the connection) or might arrive in the wrong + * order. + * + * Unless latency is an issue, it is recommended that you use lossless custom packets instead. + * + * @param friendNumber The friend number of the friend this lossy packet should be sent to. + * @param data A byte array containing the packet data including packet id. + */ + // @Throws(ToxFriendCustomPacketException::class) + fun friendSendLossyPacket(friendNumber: ToxFriendNumber, data: ToxLossyPacket): Unit + + /** + * Send a custom lossless packet to a friend. + * + * The first byte of data must be in the range 160-191. Maximum length of a custom packet is + * [ [ToxCoreConstants.MaxCustomPacketSize]]. + * + * Lossless packet behaviour is comparable to TCP (reliability, arrive in order) but with packets + * instead of a stream. + * + * @param friendNumber The friend number of the friend this lossless packet should be sent to. + * @param data A byte array containing the packet data including packet id. + */ + // @Throws(ToxFriendCustomPacketException::class) + fun friendSendLosslessPacket(friendNumber: ToxFriendNumber, data: ToxLosslessPacket): Unit +} diff --git a/src/main/java/im/tox/tox4j/core/ToxCoreConstants.kt b/src/main/java/im/tox/tox4j/core/ToxCoreConstants.kt new file mode 100644 index 000000000..cf1bf542c --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/ToxCoreConstants.kt @@ -0,0 +1,61 @@ +package im.tox.tox4j.core + +import im.tox.tox4j.crypto.ToxCryptoConstants + +object ToxCoreConstants { + + /** The size of a Tox Public Key in bytes. */ + val PublicKeySize = ToxCryptoConstants.PublicKeyLength + + /** The size of a Tox Secret Key in bytes. */ + val SecretKeySize = ToxCryptoConstants.SecretKeyLength + + /** + * The size of a Tox address in bytes. Tox addresses are in the format + * [Public Key ([ [PublicKeySize]] bytes)][nospam (4 bytes)][checksum (2 bytes)]. + * + * The checksum is computed over the Public Key and the nospam value. The first byte is an XOR of + * all the odd bytes, the second byte is an XOR of all the even bytes of the Public Key and + * nospam. + */ + val AddressSize = PublicKeySize + 4 + 2 + + /** Maximum length of a nickname in bytes. */ + val MaxNameLength = 128 + + /** Maximum length of a status message in bytes. */ + val MaxStatusMessageLength = 1007 + + /** Maximum length of a friend request message in bytes. */ + val MaxFriendRequestLength = 1016 + + /** Maximum length of a single message after which it should be split. */ + val MaxMessageLength = 1372 + + /** Maximum size of custom packets. TODO: should be LENGTH? */ + val MaxCustomPacketSize = 1373 + + /** Maximum file name length for file transfers. */ + val MaxFilenameLength = 255 + + /** + * Maximum hostname length. This is determined by calling `getconf HOST_NAME_MAX` on the console. + * The value presented here is valid for most systems. + */ + val MaxHostnameLength = 255 + + /** The number of bytes in a file id. */ + val FileIdLength = ToxCryptoConstants.HashLength + + /** Default port for HTTP proxies. */ + val DefaultProxyPort = 8080.toUShort() + + /** Default start port for Tox UDP sockets. */ + val DefaultStartPort = 33445.toUShort() + + /** Default end port for Tox UDP sockets. */ + val DefaultEndPort = (DefaultStartPort + 100.toUShort()).toUShort() + + /** Default port for Tox TCP relays. A value of 0 means disabled. */ + val DefaultTcpPort = 0.toUShort() +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FileChunkRequestCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FileChunkRequestCallback.kt new file mode 100644 index 000000000..3f360cba4 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FileChunkRequestCallback.kt @@ -0,0 +1,36 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber + +/** This event is triggered when Core is ready to send more file data. */ +interface FileChunkRequestCallback { + /** + * If the length parameter is 0, the file transfer is finished, and the client's resources + * associated with the file number should be released. After a call with zero length, the file + * number can be reused for future file transfers. + * + * If the requested position is not equal to the client's idea of the current file or stream + * position, it will need to seek. In case of read-once streams, the client should keep the last + * read chunk so that a seek back can be supported. A seek-back only ever needs to read from the + * last requested chunk. This happens when a chunk was requested, but the send failed. A seek-back + * request can occur an arbitrary number of times for any given chunk. + * + * In response to receiving this callback, the client should call the function + * [ [ToxCore.fileSendChunk]] with the requested chunk. If the number of bytes sent through that + * function is zero, the file transfer is assumed complete. A client must send the full length of + * data requested with this callback. + * + * @param friendNumber The friend number of the receiving friend for this file. + * @param fileNumber The file transfer identifier returned by { @link + * im.tox.tox4j.core.ToxCore#fileSend}. + * @param position The file or stream position from which to continue reading. + * @param length The number of bytes requested for the current chunk. + */ + fun fileChunkRequest( + friendNumber: ToxFriendNumber, + fileNumber: Int, + position: Long, + length: Int, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FileRecvCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FileRecvCallback.kt new file mode 100644 index 000000000..2d1a0055c --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FileRecvCallback.kt @@ -0,0 +1,30 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFilename +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.enums.ToxFileControl + +/** This event is triggered when a file transfer request is received. */ +interface FileRecvCallback { + /** + * The client should acquire resources to be associated with the file transfer. Incoming file + * transfers start in the PAUSED state. After this callback returns, a transfer can be rejected by + * sending a [[ToxFileControl.CANCEL]] control command before any other control commands. It can + * be accepted by sending [[ToxFileControl.RESUME]]. + * + * @param friendNumber The friend number of the friend who is sending the file transfer request. + * @param fileNumber The friend-specific file number the data received is associated with. + * @param kind The meaning of the file to be sent. + * @param fileSize Size in bytes of the file the client wants to send, -1 if unknown or streaming. + * @param filename Name of the file. May not be the actual name. This name was sent along with the + * file send request. + */ + fun fileRecv( + friendNumber: ToxFriendNumber, + fileNumber: Int, + kind: Int, + fileSize: Long, + filename: ToxFilename, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FileRecvChunkCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FileRecvChunkCallback.kt new file mode 100644 index 000000000..ff6f819c5 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FileRecvChunkCallback.kt @@ -0,0 +1,31 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber + +/** + * This event is first triggered when a file transfer request is received, and subsequently when a + * chunk of file data for an accepted request was received. + */ +interface FileRecvChunkCallback { + /** + * When length is 0, the transfer is finished and the client should release the resources it + * acquired for the transfer. After a call with length = 0, the file number can be reused for new + * file transfers. + * + * If position is equal to file_size (received in the [[FileRecvCallback]] callback) when the + * transfer finishes, the file was received completely. Otherwise, if fileSize was negative, + * streaming ended successfully when length is 0. + * + * @param friendNumber The friend number of the friend who is sending the file. + * @param fileNumber The friend-specific file number the data received is associated with. + * @param position The file position of the first byte in data. + * @param data A byte array containing the received chunk. + */ + fun fileRecvChunk( + friendNumber: ToxFriendNumber, + fileNumber: Int, + position: Long, + data: ByteArray, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FileRecvControlCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FileRecvControlCallback.kt new file mode 100644 index 000000000..38f29036d --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FileRecvControlCallback.kt @@ -0,0 +1,22 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.enums.ToxFileControl + +/** This event is triggered when a file control command is received from a friend. */ +interface FileRecvControlCallback { + /** + * When receiving [[ToxFileControl.CANCEL]], the client should release the resources associated + * with the file number and consider the transfer failed. + * + * @param friendNumber The friend number of the friend who is sending the file. + * @param fileNumber The friend-specific file number the data received is associated with. + * @param control The file control command received. + */ + fun fileRecvControl( + friendNumber: ToxFriendNumber, + fileNumber: Int, + control: ToxFileControl, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendConnectionStatusCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendConnectionStatusCallback.kt new file mode 100644 index 000000000..352caa18c --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendConnectionStatusCallback.kt @@ -0,0 +1,29 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.enums.ToxConnection + +/** + * This event is triggered when a friend goes offline after having been online, when a friend goes + * online, or when the connection type (TCP/UDP) changes. + * + * This callback is not called when adding friends. It is assumed that when adding friends, their + * connection status is initially offline. + */ +interface FriendConnectionStatusCallback { + /** + * @param friendNumber The friend number of the friend whose connection status changed. + * @param connectionStatus The new connection status. + */ + fun friendConnectionStatus( + friendNumber: ToxFriendNumber, + connectionStatus: ToxConnection, + state: ToxCoreState + ): ToxCoreState = state + + fun friendConnectionStatus( + friendNumber: Int, + connectionStatus: ToxConnection, + state: ToxCoreState + ): ToxCoreState = friendConnectionStatus(ToxFriendNumber(friendNumber), connectionStatus, state) +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendLosslessPacketCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendLosslessPacketCallback.kt new file mode 100644 index 000000000..9e74a152a --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendLosslessPacketCallback.kt @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.data.ToxLosslessPacket + +/** This event is triggered when a custom lossless packet arrives from a friend. */ +interface FriendLosslessPacketCallback { + /** + * @param friendNumber The friend number of the friend who sent a lossless packet. + * @param data A byte array containing the received packet data. The first byte is the packet id. + */ + fun friendLosslessPacket( + friendNumber: ToxFriendNumber, + data: ToxLosslessPacket, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendLossyPacketCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendLossyPacketCallback.kt new file mode 100644 index 000000000..74433c48e --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendLossyPacketCallback.kt @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.data.ToxLossyPacket + +/** This event is triggered when a custom lossy packet arrives from a friend. */ +interface FriendLossyPacketCallback { + /** + * @param friendNumber The friend number of the friend who sent a lossy packet. + * @param data A byte array containing the received packet data. The first byte is the packet id. + */ + fun friendLossyPacket( + friendNumber: ToxFriendNumber, + data: ToxLossyPacket, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendMessageCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendMessageCallback.kt new file mode 100644 index 000000000..1c4b8094d --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendMessageCallback.kt @@ -0,0 +1,31 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendMessage +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.enums.ToxMessageType + +/** This event is triggered when a message from a friend is received. */ +interface FriendMessageCallback { + /** + * @param friendNumber The friend number of the friend who sent the message. + * @param messageType Message type (normal, action, ...). + * @param timeDelta A delta in seconds between when the message was composed + * + * ``` + * and when it is being transmitted. For messages that are sent immediately, + * it will be 0. If a message was written and couldn't be sent immediately + * (due to a connection failure, for example), the timeDelta is an + * approximation of when it was composed. + * @param message + * ``` + * + * The message data they sent. + */ + fun friendMessage( + friendNumber: ToxFriendNumber, + messageType: ToxMessageType, + timeDelta: Int, + message: ToxFriendMessage, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendNameCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendNameCallback.kt new file mode 100644 index 000000000..2a67b9e5a --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendNameCallback.kt @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.data.ToxNickname + +/** This event is triggered when a friend changes their name. */ +interface FriendNameCallback { + /** + * @param friendNumber The friend number of the friend whose name changed. + * @param name The new nickname. + */ + fun friendName( + friendNumber: ToxFriendNumber, + name: ToxNickname, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendReadReceiptCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendReadReceiptCallback.kt new file mode 100644 index 000000000..64510c4d4 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendReadReceiptCallback.kt @@ -0,0 +1,20 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber + +/** + * This event is triggered when the friend receives the message sent with + * [ [ToxCore.friendSendMessage]] with the corresponding message ID. + */ +interface FriendReadReceiptCallback { + /** + * @param friendNumber The friend number of the friend who received the message. + * @param messageId The message ID as returned from [[ToxCore.friendSendMessage]] corresponding to + * the message sent. + */ + fun friendReadReceipt( + friendNumber: ToxFriendNumber, + messageId: Int, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendRequestCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendRequestCallback.kt new file mode 100644 index 000000000..d2bfbe9fa --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendRequestCallback.kt @@ -0,0 +1,25 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendRequestMessage +import im.tox.tox4j.core.data.ToxPublicKey + +/** This event is triggered when a friend request is received. */ +interface FriendRequestCallback { + /** + * @param publicKey The Public Key of the user who sent the friend request. + * @param timeDelta A delta in seconds between when the message was composed + * + * ``` + * and when it is being transmitted. + * @param message + * ``` + * + * The message they sent along with the request. + */ + fun friendRequest( + publicKey: ToxPublicKey, + timeDelta: Int, + message: ToxFriendRequestMessage, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendStatusCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendStatusCallback.kt new file mode 100644 index 000000000..8070181b6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendStatusCallback.kt @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.enums.ToxUserStatus + +/** This event is triggered when a friend changes their user status. */ +interface FriendStatusCallback { + /** + * @param friendNumber The friend number of the friend whose user status changed. + * @param status The new user status. + */ + fun friendStatus( + friendNumber: ToxFriendNumber, + status: ToxUserStatus, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendStatusMessageCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendStatusMessageCallback.kt new file mode 100644 index 000000000..521eb79e6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendStatusMessageCallback.kt @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.data.ToxStatusMessage + +/** This event is triggered when a friend changes their status message. */ +interface FriendStatusMessageCallback { + /** + * @param friendNumber The friend number of the friend whose status message changed. + * @param message The new status message. + */ + fun friendStatusMessage( + friendNumber: ToxFriendNumber, + message: ToxStatusMessage, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/FriendTypingCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/FriendTypingCallback.kt new file mode 100644 index 000000000..d85ae48f6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/FriendTypingCallback.kt @@ -0,0 +1,16 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.data.ToxFriendNumber + +/** This event is triggered when a friend starts or stops typing. */ +interface FriendTypingCallback { + /** + * @param friendNumber The friend number of the friend who started or stopped typing. + * @param isTyping Whether the friend started (true) or stopped (false) typing. + */ + fun friendTyping( + friendNumber: ToxFriendNumber, + isTyping: Boolean, + state: ToxCoreState + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/SelfConnectionStatusCallback.kt b/src/main/java/im/tox/tox4j/core/callbacks/SelfConnectionStatusCallback.kt new file mode 100644 index 000000000..7dd7424fc --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/SelfConnectionStatusCallback.kt @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.callbacks + +import im.tox.tox4j.core.enums.ToxConnection + +/** + * This event is triggered whenever there is a change in the DHT connection state. When + * disconnected, a client may choose to call [[ToxCore.bootstrap]] again, to reconnect to the DHT. + * Note that this state may frequently change for short amounts of time. Clients should therefore + * not immediately bootstrap on receiving a disconnect. + */ +interface SelfConnectionStatusCallback { + /** @param connectionStatus The new connection status. */ + fun selfConnectionStatus( + connectionStatus: ToxConnection, + state: ToxCoreState, + ): ToxCoreState = state +} diff --git a/src/main/java/im/tox/tox4j/core/callbacks/ToxCoreEventAdapter.kt b/src/main/java/im/tox/tox4j/core/callbacks/ToxCoreEventAdapter.kt new file mode 100644 index 000000000..5eb215b26 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/ToxCoreEventAdapter.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.callbacks + +abstract class ToxCoreEventAdapter : ToxCoreEventListener diff --git a/src/main/java/im/tox/tox4j/core/callbacks/ToxCoreEventListener.kt b/src/main/java/im/tox/tox4j/core/callbacks/ToxCoreEventListener.kt new file mode 100644 index 000000000..de748a683 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/callbacks/ToxCoreEventListener.kt @@ -0,0 +1,18 @@ +package im.tox.tox4j.core.callbacks + +interface ToxCoreEventListener : + SelfConnectionStatusCallback, + FileRecvControlCallback, + FileRecvCallback, + FileRecvChunkCallback, + FileChunkRequestCallback, + FriendConnectionStatusCallback, + FriendMessageCallback, + FriendNameCallback, + FriendRequestCallback, + FriendStatusCallback, + FriendStatusMessageCallback, + FriendTypingCallback, + FriendLosslessPacketCallback, + FriendLossyPacketCallback, + FriendReadReceiptCallback diff --git a/src/main/java/im/tox/tox4j/core/data/ToxFileId.kt b/src/main/java/im/tox/tox4j/core/data/ToxFileId.kt new file mode 100644 index 000000000..db160f09a --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxFileId.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxFileId(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxFilename.kt b/src/main/java/im/tox/tox4j/core/data/ToxFilename.kt new file mode 100644 index 000000000..2eb5634df --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxFilename.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxFilename(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxFriendAddress.kt b/src/main/java/im/tox/tox4j/core/data/ToxFriendAddress.kt new file mode 100644 index 000000000..eb3b0d2f6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxFriendAddress.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxFriendAddress(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxFriendMessage.kt b/src/main/java/im/tox/tox4j/core/data/ToxFriendMessage.kt new file mode 100644 index 000000000..26549ab73 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxFriendMessage.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxFriendMessage(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxFriendNumber.kt b/src/main/java/im/tox/tox4j/core/data/ToxFriendNumber.kt new file mode 100644 index 000000000..fdad58e2e --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxFriendNumber.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxFriendNumber(val value: Int) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxFriendRequestMessage.kt b/src/main/java/im/tox/tox4j/core/data/ToxFriendRequestMessage.kt new file mode 100644 index 000000000..a6256e186 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxFriendRequestMessage.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxFriendRequestMessage(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxLosslessPacket.kt b/src/main/java/im/tox/tox4j/core/data/ToxLosslessPacket.kt new file mode 100644 index 000000000..bb5632849 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxLosslessPacket.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxLosslessPacket(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxLossyPacket.kt b/src/main/java/im/tox/tox4j/core/data/ToxLossyPacket.kt new file mode 100644 index 000000000..18527eed9 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxLossyPacket.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxLossyPacket(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxNickname.kt b/src/main/java/im/tox/tox4j/core/data/ToxNickname.kt new file mode 100644 index 000000000..9ef022474 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxNickname.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxNickname(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxPublicKey.kt b/src/main/java/im/tox/tox4j/core/data/ToxPublicKey.kt new file mode 100644 index 000000000..405d27426 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxPublicKey.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxPublicKey(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxSecretKey.kt b/src/main/java/im/tox/tox4j/core/data/ToxSecretKey.kt new file mode 100644 index 000000000..7744d4e26 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxSecretKey.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxSecretKey(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/data/ToxStatusMessage.kt b/src/main/java/im/tox/tox4j/core/data/ToxStatusMessage.kt new file mode 100644 index 000000000..64c4e5d38 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/data/ToxStatusMessage.kt @@ -0,0 +1,3 @@ +package im.tox.tox4j.core.data + +@JvmInline value class ToxStatusMessage(val value: ByteArray) diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxConnection.java b/src/main/java/im/tox/tox4j/core/enums/ToxConnection.java new file mode 100644 index 000000000..76668b12c --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxConnection.java @@ -0,0 +1,25 @@ +package im.tox.tox4j.core.enums; + +/** + * Protocols that can be used to connect to the network or friends. + */ +public enum ToxConnection { + /** + * There is no connection. This instance, or the friend the state change is + * about, is now offline. + */ + NONE, + /** + * A TCP connection has been established. For the own instance, this means it + * is connected through a TCP relay, only. For a friend, this means that the + * connection to that particular friend goes through a TCP relay. + */ + TCP, + /** + * A UDP connection has been established. For the own instance, this means it + * is able to send UDP packets to DHT nodes, but may still be connected to + * a TCP relay. For a friend, this means that the connection to that + * particular friend was built using direct UDP packets. + */ + UDP, +} diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxFileControl.java b/src/main/java/im/tox/tox4j/core/enums/ToxFileControl.java new file mode 100644 index 000000000..37e82197f --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxFileControl.java @@ -0,0 +1,21 @@ +package im.tox.tox4j.core.enums; + +public enum ToxFileControl { + /** + * Sent by the receiving side to accept a file send request. Also sent after a + * {@link #PAUSE} command to continue sending or receiving. + */ + RESUME, + /** + * Sent by clients to pause the file transfer. The initial state of a file + * transfer is always paused on the receiving side and running on the sending + * side. If both the sending and receiving side pause the transfer, then both + * need to send {@link #RESUME} for the transfer to resume. + */ + PAUSE, + /** + * Sent by the receiving side to reject a file send request before any other + * commands are sent. Also sent by either side to terminate a file transfer. + */ + CANCEL, +} diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxFileKind.java b/src/main/java/im/tox/tox4j/core/enums/ToxFileKind.java new file mode 100644 index 000000000..ce68e81cd --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxFileKind.java @@ -0,0 +1,33 @@ +package im.tox.tox4j.core.enums; + +public final class ToxFileKind { + /** + * Arbitrary file data. Clients can choose to handle it based on the file name + * or magic or any other way they choose. + */ + public static final int DATA = 0; + /** + * Avatar image data. + * + *

+ * Avatars can be sent at any time the client wishes. Generally, a client will + * send the avatar to a friend when that friend comes online, and to all + * friends when the avatar changed. A client can save some traffic by + * remembering which friend received the updated avatar already and only send + * it if the friend has an out of date avatar. + * + *

+ * Clients who receive avatar send requests can reject it (by sending + * {@link FileControl#CANCEL} before any other controls), or accept it + * (by sending {@link FileControl#RESUME}). + * The file_id of length {@link ToxCryptoConstants#HASH_LENGTH} bytes (same + * length as {@link ToxCoreConstants#FILE_ID_LENGTH}) will contain the hash. + * A client can compare this hash with a saved hash and send + * {@link FileControl#CANCEL} to terminate the avatar transfer if it matches. + * + *

+ * When fileSize is set to 0 in the transfer request it means that the client + * has no avatar. + */ + public static final int AVATAR = 1; +} diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxMessageType.java b/src/main/java/im/tox/tox4j/core/enums/ToxMessageType.java new file mode 100644 index 000000000..ab401229f --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxMessageType.java @@ -0,0 +1,17 @@ +package im.tox.tox4j.core.enums; + +/** + * Represents message types for {@link + * im.tox.tox4j.core.ToxCore#friendSendMessage} and group chat messages. + */ +public enum ToxMessageType { + /** + * Normal text message. Similar to PRIVMSG on IRC. + */ + NORMAL, + /** + * A message describing an user action. This is similar to /me (CTCP ACTION) + * on IRC. + */ + ACTION, +} diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxProxyType.java b/src/main/java/im/tox/tox4j/core/enums/ToxProxyType.java new file mode 100644 index 000000000..cadfa4786 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxProxyType.java @@ -0,0 +1,19 @@ +package im.tox.tox4j.core.enums; + +/** + * Type of proxy used to connect to TCP relays. + */ +public enum ToxProxyType { + /** + * Don't use a proxy. + */ + NONE, + /** + * HTTP proxy using CONNECT. + */ + HTTP, + /** + * SOCKS proxy for simple socket pipes. + */ + SOCKS5, +} diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxSavedataType.java b/src/main/java/im/tox/tox4j/core/enums/ToxSavedataType.java new file mode 100644 index 000000000..1a3a4d789 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxSavedataType.java @@ -0,0 +1,20 @@ +package im.tox.tox4j.core.enums; + +/** + * Type of savedata to create the Tox instance from. + */ +public enum ToxSavedataType { + /** + * No savedata. + */ + NONE, + /** + * Savedata is one that was obtained from {@link + * im.tox.tox4j.core.ToxCore#getSavedata}. + */ + TOX_SAVE, + /** + * Savedata is a secret key of length {@link ToxCoreConstants#SecretKeySize}. + */ + SECRET_KEY, +} diff --git a/src/main/java/im/tox/tox4j/core/enums/ToxUserStatus.java b/src/main/java/im/tox/tox4j/core/enums/ToxUserStatus.java new file mode 100644 index 000000000..c6464dc82 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/enums/ToxUserStatus.java @@ -0,0 +1,21 @@ +package im.tox.tox4j.core.enums; + +/** + * Represents the possible statuses a client can have. + */ +public enum ToxUserStatus { + /** + * User is online and available. + */ + NONE, + /** + * User is away. Clients can set this e.g. after a user defined + * inactivity time. + */ + AWAY, + /** + * User is busy. Signals to other clients that this client does not + * currently wish to communicate. + */ + BUSY, +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/Makefile b/src/main/java/im/tox/tox4j/core/exceptions/Makefile new file mode 100644 index 000000000..cbc0eac30 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/Makefile @@ -0,0 +1,2 @@ +all: ../../mkexceptions exceptions.json + $+ diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxBootstrapException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxBootstrapException.java new file mode 100644 index 000000000..4f96907c6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxBootstrapException.java @@ -0,0 +1,34 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.JavaOnly; +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxBootstrapException extends ToxException { + public enum Code { + /** + * The public key was of invalid length. + */ + @JavaOnly BAD_KEY, + /** + * The address could not be resolved to an IP address, or the IP address + * passed was invalid. + */ + BAD_HOST, + /** + * The port passed was invalid. The valid port range is (1, 65535). + */ + BAD_PORT, + /** + * An argument was null. + */ + NULL, + } + + public ToxBootstrapException(Code code) { + this(code, ""); + } + + public ToxBootstrapException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFileControlException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileControlException.java new file mode 100644 index 000000000..a43134218 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileControlException.java @@ -0,0 +1,49 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFileControlException extends ToxException { + public enum Code { + /** + * A {@link ToxFileControl#PAUSE} control was sent, but the file transfer + * was already paused. + */ + ALREADY_PAUSED, + /** + * A {@link ToxFileControl#RESUME} control was sent, but the file transfer + * was paused by the other party. Only the party that paused the transfer + * can resume it. + */ + DENIED, + /** + * This client is currently not connected to the friend. + */ + FRIEND_NOT_CONNECTED, + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * No file transfer with the given file number was found for the given + * friend. + */ + NOT_FOUND, + /** + * A {@link ToxFileControl#RESUME} control was sent, but the file transfer + * is running normally. + */ + NOT_PAUSED, + /** + * An allocation error occurred while increasing the send queue size. + */ + SENDQ, + } + + public ToxFileControlException(Code code) { + this(code, ""); + } + + public ToxFileControlException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFileGetException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileGetException.java new file mode 100644 index 000000000..35f199224 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileGetException.java @@ -0,0 +1,29 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFileGetException extends ToxException { + public enum Code { + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * No file transfer with the given file number was found for the given + * friend. + */ + NOT_FOUND, + /** + * An argument was null. + */ + NULL, + } + + public ToxFileGetException(Code code) { + this(code, ""); + } + + public ToxFileGetException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSeekException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSeekException.java new file mode 100644 index 000000000..9950c8c6b --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSeekException.java @@ -0,0 +1,41 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFileSeekException extends ToxException { + public enum Code { + /** + * File was not in a state where it could be seeked. + */ + DENIED, + /** + * This client is currently not connected to the friend. + */ + FRIEND_NOT_CONNECTED, + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * Seek position was invalid. + */ + INVALID_POSITION, + /** + * No file transfer with the given file number was found for the given + * friend. + */ + NOT_FOUND, + /** + * An allocation error occurred while increasing the send queue size. + */ + SENDQ, + } + + public ToxFileSeekException(Code code) { + this(code, ""); + } + + public ToxFileSeekException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSendChunkException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSendChunkException.java new file mode 100644 index 000000000..43f90aea1 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSendChunkException.java @@ -0,0 +1,54 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFileSendChunkException extends ToxException { + public enum Code { + /** + * This client is currently not connected to the friend. + */ + FRIEND_NOT_CONNECTED, + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * Attempted to send more or less data than requested. The requested data + * size is adjusted according to maximum transmission unit and the expected + * end of the file. Trying to send less or more than requested will return + * this error. + */ + INVALID_LENGTH, + /** + * No file transfer with the given file number was found for the given + * friend. + */ + NOT_FOUND, + /** + * File transfer was found but isn't in a transferring state: (paused, done, + * broken, etc...) (happens only when not called from the request chunk + * callback). + */ + NOT_TRANSFERRING, + /** + * An argument was null. + */ + NULL, + /** + * An allocation error occurred while increasing the send queue size. + */ + SENDQ, + /** + * Position parameter was wrong. + */ + WRONG_POSITION, + } + + public ToxFileSendChunkException(Code code) { + this(code, ""); + } + + public ToxFileSendChunkException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSendException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSendException.java new file mode 100644 index 000000000..23b54fcc1 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFileSendException.java @@ -0,0 +1,39 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFileSendException extends ToxException { + public enum Code { + /** + * This client is currently not connected to the friend. + */ + FRIEND_NOT_CONNECTED, + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * Filename length exceeded {@link ToxCoreConstants#MAX_FILENAME_LENGTH} + * bytes. + */ + NAME_TOO_LONG, + /** + * An argument was null. + */ + NULL, + /** + * Too many ongoing transfers. The maximum number of concurrent file + * transfers is 256 per friend per direction (sending and receiving, so 512 + * total). + */ + TOO_MANY, + } + + public ToxFileSendException(Code code) { + this(code, ""); + } + + public ToxFileSendException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendAddException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendAddException.java new file mode 100644 index 000000000..17e350471 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendAddException.java @@ -0,0 +1,53 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFriendAddException extends ToxException { + public enum Code { + /** + * A friend request has already been sent, or the address belongs to a + * friend that is already on the friend list. To resend a friend request, + * first remove the friend, and then call addFriend again. + */ + ALREADY_SENT, + /** + * The friend address checksum failed. + */ + BAD_CHECKSUM, + /** + * A memory allocation failed when trying to increase the friend list size. + */ + MALLOC, + /** + * The friend request message was empty. This, and the TOO_LONG code will + * never be returned from {@link ToxCore#addFriendNoRequest}. + */ + NO_MESSAGE, + /** + * An argument was null. + */ + NULL, + /** + * The friend address belongs to the sending client. + */ + OWN_KEY, + /** + * The friend was already on the friend list, but the noSpam value was + * different. + */ + SET_NEW_NOSPAM, + /** + * The length of the friend request message exceeded {@link + * ToxCoreConstants#MAX_FRIEND_REQUEST_LENGTH}. + */ + TOO_LONG, + } + + public ToxFriendAddException(Code code) { + this(code, ""); + } + + public ToxFriendAddException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendByPublicKeyException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendByPublicKeyException.java new file mode 100644 index 000000000..e9ea80716 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendByPublicKeyException.java @@ -0,0 +1,24 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFriendByPublicKeyException extends ToxException { + public enum Code { + /** + * No friend with the given Public Key exists on the friend list. + */ + NOT_FOUND, + /** + * An argument was null. + */ + NULL, + } + + public ToxFriendByPublicKeyException(Code code) { + this(code, ""); + } + + public ToxFriendByPublicKeyException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendCustomPacketException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendCustomPacketException.java new file mode 100644 index 000000000..68b72cffc --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendCustomPacketException.java @@ -0,0 +1,46 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFriendCustomPacketException extends ToxException { + public enum Code { + /** + * Attempted to send an empty packet. + */ + EMPTY, + /** + * This client is currently not connected to the friend. + */ + FRIEND_NOT_CONNECTED, + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * The first byte of data was not in the specified range for the packet + * type. This range is 200-254 for lossy, and 160-191 for lossless packets. + */ + INVALID, + /** + * An argument was null. + */ + NULL, + /** + * An allocation error occurred while increasing the send queue size. + */ + SENDQ, + /** + * Packet data length exceeded {@link + * ToxCoreConstants#MAX_CUSTOM_PACKET_SIZE}. + */ + TOO_LONG, + } + + public ToxFriendCustomPacketException(Code code) { + this(code, ""); + } + + public ToxFriendCustomPacketException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendDeleteException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendDeleteException.java new file mode 100644 index 000000000..0d1af68b8 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendDeleteException.java @@ -0,0 +1,21 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFriendDeleteException extends ToxException { + public enum Code { + /** + * There was no friend with the given friend number. No friends were + * deleted. + */ + FRIEND_NOT_FOUND, + } + + public ToxFriendDeleteException(Code code) { + this(code, ""); + } + + public ToxFriendDeleteException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendGetPublicKeyException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendGetPublicKeyException.java new file mode 100644 index 000000000..ec808864d --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendGetPublicKeyException.java @@ -0,0 +1,20 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFriendGetPublicKeyException extends ToxException { + public enum Code { + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + } + + public ToxFriendGetPublicKeyException(Code code) { + this(code, ""); + } + + public ToxFriendGetPublicKeyException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendSendMessageException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendSendMessageException.java new file mode 100644 index 000000000..4fdcfe3b9 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxFriendSendMessageException.java @@ -0,0 +1,40 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxFriendSendMessageException extends ToxException { + public enum Code { + /** + * Attempted to send a zero-length message. + */ + EMPTY, + /** + * This client is currently not connected to the friend. + */ + FRIEND_NOT_CONNECTED, + /** + * The friend number did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * An argument was null. + */ + NULL, + /** + * An allocation error occurred while increasing the send queue size. + */ + SENDQ, + /** + * Message length exceeded {@link ToxCoreConstants#MAX_MESSAGE_LENGTH}. + */ + TOO_LONG, + } + + public ToxFriendSendMessageException(Code code) { + this(code, ""); + } + + public ToxFriendSendMessageException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxGetPortException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxGetPortException.java new file mode 100644 index 000000000..256f472fb --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxGetPortException.java @@ -0,0 +1,20 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxGetPortException extends ToxException { + public enum Code { + /** + * The instance was not bound to any port. + */ + NOT_BOUND, + } + + public ToxGetPortException(Code code) { + this(code, ""); + } + + public ToxGetPortException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxNewException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxNewException.java new file mode 100644 index 000000000..2c23b11c0 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxNewException.java @@ -0,0 +1,63 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxNewException extends ToxException { + public enum Code { + /** + * The data format was invalid. This can happen when loading data that was + * saved by an older version of Tox, or when the data has been corrupted. + * When loading from badly formatted data, some data may have been loaded, + * and the rest is discarded. Passing an invalid length parameter also + * causes this error. + */ + LOAD_BAD_FORMAT, + /** + * The byte array to be loaded contained an encrypted save. + */ + LOAD_ENCRYPTED, + /** + * The function was unable to allocate enough memory to store the internal + * structures for the Tox object. + */ + MALLOC, + /** + * An argument was null. + */ + NULL, + /** + * The function was unable to bind to a port. This may mean that all ports + * have already been bound, e.g. by other Tox instances, or it may mean + * a permission error. You may be able to gather more information from + * errno. + */ + PORT_ALLOC, + /** + * {@link im.tox.tox4j.core.ToxOptions#proxyType} was valid, + * but the {@link im.tox.tox4j.core.ToxOptions#proxyAddress} passed had an + * invalid format. + */ + PROXY_BAD_HOST, + /** + * {@link im.tox.tox4j.core.ToxOptions#proxyType} was valid, + * but the {@link im.tox.tox4j.core.ToxOptions#proxyPort} was invalid. + */ + PROXY_BAD_PORT, + /** + * {@link im.tox.tox4j.core.ToxOptions#proxyType} was invalid. + */ + PROXY_BAD_TYPE, + /** + * The proxy address passed could not be resolved. + */ + PROXY_NOT_FOUND, + } + + public ToxNewException(Code code) { + this(code, ""); + } + + public ToxNewException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxSetInfoException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxSetInfoException.java new file mode 100644 index 000000000..89845271b --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxSetInfoException.java @@ -0,0 +1,24 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxSetInfoException extends ToxException { + public enum Code { + /** + * An argument was null. + */ + NULL, + /** + * Information length exceeded maximum permissible size. + */ + TOO_LONG, + } + + public ToxSetInfoException(Code code) { + this(code, ""); + } + + public ToxSetInfoException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/ToxSetTypingException.java b/src/main/java/im/tox/tox4j/core/exceptions/ToxSetTypingException.java new file mode 100644 index 000000000..6328ba198 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/ToxSetTypingException.java @@ -0,0 +1,20 @@ +package im.tox.tox4j.core.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxSetTypingException extends ToxException { + public enum Code { + /** + * The friendNumber passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + } + + public ToxSetTypingException(Code code) { + this(code, ""); + } + + public ToxSetTypingException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/core/exceptions/exceptions.json b/src/main/java/im/tox/tox4j/core/exceptions/exceptions.json new file mode 100644 index 000000000..85d1a2501 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/exceptions/exceptions.json @@ -0,0 +1,228 @@ +[ + "core", + "", + { + "Bootstrap": { + "NULL": ["An argument was null."], + "BAD_HOST": [ + "The address could not be resolved to an IP address, or the IP address", + "passed was invalid." + ], + "BAD_PORT": [ + "The port passed was invalid. The valid port range is (1, 65535)." + ], + "@JavaOnly BAD_KEY": ["The public key was of invalid length."] + }, + "FileControl": { + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "This client is currently not connected to the friend." + ], + "NOT_FOUND": [ + "No file transfer with the given file number was found for the given friend." + ], + "NOT_PAUSED": [ + "A {@link ToxFileControl#RESUME} control was sent, but the file transfer is running normally." + ], + "DENIED": [ + "A {@link ToxFileControl#RESUME} control was sent, but the file transfer was paused by the other", + "party. Only the party that paused the transfer can resume it." + ], + "ALREADY_PAUSED": [ + "A {@link ToxFileControl#PAUSE} control was sent, but the file transfer was already paused." + ], + "SENDQ": [ + "An allocation error occurred while increasing the send queue size." + ] + }, + "FileSeek": { + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "This client is currently not connected to the friend." + ], + "NOT_FOUND": [ + "No file transfer with the given file number was found for the given friend." + ], + "DENIED": ["File was not in a state where it could be seeked."], + "INVALID_POSITION": ["Seek position was invalid."], + "SENDQ": [ + "An allocation error occurred while increasing the send queue size." + ] + }, + "FileSendChunk": { + "NULL": ["An argument was null."], + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "This client is currently not connected to the friend." + ], + "NOT_FOUND": [ + "No file transfer with the given file number was found for the given friend." + ], + "NOT_TRANSFERRING": [ + "File transfer was found but isn't in a transferring state: (paused, done,", + "broken, etc...) (happens only when not called from the request chunk callback)." + ], + "INVALID_LENGTH": [ + "Attempted to send more or less data than requested. The requested data size is", + "adjusted according to maximum transmission unit and the expected end of", + "the file. Trying to send less or more than requested will return this error." + ], + "SENDQ": [ + "An allocation error occurred while increasing the send queue size." + ], + "WRONG_POSITION": ["Position parameter was wrong."] + }, + "FileSend": { + "NULL": ["An argument was null."], + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "This client is currently not connected to the friend." + ], + "NAME_TOO_LONG": [ + "Filename length exceeded {@link ToxCoreConstants#MAX_FILENAME_LENGTH} bytes." + ], + "TOO_MANY": [ + "Too many ongoing transfers. The maximum number of concurrent file transfers", + "is 256 per friend per direction (sending and receiving, so 512 total)." + ] + }, + "FileGet": { + "NULL": ["An argument was null."], + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ], + "NOT_FOUND": [ + "No file transfer with the given file number was found for the given friend." + ] + }, + "FriendAdd": { + "NULL": ["An argument was null."], + "TOO_LONG": [ + "The length of the friend request message exceeded {@link ToxCoreConstants#MAX_FRIEND_REQUEST_LENGTH}." + ], + "NO_MESSAGE": [ + "The friend request message was empty. This, and the TOO_LONG code will", + "never be returned from {@link ToxCore#addFriendNoRequest}." + ], + "OWN_KEY": ["The friend address belongs to the sending client."], + "ALREADY_SENT": [ + "A friend request has already been sent, or the address belongs to a friend", + "that is already on the friend list. To resend a friend request, first remove", + "the friend, and then call addFriend again." + ], + "BAD_CHECKSUM": ["The friend address checksum failed."], + "SET_NEW_NOSPAM": [ + "The friend was already on the friend list, but the noSpam value was different." + ], + "MALLOC": [ + "A memory allocation failed when trying to increase the friend list size." + ] + }, + "FriendByPublicKey": { + "NULL": ["An argument was null."], + "NOT_FOUND": [ + "No friend with the given Public Key exists on the friend list." + ] + }, + "FriendDelete": { + "FRIEND_NOT_FOUND": [ + "There was no friend with the given friend number. No friends were deleted." + ] + }, + "FriendGetPublicKey": { + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ] + }, + "GetPort": { + "NOT_BOUND": ["The instance was not bound to any port."] + }, + "New": { + "NULL": ["An argument was null."], + "MALLOC": [ + "The function was unable to allocate enough memory to store the internal", + "structures for the Tox object." + ], + "PORT_ALLOC": [ + "The function was unable to bind to a port. This may mean that all ports", + "have already been bound, e.g. by other Tox instances, or it may mean", + "a permission error. You may be able to gather more information from errno." + ], + "PROXY_BAD_TYPE": [ + "{@link im.tox.tox4j.core.ToxOptions#proxyType} was invalid." + ], + "PROXY_BAD_HOST": [ + "{@link im.tox.tox4j.core.ToxOptions#proxyType} was valid,", + "but the {@link im.tox.tox4j.core.ToxOptions#proxyAddress} passed had an invalid format." + ], + "PROXY_BAD_PORT": [ + "{@link im.tox.tox4j.core.ToxOptions#proxyType} was valid,", + "but the {@link im.tox.tox4j.core.ToxOptions#proxyPort} was invalid." + ], + "PROXY_NOT_FOUND": ["The proxy address passed could not be resolved."], + "LOAD_ENCRYPTED": [ + "The byte array to be loaded contained an encrypted save." + ], + "LOAD_BAD_FORMAT": [ + "The data format was invalid. This can happen when loading data that was", + "saved by an older version of Tox, or when the data has been corrupted.", + "When loading from badly formatted data, some data may have been loaded,", + "and the rest is discarded. Passing an invalid length parameter also", + "causes this error." + ] + }, + "FriendCustomPacket": { + "NULL": ["An argument was null."], + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "This client is currently not connected to the friend." + ], + "INVALID": [ + "The first byte of data was not in the specified range for the packet type.", + "This range is 200-254 for lossy, and 160-191 for lossless packets." + ], + "EMPTY": ["Attempted to send an empty packet."], + "TOO_LONG": [ + "Packet data length exceeded {@link ToxCoreConstants#MAX_CUSTOM_PACKET_SIZE}." + ], + "SENDQ": [ + "An allocation error occurred while increasing the send queue size." + ] + }, + "FriendSendMessage": { + "NULL": ["An argument was null."], + "FRIEND_NOT_FOUND": [ + "The friend number did not designate a valid friend." + ], + "FRIEND_NOT_CONNECTED": [ + "This client is currently not connected to the friend." + ], + "SENDQ": [ + "An allocation error occurred while increasing the send queue size." + ], + "TOO_LONG": [ + "Message length exceeded {@link ToxCoreConstants#MAX_MESSAGE_LENGTH}." + ], + "EMPTY": ["Attempted to send a zero-length message."] + }, + "SetInfo": { + "NULL": ["An argument was null."], + "TOO_LONG": ["Information length exceeded maximum permissible size."] + }, + "SetTyping": { + "FRIEND_NOT_FOUND": [ + "The friendNumber passed did not designate a valid friend." + ] + } + } +] diff --git a/src/main/java/im/tox/tox4j/core/options/ProxyOptions.kt b/src/main/java/im/tox/tox4j/core/options/ProxyOptions.kt new file mode 100644 index 000000000..a1ba8d4d1 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/options/ProxyOptions.kt @@ -0,0 +1,55 @@ +package im.tox.tox4j.core.options + +import im.tox.tox4j.core.enums.ToxProxyType + +/** Proxy options for [[ToxCore.load]] */ +object ProxyOptions { + /** Base type for all proxy kinds. */ + sealed interface Type { + /** Low level enumeration value to pass to [[ToxCore.load]]. */ + val proxyType: ToxProxyType + + /** + * The IP address or DNS name of the proxy to be used. + * + * If used, this must be a valid DNS name. The name must not exceed + * [ [ToxCoreConstants.MaxHostnameLength]] characters. This member is ignored (it can be + * anything) if [[proxyType]] is [[ToxProxyType.NONE]]. + */ + val proxyAddress: String + + /** + * The port to use to connect to the proxy server. + * + * Ports must be in the range (1, 65535). The value is ignored if [[proxyType]] is + * [ [ToxProxyType.NONE]]. + */ + val proxyPort: UShort + } + + /** Don't use a proxy. Attempt to directly connect to other nodes. */ + object None : Type { + override val proxyType: ToxProxyType = ToxProxyType.NONE + override val proxyAddress: String = "" + override val proxyPort: UShort = 0.toUShort() + } + + /** Tunnel Tox TCP traffic over an HTTP proxy. The proxy must support CONNECT. */ + final data class Http( + override val proxyAddress: String, + override val proxyPort: UShort, + ) : Type { + override val proxyType: ToxProxyType = ToxProxyType.HTTP + } + + /** + * Use a SOCKS5 proxy to make TCP connections. Although some SOCKS5 servers support UDP sockets, + * the main use case (Tor) does not, and Tox will not use the proxy for UDP connections. + */ + final data class Socks5( + override val proxyAddress: String, + override val proxyPort: UShort, + ) : Type { + override val proxyType: ToxProxyType = ToxProxyType.SOCKS5 + } +} diff --git a/src/main/java/im/tox/tox4j/core/options/SaveDataOptions.kt b/src/main/java/im/tox/tox4j/core/options/SaveDataOptions.kt new file mode 100644 index 000000000..694827b70 --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/options/SaveDataOptions.kt @@ -0,0 +1,41 @@ +package im.tox.tox4j.core.options + +import im.tox.tox4j.core.data.ToxSecretKey +import im.tox.tox4j.core.enums.ToxSavedataType + +/** Base type for all save data kinds. */ +object SaveDataOptions { + sealed interface Type { + /** The low level [[ToxSavedataType]] enum to pass to [[ToxCore.load]]. */ + val kind: ToxSavedataType + + /** Serialised save data. The format depends on [[kind]]. */ + val data: ByteArray + } + + /** The various kinds of save data that can be loaded by [[ToxCore.load]]. */ + + /** No save data. */ + object None : Type { + override val kind: ToxSavedataType = ToxSavedataType.NONE + override val data: ByteArray = byteArrayOf() + } + + /** + * Full save data containing friend list, last seen DHT nodes, name, and all other information + * contained within a Tox instance. + */ + final data class ToxSave(override val data: ByteArray) : Type { + override val kind: ToxSavedataType = ToxSavedataType.TOX_SAVE + } + + /** + * Minimal save data with just the secret key. The public key can be derived from it. Saving this + * secret key, the friend list, name, and noSpam value is sufficient to restore the observable + * behaviour of a Tox instance without the full save data in [[ToxSave]]. + */ + final data class SecretKey(private val key: ToxSecretKey) : Type { + override val kind: ToxSavedataType = ToxSavedataType.SECRET_KEY + override val data: ByteArray = key.value + } +} diff --git a/src/main/java/im/tox/tox4j/core/options/ToxOptions.kt b/src/main/java/im/tox/tox4j/core/options/ToxOptions.kt new file mode 100644 index 000000000..0c9a9055e --- /dev/null +++ b/src/main/java/im/tox/tox4j/core/options/ToxOptions.kt @@ -0,0 +1,49 @@ +package im.tox.tox4j.core.options + +import im.tox.tox4j.core.ToxCoreConstants + +/** + * This class contains all the startup options for Tox. + * + * @param ipv6Enabled The type of socket to create. + * + * If this is set to false, an IPv4 socket is created, which subsequently only allows IPv4 + * communication. If it is set to true, an IPv6 socket is created, allowing both IPv4 and IPv6 + * communication. + * + * @param udpEnabled Enable the use of UDP communication when available. + * + * Setting this to false will force Tox to use TCP only. Communications will need to be relayed + * through a TCP relay node, potentially slowing them down. Disabling UDP support is necessary when + * using anonymous proxies or Tor. If UDP is enabled, it will be used, even if a proxy is + * configured. The proxy is only used for TCP connections. + * + * @param proxy Pass communications through a proxy. + * @param startPort The start port of the inclusive port range to attempt to use. + * + * If both startPort and endPort are 0, the default port range will be used: [33445, 33545]. + * + * If either startPort or endPort is 0 while the other is non-zero, the non-zero port will be the + * only port in the range. + * + * @param endPort The end port of the inclusive port range to attempt to use. + * @param tcpPort The port to use for the TCP server. If 0, the tcp server is disabled. + * @param saveData Optional serialised instance data from [[ToxCore.load]] or secret key from + * [ [ToxCore.getSecretKey]]. + * @param fatalErrors Whether exceptions in [[ToxCore.iterate]] should abort the iteration. + */ +final data class ToxOptions( + val ipv6Enabled: Boolean = true, + val udpEnabled: Boolean = true, + val localDiscoveryEnabled: Boolean = true, + val proxy: ProxyOptions.Type = ProxyOptions.None, + val startPort: UShort = ToxCoreConstants.DefaultStartPort, + val endPort: UShort = ToxCoreConstants.DefaultEndPort, + val tcpPort: UShort = ToxCoreConstants.DefaultTcpPort, + val saveData: SaveDataOptions.Type = SaveDataOptions.None, + val fatalErrors: Boolean = true, +) { + companion object { + val defaultInstance = ToxOptions() + } +} diff --git a/src/main/java/im/tox/tox4j/crypto/ToxCrypto.kt b/src/main/java/im/tox/tox4j/crypto/ToxCrypto.kt new file mode 100644 index 000000000..68ae3bd61 --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/ToxCrypto.kt @@ -0,0 +1,116 @@ +package im.tox.tox4j.crypto + +import im.tox.tox4j.crypto.exceptions.* + +/** + * To perform encryption, first derive an encryption key from a password with + * [[ToxCrypto.passKeyDerive]], and use the returned key to encrypt the data. + * + * The encrypted data is prepended with a magic number, to aid validity checking (no guarantees are + * made of course). Any data to be decrypted must start with the magic number. + * + * Clients should consider alerting their users that, unlike plain data, if even one bit becomes + * corrupted, the data will be entirely unrecoverable. Ditto if they forget their password, there is + * no way to recover the data. + */ +interface ToxCrypto { + + /** + * Compares two [[PassKey]]s for equality. + * + * @return true if the [[PassKey]]s are equal. + */ + fun passKeyEquals(a: PassKey, b: PassKey): Boolean + + /** + * Serialise the [[PassKey]] to a byte sequence. + * + * @return A sequence of bytes making up a [[PassKey]]. + */ + fun passKeyToBytes(passKey: PassKey): List + + /** + * Deserialise a [[PassKey]] from a byte sequence. + * + * @return [[Some]]([[PassKey]]) if the key was valid, [[None]] otherwise. + */ + fun passKeyFromBytes(bytes: List): PassKey? + + /** + * Generates a secret symmetric key from the given passphrase. + * + * Be sure to not compromise the key! Only keep it in memory, do not write to disk. The key should + * only be used with the other functions in this module, as it includes a salt. + * + * Note that this function is not deterministic; to derive the same key from a password, you also + * must know the random salt that was used. See below. + * + * @param passphrase A non-empty byte array containing the passphrase. + * @return the generated symmetric key. + */ + // @throws[ToxKeyDerivationException] + fun passKeyDerive(passphrase: ByteArray): PassKey + + /** + * Same as above, except use the given salt for deterministic key derivation. + * + * @param passphrase A non-empty byte array containing the passphrase. + * @param salt Array of size [[ToxCryptoConstants.SaltLength]]. + */ + // @throws[ToxKeyDerivationException] + fun passKeyDeriveWithSalt(passphrase: ByteArray, salt: ByteArray): PassKey + + /** + * This retrieves the salt used to encrypt the given data, which can then be passed to + * [[passKeyDeriveWithSalt]] to produce the same key as was previously used. Any encrypted data + * with this module can be used as input. + * + * Success does not say anything about the validity of the data, only that data of the appropriate + * size was copied. + * + * @return the salt, or an empty array if the magic number did not match. + */ + // @throws[ToxGetSaltException] + fun getSalt(data: ByteArray): ByteArray + + /* Now come the functions that are analogous to the part 2 functions. */ + + /** + * Encrypt arbitrary data with a key produced by [[passKeyDerive]] or [[passKeyDeriveWithSalt]]. + * + * The output array will be [[ToxCryptoConstants.EncryptionExtraLength]] bytes longer than the + * input array. + * + * The result will be different on each call. + * + * @return the encrypted output array. + */ + // @throws[ToxEncryptionException] + fun encrypt(data: ByteArray, passKey: PassKey): ByteArray + + /** + * This is the inverse of [[encrypt]], also using only keys produced by [[passKeyDerive]]. + * + * The output data has size data_length - [[ToxCryptoConstants.EncryptionExtraLength]]. + * + * @return the decrypted output array. + */ + // @throws[ToxDecryptionException] + fun decrypt(data: ByteArray, passKey: PassKey): ByteArray + + /** Determines whether or not the given data is encrypted (by checking the magic number) */ + fun isDataEncrypted(data: ByteArray): Boolean + + /** + * Generates a cryptographic hash of the given data. + * + * This function may be used by clients for any purpose, but is provided primarily for validating + * cached avatars. This use is highly recommended to avoid unnecessary avatar updates. + * + * This function is a wrapper to internal message-digest functions. + * + * @param data Data to be hashed. + * @return hash of the data. + */ + fun hash(data: ByteArray): ByteArray +} diff --git a/src/main/java/im/tox/tox4j/crypto/ToxCryptoConstants.kt b/src/main/java/im/tox/tox4j/crypto/ToxCryptoConstants.kt new file mode 100644 index 000000000..1e69eea13 --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/ToxCryptoConstants.kt @@ -0,0 +1,27 @@ +package im.tox.tox4j.crypto + +object ToxCryptoConstants { + + /** Length of salt in bytes. */ + val SaltLength = 32 + + /** + * The number of bytes in a serialised [[ToxCrypto.PassKey]] without salt. The serialised size is + * [[KeyLength]] + [[SaltLength]]. + */ + val KeyLength = 32 + + /** Number of bytes added to any encrypted data. */ + val EncryptionExtraLength = 80 + + /** The number of bytes in a hash generated by tox_hash. */ + val HashLength = 32 + + val PublicKeyLength = 32 + val SecretKeyLength = 32 + val SharedKeyLength = 32 + val NonceLength = 24 + + val ZeroBytes = 32 + val BoxZeroBytes = 16 +} diff --git a/src/main/java/im/tox/tox4j/crypto/exceptions/Makefile b/src/main/java/im/tox/tox4j/crypto/exceptions/Makefile new file mode 100644 index 000000000..cbc0eac30 --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/exceptions/Makefile @@ -0,0 +1,2 @@ +all: ../../mkexceptions exceptions.json + $+ diff --git a/src/main/java/im/tox/tox4j/crypto/exceptions/ToxDecryptionException.java b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxDecryptionException.java new file mode 100644 index 000000000..a3304b3bb --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxDecryptionException.java @@ -0,0 +1,41 @@ +package im.tox.tox4j.crypto.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxDecryptionException extends ToxException { + public enum Code { + /** + * The input data is missing the magic number (i.e. wasn't created by this + * module, or is corrupted) + */ + BAD_FORMAT, + /** + * The encrypted byte array could not be decrypted. Either the data was + * corrupt or the password/key was incorrect. + */ + FAILED, + /** + * The input data was shorter than {@link + * ToxCryptoConstants.ENCRYPTION_EXTRA_LENGTH} bytes. + */ + INVALID_LENGTH, + /** + * The crypto lib was unable to derive a key from the given passphrase, + * which is usually a lack of memory issue. The functions accepting keys + * do not produce this error. + */ + KEY_DERIVATION_FAILED, + /** + * The key or input data was null or empty. + */ + NULL, + } + + public ToxDecryptionException(Code code) { + this(code, ""); + } + + public ToxDecryptionException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/crypto/exceptions/ToxEncryptionException.java b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxEncryptionException.java new file mode 100644 index 000000000..bf0ff894e --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxEncryptionException.java @@ -0,0 +1,30 @@ +package im.tox.tox4j.crypto.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxEncryptionException extends ToxException { + public enum Code { + /** + * The encryption itself failed. + */ + FAILED, + /** + * The crypto lib was unable to derive a key from the given passphrase, + * which is usually a lack of memory issue. The functions accepting keys + * do not produce this error. + */ + KEY_DERIVATION_FAILED, + /** + * The key or input data was null or empty. + */ + NULL, + } + + public ToxEncryptionException(Code code) { + this(code, ""); + } + + public ToxEncryptionException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/crypto/exceptions/ToxGetSaltException.java b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxGetSaltException.java new file mode 100644 index 000000000..7e631b47d --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxGetSaltException.java @@ -0,0 +1,25 @@ +package im.tox.tox4j.crypto.exceptions; + +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxGetSaltException extends ToxException { + public enum Code { + /** + * The input data is missing the magic number (i.e. wasn't created by this + * module, or is corrupted) + */ + BAD_FORMAT, + /** + * The data or salt were null. + */ + NULL, + } + + public ToxGetSaltException(Code code) { + this(code, ""); + } + + public ToxGetSaltException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/crypto/exceptions/ToxKeyDerivationException.java b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxKeyDerivationException.java new file mode 100644 index 000000000..00e03d1b6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/exceptions/ToxKeyDerivationException.java @@ -0,0 +1,31 @@ +package im.tox.tox4j.crypto.exceptions; + +import im.tox.tox4j.exceptions.JavaOnly; +import im.tox.tox4j.exceptions.ToxException; + +public final class ToxKeyDerivationException extends ToxException { + public enum Code { + /** + * The salt was of incorrect length. + */ + @JavaOnly INVALID_LENGTH, + /** + * The crypto lib was unable to derive a key from the given passphrase, + * which is usually a lack of memory issue. The functions accepting keys + * do not produce this error. + */ + FAILED, + /** + * The passphrase was null or empty. + */ + NULL, + } + + public ToxKeyDerivationException(Code code) { + this(code, ""); + } + + public ToxKeyDerivationException(Code code, String message) { + super(code, message); + } +} diff --git a/src/main/java/im/tox/tox4j/crypto/exceptions/exceptions.json b/src/main/java/im/tox/tox4j/crypto/exceptions/exceptions.json new file mode 100644 index 000000000..e9cb37cc8 --- /dev/null +++ b/src/main/java/im/tox/tox4j/crypto/exceptions/exceptions.json @@ -0,0 +1,50 @@ +[ + "crypto", + "", + { + "KeyDerivation": { + "NULL": ["The passphrase was null or empty."], + "FAILED": [ + "The crypto lib was unable to derive a key from the given passphrase,", + "which is usually a lack of memory issue. The functions accepting keys", + "do not produce this error." + ], + "@JavaOnly INVALID_LENGTH": ["The salt was of incorrect length."] + }, + "Encryption": { + "NULL": ["The key or input data was null or empty."], + "KEY_DERIVATION_FAILED": [ + "The crypto lib was unable to derive a key from the given passphrase,", + "which is usually a lack of memory issue. The functions accepting keys", + "do not produce this error." + ], + "FAILED": ["The encryption itself failed."] + }, + "Decryption": { + "NULL": ["The key or input data was null or empty."], + "INVALID_LENGTH": [ + "The input data was shorter than {@link ToxCryptoConstants.ENCRYPTION_EXTRA_LENGTH} bytes." + ], + "BAD_FORMAT": [ + "The input data is missing the magic number (i.e. wasn't created by this", + "module, or is corrupted)" + ], + "KEY_DERIVATION_FAILED": [ + "The crypto lib was unable to derive a key from the given passphrase,", + "which is usually a lack of memory issue. The functions accepting keys", + "do not produce this error." + ], + "FAILED": [ + "The encrypted byte array could not be decrypted. Either the data was", + "corrupt or the password/key was incorrect." + ] + }, + "GetSalt": { + "NULL": ["The data or salt were null."], + "BAD_FORMAT": [ + "The input data is missing the magic number (i.e. wasn't created by this", + "module, or is corrupted)" + ] + } + } +] diff --git a/src/main/java/im/tox/tox4j/exceptions/JavaOnly.java b/src/main/java/im/tox/tox4j/exceptions/JavaOnly.java new file mode 100644 index 000000000..1da5c3670 --- /dev/null +++ b/src/main/java/im/tox/tox4j/exceptions/JavaOnly.java @@ -0,0 +1,20 @@ +package im.tox.tox4j.exceptions; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark error codes in Java exception enums as Java-only, so they + * are not emitted as part of the error code conversion fragments in C++ (see + * {@link im.tox.tox4j.impl.jni.codegen.JniErrorCodes}). + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface JavaOnly { + /** + * This is just here so the annotation is retained at runtime. + */ + String value() default ""; +} diff --git a/src/main/java/im/tox/tox4j/exceptions/ToxException.kt b/src/main/java/im/tox/tox4j/exceptions/ToxException.kt new file mode 100644 index 000000000..e0d7c5ec9 --- /dev/null +++ b/src/main/java/im/tox/tox4j/exceptions/ToxException.kt @@ -0,0 +1,12 @@ +package im.tox.tox4j.exceptions + +abstract class ToxException constructor(val code: Enum<*>, private val extraMessage: String) : + Exception(extraMessage) { + + final override val message: String + get() = + when (extraMessage) { + "" -> "Error code: " + code.name + else -> extraMessage + ", error code: " + code.name + } +} diff --git a/src/main/java/im/tox/tox4j/exceptions/ToxKilledException.java b/src/main/java/im/tox/tox4j/exceptions/ToxKilledException.java new file mode 100644 index 000000000..3212a9ce6 --- /dev/null +++ b/src/main/java/im/tox/tox4j/exceptions/ToxKilledException.java @@ -0,0 +1,15 @@ +package im.tox.tox4j.exceptions; + +import org.jetbrains.annotations.NotNull; + +/** + * Exception to be thrown when a method is invoked on a tox instance that has + * been closed. + * + * @author Simon Levermann (sonOfRa) + */ +public final class ToxKilledException extends RuntimeException { + public ToxKilledException(@NotNull String message) { + super(message); + } +} diff --git a/src/main/java/im/tox/tox4j/impl/ToxImplBase.scala b/src/main/java/im/tox/tox4j/impl/ToxImplBase.scala deleted file mode 100644 index 894cb48d3..000000000 --- a/src/main/java/im/tox/tox4j/impl/ToxImplBase.scala +++ /dev/null @@ -1,36 +0,0 @@ -package im.tox.tox4j.impl - -import com.typesafe.scalalogging.Logger -import org.slf4j.LoggerFactory - -import scala.util.control.NonFatal - -object ToxImplBase { - - private val logger = Logger(LoggerFactory.getLogger(this.getClass)) - - /** - * Calls a callback and catches any [[NonFatal]] exceptions it throws and logs them. - * - * @param fatal If this is false, exceptions thrown by callbacks are caught and logged. - * @param state State to pass through the callback. - * @param eventHandler The callback object. - * @param callback The method to call on the callback object. - * @tparam T The type of the callback object. - */ - @SuppressWarnings(Array("org.brianmckenna.wartremover.warts.Throw")) - def tryAndLog[ToxCoreState, T](fatal: Boolean, state: ToxCoreState, eventHandler: T)(callback: T => ToxCoreState => ToxCoreState): ToxCoreState = { - if (!fatal) { - try { - callback(eventHandler)(state) - } catch { - case NonFatal(e) => - logger.warn("Exception caught while executing " + eventHandler.getClass.getName, e) - state - } - } else { - callback(eventHandler)(state) - } - } - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/AutoGenerated.java b/src/main/java/im/tox/tox4j/impl/jni/AutoGenerated.java deleted file mode 100644 index 5d8ff76c0..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/AutoGenerated.java +++ /dev/null @@ -1,22 +0,0 @@ -package im.tox.tox4j.impl.jni; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Mark methods that are to be generated by JniMethodImpls. - * - *

In {@link ToxCoreJni} and {@link ToxAvJni}, methods marked with this annotation do not need a manual - * implementation in C++, as their JNI bridge implementations will be generated. This annotation exists only until the - * majority of methods is generated, at which point there will be a "manual" override marker that inhibits - * auto-generation. Ideally, all bridge method implementations will be generated, but in reality there are probably - * going to be some complications that are too costly to overcome, so some manual overrides will remain. - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface AutoGenerated { - /** - * Override the C function name this native method should call. By default, a method called e.g. toxFooBar will call - * the C function tox_foo_bar. This can be overridden here. - */ - String value() default ""; -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxAvEventDispatch.kt b/src/main/java/im/tox/tox4j/impl/jni/ToxAvEventDispatch.kt new file mode 100644 index 000000000..17aad4a38 --- /dev/null +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxAvEventDispatch.kt @@ -0,0 +1,192 @@ +package im.tox.tox4j.impl.jni + +import com.google.protobuf.ByteString +import im.tox.tox4j.av.callbacks.* +import im.tox.tox4j.av.data.* +import im.tox.tox4j.av.enums.ToxavFriendCallState +import im.tox.tox4j.av.proto.* +import im.tox.tox4j.core.data.ToxFriendNumber +import java.util.EnumSet + +object ToxAvEventDispatch { + + fun convert(channels: Int): AudioChannels = + when (channels) { + 1 -> AudioChannels.Mono + 2 -> AudioChannels.Stereo + else -> AudioChannels.Stereo + } + + fun convert(kind: CallState.Kind): ToxavFriendCallState = + when (kind) { + CallState.Kind.ERROR -> ToxavFriendCallState.ERROR + CallState.Kind.FINISHED -> ToxavFriendCallState.FINISHED + CallState.Kind.SENDING_A -> ToxavFriendCallState.SENDING_A + CallState.Kind.SENDING_V -> ToxavFriendCallState.SENDING_V + CallState.Kind.ACCEPTING_A -> ToxavFriendCallState.ACCEPTING_A + CallState.Kind.ACCEPTING_V -> ToxavFriendCallState.ACCEPTING_V + CallState.Kind.UNRECOGNIZED -> ToxavFriendCallState.ERROR + } + + fun convert(callState: EnumSet): Int = + callState.fold( + 0, + { bitMask, bit -> + val nextMask = + when (bit) { + ToxavFriendCallState.ERROR -> 1 shl 0 + ToxavFriendCallState.FINISHED -> 1 shl 1 + ToxavFriendCallState.SENDING_A -> 1 shl 2 + ToxavFriendCallState.SENDING_V -> 1 shl 3 + ToxavFriendCallState.ACCEPTING_A -> 1 shl 4 + ToxavFriendCallState.ACCEPTING_V -> 1 shl 5 + null -> 0 + } + bitMask or nextMask + }) + + private fun dispatchCall(handler: CallCallback, call: List, state: S): S = + call.fold( + state, + { next, ev -> + handler.call( + ToxFriendNumber(ev.getFriendNumber()), + ev.getAudioEnabled(), + ev.getVideoEnabled(), + next) + }) + + private fun dispatchCallState( + handler: CallStateCallback, + callState: List, + state: S + ): S = + callState.fold( + state, + { next, ev -> + val bits = ev.getCallStateList().map { convert(it) } + handler.callState( + ToxFriendNumber(ev.getFriendNumber()), + EnumSet.of(bits[0], *bits.drop(1).toTypedArray()), + next) + }) + + private fun dispatchAudioBitRate( + handler: AudioBitRateCallback, + audioBitRate: List, + state: S + ): S = + audioBitRate.fold( + state, + { next, ev -> + handler.audioBitRate( + ToxFriendNumber(ev.getFriendNumber()), BitRate(ev.getAudioBitRate()), next) + }) + + private fun dispatchVideoBitRate( + handler: VideoBitRateCallback, + videoBitRate: List, + state: S + ): S = + videoBitRate.fold( + state, + { next, ev -> + handler.videoBitRate( + ToxFriendNumber(ev.getFriendNumber()), BitRate(ev.getVideoBitRate()), next) + }) + + private fun toShortArray(bytes: ByteString): ShortArray { + val shortBuffer = bytes.asReadOnlyByteBuffer().asShortBuffer() + val shortArray = ShortArray(shortBuffer.capacity()) + shortBuffer.get(shortArray) + return shortArray + } + + private fun dispatchAudioReceiveFrame( + handler: AudioReceiveFrameCallback, + audioReceiveFrame: List, + state: S + ): S = + audioReceiveFrame.fold( + state, + { next, ev -> + handler.audioReceiveFrame( + ToxFriendNumber(ev.getFriendNumber()), + toShortArray(ev.getPcm()), + convert(ev.getChannels()), + (SamplingRate.values().filter { it.value == ev.getSamplingRate() })[0], + next) + }) + + private fun convert( + arrays: Triple?, + y: ByteString, + u: ByteString, + v: ByteString + ): Triple = + if (arrays == null) { + Triple(y.toByteArray(), u.toByteArray(), v.toByteArray()) + } else { + y.copyTo(arrays.first, 0) + u.copyTo(arrays.second, 0) + v.copyTo(arrays.third, 0) + arrays + } + + private fun dispatchVideoReceiveFrame( + handler: VideoReceiveFrameCallback, + videoReceiveFrame: List, + state: S + ): S = + videoReceiveFrame.fold( + state, + { next, ev -> + val w = Width(ev.getWidth()) + val h = Height(ev.getHeight()) + val (yArray, uArray, vArray) = + convert( + handler.videoFrameCachedYUV( + h, ev.getYStride(), ev.getUStride(), ev.getVStride()), + ev.getY(), + ev.getU(), + ev.getV()) + + handler.videoReceiveFrame( + ToxFriendNumber(ev.getFriendNumber()), + w, + h, + yArray, + uArray, + vArray, + ev.getYStride(), + ev.getUStride(), + ev.getVStride(), + next) + }) + + private fun dispatchEvents(handler: ToxAvEventListener, events: AvEvents, state: S): S = + dispatchCall( + handler, + events.getCallList(), + dispatchCallState( + handler, + events.getCallStateList(), + dispatchAudioBitRate( + handler, + events.getAudioBitRateList(), + dispatchVideoBitRate( + handler, + events.getVideoBitRateList(), + dispatchAudioReceiveFrame( + handler, + events.getAudioReceiveFrameList(), + dispatchVideoReceiveFrame( + handler, events.getVideoReceiveFrameList(), state)))))) + + fun dispatch(handler: ToxAvEventListener, eventData: ByteArray?, state: S): S = + if (eventData == null) { + state + } else { + dispatchEvents(handler, AvEvents.parseFrom(eventData), state) + } +} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxAvEventDispatch.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxAvEventDispatch.scala deleted file mode 100644 index f564b7e33..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxAvEventDispatch.scala +++ /dev/null @@ -1,177 +0,0 @@ -package im.tox.tox4j.impl.jni - -import java.util - -import com.google.protobuf.ByteString -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.OptimisedIdOps._ -import im.tox.tox4j.av.callbacks._ -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.ToxavFriendCallState -import im.tox.tox4j.av.proto._ -import im.tox.tox4j.core.data.ToxFriendNumber -import org.jetbrains.annotations.Nullable -import org.slf4j.LoggerFactory - -object ToxAvEventDispatch { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - private val IntBytes = Integer.SIZE / java.lang.Byte.SIZE - - def convert(kind: CallState.Kind): ToxavFriendCallState = { - kind match { - case CallState.Kind.ERROR => ToxavFriendCallState.ERROR - case CallState.Kind.FINISHED => ToxavFriendCallState.FINISHED - case CallState.Kind.SENDING_A => ToxavFriendCallState.SENDING_A - case CallState.Kind.SENDING_V => ToxavFriendCallState.SENDING_V - case CallState.Kind.ACCEPTING_A => ToxavFriendCallState.ACCEPTING_A - case CallState.Kind.ACCEPTING_V => ToxavFriendCallState.ACCEPTING_V - } - } - - def convert(callState: util.EnumSet[ToxavFriendCallState]): Int = { - import scala.collection.JavaConverters._ - callState.asScala.foldLeft(0) { (bitMask, state) => - val nextMask = state match { - case ToxavFriendCallState.ERROR => 1 << 0 - case ToxavFriendCallState.FINISHED => 1 << 1 - case ToxavFriendCallState.SENDING_A => 1 << 2 - case ToxavFriendCallState.SENDING_V => 1 << 3 - case ToxavFriendCallState.ACCEPTING_A => 1 << 4 - case ToxavFriendCallState.ACCEPTING_V => 1 << 5 - } - bitMask | nextMask - } - } - - private def dispatchCall[S](handler: CallCallback[S], call: Seq[Call])(state: S): S = { - call.foldLeft(state) { - case (state, Call(friendNumber, audioEnabled, videoEnabled)) => - handler.call( - ToxFriendNumber.unsafeFromInt(friendNumber), - audioEnabled, - videoEnabled - )(state) - } - } - - private def dispatchCallState[S](handler: CallStateCallback[S], callState: Seq[CallState])(state: S): S = { - callState.foldLeft(state) { - case (state, CallState(friendNumber, callStateHead +: callStateTail)) => - handler.callState( - ToxFriendNumber.unsafeFromInt(friendNumber), - util.EnumSet.of(convert(callStateHead), callStateTail.map(convert): _*) - )(state) - } - } - - private def dispatchAudioBitRate[S](handler: AudioBitRateCallback[S], audioBitRate: Seq[AudioBitRate])(state: S): S = { - audioBitRate.foldLeft(state) { - case (state, AudioBitRate(friendNumber, audioBitRate)) => - handler.audioBitRate( - ToxFriendNumber.unsafeFromInt(friendNumber), - BitRate.unsafeFromInt(audioBitRate) - )(state) - } - } - - private def dispatchVideoBitRate[S](handler: VideoBitRateCallback[S], videoBitRate: Seq[VideoBitRate])(state: S): S = { - videoBitRate.foldLeft(state) { - case (state, VideoBitRate(friendNumber, videoBitRate)) => - handler.videoBitRate( - ToxFriendNumber.unsafeFromInt(friendNumber), - BitRate.unsafeFromInt(videoBitRate) - )(state) - } - } - - private def toShortArray(bytes: ByteString): Array[Short] = { - val shortBuffer = bytes.asReadOnlyByteBuffer().asShortBuffer() - val shortArray = Array.ofDim[Short](shortBuffer.capacity) - shortBuffer.get(shortArray) - shortArray - } - - private def dispatchAudioReceiveFrame[S](handler: AudioReceiveFrameCallback[S], audioReceiveFrame: Seq[AudioReceiveFrame])(state: S): S = { - audioReceiveFrame.foldLeft(state) { - case (state, AudioReceiveFrame(friendNumber, pcm, channels, samplingRate)) => - handler.audioReceiveFrame( - ToxFriendNumber.unsafeFromInt(friendNumber), - toShortArray(pcm), - AudioChannels.unsafeFromInt(channels), - SamplingRate.unsafeFromInt(samplingRate) - )(state) - } - } - - private def convert( - arrays: Option[(Array[Byte], Array[Byte], Array[Byte])], - y: ByteString, u: ByteString, v: ByteString - ): (Array[Byte], Array[Byte], Array[Byte]) = { - arrays match { - case None => - (y.toByteArray, u.toByteArray, v.toByteArray) - case Some(arrays) => - y.copyTo(arrays._1, 0) - u.copyTo(arrays._2, 0) - v.copyTo(arrays._3, 0) - arrays - } - } - - private def dispatchVideoReceiveFrame[S](handler: VideoReceiveFrameCallback[S], videoReceiveFrame: Seq[VideoReceiveFrame])(state: S): S = { - videoReceiveFrame.foldLeft(state) { - case (state, VideoReceiveFrame(friendNumber, width, height, y, u, v, yStride, uStride, vStride)) => - val w = Width.unsafeFromInt(width) - val h = Height.unsafeFromInt(height) - val (yArray, uArray, vArray) = convert(handler.videoFrameCachedYUV(h, yStride, uStride, vStride), y, u, v) - - handler.videoReceiveFrame( - ToxFriendNumber.unsafeFromInt(friendNumber), - w, - h, - yArray, - uArray, - vArray, - yStride, - uStride, - vStride - )(state) - } - } - - private def dispatchEvents[S](handler: ToxAvEventListener[S], events: AvEvents)(state: S): S = { - (state - |> dispatchCall(handler, events.call) - |> dispatchCallState(handler, events.callState) - |> dispatchAudioBitRate(handler, events.audioBitRate) - |> dispatchVideoBitRate(handler, events.videoBitRate) - |> dispatchAudioReceiveFrame(handler, events.audioReceiveFrame) - |> dispatchVideoReceiveFrame(handler, events.videoReceiveFrame)) - } - - private def decodeInt32(eventData: Array[Byte]): Int = { - assert(eventData.length >= IntBytes) - (0 - | eventData(0) << (8 * 3) - | eventData(1) << (8 * 2) - | eventData(2) << (8 * 1) - | eventData(3) << (8 * 0)) - } - - @SuppressWarnings(Array( - "org.wartremover.warts.ArrayEquals", - "org.wartremover.warts.Equals", - "org.wartremover.warts.Null" - )) - def dispatch[S](handler: ToxAvEventListener[S], @Nullable eventData: Array[Byte])(state: S): S = { - if (eventData == null) { // scalastyle:ignore null - state - } else { - val events = AvEvents.parseFrom(eventData) - dispatchEvents(handler, events)(state) - } - } - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxAvImpl.kt b/src/main/java/im/tox/tox4j/impl/jni/ToxAvImpl.kt new file mode 100644 index 000000000..be7d7a375 --- /dev/null +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxAvImpl.kt @@ -0,0 +1,145 @@ +package im.tox.tox4j.impl.jni + +import im.tox.tox4j.av.* +import im.tox.tox4j.av.callbacks.* +import im.tox.tox4j.av.data.* +import im.tox.tox4j.av.enums.* +import im.tox.tox4j.av.exceptions.* +import im.tox.tox4j.core.ToxCore +import im.tox.tox4j.core.data.ToxFriendNumber +import java.util.EnumSet + +/** + * Initialise an A/V session for the existing Tox instance. + * + * @param tox An instance of the C-backed ToxCore implementation. + */ +// @throws[ToxavNewException]("If there was already an A/V session.") +final class ToxAvImpl(private val tox: ToxCoreImpl) : ToxAv { + + internal val instanceNumber = ToxAvJni.toxavNew(tox.instanceNumber) + + override fun create(tox: ToxCore): ToxAv = + try { + ToxAvImpl(tox as ToxCoreImpl) + } catch (_: ClassCastException) { + throw ToxavNewException( + ToxavNewException.Code.INCOMPATIBLE, tox::class.java.getCanonicalName()) + } + + override fun close(): Unit = ToxAvJni.toxavKill(instanceNumber) + + protected fun finalize(): Unit = ToxAvJni.toxavFinalize(instanceNumber) + + override fun iterate(handler: ToxAvEventListener, state: S): S = + ToxAvEventDispatch.dispatch(handler, ToxAvJni.toxavIterate(instanceNumber), state) + + override val iterationInterval: Int + get() = ToxAvJni.toxavIterationInterval(instanceNumber) + + // @throws[ToxavCallException] + override fun call( + friendNumber: ToxFriendNumber, + audioBitRate: BitRate, + videoBitRate: BitRate + ): Unit = + ToxAvJni.toxavCall(instanceNumber, friendNumber.value, audioBitRate.value, videoBitRate.value) + + // @throws[ToxavAnswerException] + override fun answer( + friendNumber: ToxFriendNumber, + audioBitRate: BitRate, + videoBitRate: BitRate + ): Unit = + ToxAvJni.toxavAnswer( + instanceNumber, friendNumber.value, audioBitRate.value, videoBitRate.value) + + // @throws[ToxavCallControlException] + override fun callControl(friendNumber: ToxFriendNumber, control: ToxavCallControl): Unit = + ToxAvJni.toxavCallControl(instanceNumber, friendNumber.value, control.ordinal) + + // @throws[ToxavBitRateSetException] + override fun setAudioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate): Unit = + ToxAvJni.toxavAudioSetBitRate(instanceNumber, friendNumber.value, audioBitRate.value) + + // @throws[ToxavBitRateSetException] + override fun setVideoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate): Unit = + ToxAvJni.toxavVideoSetBitRate(instanceNumber, friendNumber.value, videoBitRate.value) + + // @throws[ToxavSendFrameException] + override fun audioSendFrame( + friendNumber: ToxFriendNumber, + pcm: ShortArray, + sampleCount: SampleCount, + channels: AudioChannels, + samplingRate: SamplingRate + ): Unit = + ToxAvJni.toxavAudioSendFrame( + instanceNumber, + friendNumber.value, + pcm, + sampleCount.value, + channels.value, + samplingRate.value) + + // @throws[ToxavSendFrameException] + override fun videoSendFrame( + friendNumber: ToxFriendNumber, + width: Int, + height: Int, + y: ByteArray, + u: ByteArray, + v: ByteArray + ): Unit = ToxAvJni.toxavVideoSendFrame(instanceNumber, friendNumber.value, width, height, y, u, v) + + fun invokeAudioReceiveFrame( + friendNumber: ToxFriendNumber, + pcm: ShortArray, + channels: AudioChannels, + samplingRate: SamplingRate + ): Unit = + ToxAvJni.invokeAudioReceiveFrame( + instanceNumber, friendNumber.value, pcm, channels.value, samplingRate.value) + + fun invokeAudioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate): Unit = + ToxAvJni.invokeAudioBitRate(instanceNumber, friendNumber.value, audioBitRate.value) + + fun invokeVideoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate): Unit = + ToxAvJni.invokeVideoBitRate(instanceNumber, friendNumber.value, videoBitRate.value) + + fun invokeCall( + friendNumber: ToxFriendNumber, + audioEnabled: Boolean, + videoEnabled: Boolean + ): Unit = ToxAvJni.invokeCall(instanceNumber, friendNumber.value, audioEnabled, videoEnabled) + + fun invokeCallState( + friendNumber: ToxFriendNumber, + callState: EnumSet + ): Unit = + ToxAvJni.invokeCallState( + instanceNumber, friendNumber.value, ToxAvEventDispatch.convert(callState)) + + fun invokeVideoReceiveFrame( + friendNumber: ToxFriendNumber, + width: Width, + height: Height, + y: ByteArray, + u: ByteArray, + v: ByteArray, + yStride: Int, + uStride: Int, + vStride: Int + ): Unit = + ToxAvJni.invokeVideoReceiveFrame( + instanceNumber, + friendNumber.value, + width.value, + height.value, + y, + u, + v, + yStride, + uStride, + vStride) +} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxAvImpl.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxAvImpl.scala deleted file mode 100644 index 6d0b87c00..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxAvImpl.scala +++ /dev/null @@ -1,120 +0,0 @@ -package im.tox.tox4j.impl.jni - -import java.util - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.av._ -import im.tox.tox4j.av.callbacks._ -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.{ ToxavCallControl, ToxavFriendCallState } -import im.tox.tox4j.av.exceptions._ -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.impl.jni.ToxAvImpl.logger -import im.tox.tox4j.impl.jni.internal.Event -import org.jetbrains.annotations.NotNull -import org.slf4j.LoggerFactory - -private object ToxAvImpl { - private val logger = Logger(LoggerFactory.getLogger(getClass)) -} - -/** - * Initialise an A/V session for the existing Tox instance. - * - * @param tox An instance of the C-backed ToxCore implementation. - */ -// scalastyle:off no.finalize -@throws[ToxavNewException]("If there was already an A/V session.") -final class ToxAvImpl(@NotNull private val tox: ToxCoreImpl) extends ToxAv { - - private[this] val onClose: Event.Id = tox.addOnCloseCallback(close) - - private[jni] val instanceNumber = ToxAvJni.toxavNew(tox.instanceNumber) - - @SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf")) - override def create(tox: ToxCore): ToxAv = { - try { - new ToxAvImpl(tox.asInstanceOf[ToxCoreImpl]) - } catch { - case _: ClassCastException => - throw new ToxavNewException(ToxavNewException.Code.INCOMPATIBLE, tox.getClass.getCanonicalName) - } - } - - override def close(): Unit = { - tox.removeOnCloseCallback(onClose) - ToxAvJni.toxavKill(instanceNumber) - } - - protected override def finalize(): Unit = { - try { - ToxAvJni.toxavFinalize(instanceNumber) - } catch { - case e: Throwable => - logger.error("Exception caught in finalizer; this indicates a serious problem in native code", e) - } - super.finalize() - } - - override def iterate[S](@NotNull handler: ToxAvEventListener[S])(state: S): S = { - ToxAvEventDispatch.dispatch(handler, ToxAvJni.toxavIterate(instanceNumber))(state) - } - - override def iterationInterval: Int = - ToxAvJni.toxavIterationInterval(instanceNumber) - - @throws[ToxavCallException] - override def call(friendNumber: ToxFriendNumber, audioBitRate: BitRate, videoBitRate: BitRate): Unit = - ToxAvJni.toxavCall(instanceNumber, friendNumber.value, audioBitRate.value, videoBitRate.value) - - @throws[ToxavAnswerException] - override def answer(friendNumber: ToxFriendNumber, audioBitRate: BitRate, videoBitRate: BitRate): Unit = - ToxAvJni.toxavAnswer(instanceNumber, friendNumber.value, audioBitRate.value, videoBitRate.value) - - @throws[ToxavCallControlException] - override def callControl(friendNumber: ToxFriendNumber, control: ToxavCallControl): Unit = - ToxAvJni.toxavCallControl(instanceNumber, friendNumber.value, control.ordinal) - - @throws[ToxavBitRateSetException] - override def setAudioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate): Unit = - ToxAvJni.toxavAudioSetBitRate(instanceNumber, friendNumber.value, audioBitRate.value) - - @throws[ToxavBitRateSetException] - override def setVideoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate): Unit = - ToxAvJni.toxavVideoSetBitRate(instanceNumber, friendNumber.value, videoBitRate.value) - - @throws[ToxavSendFrameException] - override def audioSendFrame( - friendNumber: ToxFriendNumber, - pcm: Array[Short], - sampleCount: SampleCount, - channels: AudioChannels, - samplingRate: SamplingRate - ): Unit = { - ToxAvJni.toxavAudioSendFrame(instanceNumber, friendNumber.value, pcm, sampleCount.value, channels.value, samplingRate.value) - } - - @throws[ToxavSendFrameException] - override def videoSendFrame( - friendNumber: ToxFriendNumber, - width: Int, height: Int, - y: Array[Byte], u: Array[Byte], v: Array[Byte] - ): Unit = { - ToxAvJni.toxavVideoSendFrame(instanceNumber, friendNumber.value, width, height, y, u, v) - } - - def invokeAudioReceiveFrame(friendNumber: ToxFriendNumber, pcm: Array[Short], channels: AudioChannels, samplingRate: SamplingRate): Unit = - ToxAvJni.invokeAudioReceiveFrame(instanceNumber, friendNumber.value, pcm, channels.value, samplingRate.value) - def invokeAudioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate): Unit = - ToxAvJni.invokeAudioBitRate(instanceNumber, friendNumber.value, audioBitRate.value) - def invokeVideoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate): Unit = - ToxAvJni.invokeVideoBitRate(instanceNumber, friendNumber.value, videoBitRate.value) - def invokeCall(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean): Unit = - ToxAvJni.invokeCall(instanceNumber, friendNumber.value, audioEnabled, videoEnabled) - def invokeCallState(friendNumber: ToxFriendNumber, callState: util.EnumSet[ToxavFriendCallState]): Unit = - ToxAvJni.invokeCallState(instanceNumber, friendNumber.value, ToxAvEventDispatch.convert(callState)) - def invokeVideoReceiveFrame(friendNumber: ToxFriendNumber, width: Width, height: Height, y: Array[Byte], u: Array[Byte], v: Array[Byte], yStride: Int, uStride: Int, vStride: Int): Unit = // scalastyle:ignore line.size.limit - ToxAvJni.invokeVideoReceiveFrame(instanceNumber, friendNumber.value, width.value, height.value, y, u, v, yStride, uStride, vStride) - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxAvJni.java b/src/main/java/im/tox/tox4j/impl/jni/ToxAvJni.java index 1d769c4f2..9da227883 100644 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxAvJni.java +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxAvJni.java @@ -6,49 +6,43 @@ @SuppressWarnings({"checkstyle:emptylineseparator", "checkstyle:linelength"}) public final class ToxAvJni { - static { - ToxLoadJniLibrary.load("tox4j-c"); + System.loadLibrary("tox4j-c"); } static native int toxavNew(int toxInstanceNumber) throws ToxavNewException; static native void toxavKill(int instanceNumber); static native void toxavFinalize(int instanceNumber); static native int toxavIterationInterval(int instanceNumber); - @Nullable - static native byte[] toxavIterate(int instanceNumber); - static native void toxavCall(int instanceNumber, int friendNumber, int audioBitRate, int videoBitRate) throws ToxavCallException; - static native void toxavAnswer(int instanceNumber, int friendNumber, int audioBitRate, int videoBitRate) throws ToxavAnswerException; - static native void toxavCallControl(int instanceNumber, int friendNumber, int control) throws ToxavCallControlException; - static native void toxavAudioSetBitRate(int instanceNumber, int friendNumber, int audioBitRate) throws ToxavBitRateSetException; - static native void toxavVideoSetBitRate(int instanceNumber, int friendNumber, int videoBitRate) throws ToxavBitRateSetException; - - static native void toxavAudioSendFrame( - int instanceNumber, - int friendNumber, - @NotNull short[] pcm, int sampleCount, int channels, int samplingRate - ) throws ToxavSendFrameException; + @Nullable static native byte[] toxavIterate(int instanceNumber); + static native void toxavCall(int instanceNumber, int friendNumber, int audioBitRate, + int videoBitRate) throws ToxavCallException; + static native void toxavAnswer(int instanceNumber, int friendNumber, int audioBitRate, + int videoBitRate) throws ToxavAnswerException; + static native void toxavCallControl(int instanceNumber, int friendNumber, int control) + throws ToxavCallControlException; + static native void toxavAudioSetBitRate(int instanceNumber, int friendNumber, int audioBitRate) + throws ToxavBitRateSetException; + static native void toxavVideoSetBitRate(int instanceNumber, int friendNumber, int videoBitRate) + throws ToxavBitRateSetException; + + static native void toxavAudioSendFrame(int instanceNumber, int friendNumber, @NotNull short[] pcm, + int sampleCount, int channels, int samplingRate) throws ToxavSendFrameException; @SuppressWarnings("checkstyle:parametername") - static native void toxavVideoSendFrame( - int instanceNumber, - int friendNumber, - int width, int height, - @NotNull byte[] y, @NotNull byte[] u, @NotNull byte[] v - ) throws ToxavSendFrameException; + static native void toxavVideoSendFrame(int instanceNumber, int friendNumber, int width, + int height, @NotNull byte[] y, @NotNull byte[] u, @NotNull byte[] v) + throws ToxavSendFrameException; - static native void invokeAudioReceiveFrame(int instanceNumber, int friendNumber, short[] pcm, int channels, int samplingRate); + static native void invokeAudioReceiveFrame( + int instanceNumber, int friendNumber, short[] pcm, int channels, int samplingRate); static native void invokeAudioBitRate(int instanceNumber, int friendNumber, int audioBitRate); static native void invokeVideoBitRate(int instanceNumber, int friendNumber, int videoBitRate); - static native void invokeCall(int instanceNumber, int friendNumber, boolean audioEnabled, boolean videoEnabled); + static native void invokeCall( + int instanceNumber, int friendNumber, boolean audioEnabled, boolean videoEnabled); static native void invokeCallState(int instanceNumber, int friendNumber, int callState); @SuppressWarnings("checkstyle:parametername") - static native void invokeVideoReceiveFrame( - int instanceNumber, - int friendNumber, - int width, int height, - @NotNull byte[] y, @NotNull byte[] u, @NotNull byte[] v, - int yStride, int uStride, int vStride - ); - + static native void invokeVideoReceiveFrame(int instanceNumber, int friendNumber, int width, + int height, @NotNull byte[] y, @NotNull byte[] u, @NotNull byte[] v, int yStride, int uStride, + int vStride); } diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreEventDispatch.kt b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreEventDispatch.kt new file mode 100644 index 000000000..1254def23 --- /dev/null +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreEventDispatch.kt @@ -0,0 +1,314 @@ +package im.tox.tox4j.impl.jni + +import im.tox.tox4j.core.callbacks.ToxCoreEventListener +import im.tox.tox4j.core.data.* +import im.tox.tox4j.core.enums.* +import im.tox.tox4j.core.proto.* + +object ToxCoreEventDispatch { + + fun convert(status: Connection.Type): ToxConnection = + when (status) { + Connection.Type.NONE -> ToxConnection.NONE + Connection.Type.TCP -> ToxConnection.TCP + Connection.Type.UDP -> ToxConnection.UDP + Connection.Type.UNRECOGNIZED -> ToxConnection.NONE + } + + fun convert(status: UserStatus.Type): ToxUserStatus = + when (status) { + UserStatus.Type.NONE -> ToxUserStatus.NONE + UserStatus.Type.AWAY -> ToxUserStatus.AWAY + UserStatus.Type.BUSY -> ToxUserStatus.BUSY + UserStatus.Type.UNRECOGNIZED -> ToxUserStatus.NONE + } + + fun convert(status: ToxUserStatus): UserStatus.Type = + when (status) { + ToxUserStatus.NONE -> UserStatus.Type.NONE + ToxUserStatus.AWAY -> UserStatus.Type.AWAY + ToxUserStatus.BUSY -> UserStatus.Type.BUSY + } + + fun convert(control: FileControl.Type): ToxFileControl = + when (control) { + FileControl.Type.RESUME -> ToxFileControl.RESUME + FileControl.Type.PAUSE -> ToxFileControl.PAUSE + FileControl.Type.CANCEL -> ToxFileControl.CANCEL + FileControl.Type.UNRECOGNIZED -> ToxFileControl.CANCEL + } + + fun convert(messageType: MessageType.Type): ToxMessageType = + when (messageType) { + MessageType.Type.NORMAL -> ToxMessageType.NORMAL + MessageType.Type.ACTION -> ToxMessageType.ACTION + MessageType.Type.UNRECOGNIZED -> ToxMessageType.NORMAL + } + + private fun dispatchSelfConnectionStatus( + handler: ToxCoreEventListener, + selfConnectionStatus: List, + state: S + ): S = + selfConnectionStatus.fold( + state, + { next, ev -> handler.selfConnectionStatus(convert(ev.getConnectionStatus()), next) }) + + private fun dispatchFriendName( + handler: ToxCoreEventListener, + friendName: List, + state: S + ): S = + friendName.fold( + state, + { next, ev -> + handler.friendName( + ToxFriendNumber(ev.getFriendNumber()), + ToxNickname(ev.getName().toByteArray()), + next) + }) + + private fun dispatchFriendStatusMessage( + handler: ToxCoreEventListener, + friendStatusMessage: List, + state: S + ): S = + friendStatusMessage.fold( + state, + { next, ev -> + handler.friendStatusMessage( + ToxFriendNumber(ev.getFriendNumber()), + ToxStatusMessage(ev.getMessage().toByteArray()), + next) + }) + + private fun dispatchFriendStatus( + handler: ToxCoreEventListener, + friendStatus: List, + state: S + ): S = + friendStatus.fold( + state, + { next, ev -> + handler.friendStatus( + ToxFriendNumber(ev.getFriendNumber()), convert(ev.getStatus()), next) + }) + + private fun dispatchFriendConnectionStatus( + handler: ToxCoreEventListener, + friendConnectionStatus: List, + state: S + ): S = + friendConnectionStatus.fold( + state, + { next, ev -> + handler.friendConnectionStatus( + ev.getFriendNumber(), convert(ev.getConnectionStatus()), next) + }) + + private fun dispatchFriendTyping( + handler: ToxCoreEventListener, + friendTyping: List, + state: S + ): S = + friendTyping.fold( + state, + { next, ev -> + handler.friendTyping(ToxFriendNumber(ev.getFriendNumber()), ev.getIsTyping(), next) + }) + + private fun dispatchFriendReadReceipt( + handler: ToxCoreEventListener, + friendReadReceipt: List, + state: S + ): S = + friendReadReceipt.fold( + state, + { next, ev -> + handler.friendReadReceipt( + ToxFriendNumber(ev.getFriendNumber()), ev.getMessageId(), next) + }) + + private fun dispatchFriendRequest( + handler: ToxCoreEventListener, + friendRequest: List, + state: S + ): S = + friendRequest.fold( + state, + { next, ev -> + handler.friendRequest( + ToxPublicKey(ev.getPublicKey().toByteArray()), + ev.getTimeDelta(), + ToxFriendRequestMessage(ev.getMessage().toByteArray()), + next) + }) + + private fun dispatchFriendMessage( + handler: ToxCoreEventListener, + friendMessage: List, + state: S + ): S = + friendMessage.fold( + state, + { next, ev -> + handler.friendMessage( + ToxFriendNumber(ev.getFriendNumber()), + convert(ev.getType()), + ev.getTimeDelta(), + ToxFriendMessage(ev.getMessage().toByteArray()), + next) + }) + + private fun dispatchFileRecvControl( + handler: ToxCoreEventListener, + fileRecvControl: List, + state: S + ): S = + fileRecvControl.fold( + state, + { next, ev -> + handler.fileRecvControl( + ToxFriendNumber(ev.getFriendNumber()), + ev.getFileNumber(), + convert(ev.getControl()), + next) + }) + + private fun dispatchFileChunkRequest( + handler: ToxCoreEventListener, + fileChunkRequest: List, + state: S + ): S = + fileChunkRequest.fold( + state, + { next, ev -> + handler.fileChunkRequest( + ToxFriendNumber(ev.getFriendNumber()), + ev.getFileNumber(), + ev.getPosition(), + ev.getLength(), + next) + }) + + private fun dispatchFileRecv( + handler: ToxCoreEventListener, + fileRecv: List, + state: S + ): S = + fileRecv.fold( + state, + { next, ev -> + handler.fileRecv( + ToxFriendNumber(ev.getFriendNumber()), + ev.getFileNumber(), + ev.getKind(), + ev.getFileSize(), + ToxFilename(ev.getFilename().toByteArray()), + next) + }) + + private fun dispatchFileRecvChunk( + handler: ToxCoreEventListener, + fileRecvChunk: List, + state: S + ): S = + fileRecvChunk.fold( + state, + { next, ev -> + handler.fileRecvChunk( + ToxFriendNumber(ev.getFriendNumber()), + ev.getFileNumber(), + ev.getPosition(), + ev.getData().toByteArray(), + next) + }) + + private fun dispatchFriendLossyPacket( + handler: ToxCoreEventListener, + friendLossyPacket: List, + state: S + ): S = + friendLossyPacket.fold( + state, + { next, ev -> + handler.friendLossyPacket( + ToxFriendNumber(ev.getFriendNumber()), + ToxLossyPacket(ev.getData().toByteArray()), + next) + }) + + private fun dispatchFriendLosslessPacket( + handler: ToxCoreEventListener, + friendLosslessPacket: List, + state: S + ): S = + friendLosslessPacket.fold( + state, + { next, ev -> + handler.friendLosslessPacket( + ToxFriendNumber(ev.getFriendNumber()), + ToxLosslessPacket(ev.getData().toByteArray()), + next) + }) + + private fun dispatchEvents( + handler: ToxCoreEventListener, + events: CoreEvents, + state: S + ): S = + dispatchSelfConnectionStatus( + handler, + events.getSelfConnectionStatusList(), + dispatchFriendName( + handler, + events.getFriendNameList(), + dispatchFriendStatusMessage( + handler, + events.getFriendStatusMessageList(), + dispatchFriendStatus( + handler, + events.getFriendStatusList(), + dispatchFriendConnectionStatus( + handler, + events.getFriendConnectionStatusList(), + dispatchFriendTyping( + handler, + events.getFriendTypingList(), + dispatchFriendReadReceipt( + handler, + events.getFriendReadReceiptList(), + dispatchFriendRequest( + handler, + events.getFriendRequestList(), + dispatchFriendMessage( + handler, + events.getFriendMessageList(), + dispatchFileRecvControl( + handler, + events.getFileRecvControlList(), + dispatchFileChunkRequest( + handler, + events.getFileChunkRequestList(), + dispatchFileRecv( + handler, + events.getFileRecvList(), + dispatchFileRecvChunk( + handler, + events.getFileRecvChunkList(), + dispatchFriendLossyPacket( + handler, + events.getFriendLossyPacketList(), + dispatchFriendLosslessPacket( + handler, + events + .getFriendLosslessPacketList(), + state))))))))))))))) + + fun dispatch(handler: ToxCoreEventListener, eventData: ByteArray?, state: S): S = + if (eventData == null) { + state + } else { + dispatchEvents(handler, CoreEvents.parseFrom(eventData), state) + } +} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreEventDispatch.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreEventDispatch.scala deleted file mode 100644 index a94892b4b..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreEventDispatch.scala +++ /dev/null @@ -1,244 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.OptimisedIdOps._ -import im.tox.tox4j.core.callbacks.ToxCoreEventListener -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxMessageType, ToxUserStatus } -import im.tox.tox4j.core.proto._ -import org.jetbrains.annotations.Nullable - -object ToxCoreEventDispatch { - - def convert(status: Connection.Type): ToxConnection = { - status match { - case Connection.Type.NONE => ToxConnection.NONE - case Connection.Type.TCP => ToxConnection.TCP - case Connection.Type.UDP => ToxConnection.UDP - } - } - - def convert(status: UserStatus.Type): ToxUserStatus = { - status match { - case UserStatus.Type.NONE => ToxUserStatus.NONE - case UserStatus.Type.AWAY => ToxUserStatus.AWAY - case UserStatus.Type.BUSY => ToxUserStatus.BUSY - } - } - - def convert(status: ToxUserStatus): UserStatus.Type = { - status match { - case ToxUserStatus.NONE => UserStatus.Type.NONE - case ToxUserStatus.AWAY => UserStatus.Type.AWAY - case ToxUserStatus.BUSY => UserStatus.Type.BUSY - } - } - - def convert(control: FileControl.Type): ToxFileControl = { - control match { - case FileControl.Type.RESUME => ToxFileControl.RESUME - case FileControl.Type.PAUSE => ToxFileControl.PAUSE - case FileControl.Type.CANCEL => ToxFileControl.CANCEL - } - } - - def convert(messageType: MessageType.Type): ToxMessageType = { - messageType match { - case MessageType.Type.NORMAL => ToxMessageType.NORMAL - case MessageType.Type.ACTION => ToxMessageType.ACTION - } - } - - private def dispatchSelfConnectionStatus[S](handler: ToxCoreEventListener[S], selfConnectionStatus: Seq[SelfConnectionStatus])(state: S): S = { - selfConnectionStatus.foldLeft(state) { - case (state, SelfConnectionStatus(status)) => - handler.selfConnectionStatus( - convert(status) - )(state) - } - } - - private def dispatchFriendName[S](handler: ToxCoreEventListener[S], friendName: Seq[FriendName])(state: S): S = { - friendName.foldLeft(state) { - case (state, FriendName(friendNumber, name)) => - handler.friendName( - ToxFriendNumber.unsafeFromInt(friendNumber), - ToxNickname.unsafeFromValue(name.toByteArray) - )(state) - } - } - - private def dispatchFriendStatusMessage[S](handler: ToxCoreEventListener[S], friendStatusMessage: Seq[FriendStatusMessage])(state: S): S = { - friendStatusMessage.foldLeft(state) { - case (state, FriendStatusMessage(friendNumber, message)) => - handler.friendStatusMessage( - ToxFriendNumber.unsafeFromInt(friendNumber), - ToxStatusMessage.unsafeFromValue(message.toByteArray) - )(state) - } - } - - private def dispatchFriendStatus[S](handler: ToxCoreEventListener[S], friendStatus: Seq[FriendStatus])(state: S): S = { - friendStatus.foldLeft(state) { - case (state, FriendStatus(friendNumber, status)) => - handler.friendStatus( - ToxFriendNumber.unsafeFromInt(friendNumber), - convert(status) - )(state) - } - } - - private def dispatchFriendConnectionStatus[S](handler: ToxCoreEventListener[S], friendConnectionStatus: Seq[FriendConnectionStatus])(state: S): S = { - friendConnectionStatus.foldLeft(state) { - case (state, FriendConnectionStatus(friendNumber, status)) => - handler.friendConnectionStatus( - ToxFriendNumber.unsafeFromInt(friendNumber), - convert(status) - )(state) - } - } - - private def dispatchFriendTyping[S](handler: ToxCoreEventListener[S], friendTyping: Seq[FriendTyping])(state: S): S = { - friendTyping.foldLeft(state) { - case (state, FriendTyping(friendNumber, isTyping)) => - handler.friendTyping( - ToxFriendNumber.unsafeFromInt(friendNumber), - isTyping - )(state) - } - } - - private def dispatchFriendReadReceipt[S](handler: ToxCoreEventListener[S], friendReadReceipt: Seq[FriendReadReceipt])(state: S): S = { - friendReadReceipt.foldLeft(state) { - case (state, FriendReadReceipt(friendNumber, messageId)) => - handler.friendReadReceipt( - ToxFriendNumber.unsafeFromInt(friendNumber), - messageId - )(state) - } - } - - private def dispatchFriendRequest[S](handler: ToxCoreEventListener[S], friendRequest: Seq[FriendRequest])(state: S): S = { - friendRequest.foldLeft(state) { - case (state, FriendRequest(publicKey, timeDelta, message)) => - handler.friendRequest( - ToxPublicKey.unsafeFromValue(publicKey.toByteArray), - timeDelta, - ToxFriendRequestMessage.unsafeFromValue(message.toByteArray) - )(state) - } - } - - private def dispatchFriendMessage[S](handler: ToxCoreEventListener[S], friendMessage: Seq[FriendMessage])(state: S): S = { - friendMessage.foldLeft(state) { - case (state, FriendMessage(friendNumber, messageType, timeDelta, message)) => - handler.friendMessage( - ToxFriendNumber.unsafeFromInt(friendNumber), - convert(messageType), - timeDelta, - ToxFriendMessage.unsafeFromValue(message.toByteArray) - )(state) - } - } - - private def dispatchFileRecvControl[S](handler: ToxCoreEventListener[S], fileRecvControl: Seq[FileRecvControl])(state: S): S = { - fileRecvControl.foldLeft(state) { - case (state, FileRecvControl(friendNumber, fileNumber, control)) => - handler.fileRecvControl( - ToxFriendNumber.unsafeFromInt(friendNumber), - fileNumber, - convert(control) - )(state) - } - } - - private def dispatchFileChunkRequest[S](handler: ToxCoreEventListener[S], fileChunkRequest: Seq[FileChunkRequest])(state: S): S = { - fileChunkRequest.foldLeft(state) { - case (state, FileChunkRequest(friendNumber, fileNumber, position, length)) => - handler.fileChunkRequest( - ToxFriendNumber.unsafeFromInt(friendNumber), - fileNumber, - position, - length - )(state) - } - } - - private def dispatchFileRecv[S](handler: ToxCoreEventListener[S], fileRecv: Seq[FileRecv])(state: S): S = { - fileRecv.foldLeft(state) { - case (state, FileRecv(friendNumber, fileNumber, kind, fileSize, filename)) => - handler.fileRecv( - ToxFriendNumber.unsafeFromInt(friendNumber), - fileNumber, - kind, - fileSize, - ToxFilename.unsafeFromValue(filename.toByteArray) - )(state) - } - } - - private def dispatchFileRecvChunk[S](handler: ToxCoreEventListener[S], fileRecvChunk: Seq[FileRecvChunk])(state: S): S = { - fileRecvChunk.foldLeft(state) { - case (state, FileRecvChunk(friendNumber, fileNumber, position, data)) => - handler.fileRecvChunk( - ToxFriendNumber.unsafeFromInt(friendNumber), - fileNumber, - position, - data.toByteArray - )(state) - } - } - - private def dispatchFriendLossyPacket[S](handler: ToxCoreEventListener[S], friendLossyPacket: Seq[FriendLossyPacket])(state: S): S = { - friendLossyPacket.foldLeft(state) { - case (state, FriendLossyPacket(friendNumber, data)) => - handler.friendLossyPacket( - ToxFriendNumber.unsafeFromInt(friendNumber), - ToxLossyPacket.unsafeFromValue(data.toByteArray) - )(state) - } - } - - private def dispatchFriendLosslessPacket[S](handler: ToxCoreEventListener[S], friendLosslessPacket: Seq[FriendLosslessPacket])(state: S): S = { - friendLosslessPacket.foldLeft(state) { - case (state, FriendLosslessPacket(friendNumber, data)) => - handler.friendLosslessPacket( - ToxFriendNumber.unsafeFromInt(friendNumber), - ToxLosslessPacket.unsafeFromValue(data.toByteArray) - )(state) - } - } - - private def dispatchEvents[S](handler: ToxCoreEventListener[S], events: CoreEvents)(state: S): S = { - (state - |> dispatchSelfConnectionStatus(handler, events.selfConnectionStatus) - |> dispatchFriendName(handler, events.friendName) - |> dispatchFriendStatusMessage(handler, events.friendStatusMessage) - |> dispatchFriendStatus(handler, events.friendStatus) - |> dispatchFriendConnectionStatus(handler, events.friendConnectionStatus) - |> dispatchFriendTyping(handler, events.friendTyping) - |> dispatchFriendReadReceipt(handler, events.friendReadReceipt) - |> dispatchFriendRequest(handler, events.friendRequest) - |> dispatchFriendMessage(handler, events.friendMessage) - |> dispatchFileRecvControl(handler, events.fileRecvControl) - |> dispatchFileChunkRequest(handler, events.fileChunkRequest) - |> dispatchFileRecv(handler, events.fileRecv) - |> dispatchFileRecvChunk(handler, events.fileRecvChunk) - |> dispatchFriendLossyPacket(handler, events.friendLossyPacket) - |> dispatchFriendLosslessPacket(handler, events.friendLosslessPacket)) - } - - @SuppressWarnings(Array( - "org.wartremover.warts.ArrayEquals", - "org.wartremover.warts.Equals", - "org.wartremover.warts.Null" - )) - def dispatch[S](handler: ToxCoreEventListener[S], @Nullable eventData: Array[Byte])(state: S): S = { - if (eventData == null) { // scalastyle:ignore null - state - } else { - val events = CoreEvents.parseFrom(eventData) - dispatchEvents(handler, events)(state) - } - } - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreImpl.kt b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreImpl.kt new file mode 100644 index 000000000..c4e76d23f --- /dev/null +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreImpl.kt @@ -0,0 +1,199 @@ +package im.tox.tox4j.impl.jni + +import im.tox.core.network.Port +import im.tox.tox4j.core.* +import im.tox.tox4j.core.callbacks.* +import im.tox.tox4j.core.data.* +import im.tox.tox4j.core.enums.* +import im.tox.tox4j.core.exceptions.* +import im.tox.tox4j.core.options.ToxOptions + +/** + * Initialises the new Tox instance with an optional save-data received from [[savedata]]. + * + * @param options Connection options object with optional save-data. + */ +final class ToxCoreImpl(val options: ToxOptions) : ToxCore { + + /** This field has package visibility for [[ToxAvImpl]]. */ + internal val instanceNumber = + ToxCoreJni.toxNew( + options.ipv6Enabled, + options.udpEnabled, + options.localDiscoveryEnabled, + options.proxy.proxyType.ordinal, + options.proxy.proxyAddress, + options.proxy.proxyPort.toInt(), + options.startPort.toInt(), + options.endPort.toInt(), + options.tcpPort.toInt(), + options.saveData.kind.ordinal, + options.saveData.data, + ) + + override fun load(options: ToxOptions): ToxCoreImpl = ToxCoreImpl(options) + + override fun close(): Unit = ToxCoreJni.toxKill(instanceNumber) + + protected fun finalize(): Unit { + close() + ToxCoreJni.toxFinalize(instanceNumber) + } + + override fun bootstrap(address: String, port: Port, publicKey: ToxPublicKey): Unit { + ToxCoreImpl.checkBootstrapArguments(publicKey.value) + ToxCoreJni.toxBootstrap(instanceNumber, address, port.value.toInt(), publicKey.value) + } + + override fun addTcpRelay(address: String, port: Port, publicKey: ToxPublicKey): Unit { + ToxCoreImpl.checkBootstrapArguments(publicKey.value) + ToxCoreJni.toxAddTcpRelay(instanceNumber, address, port.value.toInt(), publicKey.value) + } + + override val savedata: ByteArray + get() = ToxCoreJni.toxGetSavedata(instanceNumber) + + override val udpPort: Port + get() = Port(ToxCoreJni.toxSelfGetUdpPort(instanceNumber).toUShort()) + + override val tcpPort: Port + get() = Port(ToxCoreJni.toxSelfGetTcpPort(instanceNumber).toUShort()) + + override val dhtId: ToxPublicKey + get() = ToxPublicKey(ToxCoreJni.toxSelfGetDhtId(instanceNumber)) + + override val iterationInterval: Int + get() = ToxCoreJni.toxIterationInterval(instanceNumber) + + override fun iterate(handler: ToxCoreEventListener, state: S): S = + ToxCoreEventDispatch.dispatch(handler, ToxCoreJni.toxIterate(instanceNumber), state) + + override val publicKey: ToxPublicKey + get() = ToxPublicKey(ToxCoreJni.toxSelfGetPublicKey(instanceNumber)) + + override val secretKey: ToxSecretKey + get() = ToxSecretKey(ToxCoreJni.toxSelfGetSecretKey(instanceNumber)) + + override var nospam: Int + get() = ToxCoreJni.toxSelfGetNospam(instanceNumber) + set(value) = ToxCoreJni.toxSelfSetNospam(instanceNumber, value) + + override val address: ToxFriendAddress + get() = ToxFriendAddress(ToxCoreJni.toxSelfGetAddress(instanceNumber)) + + override var name: ToxNickname + get() = ToxNickname(ToxCoreJni.toxSelfGetName(instanceNumber)) + set(value) = ToxCoreJni.toxSelfSetName(instanceNumber, value.value) + + override var statusMessage: ToxStatusMessage + get() = ToxStatusMessage(ToxCoreJni.toxSelfGetStatusMessage(instanceNumber)) + set(value) = ToxCoreJni.toxSelfSetStatusMessage(instanceNumber, value.value) + + override var status: ToxUserStatus + get() = ToxUserStatus.values()[ToxCoreJni.toxSelfGetStatus(instanceNumber)] + set(value) = ToxCoreJni.toxSelfSetStatus(instanceNumber, value.ordinal) + + override fun addFriend( + address: ToxFriendAddress, + message: ToxFriendRequestMessage + ): ToxFriendNumber { + ToxCoreImpl.checkLength("Friend Address", address.value, ToxCoreConstants.AddressSize) + return ToxFriendNumber(ToxCoreJni.toxFriendAdd(instanceNumber, address.value, message.value)) + } + + override fun addFriendNorequest(publicKey: ToxPublicKey): ToxFriendNumber { + ToxCoreImpl.checkLength("Public Key", publicKey.value, ToxCoreConstants.PublicKeySize) + return ToxFriendNumber(ToxCoreJni.toxFriendAddNorequest(instanceNumber, publicKey.value)) + } + + override fun deleteFriend(friendNumber: ToxFriendNumber): Unit = + ToxCoreJni.toxFriendDelete(instanceNumber, friendNumber.value) + + override fun friendByPublicKey(publicKey: ToxPublicKey): ToxFriendNumber = + ToxFriendNumber(ToxCoreJni.toxFriendByPublicKey(instanceNumber, publicKey.value)) + + override fun getFriendPublicKey(friendNumber: ToxFriendNumber): ToxPublicKey = + ToxPublicKey(ToxCoreJni.toxFriendGetPublicKey(instanceNumber, friendNumber.value)) + + override fun friendExists(friendNumber: ToxFriendNumber): Boolean = + ToxCoreJni.toxFriendExists(instanceNumber, friendNumber.value) + + override val friendList: IntArray + get() = ToxCoreJni.toxSelfGetFriendList(instanceNumber) + + override fun setTyping(friendNumber: ToxFriendNumber, typing: Boolean): Unit = + ToxCoreJni.toxSelfSetTyping(instanceNumber, friendNumber.value, typing) + + override fun friendSendMessage( + friendNumber: ToxFriendNumber, + messageType: ToxMessageType, + timeDelta: Int, + message: ToxFriendMessage + ): Int = + ToxCoreJni.toxFriendSendMessage( + instanceNumber, friendNumber.value, messageType.ordinal, timeDelta, message.value) + + override fun fileControl( + friendNumber: ToxFriendNumber, + fileNumber: Int, + control: ToxFileControl + ): Unit = + ToxCoreJni.toxFileControl(instanceNumber, friendNumber.value, fileNumber, control.ordinal) + + override fun fileSeek(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long): Unit = + ToxCoreJni.toxFileSeek(instanceNumber, friendNumber.value, fileNumber, position) + + override fun fileSend( + friendNumber: ToxFriendNumber, + kind: Int, + fileSize: Long, + fileId: ToxFileId, + filename: ToxFilename + ): Int = + ToxCoreJni.toxFileSend( + instanceNumber, friendNumber.value, kind, fileSize, fileId.value, filename.value) + + override fun fileSendChunk( + friendNumber: ToxFriendNumber, + fileNumber: Int, + position: Long, + data: ByteArray + ): Unit = + ToxCoreJni.toxFileSendChunk(instanceNumber, friendNumber.value, fileNumber, position, data) + + override fun getFileFileId(friendNumber: ToxFriendNumber, fileNumber: Int): ToxFileId = + ToxFileId(ToxCoreJni.toxFileGetFileId(instanceNumber, friendNumber.value, fileNumber)) + + override fun friendSendLossyPacket(friendNumber: ToxFriendNumber, data: ToxLossyPacket): Unit = + ToxCoreJni.toxFriendSendLossyPacket(instanceNumber, friendNumber.value, data.value) + + override fun friendSendLosslessPacket( + friendNumber: ToxFriendNumber, + data: ToxLosslessPacket + ): Unit = ToxCoreJni.toxFriendSendLosslessPacket(instanceNumber, friendNumber.value, data.value) + + private companion object { + + fun checkBootstrapArguments(publicKey: ByteArray): Unit { + if (publicKey.size < ToxCoreConstants.PublicKeySize) { + throw ToxBootstrapException(ToxBootstrapException.Code.BAD_KEY, "Key too short") + } + if (publicKey.size > ToxCoreConstants.PublicKeySize) { + throw ToxBootstrapException(ToxBootstrapException.Code.BAD_KEY, "Key too long") + } + } + + fun throwLengthException(name: String, message: String, expectedSize: Int): Unit { + throw IllegalArgumentException("${name} too ${message}, must be ${expectedSize} bytes") + } + + fun checkLength(name: String, bytes: ByteArray, expectedSize: Int): Unit { + if (bytes.size < expectedSize) { + throwLengthException(name, "short", expectedSize) + } + if (bytes.size > expectedSize) { + throwLengthException(name, "long", expectedSize) + } + } + } +} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreImpl.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreImpl.scala deleted file mode 100644 index 89a157844..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreImpl.scala +++ /dev/null @@ -1,295 +0,0 @@ -package im.tox.tox4j.impl.jni - -import com.typesafe.scalalogging.Logger -import im.tox.core.network.Port -import im.tox.tox4j.core._ -import im.tox.tox4j.core.callbacks._ -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxMessageType, ToxUserStatus } -import im.tox.tox4j.core.exceptions._ -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.impl.jni.ToxCoreImpl.logger -import im.tox.tox4j.impl.jni.internal.Event -import org.jetbrains.annotations.{ NotNull, Nullable } -import org.slf4j.LoggerFactory - -// scalastyle:off null -@SuppressWarnings(Array( - "org.wartremover.warts.ArrayEquals", - "org.wartremover.warts.Equals", - "org.wartremover.warts.Null" -)) -object ToxCoreImpl { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - @throws[ToxBootstrapException] - private def checkBootstrapArguments(port: Int, @Nullable publicKey: Array[Byte]): Unit = { - if (port < 0) { - throw new ToxBootstrapException(ToxBootstrapException.Code.BAD_PORT, "Port cannot be negative") - } - if (port > 65535) { - throw new ToxBootstrapException(ToxBootstrapException.Code.BAD_PORT, "Port cannot exceed 65535") - } - if (publicKey ne null) { - if (publicKey.length < ToxCoreConstants.PublicKeySize) { - throw new ToxBootstrapException(ToxBootstrapException.Code.BAD_KEY, "Key too short") - } - if (publicKey.length > ToxCoreConstants.PublicKeySize) { - throw new ToxBootstrapException(ToxBootstrapException.Code.BAD_KEY, "Key too long") - } - } - } - - private def throwLengthException(name: String, message: String, expectedSize: Int): Unit = { - throw new IllegalArgumentException(s"$name too $message, must be $expectedSize bytes") - } - - private def checkLength(name: String, @Nullable bytes: Array[Byte], expectedSize: Int): Unit = { - if (bytes ne null) { - if (bytes.length < expectedSize) { - throwLengthException(name, "short", expectedSize) - } - if (bytes.length > expectedSize) { - throwLengthException(name, "long", expectedSize) - } - } - } - - @throws[ToxSetInfoException] - private def checkInfoNotNull(info: Array[Byte]): Unit = { - if (info eq null) { - throw new ToxSetInfoException(ToxSetInfoException.Code.NULL) - } - } - -} - -/** - * Initialises the new Tox instance with an optional save-data received from [[getSavedata]]. - * - * @param options Connection options object with optional save-data. - */ -// scalastyle:off no.finalize number.of.methods -@throws[ToxNewException]("If an error was detected in the configuration or a runtime error occurred.") -final class ToxCoreImpl(@NotNull val options: ToxOptions) extends ToxCore { - - private[this] val onCloseCallbacks: Event = new Event - - /** - * This field has package visibility for [[ToxAvImpl]]. - */ - private[jni] val instanceNumber = - ToxCoreJni.toxNew( - options.ipv6Enabled, - options.udpEnabled, - options.localDiscoveryEnabled, - options.proxy.proxyType.ordinal, - options.proxy.proxyAddress, - options.proxy.proxyPort, - options.startPort, - options.endPort, - options.tcpPort, - options.saveData.kind.ordinal, - options.saveData.data - ) - - /** - * Add an onClose callback. This event is invoked just before the instance is closed. - */ - def addOnCloseCallback(callback: () => Unit): Event.Id = - onCloseCallbacks += callback - - def removeOnCloseCallback(id: Event.Id): Unit = - onCloseCallbacks -= id - - override def load(options: ToxOptions): ToxCoreImpl = - new ToxCoreImpl(options) - - override def close(): Unit = { - onCloseCallbacks() - ToxCoreJni.toxKill(instanceNumber) - } - - protected override def finalize(): Unit = { - try { - close() - ToxCoreJni.toxFinalize(instanceNumber) - } catch { - case e: Throwable => - logger.error("Exception caught in finalizer; this indicates a serious problem in native code", e) - } - super.finalize() - } - - @throws[ToxBootstrapException] - override def bootstrap(address: String, port: Port, publicKey: ToxPublicKey): Unit = { - ToxCoreImpl.checkBootstrapArguments(port.value, publicKey.value) - ToxCoreJni.toxBootstrap(instanceNumber, address, port.value, publicKey.value) - } - - @throws[ToxBootstrapException] - override def addTcpRelay(address: String, port: Port, publicKey: ToxPublicKey): Unit = { - ToxCoreImpl.checkBootstrapArguments(port.value, publicKey.value) - ToxCoreJni.toxAddTcpRelay(instanceNumber, address, port.value, publicKey.value) - } - - override def getSavedata: Array[Byte] = - ToxCoreJni.toxGetSavedata(instanceNumber) - - @throws[ToxGetPortException] - override def getUdpPort: Port = - Port.unsafeFromInt(ToxCoreJni.toxSelfGetUdpPort(instanceNumber)) - - @throws[ToxGetPortException] - override def getTcpPort: Port = - Port.unsafeFromInt(ToxCoreJni.toxSelfGetTcpPort(instanceNumber)) - - override def getDhtId: ToxPublicKey = - ToxPublicKey.unsafeFromValue(ToxCoreJni.toxSelfGetDhtId(instanceNumber)) - - override def iterationInterval: Int = - ToxCoreJni.toxIterationInterval(instanceNumber) - - override def iterate[S](@NotNull handler: ToxCoreEventListener[S])(state: S): S = { - ToxCoreEventDispatch.dispatch(handler, ToxCoreJni.toxIterate(instanceNumber))(state) - } - - override def getPublicKey: ToxPublicKey = - ToxPublicKey.unsafeFromValue(ToxCoreJni.toxSelfGetPublicKey(instanceNumber)) - - override def getSecretKey: ToxSecretKey = - ToxSecretKey.unsafeFromValue(ToxCoreJni.toxSelfGetSecretKey(instanceNumber)) - - override def setNospam(nospam: Int): Unit = - ToxCoreJni.toxSelfSetNospam(instanceNumber, nospam) - - override def getNospam: Int = - ToxCoreJni.toxSelfGetNospam(instanceNumber) - - override def getAddress: ToxFriendAddress = - ToxFriendAddress.unsafeFromValue(ToxCoreJni.toxSelfGetAddress(instanceNumber)) - - @throws[ToxSetInfoException] - override def setName(name: ToxNickname): Unit = { - ToxCoreImpl.checkInfoNotNull(name.value) - ToxCoreJni.toxSelfSetName(instanceNumber, name.value) - } - - override def getName: ToxNickname = { - ToxNickname.unsafeFromValue(ToxCoreJni.toxSelfGetName(instanceNumber)) - } - - @throws[ToxSetInfoException] - override def setStatusMessage(message: ToxStatusMessage): Unit = { - ToxCoreImpl.checkInfoNotNull(message.value) - ToxCoreJni.toxSelfSetStatusMessage(instanceNumber, message.value) - } - - override def getStatusMessage: ToxStatusMessage = - ToxStatusMessage.unsafeFromValue(ToxCoreJni.toxSelfGetStatusMessage(instanceNumber)) - - override def setStatus(status: ToxUserStatus): Unit = - ToxCoreJni.toxSelfSetStatus(instanceNumber, status.ordinal) - - override def getStatus: ToxUserStatus = - ToxUserStatus.values()(ToxCoreJni.toxSelfGetStatus(instanceNumber)) - - @throws[ToxFriendAddException] - override def addFriend(address: ToxFriendAddress, message: ToxFriendRequestMessage): ToxFriendNumber = { - ToxCoreImpl.checkLength("Friend Address", address.value, ToxCoreConstants.AddressSize) - ToxFriendNumber.unsafeFromInt(ToxCoreJni.toxFriendAdd(instanceNumber, address.value, message.value)) - } - - @throws[ToxFriendAddException] - override def addFriendNorequest(publicKey: ToxPublicKey): ToxFriendNumber = { - ToxCoreImpl.checkLength("Public Key", publicKey.value, ToxCoreConstants.PublicKeySize) - ToxFriendNumber.unsafeFromInt(ToxCoreJni.toxFriendAddNorequest(instanceNumber, publicKey.value)) - } - - @throws[ToxFriendDeleteException] - override def deleteFriend(friendNumber: ToxFriendNumber): Unit = - ToxCoreJni.toxFriendDelete(instanceNumber, friendNumber.value) - - @throws[ToxFriendByPublicKeyException] - override def friendByPublicKey(publicKey: ToxPublicKey): ToxFriendNumber = - ToxFriendNumber.unsafeFromInt(ToxCoreJni.toxFriendByPublicKey(instanceNumber, publicKey.value)) - - @throws[ToxFriendGetPublicKeyException] - override def getFriendPublicKey(friendNumber: ToxFriendNumber): ToxPublicKey = - ToxPublicKey.unsafeFromValue(ToxCoreJni.toxFriendGetPublicKey(instanceNumber, friendNumber.value)) - - override def friendExists(friendNumber: ToxFriendNumber): Boolean = - ToxCoreJni.toxFriendExists(instanceNumber, friendNumber.value) - - override def getFriendList: Array[Int] = - ToxCoreJni.toxSelfGetFriendList(instanceNumber) - - @throws[ToxSetTypingException] - override def setTyping(friendNumber: ToxFriendNumber, typing: Boolean): Unit = - ToxCoreJni.toxSelfSetTyping(instanceNumber, friendNumber.value, typing) - - @throws[ToxFriendSendMessageException] - override def friendSendMessage(friendNumber: ToxFriendNumber, messageType: ToxMessageType, timeDelta: Int, message: ToxFriendMessage): Int = - ToxCoreJni.toxFriendSendMessage(instanceNumber, friendNumber.value, messageType.ordinal, timeDelta, message.value) - - @throws[ToxFileControlException] - override def fileControl(friendNumber: ToxFriendNumber, fileNumber: Int, control: ToxFileControl): Unit = - ToxCoreJni.toxFileControl(instanceNumber, friendNumber.value, fileNumber, control.ordinal) - - @throws[ToxFileSeekException] - override def fileSeek(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long): Unit = - ToxCoreJni.toxFileSeek(instanceNumber, friendNumber.value, fileNumber, position) - - @throws[ToxFileSendException] - override def fileSend(friendNumber: ToxFriendNumber, kind: Int, fileSize: Long, @NotNull fileId: ToxFileId, filename: ToxFilename): Int = - ToxCoreJni.toxFileSend(instanceNumber, friendNumber.value, kind, fileSize, fileId.value, filename.value) - - @throws[ToxFileSendChunkException] - override def fileSendChunk(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, data: Array[Byte]): Unit = - ToxCoreJni.toxFileSendChunk(instanceNumber, friendNumber.value, fileNumber, position, data) - - @throws[ToxFileGetException] - override def getFileFileId(friendNumber: ToxFriendNumber, fileNumber: Int): ToxFileId = - ToxFileId.unsafeFromValue(ToxCoreJni.toxFileGetFileId(instanceNumber, friendNumber.value, fileNumber)) - - @throws[ToxFriendCustomPacketException] - override def friendSendLossyPacket(friendNumber: ToxFriendNumber, data: ToxLossyPacket): Unit = - ToxCoreJni.toxFriendSendLossyPacket(instanceNumber, friendNumber.value, data.value) - - @throws[ToxFriendCustomPacketException] - override def friendSendLosslessPacket(friendNumber: ToxFriendNumber, data: ToxLosslessPacket): Unit = - ToxCoreJni.toxFriendSendLosslessPacket(instanceNumber, friendNumber.value, data.value) - - def invokeFriendName(friendNumber: ToxFriendNumber, @NotNull name: ToxNickname): Unit = - ToxCoreJni.invokeFriendName(instanceNumber, friendNumber.value, name.value) - def invokeFriendStatusMessage(friendNumber: ToxFriendNumber, @NotNull message: Array[Byte]): Unit = - ToxCoreJni.invokeFriendStatusMessage(instanceNumber, friendNumber.value, message) - def invokeFriendStatus(friendNumber: ToxFriendNumber, @NotNull status: ToxUserStatus): Unit = - ToxCoreJni.invokeFriendStatus(instanceNumber, friendNumber.value, status.ordinal()) - def invokeFriendConnectionStatus(friendNumber: ToxFriendNumber, @NotNull connectionStatus: ToxConnection): Unit = - ToxCoreJni.invokeFriendConnectionStatus(instanceNumber, friendNumber.value, connectionStatus.ordinal()) - def invokeFriendTyping(friendNumber: ToxFriendNumber, isTyping: Boolean): Unit = - ToxCoreJni.invokeFriendTyping(instanceNumber, friendNumber.value, isTyping) - def invokeFriendReadReceipt(friendNumber: ToxFriendNumber, messageId: Int): Unit = - ToxCoreJni.invokeFriendReadReceipt(instanceNumber, friendNumber.value, messageId) - def invokeFriendRequest(@NotNull publicKey: ToxPublicKey, timeDelta: Int, @NotNull message: Array[Byte]): Unit = - ToxCoreJni.invokeFriendRequest(instanceNumber, publicKey.value, timeDelta, message) - def invokeFriendMessage(friendNumber: ToxFriendNumber, @NotNull messageType: ToxMessageType, timeDelta: Int, @NotNull message: Array[Byte]): Unit = - ToxCoreJni.invokeFriendMessage(instanceNumber, friendNumber.value, messageType.ordinal(), timeDelta, message) - def invokeFileChunkRequest(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, length: Int): Unit = - ToxCoreJni.invokeFileChunkRequest(instanceNumber, friendNumber.value, fileNumber, position, length) - def invokeFileRecv(friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, @NotNull filename: Array[Byte]): Unit = - ToxCoreJni.invokeFileRecv(instanceNumber, friendNumber.value, fileNumber, kind, fileSize, filename) - def invokeFileRecvChunk(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, @NotNull data: Array[Byte]): Unit = - ToxCoreJni.invokeFileRecvChunk(instanceNumber, friendNumber.value, fileNumber, position, data) - def invokeFileRecvControl(friendNumber: ToxFriendNumber, fileNumber: Int, @NotNull control: ToxFileControl): Unit = - ToxCoreJni.invokeFileRecvControl(instanceNumber, friendNumber.value, fileNumber, control.ordinal()) - def invokeFriendLossyPacket(friendNumber: ToxFriendNumber, @NotNull data: Array[Byte]): Unit = - ToxCoreJni.invokeFriendLossyPacket(instanceNumber, friendNumber.value, data) - def invokeFriendLosslessPacket(friendNumber: ToxFriendNumber, @NotNull data: Array[Byte]): Unit = - ToxCoreJni.invokeFriendLosslessPacket(instanceNumber, friendNumber.value, data) - def invokeSelfConnectionStatus(@NotNull connectionStatus: ToxConnection): Unit = - ToxCoreJni.invokeSelfConnectionStatus(instanceNumber, connectionStatus.ordinal()) - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreJni.java b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreJni.java index b8151c657..ade5f93bb 100644 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxCoreJni.java +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxCoreJni.java @@ -6,99 +6,75 @@ @SuppressWarnings({"checkstyle:emptylineseparator", "checkstyle:linelength"}) public final class ToxCoreJni { - static { - ToxLoadJniLibrary.load("tox4j-c"); + System.loadLibrary("tox4j-c"); } - static native int toxNew( - boolean ipv6Enabled, - boolean udpEnabled, - boolean localDiscoveryEnabled, - int proxyType, - @NotNull String proxyAddress, - int proxyPort, - int startPort, - int endPort, - int tcpPort, - int saveDataType, - @NotNull byte[] saveData - ) throws ToxNewException; + static native int toxNew(boolean ipv6Enabled, boolean udpEnabled, boolean localDiscoveryEnabled, + int proxyType, @NotNull String proxyAddress, int proxyPort, int startPort, int endPort, + int tcpPort, int saveDataType, @NotNull byte[] saveData) throws ToxNewException; static native void toxKill(int instanceNumber); static native void toxFinalize(int instanceNumber); - @NotNull - static native byte[] toxGetSavedata(int instanceNumber); - static native void toxBootstrap(int instanceNumber, @NotNull String address, int port, @NotNull byte[] publicKey) throws ToxBootstrapException; - static native void toxAddTcpRelay(int instanceNumber, @NotNull String address, int port, @NotNull byte[] publicKey) throws ToxBootstrapException; + @NotNull static native byte[] toxGetSavedata(int instanceNumber); + static native void toxBootstrap(int instanceNumber, @NotNull String address, int port, + @NotNull byte[] publicKey) throws ToxBootstrapException; + static native void toxAddTcpRelay(int instanceNumber, @NotNull String address, int port, + @NotNull byte[] publicKey) throws ToxBootstrapException; static native int toxSelfGetUdpPort(int instanceNumber) throws ToxGetPortException; static native int toxSelfGetTcpPort(int instanceNumber) throws ToxGetPortException; - @NotNull - static native byte[] toxSelfGetDhtId(int instanceNumber); - @AutoGenerated + @NotNull static native byte[] toxSelfGetDhtId(int instanceNumber); static native int toxIterationInterval(int instanceNumber); - @Nullable - static native byte[] toxIterate(int instanceNumber); - @NotNull - static native byte[] toxSelfGetPublicKey(int instanceNumber); - @NotNull - static native byte[] toxSelfGetSecretKey(int instanceNumber); - @AutoGenerated + @Nullable static native byte[] toxIterate(int instanceNumber); + @NotNull static native byte[] toxSelfGetPublicKey(int instanceNumber); + @NotNull static native byte[] toxSelfGetSecretKey(int instanceNumber); static native void toxSelfSetNospam(int instanceNumber, int nospam); - @AutoGenerated static native int toxSelfGetNospam(int instanceNumber); - @NotNull - static native byte[] toxSelfGetAddress(int instanceNumber); - static native void toxSelfSetName(int instanceNumber, @NotNull byte[] name) throws ToxSetInfoException; - @NotNull - static native byte[] toxSelfGetName(int instanceNumber); - static native void toxSelfSetStatusMessage(int instanceNumber, byte[] message) throws ToxSetInfoException; - @NotNull - static native byte[] toxSelfGetStatusMessage(int instanceNumber); + @NotNull static native byte[] toxSelfGetAddress(int instanceNumber); + static native void toxSelfSetName(int instanceNumber, @NotNull byte[] name) + throws ToxSetInfoException; + @NotNull static native byte[] toxSelfGetName(int instanceNumber); + static native void toxSelfSetStatusMessage(int instanceNumber, byte[] message) + throws ToxSetInfoException; + @NotNull static native byte[] toxSelfGetStatusMessage(int instanceNumber); static native void toxSelfSetStatus(int instanceNumber, int status); - @AutoGenerated static native int toxSelfGetStatus(int instanceNumber); - static native int toxFriendAdd(int instanceNumber, @NotNull byte[] address, @NotNull byte[] message) throws ToxFriendAddException; - static native int toxFriendAddNorequest(int instanceNumber, @NotNull byte[] publicKey) throws ToxFriendAddException; - static native void toxFriendDelete(int instanceNumber, int friendNumber) throws ToxFriendDeleteException; - static native int toxFriendByPublicKey(int instanceNumber, @NotNull byte[] publicKey) throws ToxFriendByPublicKeyException; + static native int toxFriendAdd(int instanceNumber, @NotNull byte[] address, + @NotNull byte[] message) throws ToxFriendAddException; + static native int toxFriendAddNorequest(int instanceNumber, @NotNull byte[] publicKey) + throws ToxFriendAddException; + static native void toxFriendDelete(int instanceNumber, int friendNumber) + throws ToxFriendDeleteException; + static native int toxFriendByPublicKey(int instanceNumber, @NotNull byte[] publicKey) + throws ToxFriendByPublicKeyException; @NotNull - static native byte[] toxFriendGetPublicKey(int instanceNumber, int friendNumber) throws ToxFriendGetPublicKeyException; - @AutoGenerated + static native byte[] toxFriendGetPublicKey(int instanceNumber, int friendNumber) + throws ToxFriendGetPublicKeyException; static native boolean toxFriendExists(int instanceNumber, int friendNumber); + @NotNull static native int[] toxSelfGetFriendList(int instanceNumber); + static native void toxSelfSetTyping(int instanceNumber, int friendNumber, boolean typing) + throws ToxSetTypingException; + static native int toxFriendSendMessage(int instanceNumber, int friendNumber, int type, + int timeDelta, @NotNull byte[] message) throws ToxFriendSendMessageException; + static native void toxFileControl(int instanceNumber, int friendNumber, int fileNumber, + int control) throws ToxFileControlException; + static native void toxFileSeek(int instanceNumber, int friendNumber, int fileNumber, + long position) throws ToxFileSeekException; + static native int toxFileSend(int instanceNumber, int friendNumber, int kind, long fileSize, + @NotNull byte[] fileId, @NotNull byte[] filename) throws ToxFileSendException; + static native void toxFileSendChunk(int instanceNumber, int friendNumber, int fileNumber, + long position, @NotNull byte[] data) throws ToxFileSendChunkException; @NotNull - static native int[] toxSelfGetFriendList(int instanceNumber); - static native void toxSelfSetTyping(int instanceNumber, int friendNumber, boolean typing) throws ToxSetTypingException; - static native int toxFriendSendMessage(int instanceNumber, int friendNumber, int type, int timeDelta, @NotNull byte[] message) throws ToxFriendSendMessageException; - static native void toxFileControl(int instanceNumber, int friendNumber, int fileNumber, int control) throws ToxFileControlException; - static native void toxFileSeek(int instanceNumber, int friendNumber, int fileNumber, long position) throws ToxFileSeekException; - static native int toxFileSend(int instanceNumber, int friendNumber, int kind, long fileSize, @NotNull byte[] fileId, @NotNull byte[] filename) throws ToxFileSendException; - static native void toxFileSendChunk(int instanceNumber, int friendNumber, int fileNumber, long position, @NotNull byte[] data) throws ToxFileSendChunkException; - @NotNull - static native byte[] toxFileGetFileId(int instanceNumber, int friendNumber, int fileNumber) throws ToxFileGetException; - static native void toxFriendSendLossyPacket(int instanceNumber, int friendNumber, @NotNull byte[] data) throws ToxFriendCustomPacketException; - static native void toxFriendSendLosslessPacket(int instanceNumber, int friendNumber, @NotNull byte[] data) throws ToxFriendCustomPacketException; - - static native void invokeSelfConnectionStatus(int instanceNumber, int connectionStatus); - static native void invokeFileRecvControl(int instanceNumber, int friendNumber, int fileNumber, int control); - static native void invokeFileRecv(int instanceNumber, int friendNumber, int fileNumber, int kind, long fileSize, @NotNull byte[] filename); - static native void invokeFileRecvChunk(int instanceNumber, int friendNumber, int fileNumber, long position, @NotNull byte[] data); - static native void invokeFileChunkRequest(int instanceNumber, int friendNumber, int fileNumber, long position, int length); - static native void invokeFriendConnectionStatus(int instanceNumber, int friendNumber, int connectionStatus); - static native void invokeFriendLosslessPacket(int instanceNumber, int friendNumber, @NotNull byte[] data); - static native void invokeFriendLossyPacket(int instanceNumber, int friendNumber, @NotNull byte[] data); - static native void invokeFriendMessage(int instanceNumber, int friendNumber, int type, int timeDelta, @NotNull byte[] message); - static native void invokeFriendName(int instanceNumber, int friendNumber, @NotNull byte[] name); - static native void invokeFriendRequest(int instanceNumber, @NotNull byte[] publicKey, int timeDelta, @NotNull byte[] message); - static native void invokeFriendStatus(int instanceNumber, int friendNumber, int status); - static native void invokeFriendStatusMessage(int instanceNumber, int friendNumber, @NotNull byte[] message); - static native void invokeFriendTyping(int instanceNumber, int friendNumber, boolean isTyping); - static native void invokeFriendReadReceipt(int instanceNumber, int friendNumber, int messageId); + static native byte[] toxFileGetFileId(int instanceNumber, int friendNumber, int fileNumber) + throws ToxFileGetException; + static native void toxFriendSendLossyPacket(int instanceNumber, int friendNumber, + @NotNull byte[] data) throws ToxFriendCustomPacketException; + static native void toxFriendSendLosslessPacket(int instanceNumber, int friendNumber, + @NotNull byte[] data) throws ToxFriendCustomPacketException; static native byte[] tox4jLastLog(); static native int tox4jGetCurrentLogSize(); static native void tox4jSetMaxLogSize(int maxSize); static native int tox4jGetMaxLogSize(); static native void tox4jSetLogFilter(String[] filter); - } diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoImpl.kt b/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoImpl.kt new file mode 100644 index 000000000..478a89784 --- /dev/null +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoImpl.kt @@ -0,0 +1,38 @@ +package im.tox.tox4j.impl.jni + +import im.tox.tox4j.crypto.ToxCrypto +import im.tox.tox4j.crypto.ToxCryptoConstants + +private typealias PassKey = ByteArray + +object ToxCryptoImpl : ToxCrypto { + + override fun passKeyEquals(a: PassKey, b: PassKey): Boolean = a.contentEquals(b) + + override fun passKeyToBytes(passKey: PassKey): List = passKey.toList() + + override fun passKeyFromBytes(bytes: List): PassKey? = + if (bytes.size == ToxCryptoConstants.KeyLength + ToxCryptoConstants.SaltLength) { + bytes.toByteArray() + } else { + null + } + + override fun encrypt(data: ByteArray, passKey: PassKey): ByteArray = + ToxCryptoJni.toxPassKeyEncrypt(data, passKey) + + override fun getSalt(data: ByteArray): ByteArray = ToxCryptoJni.toxGetSalt(data) + + override fun isDataEncrypted(data: ByteArray): Boolean = ToxCryptoJni.toxIsDataEncrypted(data) + + override fun passKeyDeriveWithSalt(passphrase: ByteArray, salt: ByteArray): PassKey = + ToxCryptoJni.toxPassKeyDeriveWithSalt(passphrase, salt) + + override fun passKeyDerive(passphrase: ByteArray): PassKey = + ToxCryptoJni.toxPassKeyDerive(passphrase) + + override fun decrypt(data: ByteArray, passKey: PassKey): ByteArray = + ToxCryptoJni.toxPassKeyDecrypt(data, passKey) + + override fun hash(data: ByteArray): ByteArray = ToxCryptoJni.toxHash(data) +} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoImpl.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoImpl.scala deleted file mode 100644 index fc324c193..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoImpl.scala +++ /dev/null @@ -1,36 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.crypto.{ ToxCrypto, ToxCryptoConstants } - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -object ToxCryptoImpl extends ToxCrypto { - - override type PassKey = Array[Byte] - - override def passKeyEquals(a: PassKey, b: PassKey): Boolean = a.deep == b.deep - override def passKeyToBytes(passKey: PassKey): Seq[Byte] = passKey - - override def passKeyFromBytes(bytes: Seq[Byte]): Option[PassKey] = { - if (bytes.length == ToxCryptoConstants.KeyLength + ToxCryptoConstants.SaltLength) { - Some(bytes.toArray) - } else { - None - } - } - - override def encrypt(data: Array[Byte], passKey: PassKey): Array[Byte] = - ToxCryptoJni.toxPassKeyEncrypt(data, passKey) - override def getSalt(data: Array[Byte]): Array[Byte] = - ToxCryptoJni.toxGetSalt(data) - override def isDataEncrypted(data: Array[Byte]): Boolean = - ToxCryptoJni.toxIsDataEncrypted(data) - override def passKeyDeriveWithSalt(passphrase: Array[Byte], salt: Array[Byte]): PassKey = - ToxCryptoJni.toxPassKeyDeriveWithSalt(passphrase, salt) - override def passKeyDerive(passphrase: Array[Byte]): PassKey = - ToxCryptoJni.toxPassKeyDerive(passphrase) - override def decrypt(data: Array[Byte], passKey: PassKey): Array[Byte] = - ToxCryptoJni.toxPassKeyDecrypt(data, passKey) - override def hash(data: Array[Byte]): Array[Byte] = - ToxCryptoJni.toxHash(data) - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoJni.java b/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoJni.java index 6f25c765e..66cbe4cc2 100644 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoJni.java +++ b/src/main/java/im/tox/tox4j/impl/jni/ToxCryptoJni.java @@ -2,9 +2,8 @@ @SuppressWarnings({"checkstyle:emptylineseparator", "checkstyle:linelength"}) public final class ToxCryptoJni { - static { - ToxLoadJniLibrary.load("tox4j-c"); + System.loadLibrary("tox4j-c"); } static native byte[] toxPassKeyEncrypt(byte[] data, byte[] passKey); @@ -14,5 +13,4 @@ public final class ToxCryptoJni { static native byte[] toxPassKeyDerive(byte[] passphrase); static native byte[] toxPassKeyDecrypt(byte[] data, byte[] passKey); static native byte[] toxHash(byte[] data); - } diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxJniLog.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxJniLog.scala deleted file mode 100644 index abc6fdaf4..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxJniLog.scala +++ /dev/null @@ -1,200 +0,0 @@ -package im.tox.tox4j.impl.jni - -import java.io.{ PrintWriter, StringWriter } - -import com.google.protobuf.InvalidProtocolBufferException -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.impl.jni.proto.Value.V -import im.tox.tox4j.impl.jni.proto._ -import org.slf4j.LoggerFactory - -import scala.concurrent.duration._ -import scala.language.postfixOps - -/** - * The JNI bridge logs every call made to toxcore and toxav functions along - * with the time taken to execute in microseconds. See the message definitions - * in ProtoLog.proto to get an idea of what can be done with this log. - */ -// scalastyle:off non.ascii.character.disallowed -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -case object ToxJniLog { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - /** - * By default, filter out the functions called on every iteration. - */ - filterNot( - "tox_iterate", - "toxav_iterate", - "tox_iteration_interval", - "toxav_iteration_interval" - ) - - /** - * Set the maximum number of entries in the log. After this limit is reached, - * logging stops and ignores any further calls until the log is fetched and cleared. - * - * Set to 0 to disable logging. - */ - def maxSize_=(maxSize: Int): Unit = ToxCoreJni.tox4jSetMaxLogSize(maxSize) - def maxSize: Int = ToxCoreJni.tox4jGetMaxLogSize - - /** - * The current size of the log on the native side. Can be used to determine - * whether it needs to be fetched. - */ - def size: Int = ToxCoreJni.tox4jGetCurrentLogSize - - /** - * Set a filter to avoid logging certain calls. - */ - def filterNot(filter: String*): Unit = ToxCoreJni.tox4jSetLogFilter(filter.toArray) - - /** - * Retrieve and clear the current call log. Calling [[ToxJniLog]] twice with no - * native calls in between will return the empty log the second time. If logging - * is disabled, this will always return the empty log. - */ - def apply(): JniLog = { - fromBytes(ToxCoreJni.tox4jLastLog()) - } - - /** - * Parse a protobuf message from bytes to [[JniLog]]. Logs an error and returns - * [[JniLog.defaultInstance]] if $bytes is invalid. Returns [[JniLog.defaultInstance]] - * if $bytes is null. - */ - def fromBytes(bytes: Array[Byte]): JniLog = { - try { - Option(bytes).map(JniLog.parseFrom).getOrElse(JniLog.defaultInstance) - } catch { - case e: InvalidProtocolBufferException => - logger.error(s"${e.getMessage}; unfinished message: ${e.getUnfinishedMessage}") - JniLog.defaultInstance - } - } - - @SuppressWarnings(Array("org.wartremover.warts.While")) - private def printDelimited[A](list: Iterable[A], separator: String)(print: A => PrintWriter => Unit)(out: PrintWriter): Unit = { - val i = list.iterator - if (i.hasNext) { - print(i.next())(out) - while (i.hasNext) { // scalastyle:ignore while - out.print(separator) - print(i.next())(out) - } - } - } - - /** - * Pretty-print the log as function calls with time offset from the first message. E.g. - * [0.000000] tox_new_unique({udp_enabled=1; ipv6_enabled=0; ...}) [20 µs, #1] - * - * The last part is the time spent in the native function followed by the instance number. - */ - def print(log: JniLog)(out: PrintWriter): Unit = { - log.entries.headOption match { - case None => - case Some(first) => - for (entry <- log.entries) { - print(first.timestamp.getOrElse(Timestamp.defaultInstance))(entry)(out) - out.println() - } - } - } - - private def printFormattedTimeDiff(a: Timestamp, b: Timestamp)(out: PrintWriter): Unit = { - assert(a.nanos < 1000000000) - assert(b.nanos < 1000000000) - - val timeDiff = { - val seconds = a.seconds - b.seconds - val nanos = a.nanos - b.nanos - if (nanos < 0) { - Timestamp(seconds - 1, nanos + (1 second).toNanos.toInt) - } else { - Timestamp(seconds, nanos) - } - } - - val micros = (timeDiff.nanos nanos).toMicros.toInt - out.print(timeDiff.seconds) - out.print('.') - out.print { - // scalastyle:off if.brace - if (false) "" - else if (micros < 10) "00000" - else if (micros < 100) "0000" - else if (micros < 1000) "000" - else if (micros < 10000) "00" - else if (micros < 100000) "0" - else if (micros < 1000000) "" - // scalastyle:on if.brace - } - out.print(micros) - } - - def print(startTime: Timestamp)(entry: JniLogEntry)(out: PrintWriter): Unit = { - out.print('[') - printFormattedTimeDiff(entry.timestamp.getOrElse(Timestamp.defaultInstance), startTime)(out) - out.print("] ") - out.print(entry.name) - out.print('(') - printDelimited(entry.arguments, ", ")(print)(out) - out.print(") = ") - print(entry.result.getOrElse(Value.defaultInstance))(out) - out.print(" [") - val elapsedNanos = entry.elapsedNanos.nanos - if (elapsedNanos.toMicros == 0) { - out.print(elapsedNanos.toNanos) - out.print(" ns") - } else { - out.print(elapsedNanos.toMicros) - out.print(" µs") - } - entry.instanceNumber match { - case 0 => - case instanceNumber => - out.print(", #") - out.print(instanceNumber) - } - out.print("]") - } - - def print(value: Value)(out: PrintWriter): Unit = { - value.v match { - case V.VBytes(bytes) => - out.print("byte[") - if (value.truncated == 0) { - out.print(bytes.size) - } else { - out.print(value.truncated) - } - out.print("]") - case V.VObject(Struct(members)) => - out.print('{') - printDelimited(members, "; ")(print)(out) - out.print('}') - case V.VSint64(sint64) => out.print(sint64) - case V.VString(string) => out.print(string) - case V.Empty => out.print("void") - } - } - - def print(member: (String, Value))(out: PrintWriter): Unit = { - out.print(member._1) - out.print('=') - print(member._2)(out) - } - - def toString(log: JniLog): String = { - val stringWriter = new StringWriter - val out = new PrintWriter(stringWriter) - print(log)(out) - out.close() - stringWriter.toString - } - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/ToxLoadJniLibrary.scala b/src/main/java/im/tox/tox4j/impl/jni/ToxLoadJniLibrary.scala deleted file mode 100644 index 82a33aaf2..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/ToxLoadJniLibrary.scala +++ /dev/null @@ -1,127 +0,0 @@ -package im.tox.tox4j.impl.jni - -import java.io.File -import java.io.InputStream - -import com.google.common.io.Files -import com.typesafe.scalalogging.Logger -import org.slf4j.LoggerFactory - -import scala.language.postfixOps - -object ToxLoadJniLibrary { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - private val AlreadyLoaded = "Native Library (.+) already loaded in another classloader".r - private val NotFoundDalvik = "Couldn't load .+ from loader .+ findLibrary returned null".r - private val NotFoundJvm = "no .+ in java.library.path.*".r - - private def withTempFile(name: String)(block: File => Boolean): Boolean = { - val (prefix, suffix) = name.splitAt(name.lastIndexOf(".")) - val file = File.createTempFile(prefix, suffix) - file.deleteOnExit() - try { - block(file) - } finally { - // This may fail if the OS doesn't support deleting files that are in use, but deleteOnExit - // will ensure that it is cleaned up on normal JVM termination. - file.delete() - } - } - - private def withResource(name: String)(block: InputStream => Boolean): Boolean = { - val stream = getClass.getResourceAsStream(name) - if (stream == null) { - logger.debug(s"Resource '$name' not found") - false - } else { - try { - block(stream) - } finally { - stream.close() - } - } - } - - /** - * Load a native library from an existing location by copying it to a new, temporary location and loading - * that new library. - * - * @param location A [[File]] pointing to the existing library. - */ - private def loadFromSystem(location: File): Boolean = { - withTempFile(location.getName) { libraryFile => - logger.info(s"Copying $location to $libraryFile") - Files.copy(location, libraryFile) - - System.load(libraryFile.getPath) - true - } - } - - /** - * Load a library from a linked resource jar by copying it to a temporary location and then loading that - * temporary file. - * - * @param name The library name without "dll" suffix or "lib" prefix. - */ - private def loadFromJar(name: String): Boolean = { - val osName = Map( - "Mac OS X" -> "darwin" - ).withDefault((x: String) => x.toLowerCase.split(" ").head) - val archName = Map( - "amd64" -> "x86_64" - ).withDefault((x: String) => x.toLowerCase) - - val resourceName = "%s-%s/%s".format( - osName(sys.props("os.name")), - archName(sys.props("os.arch")), - System.mapLibraryName(name) - ) - logger.debug(s"Loading $name from resource: $resourceName") - val location = new File(resourceName) - withTempFile(location.getName) { libraryFile => - if (withResource(resourceName) { stream => - logger.debug(s"Copying $resourceName to ${libraryFile.getPath}") - Files.asByteSink(libraryFile).writeFrom(stream) - true - }) { - System.load(libraryFile.getPath) - true - } else { - false - } - } - } - - def load(name: String): Unit = synchronized { - try { - System.loadLibrary(name) - } catch { - case exn: UnsatisfiedLinkError => - logger.debug( - s"Could not load native library '$name' (${exn.getMessage}). " + - s"java.library.path = ${sys.props("java.library.path")}." - ) - if (exn.getMessage match { - case AlreadyLoaded(location) => - logger.warn(s"${exn.getMessage} copying file and loading again") - loadFromSystem(new File(location)) - case NotFoundJvm() => - loadFromJar(name) - case NotFoundDalvik() => - logger.error(s"Could not load native library '$name'; giving up.") - false - case msg => - logger.error(s"Unhandled UnsatisfiedLinkError: '$msg'.") - false - }) { - logger.debug(s"Loading '$name' successful") - } else { - throw exn - } - } - } - -} diff --git a/src/main/java/im/tox/tox4j/impl/jni/internal/Event.scala b/src/main/java/im/tox/tox4j/impl/jni/internal/Event.scala deleted file mode 100644 index f506fc6a7..000000000 --- a/src/main/java/im/tox/tox4j/impl/jni/internal/Event.scala +++ /dev/null @@ -1,85 +0,0 @@ -package im.tox.tox4j.impl.jni.internal - -import scala.annotation.tailrec -import scala.collection.mutable.ArrayBuffer - -private[jni] object Event { - private val InvalidIndex = -1 - private val EmptyCallback = () => () - - trait Id { - /** - * @return The index in the callbacks list. - */ - private[internal] def value: Int - - /** - * Reset the index to an invalid value. - */ - private[internal] def reset(): Unit - } - - private final class IdImpl(private var index: Int) extends Id { - override def value: Int = index - override def reset(): Unit = index = InvalidIndex - } -} - -/** - * Function multiplexer to turn a collection of functions into one. - * - * This is a collection of nullary functions returning `Unit` (`() => Unit)`) and is itself also a nullary function - * returning unit. It can be used to implement events where one can register multiple handlers and selectively - * unregister them. - */ -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -private[jni] final class Event extends (() => Unit) { - private val callbacks = new ArrayBuffer[() => Unit] - - /** - * Register a callback to be called on [[apply]]. - * - * The returned [[Event.Id]] should be considered a linear value. It should only be owned by a single owner and never - * shared. - * - * @param callback A [[Runnable]] instance to be called. - * @return An [[Event.Id]] that can be used to [[apply]] the callback again. - */ - def +=(callback: () => Unit): Event.Id = { - callbacks += callback - new Event.IdImpl(callbacks.size - 1) - } - - /** - * Unregister a callback. Requires an [[Event.Id]] from [[+=]]. - * - * After calling this method, the [[Event.Id]] should be considered consumed. Removing the same event handler twice - * may result in erroneous behaviour. In particular, if between the two [[-=]] calls there is a [[+=]] call, the event - * ID may have been reused, and the second call will remove the newly added handler. - * - * @param id The callback id object. - */ - def -=(id: Event.Id): Unit = { - val index = id.value - if (index != Event.InvalidIndex) { - id.reset() - callbacks(index) = Event.EmptyCallback - pruneCallbacks() - } - } - - @tailrec - private def pruneCallbacks(): Unit = { - callbacks.lastOption match { - case Some(Event.EmptyCallback) => - callbacks.remove(callbacks.size - 1) - pruneCallbacks() - case _ => - } - } - - /** - * Invoke all callbacks. - */ - override def apply(): Unit = callbacks.foreach(_()) -} diff --git a/src/main/java/im/tox/tox4j/mkexceptions b/src/main/java/im/tox/tox4j/mkexceptions new file mode 100755 index 000000000..d2a49bdfe --- /dev/null +++ b/src/main/java/im/tox/tox4j/mkexceptions @@ -0,0 +1,59 @@ +#!/usr/bin/env perl + +use strict; +use warnings FATAL => 'all'; +use utf8; + +use JSON::PP; + +my $data = do { local $/; open my $fh, '<', 'exceptions.json' or die $!; decode_json <$fh> }; + +my $package = $data->[0]; +my $prefix = $data->[1]; +my $exns = $data->[2]; + +for my $type (keys %$exns) { + for (keys %{ $exns->{$type} }) { + my @lines = @{ $exns->{$type}{$_} }; + s/^/ * / for @lines; + $exns->{$type}{$_} = join "\n", @lines; + } +} + +for my $type (keys %$exns) { + open my $fh, '>', "Tox${prefix}${type}Exception.java" + or die $!; + + my $codes = + join ",\n", + map { " /**\n$exns->{$type}{$_}\n */\n $_" } + (sort keys %{ $exns->{$type} }); + + my $JavaOnly = + $codes =~ /\@JavaOnly/ + ? "import im.tox.tox4j.exceptions.JavaOnly;\n" + : ""; + + print $fh < Doc)(that: Doc): Doc = { - if (this == EmptyDoc) { - that - } else if (that == EmptyDoc) { - this - } else { - f(that) - } - } - - /** - * Concatenates two documents. - * Is left associative with [[gnieh.pp.empty]] as left and right unit. - */ - def ::(that: Doc): Doc = - withUnit(ConsDoc(_, this))(that) - - /** Equivalent to `that :: space :: this` */ - def :+:(that: Doc): Doc = - withUnit(_ :: space :: this)(that) - - /** Equivalent to `that :: line :: this` */ - def :|:(that: Doc): Doc = - withUnit(_ :: line :: this)(that) - - /** Equivalent to `that :: softline :: this` */ - def :\:(that: Doc): Doc = - withUnit(_ :: softline :: this)(that) - - /** Equivalent to `that :: linebreak :: this` */ - def :||:(that: Doc): Doc = - withUnit(_ :: linebreak :: this)(that) - - /** Equivalent to `that :: softbreak :: this` */ - def :\\:(that: Doc): Doc = - withUnit(_ :: softbreak :: this)(that) - - /** Equivalent to `align(this :|: that)` */ - def ||(that: Doc): Doc = - align(this :|: that) - - /** A flatten (no new lines) version of this document */ - def flatten: Doc - -} - -/** - * Nest document: new lines are indented by the given indentation. - * @author Lucas Satabin - */ -final case class NestDoc(indent: Int, inner: Doc) extends Doc { - override lazy val flatten = NestDoc(indent, inner.flatten) -} - -/** - * Union document: two variations of the same document. - * '''Note''': The `long`-document's first lines must be longer that the `short`-document's ones. - * @author Lucas Satabin - */ -final case class UnionDoc(long: Doc, short: Doc) extends Doc { - override lazy val flatten = long.flatten -} - -/** - * Empty document. - * @author Lucas Satabin - */ -case object EmptyDoc extends Doc { - override def flatten: Doc = this -} - -/** - * Text document: shall not contain any new lines. - * @author Lucas Satabin - */ -final case class TextDoc(text: String) extends Doc { - override def flatten: Doc = this -} - -/** - * Line document: renders as a new line except if discarded by a group. - * @author Lucas Satabin - */ -final case class LineDoc(repl: Doc) extends Doc { - override def flatten: Doc = repl -} - -/** - * Cons document: Concatenation of two documents. - * @author Lucas Satabin - */ -final case class ConsDoc(first: Doc, second: Doc) extends Doc { - override lazy val flatten = ConsDoc(first.flatten, second.flatten) -} - -/** - * Align document: aligns the document on the current column. - * @author Lucas Satabin - */ -final case class AlignDoc(inner: Doc) extends Doc { - override lazy val flatten = AlignDoc(inner.flatten) -} - -/** - * Column document: creates a document depending on the current column. - * @author Lucas Satabin - */ -final case class ColumnDoc(f: Int => Doc) extends Doc { - override lazy val flatten = ColumnDoc(f.andThen(_.flatten)) -} diff --git a/src/test/java/gnieh/pp/Renderer.scala b/src/test/java/gnieh/pp/Renderer.scala deleted file mode 100644 index 375216777..000000000 --- a/src/test/java/gnieh/pp/Renderer.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* - * This file is part of the gnieh-pp project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package gnieh.pp - -/** - * A pretty printer, that tries to make the document fit in the page width - * - * @author Lucas Satabin - */ -final class PrettyRenderer(width: Int) extends (Doc => SimpleDoc) { - - private type Docs = List[(Int, Doc)] - - override def apply(doc: Doc): SimpleDoc = { - best(width, 0, List((0, doc))) - } - - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def best(width: Int, column: Int, docs: Docs): SimpleDoc = { - docs match { - case Nil => - SEmpty - case (_, EmptyDoc) :: tail => - best(width, column, tail) - case (i, ConsDoc(first, second)) :: tail => - best(width, column, (i, first) :: (i, second) :: tail) - case (i, NestDoc(j, inner)) :: tail => - best(width, column, (i + j, inner) :: tail) - case (i, TextDoc(text)) :: tail => - SText(text, best(width, column + text.length, tail)) - case (i, LineDoc(_)) :: tail => - SLine(i, best(width, i, tail)) - case (i, UnionDoc(l, s)) :: tail => - better(width, column, - best(width, column, (i, l) :: tail), - best(width, column, (i, s) :: tail)) - case (i, AlignDoc(inner)) :: tail => - best(width, column, (column, inner) :: tail) - case (i, ColumnDoc(f)) :: tail => - best(width, column, (i, f(column)) :: tail) - } - } - - private def better(width: Int, column: Int, d1: SimpleDoc, d2: => SimpleDoc): SimpleDoc = { - if (d1.fits(width - column)) { - d1 - } else { - // d2 is computed only if needed... - d2 - } - } - -} - -/** - * This printer is not really pretty (but should be faster than pretty printers!): - * it does not insert any indentation and discards all groups, just renders everything as compact as possible - * - * @author Lucas Satabin - */ -object CompactRenderer extends (Doc => SimpleDoc) { - - override def apply(doc: Doc): SimpleDoc = { - scan(0, List(doc)) - } - - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def scan(column: Int, docs: List[Doc]): SimpleDoc = docs match { // scalastyle:ignore cyclomatic.complexity - case Nil => SEmpty - case doc :: docs => - doc match { - case EmptyDoc => SEmpty - case TextDoc(text) => SText(text, scan(column + text.length, docs)) - case LineDoc(_) => scan(column, doc.flatten :: docs) - case ConsDoc(first, second) => scan(column, first :: second :: docs) - case NestDoc(j, doc) => scan(column, doc :: docs) - case UnionDoc(long, _) => scan(column, long :: docs) - case AlignDoc(inner) => scan(column, inner :: docs) - case ColumnDoc(f) => scan(column, f(column) :: docs) - } - } - -} - -sealed abstract class CountUnit -/** Truncates after the count in non space characters */ -case object Characters extends CountUnit -/** Truncates after the count in words */ -case object Words extends CountUnit -/** Truncates after the count in lines */ -case object Lines extends CountUnit - -/** - * A renderer that truncates the result (once rendered by the inner renderer) with the given - * limit. It makes the assumption that the following invariants are respected: - * - a [[gnieh.pp.SText]] contains either only spaces or only a word - * - indentation characters are all modeled with the indentation in [[gnieh.pp.SLine]] - * - * @author Lucas Satabin - */ -final class TruncateRenderer(max: Int, unit: CountUnit, inner: Doc => SimpleDoc) extends (Doc => SimpleDoc) { - - override def apply(doc: Doc): SimpleDoc = { - truncate(inner(doc)) - } - - def apply(doc: SimpleDoc): SimpleDoc = { - truncate(doc) - } - - /** Truncates the simple document, depending on the constructor criterion. */ - def truncate(doc: SimpleDoc): SimpleDoc = { - unit match { - case Lines => firstLines(max, doc) - case Characters => firstChars(max, doc) - case Words => firstWords(max, doc) - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def firstLines(maxLines: Int, doc: SimpleDoc): SimpleDoc = { - doc match { - case SText(text, next) if maxLines >= 1 => SText(text, firstLines(maxLines, next)) - case SLine(indent, next) if maxLines > 1 => SLine(indent, firstLines(maxLines - 1, next)) - case _ => SEmpty - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def firstChars(maxChars: Int, doc: SimpleDoc): SimpleDoc = { - doc match { - case SText(text, next) if maxChars >= text.replaceAll(" ", "").length => SText(text, firstChars(maxChars - text.length, next)) - case SLine(indent, next) if maxChars > 0 => SLine(indent, firstChars(maxChars, next)) - case _ => SEmpty - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def firstWords(maxWords: Int, doc: SimpleDoc): SimpleDoc = { - doc match { - case SText(text, next) if text.trim.isEmpty && maxWords > 0 => SText(text, firstWords(maxWords, next)) - case SText(text, next) if maxWords > 0 => SText(text, firstWords(maxWords - 1, next)) - case SLine(indent, next) if maxWords > 0 => SLine(indent, firstWords(maxWords, next)) - case _ => SEmpty - } - } - -} diff --git a/src/test/java/gnieh/pp/SimpleDoc.scala b/src/test/java/gnieh/pp/SimpleDoc.scala deleted file mode 100644 index 7796e6ada..000000000 --- a/src/test/java/gnieh/pp/SimpleDoc.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of the gnieh-pp project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package gnieh.pp - -/** - * A simple document is the normalized form of a document. - * - * This is what is produced by the renderers depending on their rendering algorithm. - * - * @author Lucas Satabin - */ -sealed abstract class SimpleDoc { - - def fits(width: Int): Boolean - - def layout: String - - override def toString: String = layout - -} - -/** - * An empty document. - * @author Lucas Satabin - */ -case object SEmpty extends SimpleDoc { - override def fits(width: Int): Boolean = width >= 0 // always fits if there is enough place - - override def layout: String = "" -} - -/** - * A text document. Should never contain new lines - * @author Lucas Satabin - */ -final case class SText(text: String, next: SimpleDoc) extends SimpleDoc { - def fits(width: Int): Boolean = { - next.fits(width - text.length) - } - - override def layout: String = { - text + next.layout - } -} - -/** - * A new line document with the indentation level to print right after. - * If the next document is empty, the indentation is not printed. - * @author Lucas Satabin - */ -final case class SLine(indent: Int, next: SimpleDoc) extends SimpleDoc { - override def fits(width: Int): Boolean = width >= 0 // always fits if there is enough place - - override def layout: String = { - if (next.layout.isEmpty) { - "" - } else { - "\n" + (" " * indent) + next.layout - } - } -} diff --git a/src/test/java/gnieh/pp/package.scala b/src/test/java/gnieh/pp/package.scala deleted file mode 100644 index 91696dd6f..000000000 --- a/src/test/java/gnieh/pp/package.scala +++ /dev/null @@ -1,195 +0,0 @@ -/* -* This file is part of the gnieh-pp project. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -package gnieh - -import scala.collection.TraversableLike -import scala.language.implicitConversions - -/** Pretty-printer library based on the Wadler's paper "A Prettier Printer". */ -package object pp { - - /** Indents the document */ - def nest(indent: Int)(inner: Doc): Doc = { - NestDoc(indent, inner) - } - - /* Deindent the document */ - def unnest(indent: Int)(inner: Doc): Doc = { - nest(-indent)(inner) - } - - /** Renders as a space */ - val space: Doc = TextDoc(" ") - - /** Renders as a new line unless it is discarded by a group, in which case behaves like `space` */ - val line: Doc = LineDoc(TextDoc(" ")) - - /** Renders as a new line unless it is discarded by a group, in which case behaves like `empty` */ - val linebreak: Doc = LineDoc(EmptyDoc) - - /** Behaves like `space` if the result fits in the page, otherwise behaves like `line` */ - val softline: Doc = group(line) - - /** Behaves like `empty` if the result fits in the page, otherwise behaves like `line` */ - val softbreak: Doc = group(linebreak) - - /** Behaves like a new line unless it is discarded by a group, in which case, behaves like the replacement document */ - def lineOr(replacement: Doc): Doc = LineDoc(replacement) - - /** Renders as an empty string */ - val empty: Doc = EmptyDoc - - /** Renders the document with nesting level set to the current column */ - def align(doc: Doc): Doc = AlignDoc(doc) - - /** Renders the document with nesting level set to the current column plus `indent` */ - def hang(indent: Int)(doc: Doc): Doc = align(nest(indent)(doc)) - - /** Renders the text as is. If it contains new lines, `string` should be used. */ - def text(s: String): Doc = { - if (s.isEmpty) { - EmptyDoc - } else { - TextDoc(s) - } - } - - /** Concatenates all characters, using `line` for new lines and `text` for other blocks */ - def string(s: String): Doc = { - if (s.contains("\n")) { - s.split("\n").foldRight(empty) { (word, doc) => - text(word) :|: doc - } - } else { - text(s) - } - } - - /** Splits the string into words and create a document for each word */ - def words(s: String): List[Doc] = { - s.split("\\s+").map(text).toList - } - - /** Creates a document from the given character */ - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def char(c: Char): Doc = { - if (c == '\n') { - line - } else { - TextDoc(c.toString) - } - } - - /** Creates a document from the given integer */ - def int(i: Int): Doc = TextDoc(i.toString) - - /** Creates a document from the given long */ - def long(l: Long): Doc = TextDoc(l.toString) - - /** Creates a document from the given float */ - def float(f: Float): Doc = TextDoc(f.toString) - - /** Creates a document from the given double */ - def double(d: Double): Doc = TextDoc(d.toString) - - /** Discards all line breaks in the given document if the result fits in the page, otherwise, renders without any changes */ - def group(doc: Doc): Doc = UnionDoc(doc.flatten, doc) - - /** Renders the document as usual, and then fills until `width` with spaces if necessary */ - def fill(until: Int)(doc: Doc): Doc = { - width { w => - if (w >= until) { - empty - } else { - text(" " * (until - w)) - } - }(doc) - } - - /** Renders a document followed by some other document computed depending on the current width */ - def width(f: Int => Doc)(doc: Doc): Doc = { - ColumnDoc(start => doc :: ColumnDoc(end => f(end - start))) - } - - /** Renders a document in which all documents in the collection are appended horizontally, separated by a `space` */ - def hsep(docs: TraversableLike[Doc, _]): Doc = { - docs.foldRight(empty)(_ :+: _) - } - - /** Renders a document in which all documents in the collection are appended vertically, separated by a `line` */ - def vsep(docs: TraversableLike[Doc, _]): Doc = { - docs.foldRight(empty)(_ :|: _) - } - - /** Renders a document in which all documents in the collection are appended vertically, separated by a `softline` */ - def fillSep(docs: TraversableLike[Doc, _]): Doc = { - docs.foldRight(empty)(_ :\: _) - } - - /** - * Renders a document that tries to append the documents in the collection - * horizontally separated by a `space` if it fits, otherwise append them - * vertically - */ - def sep(docs: TraversableLike[Doc, _]): Doc = { - group(vsep(docs)) - } - - /** Renders a document that appends the document in the collection horizontally */ - def hcat(docs: TraversableLike[Doc, _]): Doc = { - docs.foldRight(empty)(_ :: _) - } - - /** Renders a document that appends all documents in the collection vertically, separated by a `linebreak` */ - def vcat(docs: TraversableLike[Doc, _]): Doc = { - docs.foldRight(empty)(_ :||: _) - } - - /** Renders a document that appends all document in the collection horizontally, separated by a `softbreak` */ - def fillCat(docs: TraversableLike[Doc, _]): Doc = { - docs.foldRight(empty)(_ :\\: _) - } - - /** - * Renders a document that trie to append the documents in the collection - * horizontally, otherwise append them vertically - */ - def cat(docs: TraversableLike[Doc, _]): Doc = { - group(vcat(docs)) - } - - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def s2doc(s: String): Doc = string(s) - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def i2doc(i: Int): Doc = int(i) - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def l2doc(l: Long): Doc = long(l) - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def f2doc(f: Float): Doc = float(f) - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def d2doc(d: Double): Doc = double(d) - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def c2doc(c: Char): Doc = char(c) - - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def opt2doc[T](o: Option[T])(implicit ev1: T => Doc): Doc = { - o match { - case Some(d) => d - case None => empty - } - } - -} diff --git a/src/test/java/im/tox/core/random/RandomCore.scala b/src/test/java/im/tox/core/random/RandomCore.scala deleted file mode 100644 index adabb4619..000000000 --- a/src/test/java/im/tox/core/random/RandomCore.scala +++ /dev/null @@ -1,28 +0,0 @@ -package im.tox.core.random - -import java.nio.ByteBuffer -import im.tox.core.typesafe.Equals._ - -import org.jetbrains.annotations.NotNull - -object RandomCore { - - def entropy(@NotNull data: Seq[Byte]): Double = { - val frequencies = new Array[Int](-Byte.MinValue * 2) - for (b <- data) { - frequencies(Byte.MaxValue - b) += 1 - } - - val probabilities = - for (frequency <- frequencies) yield { - if (frequency =/= 0) { - val probability = frequency.toDouble / data.length - -probability * (Math.log(probability) / Math.log(-Byte.MinValue * 2)) - } else { - 0 - } - } - probabilities.sum - } - -} diff --git a/src/test/java/im/tox/core/random/RandomCoreTest.scala b/src/test/java/im/tox/core/random/RandomCoreTest.scala deleted file mode 100644 index 88eaa4951..000000000 --- a/src/test/java/im/tox/core/random/RandomCoreTest.scala +++ /dev/null @@ -1,40 +0,0 @@ -package im.tox.core.random - -import org.scalacheck.Gen -import org.scalatest.WordSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class RandomCoreTest extends WordSpec with ScalaCheckPropertyChecks { - - "entropy" should { - "be 0 for the empty sequence" in { - assert(RandomCore.entropy(Nil) == 0) - } - - "be 0 for sequences of all the same element" in { - forAll { (bytes: Seq[Byte], filler: Byte) => - assert(RandomCore.entropy(bytes.map(_ => filler)) == 0) - } - } - - "be near 1 for long sequences" in { - assert(RandomCore.entropy((0 to 1000).map(_.toByte)) > 0.999) - } - - "be 1 for sequences containing every byte equally often (maximum)" in { - forAll(Gen.choose(1, 10)) { count => - val bytes = (1 to count).flatMap(_ => (0 to 255).map(_.toByte)) - assert(RandomCore.entropy(bytes) == 1) - } - } - - "be sorting-insensitive (symmetry)" in { - forAll { (bytes: Seq[Byte]) => - assert(RandomCore.entropy(bytes) == RandomCore.entropy(bytes.sorted)) - assert(RandomCore.entropy(bytes) == RandomCore.entropy(bytes.reverse)) - } - } - } - -} diff --git a/src/test/java/im/tox/documentation/Doc.scala b/src/test/java/im/tox/documentation/Doc.scala deleted file mode 100644 index f545b6f4a..000000000 --- a/src/test/java/im/tox/documentation/Doc.scala +++ /dev/null @@ -1,10 +0,0 @@ -package im.tox.documentation - -sealed trait Doc { - def reference: String -} - -object Doc { - final case class Ref(reference: String) extends Doc - final case class Text(reference: String, body: String) extends Doc -} diff --git a/src/test/java/im/tox/documentation/Documented.scala b/src/test/java/im/tox/documentation/Documented.scala deleted file mode 100644 index 6d816a5c8..000000000 --- a/src/test/java/im/tox/documentation/Documented.scala +++ /dev/null @@ -1,45 +0,0 @@ -package im.tox.documentation - -/** - * Every package object with documentation must inherit from this class. - */ -abstract class Documented { - /** - * Create a reference to a Java/Scala type in the documentation. - * @tparam T The type to print in the documentation. - */ - protected final def ref[T](implicit evidence: Manifest[T]): Doc = { - Doc.Ref("[[" + evidence.runtimeClass.getSimpleName + "]]") - } - - /** - * Create a reference to a Scala singleton object. - * @tparam T The type of the object to be printed. - */ - protected final def ref[T](value: T): Doc = { - Doc.Ref("[[" + value.getClass.getSimpleName + "]]") - } - - /** - * Initialised to just the name of the package object. Any [[DocumentationInterpolator.doc]] calls in the actual - * package object will override this, but [[name]] will already be set then. - */ - private var document: Doc = Doc.Ref("[[" + getClass.getName.replace(".package$", "") + "]]") - - /** - * The name of the package object. Used to link to other package object documentation blocks. - */ - final val name: Doc = document - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - implicit class DocumentationInterpolator(val sc: StringContext) { - def doc(args: Doc*): Unit = { - val packageName = (new Throwable).getStackTrace()(1).getClassName - val documentation = sc.parts.zip(args.map(_.reference)).map { case (a, b) => a + b }.mkString.stripMargin - assert(document == name) - document = Doc.Text(packageName, documentation) - } - } - - final override def toString: String = document.toString -} diff --git a/src/test/java/im/tox/tox4j/ConnectedListener.scala b/src/test/java/im/tox/tox4j/ConnectedListener.scala deleted file mode 100644 index 0f747304c..000000000 --- a/src/test/java/im/tox/tox4j/ConnectedListener.scala +++ /dev/null @@ -1,18 +0,0 @@ -package im.tox.tox4j - -import im.tox.tox4j.core.callbacks.ToxCoreEventListener -import im.tox.tox4j.core.enums.ToxConnection -import org.jetbrains.annotations.NotNull - -final class ConnectedListener extends ToxCoreEventListener[Unit] { - - @NotNull private var value = ToxConnection.NONE - - override def selfConnectionStatus(@NotNull connectionStatus: ToxConnection)(state: Unit): Unit = { - value = connectionStatus - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def isConnected: Boolean = value != ToxConnection.NONE - -} diff --git a/src/test/java/im/tox/tox4j/DhtNode.scala b/src/test/java/im/tox/tox4j/DhtNode.scala deleted file mode 100644 index 1efbffb18..000000000 --- a/src/test/java/im/tox/tox4j/DhtNode.scala +++ /dev/null @@ -1,22 +0,0 @@ -package im.tox.tox4j - -import im.tox.core.network.Port -import im.tox.tox4j.core.data.ToxPublicKey -import im.tox.tox4j.testing.GetDisjunction._ - -final case class DhtNode(ipv4: String, ipv6: String, udpPort: Port, tcpPort: Port, dhtId: ToxPublicKey) { - - def this(ipv4: String, ipv6: String, udpPort: Int, tcpPort: Int, dhtId: String) { - this( - ipv4, ipv6, - Port.unsafeFromInt(udpPort), - Port.unsafeFromInt(tcpPort), - ToxPublicKey.fromValue(ToxCoreTestBase.parsePublicKey(dhtId)).toOption.get - ) - } - - def this(ipv4: String, ipv6: String, port: Int, dhtId: String) { - this(ipv4, ipv6, port, port, dhtId) - } - -} diff --git a/src/test/java/im/tox/tox4j/DhtNodeSelector.scala b/src/test/java/im/tox/tox4j/DhtNodeSelector.scala deleted file mode 100644 index 4fbbd2136..000000000 --- a/src/test/java/im/tox/tox4j/DhtNodeSelector.scala +++ /dev/null @@ -1,90 +0,0 @@ -package im.tox.tox4j - -import java.io.IOException -import java.net.{ InetAddress, Socket } - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.impl.jni.ToxCoreImplFactory -import org.scalatest.Assertions -import org.slf4j.LoggerFactory - -object DhtNodeSelector extends Assertions { - - private val logger = Logger(LoggerFactory.getLogger(this.getClass)) - private var selectedNode: Option[DhtNode] = Some(ToxCoreTestBase.nodeCandidates(0)) - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - private def tryConnect(node: DhtNode): Option[DhtNode] = { - var socket: Socket = null - try { - socket = new Socket(InetAddress.getByName(node.ipv4), node.udpPort.value) - assume(socket.getInputStream != null) - Some(node) - } catch { - case e: IOException => - logger.info(s"TCP connection failed (${e.getMessage})") - None - } finally { - if (socket != null) { - socket.close() - } - } - } - - private def tryBootstrap( - withTox: (Boolean, Boolean) => (ToxCore => Option[DhtNode]) => Option[DhtNode], - node: DhtNode, - udpEnabled: Boolean - ): Option[DhtNode] = { - val protocol = if (udpEnabled) "UDP" else "TCP" - val port = if (udpEnabled) node.udpPort else node.tcpPort - logger.info(s"Trying to bootstrap with ${node.ipv4}:$port using $protocol") - - withTox(true, udpEnabled) { tox => - val status = new ConnectedListener - if (!udpEnabled) { - tox.addTcpRelay(node.ipv4, port, node.dhtId) - } - tox.bootstrap(node.ipv4, port, node.dhtId) - - // Try bootstrapping for 10 seconds. - (0 to 10000 / tox.iterationInterval) find { _ => - tox.iterate(status)(()) - Thread.sleep(tox.iterationInterval) - status.isConnected - } match { - case Some(time) => - logger.info(s"Bootstrapped successfully after ${time * tox.iterationInterval}ms using $protocol") - Some(node) - case None => - logger.info(s"Unable to bootstrap with $protocol") - None - } - } - } - - private def findNode(withTox: (Boolean, Boolean) => (ToxCore => Option[DhtNode]) => Option[DhtNode]): DhtNode = { - DhtNodeSelector.selectedNode match { - case Some(node) => node - case None => - logger.info("Looking for a working bootstrap node") - - DhtNodeSelector.selectedNode = ToxCoreTestBase.nodeCandidates find { node => - logger.info(s"Trying to establish a TCP connection to ${node.ipv4}") - - (for { - node <- tryConnect(node) - node <- tryBootstrap(withTox, node, udpEnabled = true) - node <- tryBootstrap(withTox, node, udpEnabled = false) - } yield node).isDefined - } - - assume(DhtNodeSelector.selectedNode.nonEmpty, "No viable nodes for bootstrap found; cannot test") - DhtNodeSelector.selectedNode.get - } - } - - def node: DhtNode = findNode(ToxCoreImplFactory.withToxUnit[Option[DhtNode]]) - -} diff --git a/src/test/java/im/tox/tox4j/SocksServer.scala b/src/test/java/im/tox/tox4j/SocksServer.scala deleted file mode 100644 index 457f18269..000000000 --- a/src/test/java/im/tox/tox4j/SocksServer.scala +++ /dev/null @@ -1,245 +0,0 @@ -package im.tox.tox4j - -import java.io.{ Closeable, IOException, InputStream, OutputStream } -import java.net.{ InetAddress, ServerSocket, Socket } - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.SocksServer.{ FirstPort, LastPort, logger } -import org.jetbrains.annotations.NotNull -import org.scalatest.Assertions -import org.slf4j.LoggerFactory - -import scala.collection.mutable.ArrayBuffer - -object SocksServer { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - private val FirstPort = 8000 - private val LastPort = 8999 - - /** - * Spawn a proxy server in a thread and pass it to the function. - * - * @param function The function to call ot the { @link SocksServer} object. - * @tparam R The return type of the function. - * @return The result of calling the function on the server object. - */ - @throws[IOException]("When a network error occurs") - @throws[InterruptedException]("On unexpected thread interrupts") - def withServer[R](function: SocksServer => R): R = { - val server = new SocksServer - val thread = new Thread(server) - thread.start() - try { - function.apply(server) - } finally { - server.close() - thread.join() - } - } - -} - -/** - * Create a simple SOCKS5 server on a port between [[FirstPort]] and [[LastPort]]. - */ -@SuppressWarnings(Array( - "org.wartremover.warts.Equals", - "org.wartremover.warts.While" -)) -@throws[IOException]("If no port could be bound") -final class SocksServer extends Closeable with Runnable with Assertions { - - private val server = connectAvailablePort() - private val threads = new ArrayBuffer[Thread] - private val sockets = new ArrayBuffer[Socket] - private var running = true - private var accepted = 0 - - @throws[IOException] - private def connectAvailablePort(): ServerSocket = { - var lastException: IOException = null - var socket: ServerSocket = null - - (FirstPort to LastPort) find { port => - try { - socket = new ServerSocket(port) - true - } catch { - case e: IOException => - lastException = e - false - } - } - - if (lastException != null) { - throw lastException - } - - socket - } - - @throws[IOException] - override def close(): Unit = { - running = false - server.close() - } - - def getPort: Int = server.getLocalPort - def getAddress: String = server.getInetAddress.getHostAddress - - override def run(): Unit = { - try { - while (running) { - val socket = server.accept - sockets += socket - val thread = new Thread(new Runnable() { - def run(): Unit = { - val input = socket.getInputStream - val output = socket.getOutputStream - try { - greeting(input, output) - } catch { - case e: IOException => - logger.error("Exception", e) - } finally { - if (input != null) input.close() - if (output != null) output.close() - } - } - }) - thread.start() - threads += thread - accepted += 1 - } - } catch { - case abort: IOException => - running = false - try { - server.close() - sockets.foreach(_.close()) - threads.foreach(_.join()) - } catch { - case error: Throwable => - logger.error("Exception", error) - } - } - } - - private def greeting(input: InputStream, output: OutputStream): Unit = { - assert(input.read == 0x05) - val numAuthenticationMethods = input.read - val authenticationMethods = new Array[Int](numAuthenticationMethods) - for (i <- 0 until numAuthenticationMethods) { - authenticationMethods(i) = input.read - } - - if (!authenticationMethods.contains(0x00)) { - throw new IOException("Client did not support any of our authentication methods") - } - - output.write(0x05) - output.write(0x00) - connection(input, output) - } - - private def connection(@NotNull input: InputStream, @NotNull output: OutputStream): Unit = { - assert(input.read == 0x05) - val command = input.read - assert(input.read == 0x00) - - val address = - input.read match { - case 0x01 => - val address4 = new Array[Byte](4) - assert(input.read(address4) == address4.length) - InetAddress.getByAddress(address4) - case 0x03 => - val domain = new Array[Byte](input.read) - assert(input.read(domain) == domain.length) - InetAddress.getByName(new String(domain)) - case 0x04 => - val address6 = new Array[Byte](16) - assert(input.read(address6) == address6.length) - InetAddress.getByAddress(address6) - case _ => - throw new IOException("Unsupported address type") - } - - val portBytes = new Array[Byte](2) - assert(input.read(portBytes) == 2) - val port = ((portBytes(0) & 0xff) << 8) | (portBytes(1) & 0xff) - - command match { - case 0x01 => establishStream(input, output, address, port) - case 0x02 => throw new IOException("TCP/IP port binding not supported") - case 0x03 => throw new IOException("Associating UDP port not supported") - case _ => throw new IOException(s"Unknown command: $command") - } - } - - private def establishStream(input: InputStream, output: OutputStream, address: InetAddress, port: Int): Unit = { - output.write(0x05) - try { - val target = new Socket(address, port) - sockets += target - pipeStream(input, output, address, port, target) - } catch { - case e: IOException if e.getMessage == "Network is unreachable" => - output.write(0x03) - } - } - - private def pipeStream(input: InputStream, output: OutputStream, address: InetAddress, port: Int, target: Socket): Unit = { - output.write(0x00) - output.write(0x00) - - val addressBytes = address.getAddress - if (addressBytes.length == 4) { - output.write(0x01) - } else { - assert(addressBytes.length == 16) - output.write(0x04) - } - - output.write(addressBytes) - output.write(port >> 8) - output.write(port & 0xff) - - val targetInput = target.getInputStream - val targetOutput = target.getOutputStream - - val inputThread = new Thread(new Runnable() { - def run(): Unit = { - try { - var inByte = targetInput.read - while (inByte != -1) { - output.write(inByte) - inByte = targetInput.read - } - } catch { - case ignored: IOException => - } - } - }) - - inputThread.start() - threads += inputThread - - try { - var outByte = input.read - while (outByte != -1) { - targetOutput.write(outByte) - outByte = input.read - } - } catch { - case ignored: IOException => - } - } - - def getAccepted: Int = { - accepted - } - -} diff --git a/src/test/java/im/tox/tox4j/TestConstants.scala b/src/test/java/im/tox/tox4j/TestConstants.scala deleted file mode 100644 index ab7f630d4..000000000 --- a/src/test/java/im/tox/tox4j/TestConstants.scala +++ /dev/null @@ -1,11 +0,0 @@ -package im.tox.tox4j - -import org.scalatest.time.SpanSugar._ - -import scala.language.postfixOps - -@SuppressWarnings(Array("org.wartremover.warts.PublicInference")) -object TestConstants { - val Timeout = 60 seconds - val Iterations = 100 -} diff --git a/src/test/java/im/tox/tox4j/ToxCoreTestBase.scala b/src/test/java/im/tox/tox4j/ToxCoreTestBase.scala deleted file mode 100644 index 333f1f494..000000000 --- a/src/test/java/im/tox/tox4j/ToxCoreTestBase.scala +++ /dev/null @@ -1,87 +0,0 @@ -package im.tox.tox4j - -import java.io.IOException -import java.net.{ InetAddress, Socket } -import java.util.Random - -import org.jetbrains.annotations.NotNull -import org.scalatest.Assertions - -object ToxCoreTestBase extends Assertions { - - private[tox4j] val nodeCandidates = Seq( - new DhtNode("tox.initramfs.io", "tox.initramfs.io", 33445, "3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25"), - new DhtNode("tox.verdict.gg", null, 33445, "1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976") - ) - - @NotNull def randomBytes(length: Int): Array[Byte] = { - val array = new Array[Byte](length) - new Random().nextBytes(array) - array - } - - @NotNull - def readablePublicKey(@NotNull id: Array[Byte]): String = { - val str = new StringBuilder - id foreach { c => str.append(f"$c%02X") } - str.toString() - } - - @NotNull - def parsePublicKey(@NotNull id: String): Array[Byte] = { - val publicKey = new Array[Byte](id.length / 2) - publicKey.indices foreach { i => - publicKey(i) = - ((fromHexDigit(id.charAt(i * 2)) << 4) + - fromHexDigit(id.charAt(i * 2 + 1))).toByte - } - publicKey - } - - private def fromHexDigit(c: Char): Byte = { - val digit = - if (false) { 0 } - else if ('0' to '9' contains c) { c - '0' } - else if ('A' to 'F' contains c) { c - 'A' + 10 } - else if ('a' to 'f' contains c) { c - 'a' + 10 } - else { throw new IllegalArgumentException(s"Non-hex digit character: $c") } - digit.toByte - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - private def hasConnection(ip: String, port: Int): Option[String] = { - var socket: Socket = null - try { - socket = new Socket(InetAddress.getByName(ip), port) - if (socket.getInputStream == null) { - Some("Socket input stream is null") - } else { - None - } - } catch { - case e: IOException => - Some(s"A network connection can't be established to $ip:$port: ${e.getMessage}") - } finally { - if (socket != null) { - socket.close() - } - } - } - - def checkIPv4: Option[String] = { - hasConnection("8.8.8.8", 53) - } - - def checkIPv6: Option[String] = { - hasConnection("2001:4860:4860::8888", 53) - } - - protected[tox4j] def assumeIPv4(): Unit = { - assume(checkIPv4.isEmpty) - } - - protected[tox4j] def assumeIPv6(): Unit = { - assume(checkIPv6.isEmpty) - } - -} diff --git a/src/test/java/im/tox/tox4j/ToxTest.scala b/src/test/java/im/tox/tox4j/ToxTest.scala deleted file mode 100644 index 64302a965..000000000 --- a/src/test/java/im/tox/tox4j/ToxTest.scala +++ /dev/null @@ -1,402 +0,0 @@ -package im.tox.tox4j - -import im.tox.core.network.Port -import im.tox.core.random.RandomCore -import im.tox.tox4j.TestConstants.Iterations -import im.tox.tox4j.core._ -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.enums.ToxUserStatus -import im.tox.tox4j.core.options.{ ProxyOptions, ToxOptions } -import im.tox.tox4j.impl.jni.ToxCoreImplFactory -import im.tox.tox4j.impl.jni.ToxCoreImplFactory.withToxUnit -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class ToxCoreTest extends FunSuite with ToxTestMixin { - - val publicKey: ToxPublicKey = ToxPublicKey.fromValue(Array.ofDim(ToxCoreConstants.PublicKeySize)).toOption.get - val eventListener: ToxCoreEventAdapter[Unit] = new ToxCoreEventAdapter[Unit] - - test("ToxNew") { - withToxUnit(ToxOptions()) { _ => } - } - - test("ToxNew00") { - withToxUnit(ipv6Enabled = false, udpEnabled = false) { _ => } - } - - test("ToxNew01") { - withToxUnit(ipv6Enabled = false, udpEnabled = true) { _ => } - } - - test("ToxNew10") { - withToxUnit(ipv6Enabled = true, udpEnabled = false) { _ => } - } - - test("ToxNew11") { - withToxUnit(ipv6Enabled = true, udpEnabled = true) { _ => } - } - - test("ToxNewProxyGood") { - withToxUnit(ipv6Enabled = true, udpEnabled = true, ProxyOptions.Socks5("localhost", 1)) { _ => } - withToxUnit(ipv6Enabled = true, udpEnabled = true, ProxyOptions.Socks5("localhost", 0xffff)) { _ => } - } - - test("ToxCreationAndImmediateDestruction") { - (0 until Iterations) foreach { _ => withToxUnit { _ => } } - } - - test("ToxCreationAndDelayedDestruction") { - ToxCoreImplFactory.withToxes(30) { _ => } - } - - test("DoubleClose") { - withToxUnit(_.close()) - } - - /* - * Can't easily test this anymore, because bootstrap now actually reports - * success/failure immediately. - */ - /* - test("BootstrapBorderlinePort1") { - withToxUnit { tox => - tox.bootstrap( - DhtNodeSelector.node.ipv4, - Port.fromInt(1).get, - publicKey - ) - } - } - - test("BootstrapBorderlinePort2") { - withToxUnit { tox => - tox.bootstrap( - DhtNodeSelector.node.ipv4, - Port.fromInt(65535).get, - publicKey - ) - } - } - */ - - test("IterationInterval") { - withToxUnit { tox => - assert(tox.iterationInterval > 0) - assert(tox.iterationInterval <= 50) - } - } - - test("Close") { - withToxUnit { _ => } - } - - test("Iteration") { - withToxUnit(_.iterate(eventListener)(())) - } - - test("GetPublicKey") { - withToxUnit { tox => - val id = tox.getPublicKey - assert(id.value.length == ToxCoreConstants.PublicKeySize) - assert(tox.getPublicKey.value sameElements id.value) - } - } - - test("GetSecretKey") { - withToxUnit { tox => - val key = tox.getSecretKey - assert(key.value.length == ToxCoreConstants.SecretKeySize) - assert(tox.getSecretKey.value sameElements key.value) - } - } - - test("PublicKeyEntropy") { - withToxUnit { tox => - val entropy = RandomCore.entropy(tox.getPublicKey.value) - assert(entropy >= 0.5, s"Entropy of public key should be >= 0.5, but was $entropy") - } - } - - test("SecretKeyEntropy") { - withToxUnit { tox => - val entropy = RandomCore.entropy(tox.getSecretKey.value) - assert(entropy >= 0.5, s"Entropy of secret key should be >= 0.5, but was $entropy") - } - } - - test("GetAddress") { - withToxUnit { tox => - assert(tox.getAddress.value.length == ToxCoreConstants.AddressSize) - assert(tox.getAddress.value sameElements tox.getAddress.value) - } - } - - test("NoSpam") { - val tests = Array(0x12345678, 0xffffffff, 0x00000000, 0x00000001, 0xfffffffe, 0x7fffffff) - withToxUnit { tox => - assert(tox.getNospam == tox.getNospam) - for (test <- tests) { - tox.setNospam(test) - assert(tox.getNospam == test) - assert(tox.getNospam == tox.getNospam) - val check = Array( - (test >> 8 * 3).toByte, - (test >> 8 * 2).toByte, - (test >> 8 * 1).toByte, - (test >> 8 * 0).toByte - ) - val nospam: Array[Byte] = tox.getAddress.value.slice(ToxCoreConstants.PublicKeySize, ToxCoreConstants.PublicKeySize + 4) - assert(nospam sameElements check) - } - } - } - - test("GetAndSetName") { - withToxUnit { tox => - assert(tox.getName.value.isEmpty) - tox.setName(ToxNickname.fromString("myname").toOption.get) - assert(new String(tox.getName.value) == "myname") - } - } - - test("SetNameMinSize") { - withToxUnit { tox => - val array = ToxCoreTestBase.randomBytes(1) - tox.setName(ToxNickname.fromValue(array).toOption.get) - assert(tox.getName.value sameElements array) - } - } - - test("SetNameMaxSize") { - withToxUnit { tox => - val array = ToxCoreTestBase.randomBytes(ToxCoreConstants.MaxNameLength) - tox.setName(ToxNickname.fromValue(array).toOption.get) - assert(tox.getName.value sameElements array) - } - } - - test("SetNameExhaustive") { - withToxUnit { tox => - (1 to ToxCoreConstants.MaxNameLength) foreach { i => - val array = ToxCoreTestBase.randomBytes(i) - tox.setName(ToxNickname.fromValue(array).toOption.get) - assert(tox.getName.value sameElements array) - } - } - } - - test("UnsetName") { - withToxUnit { tox => - assert(tox.getName.value.isEmpty) - tox.setName(ToxNickname.fromString("myname").toOption.get) - assert(tox.getName.value.nonEmpty) - tox.setName(ToxNickname.fromString("").toOption.get) - assert(tox.getName.value.isEmpty) - } - } - - test("GetAndSetStatusMessage") { - withToxUnit { tox => - assert(tox.getStatusMessage.value.isEmpty) - tox.setStatusMessage(ToxStatusMessage.fromString("message").toOption.get) - assert(new String(tox.getStatusMessage.value) == "message") - } - } - - test("SetStatusMessageMinSize") { - withToxUnit { tox => - val array = ToxCoreTestBase.randomBytes(1) - tox.setStatusMessage(ToxStatusMessage.fromValue(array).toOption.get) - assert(tox.getStatusMessage.value sameElements array) - } - } - - test("SetStatusMessageMaxSize") { - withToxUnit { tox => - val array = ToxCoreTestBase.randomBytes(ToxCoreConstants.MaxStatusMessageLength) - tox.setStatusMessage(ToxStatusMessage.fromValue(array).toOption.get) - assert(tox.getStatusMessage.value sameElements array) - } - } - - test("SetStatusMessageExhaustive") { - withToxUnit { tox => - (1 to ToxCoreConstants.MaxStatusMessageLength) foreach { i => - val array = ToxCoreTestBase.randomBytes(i) - tox.setStatusMessage(ToxStatusMessage.fromValue(array).toOption.get) - assert(tox.getStatusMessage.value sameElements array) - } - } - } - - test("UnsetStatusMessage") { - withToxUnit { tox => - assert(tox.getStatusMessage.value.isEmpty) - tox.setStatusMessage(ToxStatusMessage.fromString("message").toOption.get) - assert(tox.getStatusMessage.value.nonEmpty) - tox.setStatusMessage(ToxStatusMessage.fromString("").toOption.get) - assert(tox.getStatusMessage.value.isEmpty) - } - } - - test("GetAndSetStatus") { - withToxUnit { tox => - assert(tox.getStatus == ToxUserStatus.NONE) - ToxUserStatus.values.foreach { status => - tox.setStatus(status) - assert(tox.getStatus == status) - } - } - } - - test("AddFriend") { - withToxUnit { tox => - (0 until Iterations).map(ToxFriendNumber.fromInt(_).get) foreach { i => - withToxUnit { friend => - val friendNumber = tox.addFriend( - friend.getAddress, - ToxFriendRequestMessage.fromString("heyo").toOption.get - ) - assert(friendNumber == i) - } - } - assert(tox.getFriendList.length == Iterations) - } - } - - test("AddFriendNoRequest") { - withToxUnit { tox => - (0 until Iterations).map(ToxFriendNumber.fromInt(_).get) foreach { i => - withToxUnit { friend => - val friendNumber = tox.addFriendNorequest(friend.getPublicKey) - assert(friendNumber == i) - } - } - assert(tox.getFriendList.length == Iterations) - } - } - - test("FriendListSize") { - withToxUnit { tox => - addFriends(tox, Iterations) - assert(tox.getFriendList.length == Iterations) - } - } - - test("FriendList") { - withToxUnit { tox => - addFriends(tox, 5) - assert(tox.getFriendList sameElements Array(0, 1, 2, 3, 4)) - } - } - - test("FriendList_Empty") { - withToxUnit { tox => - assert(tox.getFriendList.isEmpty) - } - } - - test("DeleteAndReAddFriend") { - withToxUnit { tox => - addFriends(tox, 5) - assert(tox.getFriendList sameElements Array[Int](0, 1, 2, 3, 4)) - tox.deleteFriend(ToxFriendNumber.fromInt(2).get) - assert(tox.getFriendList sameElements Array[Int](0, 1, 3, 4)) - tox.deleteFriend(ToxFriendNumber.fromInt(3).get) - assert(tox.getFriendList sameElements Array[Int](0, 1, 4)) - addFriends(tox, 1) - assert(tox.getFriendList sameElements Array[Int](0, 1, 2, 4)) - addFriends(tox, 1) - assert(tox.getFriendList sameElements Array[Int](0, 1, 2, 3, 4)) - } - } - - test("FriendExists") { - withToxUnit { tox => - addFriends(tox, 3) - assert(tox.friendExists(ToxFriendNumber.fromInt(0).get)) - assert(tox.friendExists(ToxFriendNumber.fromInt(1).get)) - assert(tox.friendExists(ToxFriendNumber.fromInt(2).get)) - assert(!tox.friendExists(ToxFriendNumber.fromInt(3).get)) - assert(!tox.friendExists(ToxFriendNumber.fromInt(4).get)) - } - } - - test("FriendExists2") { - withToxUnit { tox => - addFriends(tox, 3) - assert(tox.friendExists(ToxFriendNumber.fromInt(0).get)) - assert(tox.friendExists(ToxFriendNumber.fromInt(1).get)) - assert(tox.friendExists(ToxFriendNumber.fromInt(2).get)) - tox.deleteFriend(ToxFriendNumber.fromInt(1).get) - assert(tox.friendExists(ToxFriendNumber.fromInt(0).get)) - assert(!tox.friendExists(ToxFriendNumber.fromInt(1).get)) - assert(tox.friendExists(ToxFriendNumber.fromInt(2).get)) - } - } - - test("GetFriendPublicKey") { - withToxUnit { tox => - addFriends(tox, 1) - val friendNumber = ToxFriendNumber.fromInt(0).get - assert(tox.getFriendPublicKey(friendNumber).value.length == ToxCoreConstants.PublicKeySize) - assert(tox.getFriendPublicKey(friendNumber).value sameElements tox.getFriendPublicKey(friendNumber).value) - val entropy = RandomCore.entropy(tox.getFriendPublicKey(friendNumber).value) - assert(entropy >= 0.5, s"Entropy of friend's public key should be >= 0.5, but was $entropy") - } - } - - test("GetFriendByPublicKey") { - withToxUnit { tox => - addFriends(tox, 10) - (0 until 10).map(ToxFriendNumber.fromInt(_).get) foreach { i => - assert(tox.friendByPublicKey(tox.getFriendPublicKey(i)) == i) - } - } - } - - test("SetTyping") { - withToxUnit { tox => - addFriends(tox, 1) - val friendNumber = ToxFriendNumber.fromInt(0).get - tox.setTyping(friendNumber, false) - tox.setTyping(friendNumber, true) - tox.setTyping(friendNumber, false) - tox.setTyping(friendNumber, false) - tox.setTyping(friendNumber, true) - tox.setTyping(friendNumber, true) - } - } - - test("GetUdpPort") { - withToxUnit { tox => - assert(tox.getUdpPort.value > 0) - assert(tox.getUdpPort.value <= 65535) - } - } - - test("GetTcpPort") { - withToxUnit(ToxOptions(tcpPort = 33444)) { tox => - assert(tox.getTcpPort.value == 33444) - } - } - - test("GetDhtId") { - withToxUnit { tox => - val key = tox.getDhtId - assert(key.value.length == ToxCoreConstants.PublicKeySize) - assert(tox.getDhtId.value sameElements key.value) - } - } - - test("DhtIdEntropy") { - withToxUnit { tox => - val entropy = RandomCore.entropy(tox.getDhtId.value) - assert(entropy >= 0.5, s"Entropy of public key should be >= 0.5, but was $entropy") - } - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/AvInvokeTest.scala b/src/test/java/im/tox/tox4j/av/callbacks/AvInvokeTest.scala deleted file mode 100644 index 96ee06dba..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/AvInvokeTest.scala +++ /dev/null @@ -1,179 +0,0 @@ -package im.tox.tox4j.av.callbacks - -import java.util - -import im.tox.tox4j.av.callbacks.AvInvokeTest._ -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.ToxavFriendCallState -import im.tox.tox4j.core.SmallNat -import im.tox.tox4j.core.callbacks.InvokeTest.{ ByteArray, ShortArray } -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.impl.jni.{ ToxAvImpl, ToxCoreImpl } -import org.scalacheck.Arbitrary.arbitrary -import org.scalacheck.{ Arbitrary, Gen } -import org.scalatest.FunSuite -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -import scala.collection.JavaConverters._ -import scala.language.implicitConversions -import scala.util.Random - -final class AvInvokeTest extends FunSuite with ScalaCheckPropertyChecks { - - final class TestEventListener extends ToxAvEventListener[Option[Event]] { - private def setEvent(event: Event)(state: Option[Event]): Option[Event] = { - assert(state.isEmpty) - Some(event) - } - - // scalastyle:off line.size.limit - override def audioReceiveFrame(friendNumber: ToxFriendNumber, pcm: Array[Short], channels: AudioChannels, samplingRate: SamplingRate)(state: Option[Event]): Option[Event] = setEvent(AudioReceiveFrame(friendNumber, pcm, channels, samplingRate))(state) - override def audioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate)(state: Option[Event]): Option[Event] = setEvent(AudioBitRate(friendNumber, audioBitRate))(state) - override def videoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate)(state: Option[Event]): Option[Event] = setEvent(VideoBitRate(friendNumber, videoBitRate))(state) - override def call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean)(state: Option[Event]): Option[Event] = setEvent(Call(friendNumber, audioEnabled, videoEnabled))(state) - override def callState(friendNumber: ToxFriendNumber, callState: util.EnumSet[ToxavFriendCallState])(state: Option[Event]): Option[Event] = setEvent(CallState(friendNumber, callState.asScala.toSet))(state) - override def videoReceiveFrame(friendNumber: ToxFriendNumber, width: Width, height: Height, y: Array[Byte], u: Array[Byte], v: Array[Byte], yStride: Int, uStride: Int, vStride: Int)(state: Option[Event]): Option[Event] = setEvent(VideoReceiveFrame(friendNumber, width, height, y, u, v, yStride, uStride, vStride))(state) - // scalastyle:on line.size.limit - } - - def callbackTest(invoke: ToxAvEventSynth => Unit, expected: Event): Unit = { - val tox = new ToxCoreImpl(ToxOptions()) - val toxav = new ToxAvImpl(tox) - - try { - invoke(toxav) - val listener = new TestEventListener - val event = toxav.iterate(listener)(None) - assert(event.contains(expected)) - } finally { - toxav.close() - tox.close() - } - } - - private val random = new Random - - private implicit val arbToxFriendNumber: Arbitrary[ToxFriendNumber] = { - Arbitrary(arbitrary[Int].map(ToxFriendNumber.unsafeFromInt)) - } - - private implicit val arbToxavFriendCallState: Arbitrary[ToxavFriendCallState] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map { i => ToxavFriendCallState.values()(Math.abs(i % ToxavFriendCallState.values().length)) }) - } - - private implicit val arbWidth: Arbitrary[Width] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map(Width.clamp)) - } - - private implicit val arbHeight: Arbitrary[Height] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map(Height.clamp)) - } - - private implicit val arbSamplingRate: Arbitrary[SamplingRate] = { - Arbitrary(Gen.oneOf( - SamplingRate.Rate8k, - SamplingRate.Rate12k, - SamplingRate.Rate16k, - SamplingRate.Rate24k, - SamplingRate.Rate48k - )) - } - - private implicit val arbBitRate: Arbitrary[BitRate] = { - Arbitrary(Gen.oneOf( - Gen.const(BitRate.Unchanged), - Gen.const(BitRate.Disabled), - arbitrary[Int].map(BitRate.fromInt).map(_.getOrElse(BitRate.Unchanged)) - )) - } - - test("Max friend number") { - val friendNumber = ToxFriendNumber.unsafeFromInt(Int.MaxValue) - val pcm = Array.fill(100)(1234.toShort) - callbackTest( - _.invokeAudioReceiveFrame(friendNumber, pcm, AudioChannels.Mono, SamplingRate.Rate24k), - AudioReceiveFrame(friendNumber, pcm, AudioChannels.Mono, SamplingRate.Rate24k) - ) - } - - test("AudioReceiveFrame") { - forAll { (friendNumber: ToxFriendNumber, pcm: Array[Short], samplingRate: SamplingRate) => - val channels = - pcm.length match { - case length if length % 2 === 0 => AudioChannels.Stereo - case length => AudioChannels.Mono - } - callbackTest( - _.invokeAudioReceiveFrame(friendNumber, pcm, channels, samplingRate), - AudioReceiveFrame(friendNumber, pcm, channels, samplingRate) - ) - } - } - - test("AudioBitRate") { - forAll { (friendNumber: ToxFriendNumber, audioBitRate: BitRate) => - callbackTest( - _.invokeAudioBitRate(friendNumber, audioBitRate), - AudioBitRate(friendNumber, audioBitRate) - ) - } - } - - test("VideoBitRate") { - forAll { (friendNumber: ToxFriendNumber, videoBitRate: BitRate) => - callbackTest( - _.invokeVideoBitRate(friendNumber, videoBitRate), - VideoBitRate(friendNumber, videoBitRate) - ) - } - } - - test("Call") { - forAll { (friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean) => - callbackTest( - _.invokeCall(friendNumber, audioEnabled, videoEnabled), - Call(friendNumber, audioEnabled, videoEnabled) - ) - } - } - - test("CallState") { - forAll { (friendNumber: ToxFriendNumber, callState: Set[ToxavFriendCallState]) => - whenever(callState.nonEmpty) { - callbackTest( - _.invokeCallState(friendNumber, util.EnumSet.copyOf(callState.asJavaCollection)), - CallState(friendNumber, callState) - ) - } - } - } - - test("VideoReceiveFrame") { - forAll { (friendNumber: ToxFriendNumber, width: Width, height: Height, yStride: SmallNat, uStride: SmallNat, vStride: SmallNat) => - val w = width.value - val h = height.value - val y = Array.ofDim[Byte]((w max yStride) * h) - val u = Array.ofDim[Byte](((w / 2) max Math.abs(uStride)) * (h / 2)) - val v = Array.ofDim[Byte](((w / 2) max Math.abs(vStride)) * (h / 2)) - random.nextBytes(y) - random.nextBytes(u) - random.nextBytes(v) - callbackTest( - _.invokeVideoReceiveFrame(friendNumber, width, height, y, u, v, yStride, uStride, vStride), - VideoReceiveFrame(friendNumber, width, height, y, u, v, yStride, uStride, vStride) - ) - } - } - -} - -object AvInvokeTest { - sealed trait Event - private final case class AudioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate) extends Event - private final case class AudioReceiveFrame(friendNumber: ToxFriendNumber, pcm: ShortArray, channels: AudioChannels, samplingRate: SamplingRate) extends Event - private final case class Call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean) extends Event - private final case class CallState(friendNumber: ToxFriendNumber, callState: Set[ToxavFriendCallState]) extends Event - private final case class VideoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate) extends Event - private final case class VideoReceiveFrame(friendNumber: ToxFriendNumber, width: Width, height: Height, y: ByteArray, u: ByteArray, v: ByteArray, yStride: Int, uStride: Int, vStride: Int) extends Event // scalastyle:ignore line.size.limit -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/CallCallbackTest.scala b/src/test/java/im/tox/tox4j/av/callbacks/CallCallbackTest.scala deleted file mode 100644 index b47c841a9..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/CallCallbackTest.scala +++ /dev/null @@ -1,66 +0,0 @@ -package im.tox.tox4j.av.callbacks - -import im.tox.tox4j.av.data.BitRate -import im.tox.tox4j.av.exceptions.ToxavCallException -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendMessage } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxMessageType } -import im.tox.tox4j.testing.GetDisjunction._ -import im.tox.tox4j.testing.ToxExceptionChecks -import im.tox.tox4j.testing.autotest.AutoTestSuite - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class CallCallbackTest extends AutoTestSuite with ToxExceptionChecks { - - type S = Unit - - object Handler extends EventListener(()) { - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connectionStatus: ToxConnection - )(state0: State): State = { - val state = super.friendConnectionStatus(friendNumber, connectionStatus)(state0) - - if (connectionStatus == ToxConnection.NONE || state.id(friendNumber) != state.id.next) { - state - } else { - // Call id+1. - state.addTask { (tox, av, state) => - av.call(friendNumber, BitRate.fromInt(10).get, BitRate.Disabled) - state - } - } - } - - override def call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean)(state: State): State = { - if (state.id(friendNumber) == state.id.prev) { - state.addTask { (tox, av, state) => - // Calling them back while they are ringing is invalid. - intercept(ToxavCallException.Code.FRIEND_ALREADY_IN_CALL) { - av.call(friendNumber, BitRate.fromInt(10).get, BitRate.Disabled) - } - // Say "No thanks" and stop talking. - debug(state, s"Got a call from ${state.id(friendNumber)}; rejecting") - tox.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, ToxFriendMessage.fromString("No thanks").toOption.get) - state.finish - } - } else { - fail(s"I shouldn't have been called by friend ${state.id(friendNumber)}") - state - } - } - - override def friendMessage( - friendNumber: ToxFriendNumber, - messageType: ToxMessageType, - timeDelta: Int, - message: ToxFriendMessage - )(state: State): State = { - assert(message.toString == "No thanks") - debug(state, "Aww... I got rejected :( oh well") - state.finish - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerator.scala b/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerator.scala deleted file mode 100644 index 7a194ad08..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerator.scala +++ /dev/null @@ -1,76 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import im.tox.tox4j.av.data.{ SampleCount, AudioLength, SamplingRate } - -abstract class AudioGenerator { - - def productPrefix: String - - def length(samplingRate: SamplingRate): Int - def sample(samplingRate: SamplingRate, t: Int): Int - - @SuppressWarnings(Array("org.wartremover.warts.While")) - final def nextFrame16(audioLength: AudioLength, samplingRate: SamplingRate, t: Int): Array[Short] = { - val frame = Array.ofDim[Short](SampleCount(audioLength, samplingRate).value) - var i = 0 - while (i < frame.length) { - frame(i) = sample(samplingRate, t + i).toShort - i += 1 - } - frame - } - -} - -object AudioGenerator { - - sealed abstract class Oscillator { - - protected def f(x: Double): Double - - final def osc(samplingRate: SamplingRate, t: Int, frequency: Double, volume: Double): Double = { - val samples = samplingRate.value / frequency - f(t % samples / samples) * volume - } - - } - - object Oscillator { - case object Sine extends Oscillator { - def f(x: Double): Double = Math.sin(2.0 * Math.PI * x) - } - case object Sawtooth extends Oscillator { - def f(x: Double): Double = 2.0 * (x - Math.floor(x + 0.5)) - } - } - - def int(boolean: Boolean): Int = if (boolean) 1 else 0 - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def playTones(t: Int, tones: Array[Double], samplingRate: SamplingRate, tempo: Int): Double = { - // The duration of the tone in number of samples. - val duration = samplingRate.value / tempo - // A short pause between each tone played. - val pause = samplingRate.value / 40 - - val index = t % (tones.length * duration) / duration - val previous = - if (index == 0) { - tones.length - 1 - } else { - index - 1 - } - tones(index) * int(t % duration > pause || tones(previous) == tones(index)) - } - - def playWhiteNoise(t: Int, tones: Array[Double], samplingRate: SamplingRate, tempo: Int, duration: Double): Double = { - playTones(t, tones, samplingRate, tempo) * Math.random() * { - if (t % (samplingRate.value / 8) > (samplingRate.value / 8 - (samplingRate.value * duration))) { - 1.0 - } else { - 0.0 - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerators.scala b/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerators.scala deleted file mode 100644 index 94512e6e0..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioGenerators.scala +++ /dev/null @@ -1,173 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import im.tox.tox4j.av.callbacks.audio.AudioGenerator._ -import im.tox.tox4j.av.data.SamplingRate - -object AudioGenerators { - - case object ItCrowd extends AudioGenerator { - - def length(samplingRate: SamplingRate): Int = (samplingRate.value * 12) + (samplingRate.value / 2) * 8 - - def sample(samplingRate: SamplingRate, t0: Int): Int = { - // Period - val t = t0 % length(samplingRate) - - val rate = samplingRate.value - - val a = 1 / ((rate * 16) - t) - val b = - if (t > (rate * 12)) { - t % (rate / 2) * ("'&(&*$,*".charAt(t % (rate * 12) / (rate / 2)) - 32) - } else { - { - t % (rate / 4) * { - "$$$&%%%''''%%%'&".charAt(t % (rate * 4) / (rate / 4)) - 32 - - int(t > (rate * 3 + rate / 2) && t < (rate * 4)) * 2 - } - } / { - int(t % rate < (rate / 2)) + 1 - } - } - ((a | b) / (rate / 8000) << 8).toShort / 5 - } - - } - - case object MortalKombat extends AudioGenerator { - def length(samplingRate: SamplingRate): Int = samplingRate.value * 16 - - def sample(samplingRate: SamplingRate, t: Int): Int = { - val rate = samplingRate.value - - val a = { - 2 * t % (rate / 2) * { - "!!#!$!%$".charAt(t % (rate * 2) / (rate / 4)) - 32 + - int(t % (rate * 8) > (rate * 4)) * 7 - } - } * { - int(t % (rate * 4) > (rate * 2)) + 1 - } - val b = int(t % (rate * 16) > (rate * 8)) * 2 * t * { - "%%%'%+''%%%$%+))%%%%'+'%$%%%$%%%$%%".charAt(t % (rate * 8 + (rate - rate / 4)) / (rate / 4)) - 36 - } - ((a | b) / (rate / 8000) << 8).toShort / 5 - } - } - - case object Sine1 extends AudioGenerator { - def length(samplingRate: SamplingRate): Int = samplingRate.value * 16 - def sample(samplingRate: SamplingRate, t: Int): Int = (Math.sin(t / (10000d / t)) * 128).toShort << 6 - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - case object Sine2 extends AudioGenerator { - def length(samplingRate: SamplingRate): Int = samplingRate.value * 16 - def sample(samplingRate: SamplingRate, t0: Int): Int = { - val t = - if (t0 % 2 == 0) { - 1 - } else { - t0 - } - (Math.sin(t / (10000d / t)) * 128).toShort << 6 - } - } - - case object Sine3 extends AudioGenerator { - def length(samplingRate: SamplingRate): Int = samplingRate.value * 16 - def sample(samplingRate: SamplingRate, t: Int): Int = { - ((t & 0xF) * ((0 - t) & 0xF) * (((t & 0x10) >> 3) - 1) * 0x80 / 0x41 + 0x80) << 7 - } - } - - final case class Sawtooth(frequency: Double) extends AudioGenerator { - def length(samplingRate: SamplingRate): Int = samplingRate.value * 16 - def sample(samplingRate: SamplingRate, t: Int): Int = { - (Oscillator.Sine.osc(samplingRate, t, frequency, 1) * Short.MaxValue).toShort - } - } - - // https://www.youtube.com/watch?v=S7dg0X1LskI - case object SongOfStorms extends AudioGenerator { - - def length(samplingRate: SamplingRate): Int = samplingRate.value * 16 - - private val melody = (4, Array[Double]( - 146.83, 174.61, 293.66, 293.66, - 146.83, 174.61, 293.66, 293.66, - 329.62, 349.22, 329.62, 349.22, 329.62, 261.62, 220, 220, - - 220, 220, 146.83, 146.83, 174.61, 195.99, 220, 220, - 220, 220, 146.83, 146.83, 174.61, 195.99, 164.81, 164.81, - - 146.83, 174.61, 293.66, 293.66, - 146.83, 174.61, 293.66, 293.66, - 329.62, 349.22, 329.62, 349.22, 329.62, 261.62, 220, 220, - - 220, 220, 146.83, 146.83, 174.61, 195.99, 220, 220, - 220, 220, 146.83, 146.83, 146.83, 146.83, 146.83, 146.83 - )) - - private val bass = (1, Array[Double]( - 73.41, 82.4, 87.3, 82.4, - 116.54, 87.3, 116.54, 110, - - 73.41, 82.4, 87.3, 82.4, - 116.54, 110, 73.41, 73.41 - )) - - private val organ = (8, Array[Double]( - 0, 220, 0, 220, - 0, 0, 0, 146.83, - 246.94, 246.94, 246.94, 246.94, - 0, 0, 0, 0, - - 0, 261.62, 0, 261.62, - 0, 0, 0, 146.83, - 246.94, 246.94, 246.94, 246.94, - 0, 0, 0, 0, - - 0, 220, 0, 220, - 0, 0, 0, 220, - 220, 220, 220, 220, - 0, 0, 0, 0, - - 0, 220, 0, 220, - 0, 0, 0, 220, - 220, 220, 220, 220, - 0, 0, 0, 0 - )) - - private val percussions = (8, Array[Double]( - 0, 1, 0, 1, - 0, 0, 0, 0, - 1, 1, 1, 1, - 0, 0, 0, 0 - )) - - private def playInstrument(samplingRate: SamplingRate, t: Int, input: (Int, Array[Double])): Double = { - val (tempo, tones) = input - playTones(t, tones, samplingRate, tempo) - } - - private def playPercussions(samplingRate: SamplingRate, t: Int, input: (Int, Array[Double])): Double = { - val (tempo, tones) = input - playWhiteNoise(t, tones, samplingRate, tempo, 1.0 / 40) - } - - def sample(samplingRate: SamplingRate, t: Int): Int = { - val double = (0 - + Oscillator.Sine.osc(samplingRate, t, playInstrument(samplingRate, t, melody), 0.35) - + Oscillator.Sawtooth.osc(samplingRate, t, playInstrument(samplingRate, t, bass), 0.1) - + Oscillator.Sawtooth.osc(samplingRate, t, playInstrument(samplingRate, t, organ), 0.05) - + Oscillator.Sawtooth.osc(samplingRate, t, playPercussions(samplingRate, t, percussions), 0.1)) - (double * Short.MaxValue).toShort - } - - } - - // Selected audio generator for tests. - def default: AudioGenerator = SongOfStorms - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioPlayback.scala b/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioPlayback.scala deleted file mode 100644 index 54b00da52..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioPlayback.scala +++ /dev/null @@ -1,63 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import javax.sound.sampled._ - -import im.tox.tox4j.av.data.SamplingRate - -import scala.util.Try - -object AudioPlayback { - - def showWave(pcm: Array[Short], width: Int): String = { - val height = width / 10 - - val screen = (0 until height).map(_ => Array.fill[Char](width)(' ')) - - val maxSampleValue = -Short.MinValue - for ((sample, x) <- pcm.zipWithIndex) { - val y = valueToRange(sample + maxSampleValue, maxSampleValue * 2, height) - screen(y)(valueToRange(x, pcm.length, width)) = '#' - } - - screen.map(new String(_)).mkString("\n") - } - - private def valueToRange(value: Double, maxValue: Int, maxRange: Int): Int = { - (value / maxValue * maxRange).toInt - } - - private def serialiseAudioFrame(pcm: Array[Short]): Array[Byte] = { - val buffer = Array.ofDim[Byte](pcm.length * 2) - for (i <- buffer.indices by 2) { - buffer(i) = (pcm(i / 2) >> 8).toByte - buffer(i + 1) = pcm(i / 2).toByte - } - - buffer - } - -} - -final class AudioPlayback(samplingRate: SamplingRate) { - - def play(pcm: Array[Short]): Unit = { - soundLine.foreach { soundLine => - val buffer = AudioPlayback.serialiseAudioFrame(pcm) - soundLine.write(buffer, 0, buffer.length) - } - } - - def done(length: Int): Boolean = { - soundLine.toOption.map(_.getLongFramePosition >= length).getOrElse(true) - } - - private val soundLine = Try { - val format = new AudioFormat(samplingRate.value, 16, 1, true, true) - val info = new DataLine.Info(classOf[SourceDataLine], format) - val soundLine = AudioSystem.getLine(info).asInstanceOf[SourceDataLine] - soundLine.open(format, samplingRate.value) - soundLine.start() - soundLine - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackTest.scala b/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackTest.scala deleted file mode 100644 index f956ad2d9..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackTest.scala +++ /dev/null @@ -1,161 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import java.util - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.ToxavFriendCallState -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.ToxExceptionChecks -import im.tox.tox4j.testing.autotest.AutoTestSuite -import org.slf4j.LoggerFactory - -import scala.concurrent.duration._ -import scala.language.postfixOps - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class AudioReceiveFrameCallbackTest extends AutoTestSuite with ToxExceptionChecks { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - override def maxParticipantCount: Int = 2 - - final case class S(t: Int = 0, frequencies: List[Double] = Nil) - - object Handler extends EventListener(S()) { - - /** - * How much of the sent data must be received for the test to pass. - */ - private val minCompletionRatio = 0.8 - - private val audioFrequency = 220 - private val audio = AudioGenerators.Sawtooth(audioFrequency) - - private val bitRate = BitRate.fromInt(320).get - private val audioPerFrame = AudioLength.Length60 - private val samplingRate = SamplingRate.Rate8k - private val frameSize = SampleCount(audioPerFrame, samplingRate).value - private val windowSize = audio.length(samplingRate) / frameSize / 2 - private val framesPerIteration = 2 - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connectionStatus: ToxConnection - )(state0: State): State = { - val state = super.friendConnectionStatus(friendNumber, connectionStatus)(state0) - - if (connectionStatus == ToxConnection.NONE || state.id(friendNumber) != state.id.next) { - state - } else { - // Call id+1. - state.addTask { (tox, av, state) => - debug(state, s"Ringing ${state.id(friendNumber)}") - av.call(friendNumber, bitRate, BitRate.Disabled) - state - } - } - } - - override def call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean)(state: State): State = { - if (state.id(friendNumber) == state.id.prev) { - state.addTask { (tox, av, state) => - debug(state, s"Got a call from ${state.id(friendNumber)}; accepting") - av.answer(friendNumber, BitRate.Disabled, BitRate.Disabled) - state - } - } else { - fail(s"I shouldn't have been called by friend ${state.id(friendNumber)}") - state - } - } - - // There is no stack recursion here, it pushes thunks of itself for deferred execution. - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def sendFrame(friendNumber: ToxFriendNumber)(tox: ToxCore, av: ToxAv, state0: State): State = { - val state = state0.modify(s => s.copy(t = s.t + frameSize * framesPerIteration)) - - for (t <- state0.get.t until state.get.t by frameSize) { - val pcm = audio.nextFrame16(audioPerFrame, samplingRate, t) - av.audioSendFrame( - friendNumber, - pcm, - SampleCount(audioPerFrame, samplingRate), - AudioChannels.Mono, - samplingRate - ) - } - - if (state.get.t >= audio.length(samplingRate)) { - state.finish - } else { - state.addTask(sendFrame(friendNumber)) - } - } - - override def callState(friendNumber: ToxFriendNumber, callState: util.EnumSet[ToxavFriendCallState])(state: State): State = { - debug(state, s"Call with ${state.id(friendNumber)} is now $callState") - assert(callState == util.EnumSet.of( - ToxavFriendCallState.ACCEPTING_A, - ToxavFriendCallState.ACCEPTING_V - )) - state.addTask(sendFrame(friendNumber)) - } - - override def audioBitRate(friendNumber: ToxFriendNumber, audioBitRate: BitRate)(state: State): State = { - debug(state, s"Bit rate in call with ${state.id(friendNumber)} should change to $audioBitRate for audio") - state - } - - override def videoBitRate(friendNumber: ToxFriendNumber, videoBitRate: BitRate)(state: State): State = { - debug(state, s"Bit rate in call with ${state.id(friendNumber)} should change to $videoBitRate for video") - state - } - - override def audioReceiveFrame( - friendNumber: ToxFriendNumber, - pcm: Array[Short], - channels: AudioChannels, - samplingRate: SamplingRate - )(state0: State): State = { - val binarised = pcm.map { n => - if (n > 0) { - Short.MaxValue / 2 - } else { - Short.MinValue / 2 - } - } - - val derivative = (binarised, binarised.tail).zipped.map { - (n0, n1) => (n1 - n0).toShort - } - - val vibrations = derivative.count(_ != 0) - - val frequency = vibrations * ((1 second) / audioPerFrame.value) / 2 - - val state = state0.modify(s => s.copy( - t = s.t + pcm.length, - frequencies = (frequency :: s.frequencies).take(windowSize) - )) - - val averageFrequency = state.get.frequencies.sum / state.get.frequencies.length - debug(state, s"Received audio frame: ${state.get.t} / ${audio.length(samplingRate)} (f~=$averageFrequency (last ${state.get.frequencies.length} frames))") - assert(channels == AudioChannels.Mono) - assert(samplingRate == this.samplingRate) - - // Require at least half the frames to arrive. - if (state.get.t >= audio.length(samplingRate) * minCompletionRatio) { - assert(Math.round(averageFrequency) == audioFrequency) - state.finish - } else { - state - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/ArithmeticVideoGenerator.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/ArithmeticVideoGenerator.scala deleted file mode 100644 index 81d47f3cb..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/ArithmeticVideoGenerator.scala +++ /dev/null @@ -1,58 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -@SuppressWarnings(Array("org.wartremover.warts.While")) -abstract class ArithmeticVideoGenerator extends VideoGenerator { - - def productPrefix: String - - override final def toString: String = s"$productPrefix($width, $height)" - - private val yArray = Array.ofDim[Byte](size) - private val uArray = Array.ofDim[Byte](size / 4) - private val vArray = Array.ofDim[Byte](size / 4) - - protected def y(t: Int, x: Int, y: Int): Int - protected def u(t: Int, x: Int, y: Int): Int - protected def v(t: Int, x: Int, y: Int): Int - - final def yuv(t: Int): (Array[Byte], Array[Byte], Array[Byte]) = { - yuv(t, width.value, height.value) - } - - private def yuv(t: Int, width: Int, height: Int): (Array[Byte], Array[Byte], Array[Byte]) = { - yLoop(t, yArray, height, width) - uvLoop(t, uArray, vArray, height / 2, width / 2) - - (yArray, uArray, vArray) - } - - private def yLoop(t: Int, yArray: Array[Byte], height: Int, width: Int): Unit = { - var yPos = 0 - while (yPos < height) { - var xPos = 0 - while (xPos < width) { - yArray(yPos * width + xPos) = y(t, xPos, yPos).toByte - - xPos += 1 - } - - yPos += 1 - } - } - - private def uvLoop(t: Int, uArray: Array[Byte], vArray: Array[Byte], height: Int, width: Int): Unit = { - var yPos = 0 - while (yPos < height) { - var xPos = 0 - while (xPos < width) { - uArray(yPos * width + xPos) = u(t, xPos * 2 + 1, yPos * 2 + 1).toByte - vArray(yPos * width + xPos) = v(t, xPos * 2 + 1, yPos * 2 + 1).toByte - - xPos += 1 - } - - yPos += 1 - } - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/RgbVideoGenerator.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/RgbVideoGenerator.scala deleted file mode 100644 index 3938527bb..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/RgbVideoGenerator.scala +++ /dev/null @@ -1,8 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -abstract class RgbVideoGenerator extends ArithmeticVideoGenerator { - def rgb(t: Int, y: Int, x: Int): Int - protected final def y(t: Int, x: Int, y: Int): Int = VideoConversions.RGBtoY(rgb(t, y, x)) - protected final def u(t: Int, x: Int, y: Int): Int = VideoConversions.RGBtoU(rgb(t, y, x)) - protected final def v(t: Int, x: Int, y: Int): Int = VideoConversions.RGBtoV(rgb(t, y, x)) -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/TextImageGenerator.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/TextImageGenerator.scala deleted file mode 100644 index f25de5ebe..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/TextImageGenerator.scala +++ /dev/null @@ -1,27 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import im.tox.tox4j.av.data.{ Height, Width } - -abstract class TextImageGenerator(row0: String, rowN: String*) extends VideoGenerator { - - private def rows: Seq[String] = row0 +: rowN - - override final def yuv(t: Int): (Array[Byte], Array[Byte], Array[Byte]) = { - val width = this.width.value - val height = this.height.value - - val y = rows.mkString.getBytes - val u = Array.fill((width / 2) * (height / 2))((t * 4).toByte) - val v = Array.fill((width / 2) * (height / 2))((-t * 4 - 1).toByte) - (y, u, v) - } - - override final def resize(width: Width, height: Height): VideoGenerator = { - VideoGenerator.resizeNearestNeighbour(width, height, this) - } - - override def width: Width = Width.fromInt(row0.length).get - override def height: Height = Height.fromInt(rows.size).get - override def length: Int = sys.env.get("TRAVIS").map(_ => 4).getOrElse(64) - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoConversions.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/VideoConversions.scala deleted file mode 100644 index 23ec6840f..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoConversions.scala +++ /dev/null @@ -1,99 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -// scalastyle:off method.name -object VideoConversions { - - final case class YuvPixel(y: Byte, u: Byte, v: Byte) - - def RGBtoY(r: Int, g: Int, b: Int): Byte = clamp(((66 * r + 129 * g + 25 * b + 128) >> 8) + 16) - def RGBtoU(r: Int, g: Int, b: Int): Byte = clamp(((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128) - def RGBtoV(r: Int, g: Int, b: Int): Byte = clamp(((112 * r - 94 * g - 18 * b + 128) >> 8) + 128) - - def RGBtoY(rgb: Int): Byte = RGBtoY((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff) - def RGBtoU(rgb: Int): Byte = RGBtoU((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff) - def RGBtoV(rgb: Int): Byte = RGBtoV((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff) - - object YuvPixel { - def ofRgb(r: Int, g: Int, b: Int): YuvPixel = { - YuvPixel( - y = RGBtoY(r, g, b), - u = RGBtoU(r, g, b), - v = RGBtoV(r, g, b) - ) - } - - def ofRgb(rgb: Int): YuvPixel = { - ofRgb( - (rgb >> 16) & 0xff, - (rgb >> 8) & 0xff, - rgb & 0xff - ) - } - } - - final case class RgbPixel(r: Byte, g: Byte, b: Byte) - - object RgbPixel { - - def ofYuv(y: Int, u: Int, v: Int): RgbPixel = { - RgbPixel( - r = YUVtoR(y, u, v), - g = YUVtoG(y, u, v), - b = YUVtoB(y, u, v) - ) - } - - } - - private def clamp(n: Int): Byte = (255 min n max 0).toByte - - private def C(y: Int) = y - 16 - private def D(u: Int) = u - 128 - private def E(v: Int) = v - 128 - - private def unsigned(b: Byte): Int = b & 0xff - - private def YUVtoR(y: Int, u: Int, v: Int): Byte = { - clamp((298 * C(y) + 409 * E(v) + 128) >> 8) - } - - private def YUVtoG(y: Int, u: Int, v: Int): Byte = { - clamp((298 * C(y) - 100 * D(u) - 208 * E(v) + 128) >> 8) - } - - private def YUVtoB(y: Int, u: Int, v: Int): Byte = { - clamp((298 * C(y) + 516 * D(u) + 128) >> 8) - } - - def YUVtoRGB( // scalastyle:ignore parameter.number - width: Int, height: Int, - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - ): (Array[Byte], Array[Byte], Array[Byte]) = { - val r = Array.ofDim[Byte](width * height) - val g = Array.ofDim[Byte](width * height) - val b = Array.ofDim[Byte](width * height) - - assert(r.length >= width * height) - assert(g.length >= width * height) - assert(b.length >= width * height) - - for { - yPos <- 0 until height - xPos <- 0 until width - } { - val yx = unsigned(y((yPos * yStride) + xPos)) - val ux = unsigned(u(((yPos / 2) * uStride) + (xPos / 2))) - val vx = unsigned(v(((yPos / 2) * vStride) + (xPos / 2))) - - val currPos = (yPos * width) + xPos - - r(currPos) = YUVtoR(yx, ux, vx) - g(currPos) = YUVtoG(yx, ux, vx) - b(currPos) = YUVtoB(yx, ux, vx) - } - - (r, g, b) - } - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerator.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerator.scala deleted file mode 100644 index cb0185037..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerator.scala +++ /dev/null @@ -1,77 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import im.tox.tox4j.av.data.{ Height, Width } -import org.scalatest.Assertions - -abstract class VideoGenerator { - - def width: Width - def height: Height - def length: Int - - def yuv(t: Int): (Array[Byte], Array[Byte], Array[Byte]) - def resize(width: Width, height: Height): VideoGenerator - - final def size: Int = width.value * height.value - - protected final def w: Int = width.value - protected final def h: Int = height.value - -} - -object VideoGenerator extends Assertions { - - private def resizeNearestNeighbour( - pixels: Array[Byte], - oldWidth: Int, - oldHeight: Int, - newWidth: Int, - newHeight: Int - ): Array[Byte] = { - val result = Array.ofDim[Byte](newWidth * newHeight) - - val xRatio = oldWidth / newWidth.toDouble - val yRatio = oldHeight / newHeight.toDouble - - for { - yPos <- 0 until newHeight - xPos <- 0 until newWidth - } { - val px = Math.floor(xPos * xRatio) - val py = Math.floor(yPos * yRatio) - result((yPos * newWidth) + xPos) = pixels(((py * oldWidth) + px).toInt) - } - - result - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def resizeNearestNeighbour(newWidth: Width, newHeight: Height, gen: VideoGenerator): VideoGenerator = { - if (newWidth == gen.width && newHeight == gen.height) { - gen - } else { - new VideoGenerator { - - override def toString: String = s"resizeNearestNeighbour($width, $height, $gen)" - - override def resize(width: Width, height: Height): VideoGenerator = gen.resize(width, height) - - override def yuv(t: Int): (Array[Byte], Array[Byte], Array[Byte]) = { - val yuv = gen.yuv(t) - ( - resizeNearestNeighbour(yuv._1, gen.width.value, gen.height.value, width.value, height.value), - resizeNearestNeighbour(yuv._2, gen.width.value / 2, gen.height.value / 2, width.value / 2, height.value / 2), - resizeNearestNeighbour(yuv._3, gen.width.value / 2, gen.height.value / 2, width.value / 2, height.value / 2) - ) - } - - override def width: Width = newWidth - override def height: Height = newHeight - override def length: Int = gen.length - - } - } - } - -} - diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerators.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerators.scala deleted file mode 100644 index 43ff65caf..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoGenerators.scala +++ /dev/null @@ -1,205 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import java.awt.Color - -import im.tox.tox4j.av.data.{ Height, Width } -import org.scalatest.Assertions - -object VideoGenerators extends Assertions { - - val DefaultWidth: Width = Width.fromInt(400).get - val DefaultHeight: Height = Height.fromInt(400).get - val VideoLength: Int = 100 - - // TODO(iphydf): Several of these break with the following error in - // libtoxcore.log, especially at higher resolutions: - // - // toxcore 12:13 04:53:51 3343345408 ERROR video.c:155 - Error decoding video: Unspecified internal error - - /** - * Shifting colours in xor pattern. - */ - final case class Xor1(width: Width = DefaultWidth, height: Height = DefaultHeight, length: Int = VideoLength) extends ArithmeticVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - protected def y(t: Int, x: Int, y: Int): Int = x ^ y - protected def u(t: Int, x: Int, y: Int): Int = x ^ y + t + 1 - protected def v(t: Int, x: Int, y: Int): Int = x ^ y - t - 1 - } - - /** - * Rapidly changing xor patterns. - */ - final case class Xor2(width: Width, height: Height, length: Int = VideoLength) extends RgbVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - def rgb(t: Int, y: Int, x: Int): Int = (x ^ y) * t - } - - /** - * Slowly right-shifting and colour-shifting xor. - */ - final case class Xor3(width: Width, height: Height, length: Int = VideoLength) extends RgbVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - def rgb(t: Int, y: Int, x: Int): Int = (x - (t * Math.log(t)).toInt ^ y + (t * Math.log(t)).toInt) * t - } - - /** - * Slowly colour-shifting xor patterns. - */ - final case class Xor4(width: Width, height: Height, length: Int = VideoLength) extends ArithmeticVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - protected def y(t: Int, x: Int, y: Int): Int = (x ^ y) + t - protected def u(t: Int, x: Int, y: Int): Int = t * 2 - protected def v(t: Int, x: Int, y: Int): Int = -t * 2 - 1 - } - - final case class Xor5(width: Width, height: Height, length: Int = VideoLength) extends ArithmeticVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - protected def y(t: Int, x: Int, y: Int): Int = t + x ^ y - protected def u(t: Int, x: Int, y: Int): Int = x ^ y + t + 1 - protected def v(t: Int, x: Int, y: Int): Int = x ^ y - t - 1 - } - - private def gradient(top: Boolean, left: Boolean, x: Int, y: Int, w: Int, h: Int): Double = { - val value = - if (top && left) { - (x * 512 / w) min (y * 512 / h) - } else if (top && !left) { - ((w - x) * 512 / w) min (y * 512 / h) - } else if (!top && left) { - (x * 512 / w) min ((h - y) * 512 / h) - } else { - ((w - x) * 512 / w) min ((h - y) * 512 / h) - } - assert(value >= 0) - assert(value <= 256) - value.toDouble / 256 - } - - final case class XorGradient(width: Width, height: Height, length: Int = VideoLength) extends ArithmeticVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - protected def y(t: Int, x: Int, y: Int): Int = { - val top = y <= h / 2 - val left = x <= w / 2 - - (gradient(top, left, x, y, w, h) * (x ^ y) + t).toInt - } - - protected def u(t: Int, x: Int, y: Int): Int = t * 2 - protected def v(t: Int, x: Int, y: Int): Int = -t * 2 - 1 - } - - /** - * More and more gradient boxes. - */ - final case class GradientBoxes(width: Width, height: Height, length: Int = VideoLength) extends RgbVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - def rgb(t: Int, y: Int, x: Int): Int = (x * Math.log(t) + ((y * Math.log(t)).toInt << 8)).toInt - } - - /** - * Multiplication (x * y) pattern moving up. - */ - final case class MultiplyUp(width: Width, height: Height, length: Int = VideoLength) extends RgbVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - def rgb(t: Int, y: Int, x: Int): Int = x * (y + t) - } - - object Colors { - /** - * Plain colours for testing. - */ - val values: Seq[Color] = Seq( - Color.white, - Color.lightGray, - Color.gray, - Color.darkGray, - Color.black, - Color.red, - Color.pink, - Color.orange, - Color.yellow, - Color.green, - Color.magenta, - Color.cyan, - Color.blue - ) - - private val FramesPerColor = 3 - private val VideoLength = Colors.values.length * Colors.FramesPerColor - - def get(t: Int): Color = values(t / FramesPerColor % values.length) - } - - final case class Colors(width: Width, height: Height, length: Int = Colors.VideoLength) extends RgbVideoGenerator { - def resize(width: Width, height: Height): VideoGenerator = copy(width, height) - - def rgb(t: Int, y: Int, x: Int): Int = Colors.get(t).getRGB - } - - case object Smiley extends TextImageGenerator( - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "00000000000011zzzzzzzzzzzzzzzzz000000000000000000000000000000000000zzzzzzzzzzzzzzzzzzz00000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000011zzzzzzzzzzzz0000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "00000000000000001zzzzzzzz000000000000000000000000000000000000000000000000zzzzzzzzz000000000000000000", - "000000000000000011zzzzzzzzzz000000000000000000000000000000000000000011zzzzzzzzzz00000000000000000000", - "000000000000000000001zzzzzzzzzz00000000000000000000000000000000111zzzzzzzzzz000000000000000000000000", - "000000000000000000000000zzzzzzzzzzzz00000000000000000000000011zzzzzzzzzzzz00000000000000000000000000", - "0000000000000000000000000000zzzzzzzzzzzzzzzz000000001zzzzzzzzzzzzzzzzz000000000000000000000000000000", - "00000000000000000000000000000000zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz0000000000000000000000000000000000", - "000000000000000000000000000000000000111zzzzzzzzzzzzzzzzzzzz00000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ) - - val all: Map[String, (Width, Height, Int) => VideoGenerator] = Map[String, (Width, Height, Int) => VideoGenerator]( - "xor1" -> Xor1, - "xor2" -> Xor2, - "xor3" -> Xor3, - "xor4" -> Xor4, - "xor5" -> Xor5, - "xorgradient" -> XorGradient, - "gradientboxes" -> GradientBoxes, - "multiplyup" -> MultiplyUp, - "smiley" -> { (w, h, _) => VideoGenerator.resizeNearestNeighbour(w, h, Smiley) } - ) - - val default: VideoGenerator = VideoGenerator.resizeNearestNeighbour(DefaultWidth, DefaultHeight, Smiley) - // val default = XorGradient(DefaultWidth, DefaultHeight) - -} diff --git a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackTest.scala b/src/test/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackTest.scala deleted file mode 100644 index 5e8365708..000000000 --- a/src/test/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackTest.scala +++ /dev/null @@ -1,154 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import java.awt.Color -import java.util - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.ToxavFriendCallState -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.ToxExceptionChecks -import im.tox.tox4j.testing.autotest.AutoTestSuite -import im.tox.tox4j.testing.autotest.AutoTestSuite.timed - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class VideoReceiveFrameCallbackTest extends AutoTestSuite with ToxExceptionChecks { - - /** - * How much of the sent data must be received for the test to pass. - */ - private val minCompletionRatio = 0.7 - - private val video = VideoGenerators.Colors(VideoGenerators.DefaultWidth, VideoGenerators.DefaultHeight) - - private val bitRate = BitRate.fromInt(1).get - - override def maxParticipantCount: Int = 2 - - final case class S(t: Int = 0, received: List[Color] = Nil) - - object Handler extends EventListener(S()) { - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connectionStatus: ToxConnection - )(state0: State): State = { - val state = super.friendConnectionStatus(friendNumber, connectionStatus)(state0) - - if (connectionStatus == ToxConnection.NONE || state.id(friendNumber) != state.id.next) { - state - } else { - // Call id+1. - state.addTask { (tox, av, state) => - debug(state, s"Ringing ${state.id(friendNumber)} to send ${video.length} frames") - av.call(friendNumber, BitRate.Disabled, bitRate) - state - } - } - } - - override def call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean)(state: State): State = { - if (state.id(friendNumber) == state.id.prev) { - state.addTask { (tox, av, state) => - debug(state, s"Got a call from ${state.id(friendNumber)}; accepting") - av.answer(friendNumber, BitRate.Disabled, BitRate.Disabled) - state - } - } else { - fail(s"I shouldn't have been called by friend ${state.id(friendNumber)}") - state - } - } - - // There is no stack recursion here, it pushes thunks of itself for deferred execution. - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def sendFrame(friendNumber: ToxFriendNumber)(tox: ToxCore, av: ToxAv, state0: State): State = { - val state = state0.modify(s => s.copy(t = s.t + 1)) - - val (y, u, v) = video.yuv(state0.get.t) - assert(y.length == video.size) - assert(u.length == video.size / 4) - assert(v.length == video.size / 4) - - val time = timed { - av.videoSendFrame(friendNumber, video.width.value, video.height.value, y, u, v) - } - debug(state, s"Sent frame ${state.get.t} of ${video.length} ($time ms)") - - if (state.get.t >= video.length) { - state.finish - } else { - state.addTask(sendFrame(friendNumber)) - } - } - - override def callState(friendNumber: ToxFriendNumber, callState: util.EnumSet[ToxavFriendCallState])(state: State): State = { - debug(state, s"Call with ${state.id(friendNumber)} is now $callState") - state.addTask(sendFrame(friendNumber)) - } - - private def approximatelyEqual(color1: Color)(color2: Color): Boolean = { - val rgb1 = color1.getRGBColorComponents(Array.ofDim(3)).map(c => Math.round(c * 32)) - val rgb2 = color2.getRGBColorComponents(Array.ofDim(3)).map(c => Math.round(c * 32)) - (rgb1, rgb2).zipped.forall(_ == _) - } - - private def average(plane: Array[Byte]): Int = { - plane.map(_ & 0xff).sum / plane.length - } - - override def videoReceiveFrame( - friendNumber: ToxFriendNumber, - width: Width, height: Height, - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - )(state0: State): State = { - val (conversionTime, rgbPlanar) = timed { - VideoConversions.YUVtoRGB(width.value, height.value, y, u, v, yStride, uStride, vStride) - } - - val averageR = average(rgbPlanar._1) - val averageG = average(rgbPlanar._2) - val averageB = average(rgbPlanar._3) - - val receivedColor = new Color(averageR, averageG, averageB) - - val state = state0.modify(s => s.copy( - t = s.t + 1, - received = receivedColor :: s.received - )) - - assert(state.get.t <= video.length) - - val minReceived = (video.length * minCompletionRatio).toInt - val receivedMessage = s"Received frame ${state.get.t} of (minimum) $minReceived (YUVtoRGB: $conversionTime ms)" - if (state.get.t >= minReceived) { - val assertionTime: Int = timed { - val received = state.get.received.distinct - val expected = VideoGenerators.Colors.values - - // All received in expected. - assert(received.forall { c => expected.exists(approximatelyEqual(c)) }) - - if (state.get.t >= video.length) { - // All expected in received. Only checked if every sent frame was received. - debug(state, s"Received all ${video.length} frames") - assert(expected.forall { c => received.exists(approximatelyEqual(c)) }): Unit - } - } - - debug(state, receivedMessage + s" (assertion time: $assertionTime ms)") - - state.finish - } else { - debug(state, receivedMessage) - - state - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/av/package.scala b/src/test/java/im/tox/tox4j/av/package.scala deleted file mode 100644 index c0778f27b..000000000 --- a/src/test/java/im/tox/tox4j/av/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j - -import im.tox.documentation._ - -package object av extends Documented diff --git a/src/test/java/im/tox/tox4j/core/JToxCoreTest.java b/src/test/java/im/tox/tox4j/core/JToxCoreTest.java deleted file mode 100644 index 477413a6c..000000000 --- a/src/test/java/im/tox/tox4j/core/JToxCoreTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package im.tox.tox4j.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter; -import im.tox.tox4j.core.callbacks.ToxCoreEventListener; -import im.tox.tox4j.core.enums.ToxFileControl; -import im.tox.tox4j.core.enums.ToxMessageType; -import im.tox.tox4j.core.enums.ToxUserStatus; -import im.tox.tox4j.core.exceptions.ToxFriendAddException; -import im.tox.tox4j.core.options.ProxyOptions; -import im.tox.tox4j.core.options.SaveDataOptions; -import im.tox.tox4j.core.options.ToxOptions; -import im.tox.tox4j.exceptions.ToxException; -import im.tox.tox4j.impl.jni.ToxCoreImpl; -import org.junit.Assert; -import org.junit.Test; -import org.scalatestplus.junit.JUnitSuite; - -public final class JToxCoreTest extends JUnitSuite { - - private final ToxCoreEventListener handler = - new ToxCoreEventAdapter<>(); - - private final ToxOptions options = new ToxOptions( - true, true, true, new ProxyOptions.Http("localhost", 1234), - ToxCoreConstants.DefaultStartPort(), ToxCoreConstants.DefaultEndPort(), 0, - // TODO(iphydf): This is kind of ugly. Do we want to live with this? - SaveDataOptions.None$.MODULE$, true); - - private void expectBoolean(boolean bool) {} - - private void expectInt(int integer) {} - - private void expectBytes(byte[] bytes) {} - - private void expectInts(int[] ints) {} - - @Test - public void testJavaUsability() { - try (ToxCore tox = new ToxCoreImpl(options)) { - tox.iterate(handler, null); - int friendNumber = tox.addFriend(null, null); - Assert.fail("No exception thrown, null friend added as " + friendNumber); - } catch (ToxFriendAddException e) { - assertEquals(ToxFriendAddException.Code.NULL, e.code()); - } - } - - /** - * Call every ToxCore method with Java types and check whether their return - * types conform to the expected API. This test is here to verify that the - * Scala type changes don't break Java code. - */ - @Test - public void testJavaApi() { - final byte[] bytes = "hello".getBytes(); - - try (ToxCore tox = new ToxCoreImpl(options)) { - expectInt(tox.addFriend(bytes, bytes)); - expectInt(tox.addFriendNorequest(bytes)); - tox.addTcpRelay("hello", 0, bytes); - tox.bootstrap("hello", 0, bytes); - tox.close(); - tox.deleteFriend(0); - tox.fileControl(0, 0, ToxFileControl.CANCEL); - tox.fileSeek(0, 0, 0L); - expectInt(tox.fileSend(0, 0, 0L, bytes, bytes)); - tox.fileSendChunk(0, 0, 0L, bytes); - expectInt(tox.friendByPublicKey(bytes)); - expectBoolean(tox.friendExists(0)); - tox.friendSendLosslessPacket(0, bytes); - tox.friendSendLossyPacket(0, bytes); - expectInt(tox.friendSendMessage(0, ToxMessageType.NORMAL, 0, bytes)); - expectBytes(tox.getAddress()); - expectBytes(tox.getDhtId()); - expectBytes(tox.getFileFileId(0, 0)); - expectInts(tox.getFriendList()); - expectBytes(tox.getFriendPublicKey(0)); - expectBytes(tox.getName()); - expectInt(tox.getNospam()); - expectBytes(tox.getPublicKey()); - expectBytes(tox.getSavedata()); - expectBytes(tox.getSecretKey()); - expectBytes(tox.getStatusMessage()); - ToxUserStatus status = tox.getStatus(); - expectInt(tox.getTcpPort()); - expectInt(tox.getUdpPort()); - Void nothing = tox.iterate(null, null); - expectInt(tox.iterationInterval()); - ToxCore tox2 = tox.load(options); - tox.setName(bytes); - tox.setNospam(0); - tox.setStatus(ToxUserStatus.AWAY); - tox.setStatusMessage(bytes); - tox.setTyping(0, true); - } catch (ToxException | IllegalArgumentException e) { - assertNotNull(e); - } - } -} diff --git a/src/test/java/im/tox/tox4j/core/LoadSaveTest.scala b/src/test/java/im/tox/tox4j/core/LoadSaveTest.scala deleted file mode 100644 index 1a31140ff..000000000 --- a/src/test/java/im/tox/tox4j/core/LoadSaveTest.scala +++ /dev/null @@ -1,198 +0,0 @@ -package im.tox.tox4j.core - -import im.tox.tox4j.ToxCoreTestBase -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendRequestMessage, ToxNickname, ToxStatusMessage } -import im.tox.tox4j.core.enums.ToxUserStatus -import im.tox.tox4j.core.options.{ SaveDataOptions, ToxOptions } -import im.tox.tox4j.impl.jni.ToxCoreImplFactory.{ withTox, withToxUnit } -import im.tox.tox4j.testing.GetDisjunction._ -import org.scalatest.FunSuite - -import scala.annotation.tailrec - -@SuppressWarnings(Array( - "org.wartremover.warts.ArrayEquals", - "org.wartremover.warts.Equals" -)) -final class LoadSaveTest extends FunSuite { - - private trait Check { - def change(tox: ToxCore): Boolean - def check(tox: ToxCore): Unit - } - - @tailrec - private def testLoadSave(check: Check): Unit = { - val (continue, data) = withToxUnit { tox => - (check.change(tox), tox.getSavedata) - } - - withToxUnit(SaveDataOptions.ToxSave(data)) { tox => - check.check(tox) - } - - if (continue) { - testLoadSave(check) - } - } - - test("Name") { - testLoadSave(new Check() { - private var expected = ToxNickname.unsafeFromValue(null) - - override def change(tox: ToxCore): Boolean = { - expected = - if (expected.value == null) { - ToxNickname.fromString("").toOption.get - } else { - ToxNickname.fromValue(ToxCoreTestBase.randomBytes(expected.value.length + 1)).toOption.get - } - tox.setName(expected) - expected.value.length < ToxCoreConstants.MaxNameLength - } - - override def check(tox: ToxCore): Unit = { - assert(tox.getName.value sameElements expected.value) - } - }) - } - - test("StatusMessage") { - testLoadSave(new Check() { - private var expected = ToxStatusMessage.unsafeFromValue(null) - - override def change(tox: ToxCore): Boolean = { - if (expected.value == null) { - expected = ToxStatusMessage.fromString("").toOption.get - } else { - expected = ToxStatusMessage.fromValue(ToxCoreTestBase.randomBytes(expected.value.length + 1)).toOption.get - } - tox.setStatusMessage(expected) - expected.value.length < ToxCoreConstants.MaxNameLength - } - - override def check(tox: ToxCore): Unit = { - assert(tox.getStatusMessage.value sameElements expected.value) - } - }) - } - - test("Status") { - testLoadSave(new Check() { - private var expected = ToxUserStatus.values() - - override def change(tox: ToxCore): Boolean = { - tox.setStatus(expected.head) - expected.length > 1 - } - - override def check(tox: ToxCore): Unit = { - assert(tox.getStatus == expected.head) - expected = expected.tail - } - }) - } - - test("NoSpam") { - testLoadSave(new Check() { - private var expected = -1 - - override def change(tox: ToxCore): Boolean = { - expected += 1 - tox.setNospam(expected) - expected < 100 - } - - override def check(tox: ToxCore): Unit = { - assert(tox.getNospam == expected) - } - }) - } - - test("Friend") { - testLoadSave(new Check() { - private var expected = ToxFriendNumber.fromInt(1).get - - override def change(tox: ToxCore): Boolean = { - withToxUnit { toxFriend => - expected = tox.addFriend( - toxFriend.getAddress, - ToxFriendRequestMessage.fromString("hello").toOption.get - ) - } - false - } - - override def check(tox: ToxCore): Unit = { - assert(tox.getFriendNumbers.length == 1) - assert(tox.getFriendNumbers(0) == expected) - } - }) - } - - test("SaveNotEmpty") { - withToxUnit { tox => - val data = tox.getSavedata - assert(data != null) - assert(data.nonEmpty) - } - } - - test("SaveRepeatable") { - withToxUnit { tox => - assert(tox.getSavedata sameElements tox.getSavedata) - } - } - - test("LoadSave1") { - withToxUnit { tox => - val data = tox.getSavedata - val data1 = withToxUnit(SaveDataOptions.ToxSave(data)) { tox1 => - tox1.getSavedata - } - val data2 = withToxUnit(SaveDataOptions.ToxSave(data)) { tox2 => - tox2.getSavedata - } - assert(data1 sameElements data2) - } - } - - test("LoadSave2") { - withToxUnit { tox => - val data = tox.getSavedata - withToxUnit(SaveDataOptions.ToxSave(data)) { tox1 => - assert(tox1.getSavedata.length == data.length) - } - } - } - - test("LoadSave3") { - withToxUnit { tox => - val data = tox.getSavedata - withToxUnit(SaveDataOptions.ToxSave(data)) { tox1 => - assert(tox1.getSavedata sameElements data) - } - } - } - - test("LoadSave4") { - withToxUnit { tox1 => - val data = tox1.getSecretKey - withToxUnit(SaveDataOptions.SecretKey(data)) { tox2 => - assert(tox1.getSecretKey.value sameElements tox2.getSecretKey.value) - assert(tox1.getPublicKey.value sameElements tox2.getPublicKey.value) - } - } - } - - test("LoadSave5") { - withToxUnit { tox1 => - val data = tox1.getSecretKey - withTox(tox1.load(ToxOptions(saveData = SaveDataOptions.SecretKey(data)))) { tox2 => - assert(tox1.getSecretKey.value sameElements tox2.getSecretKey.value) - assert(tox1.getPublicKey.value sameElements tox2.getPublicKey.value) - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/NetworkTest.scala b/src/test/java/im/tox/tox4j/core/NetworkTest.scala deleted file mode 100644 index 387f377cc..000000000 --- a/src/test/java/im/tox/tox4j/core/NetworkTest.scala +++ /dev/null @@ -1,123 +0,0 @@ -package im.tox.tox4j.core - -import im.tox.core.network.Port -import im.tox.tox4j.DhtNodeSelector.node -import im.tox.tox4j.TestConstants.Timeout -import im.tox.tox4j._ -import im.tox.tox4j.core.NetworkTest.logger -import im.tox.tox4j.core.data.ToxPublicKey -import im.tox.tox4j.impl.jni.ToxCoreImplFactory.{ withToxUnit, withToxes } -import org.scalatest.FlatSpec -import org.scalatest.concurrent.TimeLimits -import org.slf4j.LoggerFactory - -import scala.language.postfixOps - -object NetworkTest { - private val logger = LoggerFactory.getLogger(classOf[NetworkTest]) - private val ToxCount = 10 -} - -@SuppressWarnings(Array("org.wartremover.warts.While")) -final class NetworkTest extends FlatSpec with TimeLimits { - - // TODO(iphydf): Figure out why the bootstrap tests all fail on Travis. - /* - private def testBootstrap(ipv6Enabled: Boolean, udpEnabled: Boolean, ip: String, port: Port, dhtId: ToxPublicKey): Unit = { - val action = s"bootstrap to remote node on $ip:$port with ${if (udpEnabled) "UDP" else "TCP"}${if (ipv6Enabled) 6 else 4}" - - withToxUnit(ipv6Enabled, udpEnabled) { tox => - logger.info(s"Attempting to $action") - val start = System.currentTimeMillis - - if (!udpEnabled) { - tox.addTcpRelay(ip, port, dhtId) - } - tox.bootstrap(ip, port, dhtId) - - val status = new ConnectedListener - while (!status.isConnected) { - tox.iterate(status)(()) - Thread.sleep(tox.iterationInterval) - } - - val end = System.currentTimeMillis - - logger.info(s"Success: $action took ${end - start} ms") - } - } - - "bootstrap" should "connect with UDP4" in { - assume(ToxCoreTestBase.checkIPv4.isEmpty) - failAfter(Timeout) { - testBootstrap(ipv6Enabled = false, udpEnabled = true, node.ipv4, node.udpPort, node.dhtId) - } - } - - it should "connect with UDP6" in { - assume(ToxCoreTestBase.checkIPv6.isEmpty) - failAfter(Timeout) { - testBootstrap(ipv6Enabled = true, udpEnabled = true, node.ipv6, node.udpPort, node.dhtId) - } - } - - it should "connect with TCP4" in { - assume(ToxCoreTestBase.checkIPv4.isEmpty) - failAfter(Timeout) { - testBootstrap(ipv6Enabled = false, udpEnabled = false, node.ipv4, node.tcpPort, node.dhtId) - } - } - - it should "connect with TCP6" in { - assume(ToxCoreTestBase.checkIPv6.isEmpty) - failAfter(Timeout) { - testBootstrap(ipv6Enabled = true, udpEnabled = false, node.ipv6, node.tcpPort, node.dhtId) - } - } - */ - - "LAN discovery" should "connect all nodes" in { - failAfter(Timeout) { - withToxes(NetworkTest.ToxCount) { toxes => - val action = s"Connecting all of ${toxes.size} toxes with LAN discovery" - logger.info(action) - - val start = System.currentTimeMillis - - while (!toxes.isAllConnected) { - toxes.iterate() - Thread.sleep(toxes.iterationInterval) - } - - val end = System.currentTimeMillis - - logger.info(s"$action took ${end - start} ms") - } - } - } - - it should "connect at least one instance" in { - failAfter(Timeout) { - withToxes(NetworkTest.ToxCount) { toxes => - val action = s"Connecting one of ${toxes.size} toxes with LAN discovery" - logger.info(action) - - val start = System.currentTimeMillis - - while (!toxes.isAnyConnected) { - toxes.iterate() - try { - Thread.sleep(toxes.iterationInterval) - } catch { - case e: InterruptedException => - } - } - - val end = System.currentTimeMillis - - logger.info(s"$action took ${end - start} ms") - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/SmallNat.scala b/src/test/java/im/tox/tox4j/core/SmallNat.scala deleted file mode 100644 index 8675913c1..000000000 --- a/src/test/java/im/tox/tox4j/core/SmallNat.scala +++ /dev/null @@ -1,28 +0,0 @@ -package im.tox.tox4j.core - -import org.scalacheck.{ Arbitrary, Gen } - -import scala.language.implicitConversions -import scala.runtime.IntegralProxy - -/** - * A wrapper class for [[Int]] to be used in property based testing where a - * small positive integer is required (e.g. for array bounds). - * - * arbitrary[Int] generates numbers from the full range of integers, and is - * thus unfit for allocation sizes and iteration counts. - */ -final case class SmallNat(self: Int) extends AnyVal with IntegralProxy[Int] { - override protected def num = scala.math.Numeric.IntIsIntegral - override protected def ord = scala.math.Ordering.Int - override def isWhole(): Boolean = true -} - -@SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) -object SmallNat { - val MinValue: Int = 0 - val MaxValue: Int = 100 - - implicit val arbSmallNat: Arbitrary[SmallNat] = Arbitrary(Gen.choose(MinValue, MaxValue).map(apply)) - implicit def smallNatToInt(smallNat: SmallNat): Int = smallNat.self -} diff --git a/src/test/java/im/tox/tox4j/core/ToxCoreFactory.scala b/src/test/java/im/tox/tox4j/core/ToxCoreFactory.scala deleted file mode 100644 index 34fe800e0..000000000 --- a/src/test/java/im/tox/tox4j/core/ToxCoreFactory.scala +++ /dev/null @@ -1,55 +0,0 @@ -package im.tox.tox4j.core - -import im.tox.tox4j.core.options.ToxOptions - -abstract class ToxCoreFactory { - - def withTox[R](options: ToxOptions)(f: ToxCore => R): R - - final def withTox[R](tox: ToxCore)(f: ToxCore => R): R = { - try { - f(tox) - } finally { - tox.close() - } - } - - @SuppressWarnings(Array( - "org.wartremover.warts.Equals", - "org.wartremover.warts.Recursion" - )) - final def withToxN[R]( - count: Int, - options: ToxOptions = ToxOptions(), - initial: List[ToxCore] = Nil - )( - f: List[ToxCore] => R - ): R = { - if (count == 0) { - f(initial) - } else { - withTox[R](options) { tox => - withToxN(count - 1, options, tox :: initial)(f) - } - } - } - - final def withToxN[R]( - options: List[ToxOptions] - )( - f: List[ToxCore] => R - ): R = { - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - def go(options: List[ToxOptions], toxes: List[ToxCore]): R = { - options match { - case Nil => f(toxes.reverse) - case opts :: tail => - withTox[R](opts) { tox => - go(tail, tox :: toxes) - } - } - } - go(options, Nil) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/ToxCoreJavaTest.java b/src/test/java/im/tox/tox4j/core/ToxCoreJavaTest.java new file mode 100644 index 000000000..84d8fe64d --- /dev/null +++ b/src/test/java/im/tox/tox4j/core/ToxCoreJavaTest.java @@ -0,0 +1,42 @@ +package im.tox.tox4j.core; + +import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter; +import im.tox.tox4j.core.data.ToxFriendNumber; +import im.tox.tox4j.core.enums.ToxConnection; +import im.tox.tox4j.core.options.ToxOptions; +import im.tox.tox4j.impl.jni.ToxCoreImpl; +import org.junit.Test; + +public class ToxCoreJavaTest { + @Test + public void addFriendNorequest_shouldConnectTwoToxes() throws InterruptedException { + ToxCore tox1 = new ToxCoreImpl(ToxOptions.Companion.getDefaultInstance()); + ToxCore tox2 = new ToxCoreImpl(ToxOptions.Companion.getDefaultInstance()); + + tox2.bootstrap("localhost", tox1.getUdpPort(), tox1.getDhtId()); + + tox1.addFriendNorequest(tox2.getPublicKey()); + tox2.addFriendNorequest(tox1.getPublicKey()); + + boolean connected1 = false; + boolean connected2 = false; + + while (!connected1 && !connected2) { + connected1 = tox1.iterate(new ToxCoreEventAdapter() { + @Override + public Boolean friendConnectionStatus( + int friendNumber, ToxConnection connectionStatus, Boolean state) { + return connectionStatus != ToxConnection.NONE; + } + }, connected1); + connected2 = tox2.iterate(new ToxCoreEventAdapter() { + @Override + public Boolean friendConnectionStatus( + int friendNumber, ToxConnection connectionStatus, Boolean state) { + return connectionStatus != ToxConnection.NONE; + } + }, connected2); + Thread.sleep(tox1.getIterationInterval()); + } + } +} diff --git a/src/test/java/im/tox/tox4j/core/ToxCoreTest.kt b/src/test/java/im/tox/tox4j/core/ToxCoreTest.kt new file mode 100644 index 000000000..5953c17fb --- /dev/null +++ b/src/test/java/im/tox/tox4j/core/ToxCoreTest.kt @@ -0,0 +1,92 @@ +package im.tox.tox4j.core + +import im.tox.tox4j.core.callbacks.ToxCoreEventListener +import im.tox.tox4j.core.data.ToxFriendNumber +import im.tox.tox4j.core.enums.ToxConnection +import im.tox.tox4j.core.options.ToxOptions +import im.tox.tox4j.impl.jni.ToxCoreImpl +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext +import kotlin.test.Test +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking + +class ToxCoreTest { + private class Toxes : CoroutineContext.Element { + private val list: MutableList = mutableListOf() + + override val key = Toxes + + companion object : CoroutineContext.Key { + suspend fun add(makeTox: () -> ToxCore): ToxCore { + val ctx = coroutineContext.get(Toxes) + if (ctx == null) { + throw IllegalStateException("coroutine context has no Toxes object") + } + val tox = makeTox() + ctx.list.add(tox) + return tox + } + + suspend fun close(): Unit { + val ctx = coroutineContext.get(Toxes) + if (ctx == null) { + throw IllegalStateException("coroutine context has no Toxes object") + } + for (tox in ctx.list) { + tox.close() + } + } + } + } + + private suspend fun newToxCore(options: ToxOptions): ToxCore = Toxes.add { ToxCoreImpl(options) } + + private fun runTox(block: suspend CoroutineScope.() -> Unit): Unit = + runBlocking(Toxes()) { + try { + block() + } finally { + Toxes.close() + } + } + + @Test + fun addFriendNorequest_shouldConnectTwoToxes() = runTox { + val tox1 = newToxCore(ToxOptions()) + val tox2 = newToxCore(ToxOptions()) + + tox2.bootstrap("localhost", tox1.udpPort, tox1.dhtId) + + tox1.addFriendNorequest(tox2.publicKey) + tox2.addFriendNorequest(tox1.publicKey) + + var connected1 = false + var connected2 = false + + while (!connected1 && !connected2) { + connected1 = + tox1.iterate( + object : ToxCoreEventListener { + override fun friendConnectionStatus( + friendNumber: ToxFriendNumber, + connectionStatus: ToxConnection, + state: Boolean, + ) = connectionStatus != ToxConnection.NONE + }, + connected1) + connected2 = + tox2.iterate( + object : ToxCoreEventListener { + override fun friendConnectionStatus( + friendNumber: ToxFriendNumber, + connectionStatus: ToxConnection, + state: Boolean, + ) = connectionStatus != ToxConnection.NONE + }, + connected2) + delay(tox1.iterationInterval.toLong()) + } + } +} diff --git a/src/test/java/im/tox/tox4j/core/ToxCoreTest.scala b/src/test/java/im/tox/tox4j/core/ToxCoreTest.scala deleted file mode 100644 index d383f6567..000000000 --- a/src/test/java/im/tox/tox4j/core/ToxCoreTest.scala +++ /dev/null @@ -1,67 +0,0 @@ -package im.tox.tox4j.core - -import im.tox.tox4j.core.SmallNat._ -import im.tox.tox4j.core.callbacks.ToxCoreEventListener -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendRequestMessage } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.impl.jni.ToxCoreImpl -import im.tox.tox4j.impl.jni.ToxCoreImplFactory.withToxUnit -import im.tox.tox4j.testing.GetDisjunction._ -import org.scalatest.FlatSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class ToxCoreTest extends FlatSpec with ScalaCheckPropertyChecks { - - "addFriend" should "return increasing friend numbers and increment the friend list size" in { - forAll { (count: SmallNat, message: Array[Byte]) => - whenever(message.length >= 1 && message.length <= ToxCoreConstants.MaxFriendRequestLength) { - withToxUnit { tox => - (0 until count).map(ToxFriendNumber.fromInt(_).get) foreach { i => - withToxUnit { friend => - val friendNumber = tox.addFriend( - friend.getAddress, - ToxFriendRequestMessage.fromValue(message).toOption.get - ) - assert(friendNumber == i) - } - } - assert(tox.getFriendList.length == count.self) - } - } - } - } - - "onClose callbacks" should "have been called after close" in { - var called = false - withToxUnit { tox => - tox.asInstanceOf[ToxCoreImpl].addOnCloseCallback { () => - called = true - } - } - assert(called) - } - - they should "not be called before close" in { - var called = false - withToxUnit { tox => - tox.asInstanceOf[ToxCoreImpl].addOnCloseCallback { () => - called = true - } - assert(!called) - } - } - - they should "not be called if they were unregistered" in { - var called = false - withToxUnit { tox => - val toxImpl = tox.asInstanceOf[ToxCoreImpl] - val id = toxImpl.addOnCloseCallback { () => - called = true - } - toxImpl.removeOnCloseCallback(id) - } - assert(!called) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/ToxList.scala b/src/test/java/im/tox/tox4j/core/ToxList.scala deleted file mode 100644 index a07e94798..000000000 --- a/src/test/java/im/tox/tox4j/core/ToxList.scala +++ /dev/null @@ -1,52 +0,0 @@ -package im.tox.tox4j.core - -import im.tox.core.typesafe.Equals._ -import im.tox.tox4j.core.callbacks.ToxCoreEventListener -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.core.exceptions.ToxNewException - -import scala.collection.mutable.ArrayBuffer - -object ToxList { - final case class Entry(tox: ToxCore, var connected: ToxConnection) -} - -final class ToxList(newTox: () => ToxCore, count: Int) { - - private val handler = new ToxCoreEventListener[ToxList.Entry] { - override def selfConnectionStatus(connectionStatus: ToxConnection)(state: ToxList.Entry): ToxList.Entry = { - state.connected = connectionStatus - state - } - } - - private val toxes = { - val temporary = new ArrayBuffer[ToxCore] - val instances = try { - (0 until count) map { i => - val instance = ToxList.Entry(newTox(), ToxConnection.NONE) - temporary += instance.tox - instance - } - } catch { - case e: ToxNewException => - temporary.foreach(_.close()) - throw e - } - - instances - } - - def close(): Unit = toxes.foreach(_.tox.close()) - - def isAllConnected: Boolean = toxes.forall(_.connected =/= ToxConnection.NONE) - def isAnyConnected: Boolean = toxes.exists(_.connected =/= ToxConnection.NONE) - - def iterate(): Unit = toxes.foreach(entry => entry.tox.iterate(handler)(entry)) - - def iterationInterval: Int = toxes.map(_.tox.iterationInterval).max - - def get(index: Int): ToxCore = toxes(index).tox - def size: Int = toxes.length - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/CoreInvokeTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/CoreInvokeTest.scala deleted file mode 100644 index 3886d18fe..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/CoreInvokeTest.scala +++ /dev/null @@ -1,246 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.callbacks.CoreInvokeTest._ -import im.tox.tox4j.core.callbacks.InvokeTest.ByteArray -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxMessageType, ToxUserStatus } -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.impl.jni.ToxCoreImpl -import im.tox.tox4j.testing.GetDisjunction._ -import org.scalacheck.Arbitrary.arbitrary -import org.scalacheck.{ Arbitrary, Gen } -import org.scalatest.FunSuite -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -import scala.language.implicitConversions -import scala.util.Random - -final class CoreInvokeTest extends FunSuite with ScalaCheckPropertyChecks { - - final class TestEventListener extends ToxCoreEventListener[Option[Event]] { - private def setEvent(event: Event)(state: Option[Event]): Option[Event] = { - assert(state.isEmpty) - Some(event) - } - - // scalastyle:off line.size.limit - override def friendTyping(friendNumber: ToxFriendNumber, isTyping: Boolean)(state: Option[Event]): Option[Event] = setEvent(FriendTyping(friendNumber, isTyping))(state) - override def friendStatusMessage(friendNumber: ToxFriendNumber, message: ToxStatusMessage)(state: Option[Event]): Option[Event] = setEvent(FriendStatusMessage(friendNumber, message.value))(state) - override def fileChunkRequest(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, length: Int)(state: Option[Event]): Option[Event] = setEvent(FileChunkRequest(friendNumber, fileNumber, position, length))(state) - override def fileRecvChunk(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, data: Array[Byte])(state: Option[Event]): Option[Event] = setEvent(FileRecvChunk(friendNumber, fileNumber, position, data))(state) - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: Option[Event]): Option[Event] = setEvent(FriendConnectionStatus(friendNumber, connectionStatus))(state) - override def friendRequest(publicKey: ToxPublicKey, timeDelta: Int, message: ToxFriendRequestMessage)(state: Option[Event]): Option[Event] = setEvent(FriendRequest(publicKey.value, timeDelta, message.value))(state) - override def friendLossyPacket(friendNumber: ToxFriendNumber, data: ToxLossyPacket)(state: Option[Event]): Option[Event] = setEvent(FriendLossyPacket(friendNumber, data.value))(state) - override def friendStatus(friendNumber: ToxFriendNumber, status: ToxUserStatus)(state: Option[Event]): Option[Event] = setEvent(FriendStatus(friendNumber, status))(state) - override def selfConnectionStatus(connectionStatus: ToxConnection)(state: Option[Event]): Option[Event] = setEvent(SelfConnectionStatus(connectionStatus))(state) - override def friendReadReceipt(friendNumber: ToxFriendNumber, messageId: Int)(state: Option[Event]): Option[Event] = setEvent(FriendReadReceipt(friendNumber, messageId))(state) - override def friendName(friendNumber: ToxFriendNumber, name: ToxNickname)(state: Option[Event]): Option[Event] = setEvent(FriendName(friendNumber, name.value))(state) - override def friendLosslessPacket(friendNumber: ToxFriendNumber, data: ToxLosslessPacket)(state: Option[Event]): Option[Event] = setEvent(FriendLosslessPacket(friendNumber, data.value))(state) - override def friendMessage(friendNumber: ToxFriendNumber, messageType: ToxMessageType, timeDelta: Int, message: ToxFriendMessage)(state: Option[Event]): Option[Event] = setEvent(FriendMessage(friendNumber, messageType, timeDelta, message.value))(state) - override def fileRecv(friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, filename: ToxFilename)(state: Option[Event]): Option[Event] = setEvent(FileRecv(friendNumber, fileNumber, kind, fileSize, filename.value))(state) - override def fileRecvControl(friendNumber: ToxFriendNumber, fileNumber: Int, control: ToxFileControl)(state: Option[Event]): Option[Event] = setEvent(FileRecvControl(friendNumber, fileNumber, control))(state) - // scalastyle:on line.size.limit - } - - def callbackTest(invoke: ToxCoreEventSynth => Unit, expected: Event): Unit = { - val tox = new ToxCoreImpl(ToxOptions()) - - try { - invoke(tox) - val listener = new TestEventListener - val event = tox.iterate(listener)(None) - assert(event.contains(expected)) - } finally { - tox.close() - } - } - - private val random = new Random - - private implicit val arbToxPublicKey: Arbitrary[ToxPublicKey] = { - Arbitrary(Gen.const(ToxPublicKey.Size).map(Array.ofDim[Byte]).map { array => - random.nextBytes(array) - array(array.length - 1) = 0 - ToxPublicKey.fromValue(array).toOption.get - }) - } - - private implicit val arbToxFriendNumber: Arbitrary[ToxFriendNumber] = { - Arbitrary(arbitrary[Int].map(ToxFriendNumber.unsafeFromInt)) - } - - private implicit val arbToxNickname: Arbitrary[ToxNickname] = { - Arbitrary(arbitrary[Array[Byte]].map(ToxNickname.unsafeFromValue)) - } - - private implicit val arbToxConnection: Arbitrary[ToxConnection] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map { i => ToxConnection.values()(Math.abs(i % ToxConnection.values().length)) }) - } - - private implicit val arbToxFileControl: Arbitrary[ToxFileControl] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map { i => ToxFileControl.values()(Math.abs(i % ToxFileControl.values().length)) }) - } - - private implicit val arbToxUserStatus: Arbitrary[ToxUserStatus] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map { i => ToxUserStatus.values()(Math.abs(i % ToxUserStatus.values().length)) }) - } - - private implicit val arbToxMessageType: Arbitrary[ToxMessageType] = { - Arbitrary(Arbitrary.arbInt.arbitrary.map { i => ToxMessageType.values()(Math.abs(i % ToxMessageType.values().length)) }) - } - - test("FriendTyping") { - forAll { (friendNumber: ToxFriendNumber, isTyping: Boolean) => - callbackTest( - _.invokeFriendTyping(friendNumber, isTyping), - FriendTyping(friendNumber, isTyping) - ) - } - } - - test("FriendStatusMessage") { - forAll { (friendNumber: ToxFriendNumber, message: Array[Byte]) => - callbackTest( - _.invokeFriendStatusMessage(friendNumber, message), - FriendStatusMessage(friendNumber, message) - ) - } - } - - test("FileChunkRequest") { - forAll { (friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, length: Int) => - callbackTest( - _.invokeFileChunkRequest(friendNumber, fileNumber, position, length), - FileChunkRequest(friendNumber, fileNumber, position, length) - ) - } - } - - test("FileRecvChunk") { - forAll { (friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, data: Array[Byte]) => - callbackTest( - _.invokeFileRecvChunk(friendNumber, fileNumber, position, data), - FileRecvChunk(friendNumber, fileNumber, position, data) - ) - } - } - - test("FriendConnectionStatus") { - forAll { (friendNumber: ToxFriendNumber, connectionStatus: ToxConnection) => - callbackTest( - _.invokeFriendConnectionStatus(friendNumber, connectionStatus), - FriendConnectionStatus(friendNumber, connectionStatus) - ) - } - } - - test("FriendRequest") { - forAll { (publicKey: ToxPublicKey, timeDelta: Int, message: Array[Byte]) => - callbackTest( - _.invokeFriendRequest(publicKey, timeDelta, message), - FriendRequest(publicKey.value, /* timeDelta */ 0, message) - ) - } - } - - test("FriendLossyPacket") { - forAll { (friendNumber: ToxFriendNumber, data: Array[Byte]) => - callbackTest( - _.invokeFriendLossyPacket(friendNumber, data), - FriendLossyPacket(friendNumber, data) - ) - } - } - - test("FriendStatus") { - forAll { (friendNumber: ToxFriendNumber, status: ToxUserStatus) => - callbackTest( - _.invokeFriendStatus(friendNumber, status), - FriendStatus(friendNumber, status) - ) - } - } - - test("SelfConnectionStatus") { - forAll { (connectionStatus: ToxConnection) => - callbackTest( - _.invokeSelfConnectionStatus(connectionStatus), - SelfConnectionStatus(connectionStatus) - ) - } - } - - test("FriendReadReceipt") { - forAll { (friendNumber: ToxFriendNumber, messageId: Int) => - callbackTest( - _.invokeFriendReadReceipt(friendNumber, messageId), - FriendReadReceipt(friendNumber, messageId) - ) - } - } - - test("FriendName") { - forAll { (friendNumber: ToxFriendNumber, name: ToxNickname) => - callbackTest( - _.invokeFriendName(friendNumber, name), - FriendName(friendNumber, name.value) - ) - } - } - - test("FriendLosslessPacket") { - forAll { (friendNumber: ToxFriendNumber, data: Array[Byte]) => - callbackTest( - _.invokeFriendLosslessPacket(friendNumber, data), - FriendLosslessPacket(friendNumber, data) - ) - } - } - - test("FriendMessage") { - forAll { (friendNumber: ToxFriendNumber, messageType: ToxMessageType, timeDelta: Int, message: Array[Byte]) => - callbackTest( - _.invokeFriendMessage(friendNumber, messageType, timeDelta, message), - FriendMessage(friendNumber, messageType, /* timeDelta */ 0, message) - ) - } - } - - test("FileRecv") { - forAll { (friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, filename: Array[Byte]) => - callbackTest( - _.invokeFileRecv(friendNumber, fileNumber, kind, fileSize, filename), - FileRecv(friendNumber, fileNumber, kind, fileSize, filename) - ) - } - } - - test("FileRecvControl") { - forAll { (friendNumber: ToxFriendNumber, fileNumber: Int, control: ToxFileControl) => - callbackTest( - _.invokeFileRecvControl(friendNumber, fileNumber, control), - FileRecvControl(friendNumber, fileNumber, control) - ) - } - } - -} - -object CoreInvokeTest { - sealed trait Event - private final case class FriendTyping(friendNumber: ToxFriendNumber, isTyping: Boolean) extends Event - private final case class FriendStatusMessage(friendNumber: ToxFriendNumber, message: ByteArray) extends Event - private final case class FileChunkRequest(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, length: Int) extends Event - private final case class FileRecvChunk(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, data: ByteArray) extends Event - private final case class FriendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection) extends Event - private final case class FriendRequest(publicKey: ByteArray, timeDelta: Int, message: ByteArray) extends Event - private final case class FriendLossyPacket(friendNumber: ToxFriendNumber, data: ByteArray) extends Event - private final case class FriendStatus(friendNumber: ToxFriendNumber, status: ToxUserStatus) extends Event - private final case class SelfConnectionStatus(connectionStatus: ToxConnection) extends Event - private final case class FriendReadReceipt(friendNumber: ToxFriendNumber, messageId: Int) extends Event - private final case class FriendName(friendNumber: ToxFriendNumber, name: ByteArray) extends Event - private final case class FriendLosslessPacket(friendNumber: ToxFriendNumber, data: ByteArray) extends Event - private final case class FriendMessage(friendNumber: ToxFriendNumber, messageType: ToxMessageType, timeDelta: Int, message: ByteArray) extends Event - private final case class FileRecv(friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, filename: ByteArray) extends Event - private final case class FileRecvControl(friendNumber: ToxFriendNumber, fileNumber: Int, control: ToxFileControl) extends Event -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeTestBase.scala b/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeTestBase.scala deleted file mode 100644 index de40e7043..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeTestBase.scala +++ /dev/null @@ -1,228 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.TestConstants -import im.tox.tox4j.core._ -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFileId, ToxFilename, ToxFriendMessage } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxFileKind, ToxMessageType } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } -import java.util.Random - -/** - * This test intends to simulate the situation of file pause - * and resume initiated by both the sending side and the receiving side. - * - Alice initiated the file transmission and Bob accepted - * - After sending 1/4 of the file, Alice paused the transmission - * - Bob saw Alice's paused transmission and sent a message to request resuming - * - Alice resumed the transmission - * - Bob paused the transmission after receiving 2/4 of the file - * - Alice saw Bob paused transmission and sent a message to request resuming - * - Bob resumed the transmission and received all the data - */ -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -abstract class FilePauseResumeTestBase extends AliceBobTest { - - val fileData: Seq[Byte] = { - val data = new Array[Byte](TestConstants.Iterations * ToxCoreConstants.MaxCustomPacketSize) - new Random().nextBytes(data) - data.toSeq - } - - @SuppressWarnings(Array("org.wartremover.warts.ArrayEquals")) - final case class State( - aliceSentFileNumber: Int = -1, - aliceOffset: Long = 0L, - aliceShouldPause: Int = -1, - fileId: ToxFileId = ToxFileId.empty, - receivedData: Array[Byte] = Array.ofDim(fileData.length), - bobSentFileNumber: Int = -1, - bobOffset: Long = 0L, - bobShouldPause: Int = -1 - ) - - final override def initialState: State = State() - - abstract class Alice(name: String, expectedFriendName: String) extends ChatClient(name, expectedFriendName) { - - protected def addFriendMessageTask( - friendNumber: ToxFriendNumber, - bobSentFileNumber: Int, - fileId: ToxFileId, - tox: ToxCore - )(state: State): State - protected def addFileRecvTask( - friendNumber: ToxFriendNumber, - bobSentFileNumber: Int, - bobOffset: Long, - tox: ToxCore - )(state: State): State - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connection: ToxConnection - )(state: ChatState): ChatState = { - if (isAlice) { - if (connection != ToxConnection.NONE) { - debug(s"is now connected to friend $friendNumber") - debug(s"initiate file sending to friend $friendNumber") - assert(friendNumber == AliceBobTestBase.FriendNumber) - state.addTask { (tox, av, state) => - val aliceSentFileNumber = tox.fileSend( - friendNumber, - ToxFileKind.DATA, - fileData.length, - ToxFileId.empty, - ToxFilename.fromValue(("file for " + expectedFriendName + ".png").getBytes).toOption.get - ) - state.map(_.copy( - fileId = tox.getFileFileId(friendNumber, aliceSentFileNumber), - aliceSentFileNumber = aliceSentFileNumber - )) - } - } else { - state - } - } else { - if (connection != ToxConnection.NONE) { - debug(s"is now connected to friend $friendNumber") - assert(friendNumber == AliceBobTestBase.FriendNumber) - } - state - } - } - - override def fileRecv( - friendNumber: ToxFriendNumber, - fileNumber: Int, - kind: Int, - fileSize: Long, - filename: ToxFilename - )(state: ChatState): ChatState = { - assert(isBob) - debug(s"received file send request $fileNumber from friend number $friendNumber current offset ${state.get.bobOffset}") - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(kind == ToxFileKind.DATA) - assert(new String(filename.value) == s"file for $name.png") - - state.addTask { (tox, av, state) => - assert(state.get.bobSentFileNumber == fileNumber) - state.map(addFileRecvTask(friendNumber, state.get.bobSentFileNumber, state.get.bobOffset, tox)) - }.map(_.copy(bobSentFileNumber = fileNumber)) - } - - override def fileChunkRequest( - friendNumber: ToxFriendNumber, - fileNumber: Int, - position: Long, - length: Int - )(state: ChatState): ChatState = { - assert(isAlice) - debug(s"got request for ${length}B from $friendNumber for file $fileNumber at $position") - assert(length >= 0) - if (length == 0) { - debug("finish transmission") - state.map(_.copy(aliceSentFileNumber = -1)).finish - } else { - val nextState = state.addTask { (tox, av, state) => - debug(s"sending ${length}B to $friendNumber from position $position") - tox.fileSendChunk(friendNumber, fileNumber, position, - fileData.slice(position.toInt, Math.min(position.toInt + length, fileData.length)).toArray) - state - }.map(state => state.copy(aliceOffset = state.aliceOffset + length)) - if (state.get.aliceOffset >= fileData.length / 4 && state.get.aliceShouldPause == -1) { - nextState - .map(_.copy(aliceShouldPause = 0)) - .addTask { (tox, av, state) => - tox.fileControl(friendNumber, fileNumber, ToxFileControl.PAUSE) - debug("pause file transmission") - state - } - } else { - nextState - } - } - } - - override def fileRecvControl( - friendNumber: ToxFriendNumber, - fileNumber: Int, - control: ToxFileControl - )(state: ChatState): ChatState = { - if (isAlice) { - debug("receive file control from Bob") - if (control == ToxFileControl.RESUME) { - if (state.get.aliceShouldPause != 0) { - debug("bob accept file transmission request") - state - } else { - debug("see bob resume file transmission") - state.map(_.copy(aliceShouldPause = 1)) - } - } else if (control == ToxFileControl.PAUSE) { - state.addTask { (tox, av, state) => - tox.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, - ToxFriendMessage.fromValue("Please resume the file transfer".getBytes).toOption.get) - state.map(_.copy(aliceShouldPause = 0)) - } - } else { - state - } - } else { - if (control == ToxFileControl.PAUSE) { - debug("see alice pause file transmission") - state.addTask { (tox, av, state) => - debug("request to resume file transmission") - tox.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, - ToxFriendMessage.fromValue("Please resume the file transfer".getBytes).toOption.get) - state - } - } else { - state - } - } - } - - override def fileRecvChunk( - friendNumber: ToxFriendNumber, - fileNumber: Int, - position: Long, - data: Array[Byte] - )(state: ChatState): ChatState = { - assert(isBob) - debug(s"receive file chunk from position $position of length ${data.length} shouldPause ${state.get.bobShouldPause}") - if (data.length == 0 && state.get.bobOffset == fileData.length) { - assert(state.get.receivedData sameElements fileData) - debug("finish transmission") - state.finish - } else { - System.arraycopy(data, 0, state.get.receivedData, position.toInt, data.length) - val nextState = state.map(state => state.copy(bobOffset = state.bobOffset + data.length)) - if (nextState.get.bobOffset >= fileData.length * 2 / 4 && nextState.get.bobShouldPause == -1) { - nextState - .map(_.copy(bobShouldPause = 0)) - .addTask { (tox, av, state) => - debug("send file control to pause") - tox.fileControl(friendNumber, state.get.bobSentFileNumber, ToxFileControl.PAUSE) - state - } - } else { - nextState - } - } - } - - override def friendMessage( - friendNumber: ToxFriendNumber, - messageType: ToxMessageType, - timeDelta: Int, - message: ToxFriendMessage - )(state: ChatState): ChatState = { - debug(s"received a message: ${new String(message.value)}") - assert(new String(message.value) == "Please resume the file transfer") - state.addTask { (tox, av, state) => - state.map(addFriendMessageTask(friendNumber, state.get.bobSentFileNumber, state.get.fileId, tox)) - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeWithControlTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeWithControlTest.scala deleted file mode 100644 index e0d852ce6..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeWithControlTest.scala +++ /dev/null @@ -1,45 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFileId } -import im.tox.tox4j.core.enums.ToxFileControl - -final class FilePauseResumeWithControlTest extends FilePauseResumeTestBase { - - protected override def newChatClient(name: String, expectedFriendName: String) = new Alice(name, expectedFriendName) - - final class Alice(name: String, expectedFriendName: String) extends super.Alice(name, expectedFriendName) { - - protected override def addFriendMessageTask( - friendNumber: ToxFriendNumber, - bobSentFileNumber: Int, - fileId: ToxFileId, - tox: ToxCore - )(state: State): State = { - debug("send resume control") - if (isBob) { - tox.fileControl(friendNumber, bobSentFileNumber, ToxFileControl.RESUME) - state.copy(bobShouldPause = 1) - } else if (isAlice) { - tox.fileControl(friendNumber, state.aliceSentFileNumber, ToxFileControl.RESUME) - state.copy(aliceShouldPause = 1) - } else { - fail("Unexpected client (not Alice or Bob)") - state - } - } - - protected override def addFileRecvTask( - friendNumber: ToxFriendNumber, - bobSentFileNumber: Int, - bobOffset: Long, - tox: ToxCore - )(state: State): State = { - debug(s"sending control RESUME for $bobSentFileNumber") - tox.fileControl(friendNumber, bobSentFileNumber, ToxFileControl.RESUME) - state - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeWithResendTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeWithResendTest.scala deleted file mode 100644 index e19b5ca93..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FilePauseResumeWithResendTest.scala +++ /dev/null @@ -1,52 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFileId, ToxFilename } -import im.tox.tox4j.core.enums.{ ToxFileControl, ToxFileKind } - -final class FilePauseResumeWithResendTest extends FilePauseResumeTestBase { - - protected override def newChatClient(name: String, expectedFriendName: String) = new Alice(name, expectedFriendName) - - final class Alice(name: String, expectedFriendName: String) extends super.Alice(name, expectedFriendName) { - - protected override def addFriendMessageTask( - friendNumber: ToxFriendNumber, - bobSentFileNumber: Int, - fileId: ToxFileId, - tox: ToxCore - )(state: State): State = { - if (isAlice) { - state.copy( - aliceShouldPause = 1, - aliceSentFileNumber = tox.fileSend( - friendNumber, - ToxFileKind.DATA, - fileData.length, - fileId, - ToxFilename.fromValue(("file for " + expectedFriendName + ".png").getBytes).toOption.get - ) - ) - } else { - debug("Send resume file transmission control") - tox.fileControl(friendNumber, bobSentFileNumber, ToxFileControl.RESUME) - state - } - } - - protected override def addFileRecvTask( - friendNumber: ToxFriendNumber, - bobSentFileNumber: Int, - bobOffset: Long, - tox: ToxCore - )(state: State): State = { - debug(s"seek file to $bobOffset") - tox.fileSeek(friendNumber, bobSentFileNumber, bobOffset) - debug(s"sending control RESUME for $bobSentFileNumber") - tox.fileControl(friendNumber, bobSentFileNumber, ToxFileControl.RESUME) - state.copy(bobShouldPause = 1) - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FileRecvCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FileRecvCallbackTest.scala deleted file mode 100644 index 5fab284c2..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FileRecvCallbackTest.scala +++ /dev/null @@ -1,62 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFileId, ToxFilename } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileKind } -import im.tox.tox4j.core.{ ToxCore, ToxCoreConstants } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class FileRecvCallbackTest extends AliceBobTest { - - override type State = Seq[Byte] - override def initialState: State = Nil - - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def setup(tox: ToxCore)(state: ChatState): ChatState = { - if (isAlice) { - state.set("This is a file for Bob".getBytes) - } else { - state.set("This is a file for Alice".getBytes) - } - } - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - assert(friendNumber == AliceBobTestBase.FriendNumber) - state.addTask { (tox, av, state) => - tox.fileSend( - friendNumber, - ToxFileKind.DATA, - state.get.length, - ToxFileId.empty, - ToxFilename.fromValue(s"file for $expectedFriendName.png".getBytes).toOption.get - ) - state - } - } else { - state - } - } - - override def fileRecv(friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, filename: ToxFilename)(state: ChatState): ChatState = { - debug(s"received file send request $fileNumber from friend number $friendNumber") - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(fileNumber == (0 | 0x10000)) - assert(kind == ToxFileKind.DATA) - if (isAlice) { - assert(fileSize == "This is a file for Alice".length) - } else { - assert(fileSize == "This is a file for Bob".length) - } - assert(new String(filename.value) == s"file for $selfName.png") - state.addTask { (tox, av, state) => - val fileId = tox.getFileFileId(friendNumber, fileNumber) - assert(fileId.value != null) - assert(fileId.value.length == ToxCoreConstants.FileIdLength) - state.finish - } - } - } -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FileResumeAfterRestartTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FileResumeAfterRestartTest.scala deleted file mode 100644 index f78eb9402..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FileResumeAfterRestartTest.scala +++ /dev/null @@ -1,156 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import java.util.Random - -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxFileKind } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -/** - * This test intends to simulate the situation of resuming a file - * transmission after deleting and re-adding a friend. - * - Alice initiated the file transmission and Bob accepted - * - Alice paused the file transmission after sending 1/10 of the file - * and deleted Bob from the friend list - * - Bob saw Alice went offline and sent a friend request to Alice - * - Alice accepted the friend request and resumed the file transmission - * - Bob received all the file data - */ -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class FileResumeAfterRestartTest extends AliceBobTest { - - override type State = Unit - override def initialState: State = () - - private val fileData = new Array[Byte](13710) - private var aliceAddress = ToxFriendAddress.unsafeFromValue(Array.empty) - new Random().nextBytes(fileData) - - protected override def newChatClient(name: String, expectedFriendName: String) = new Alice(name, expectedFriendName) - - final class Alice(name: String, expectedFriendName: String) extends ChatClient(name, expectedFriendName) { - - private var aliceOffset = 0L - private var fileId = ToxFileId.empty - private var aliceSentFileNumber = -1 - private var aliceShouldPause = -1 - private val receivedData = new Array[Byte](fileData.length) - private var bobSentFileNumber = -1 - private var bobOffset = 0L - private var selfPublicKey = ToxPublicKey.unsafeFromValue(Array.empty) - - override def friendRequest(publicKey: ToxPublicKey, timeDelta: Int, message: ToxFriendRequestMessage)(state: ChatState): ChatState = { - assert(isAlice) - state.addTask { (tox, av, state) => - debug("accept Bob's friend request") - tox.addFriendNorequest(publicKey) - aliceShouldPause = 1 - state - } - } - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connection: ToxConnection)(state: ChatState): ChatState = { - if (isAlice) { - if (connection != ToxConnection.NONE) { - debug(s"is now connected to friend $friendNumber") - assert(friendNumber == AliceBobTestBase.FriendNumber) - debug(s"initiate file sending to friend $friendNumber") - state.addTask { (tox, av, state) => - aliceSentFileNumber = tox.fileSend( - friendNumber, - ToxFileKind.DATA, - fileData.length, - ToxFileId.empty, - ToxFilename.fromValue(s"file for $expectedFriendName.png".getBytes).toOption.get - ) - fileId = tox.getFileFileId(friendNumber, aliceSentFileNumber) - aliceAddress = tox.getAddress - state - } - } else { - state - } - } else { - if (connection != ToxConnection.NONE) { - debug(s"is now connected to friend $friendNumber") - assert(friendNumber == AliceBobTestBase.FriendNumber) - state - } else { - debug("See alice go off-line") - state.addTask { (tox, av, state) => - tox.deleteFriend(friendNumber) - tox.addFriend(aliceAddress, ToxFriendRequestMessage.fromValue("Please add me back".getBytes).toOption.get) - state - } - } - } - } - - override def fileChunkRequest(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, length: Int)(state: ChatState): ChatState = { - assert(isAlice) - debug(s"got request for ${length}B from $friendNumber for file $fileNumber at $position") - assert(length >= 0) - if (length == 0) { - aliceSentFileNumber = -1 - debug("finish transmission") - state.finish - } else { - if (aliceShouldPause != 0) { - val nextState = state.addTask { (tox, av, state) => - debug(s"sending $length B to $friendNumber from position $position") - tox.fileSendChunk(friendNumber, fileNumber, position, - fileData.slice(position.toInt, Math.min(position.toInt + length, fileData.length))) - state - } - aliceOffset += length - if (aliceOffset >= fileData.length / 10 && aliceShouldPause == -1) { - aliceShouldPause = 0 - nextState.addTask { (tox, av, state) => - debug("pause file transmission") - tox.fileControl(friendNumber, fileNumber, ToxFileControl.PAUSE) - tox.deleteFriend(friendNumber) - state - } - } else { - nextState - } - } else { - state - } - } - } - - override def fileRecv(friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, filename: ToxFilename)(state: ChatState): ChatState = { - assert(isBob) - debug(s"received file send request $fileNumber from friend number $friendNumber current offset $bobOffset") - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(kind == ToxFileKind.DATA) - assert(new String(filename.value) == s"file for $name.png") - bobSentFileNumber = fileNumber - state.addTask { (tox, av, state) => - selfPublicKey = tox.getPublicKey - debug(s"sending control RESUME for $fileNumber") - debug(s"seek file to $bobOffset") - tox.fileSeek(friendNumber, bobSentFileNumber, bobOffset) - tox.fileControl(friendNumber, fileNumber, ToxFileControl.RESUME) - state - } - } - - override def fileRecvChunk(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, data: Array[Byte])(state: ChatState): ChatState = { - assert(isBob) - debug(s"receive file chunk from position $position of length ${data.length}") - if (data.length == 0 && receivedData.length == bobOffset) { - assert(receivedData sameElements fileData) - debug("finish transmission") - state.finish - } else { - System.arraycopy(data, 0, receivedData, position.toInt, data.length) - bobOffset += data.length - state - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FileTransferTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FileTransferTest.scala deleted file mode 100644 index 1a777692f..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FileTransferTest.scala +++ /dev/null @@ -1,118 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import java.util.Random - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFileId, ToxFilename } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxFileControl, ToxFileKind } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class FileTransferTest extends AliceBobTest { - - private val fileData = new Array[Byte](1500) - new Random().nextBytes(fileData) - - @SuppressWarnings(Array("org.wartremover.warts.ArrayEquals")) - sealed case class State( - receivedData: Array[Byte] = Array.ofDim[Byte](fileData.length), - position: Long = 0L, - sentFileNumber: Int = -1 - ) { - def addData(position: Long, data: Array[Byte]): State = { - assert(data.nonEmpty) - val nextPosition = this.position + data.length - assert(nextPosition <= this.receivedData.length) - System.arraycopy(data, 0, this.receivedData, position.toInt, data.length) - copy(position = nextPosition) - } - } - - override def initialState: State = State() - - protected override def newChatClient(name: String, expectedFriendName: String) = new Alice(name, expectedFriendName) - - class Alice(name: String, expectedFriendName: String) extends ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE && isAlice) { - state.addTask { (tox, av, state) => - val sentFileNumber = tox.fileSend( - friendNumber, - ToxFileKind.DATA, - fileData.length, - ToxFileId.empty, - ToxFilename.fromValue(s"file for $expectedFriendName.png".getBytes).toOption.get - ) - state.map(_.copy(sentFileNumber = sentFileNumber)) - } - } else { - state - } - } - - override def fileRecv(friendNumber: ToxFriendNumber, fileNumber: Int, kind: Int, fileSize: Long, filename: ToxFilename)(state: ChatState): ChatState = { - debug(s"received file send request $fileNumber from friend number $friendNumber") - assert(isBob) - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(fileNumber == (0 | 0x10000)) - assert(kind == ToxFileKind.DATA) - assert(fileSize == fileData.length) - assert(new String(filename.value) == s"file for $name.png") - state.addTask { (tox, av, state) => - debug(s"sending control RESUME for $fileNumber") - tox.fileControl(friendNumber, fileNumber, ToxFileControl.RESUME) - state - } - } - - override def fileRecvControl(friendNumber: ToxFriendNumber, fileNumber: Int, control: ToxFileControl)(state: ChatState): ChatState = { - debug(s"file control from $friendNumber for file $fileNumber: $control") - assert(isAlice) - state - } - - override def fileChunkRequest(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, length: Int)(state: ChatState): ChatState = { - debug(s"got request for ${length}B from $friendNumber for file $fileNumber at $position") - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(isAlice) - assert(position >= 0) - assert(position < Int.MaxValue) - assert(fileNumber == state.get.sentFileNumber) - if (length == 0) { - state.set(state.get.copy(sentFileNumber = -1)).finish - } else { - state.addTask { (tox, av, state) => - debug(s"sending ${length}B to $friendNumber") - tox.fileSendChunk( - friendNumber, - fileNumber, - position, - fileData.slice( - position.toInt, - Math.min(position.toInt + length, fileData.length) - ) - ) - state - } - } - } - - override def fileRecvChunk(friendNumber: ToxFriendNumber, fileNumber: Int, position: Long, data: Array[Byte])(state: ChatState): ChatState = { - debug(s"got ${data.length}B from $friendNumber at $position") - assert(isBob) - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(fileNumber == (0 | 0x10000)) - assert(position == state.get.position) - assert(data != null) - if (state.get.position == state.get.receivedData.length) { - assert(data.isEmpty) - assert(state.get.receivedData sameElements fileData) - state.finish - } else { - state.set(state.get.addData(position, data)) - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendConnectionStatusCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendConnectionStatusCallbackTest.scala deleted file mode 100644 index cc36d2b42..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendConnectionStatusCallbackTest.scala +++ /dev/null @@ -1,25 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.AutoTestSuite - -final class FriendConnectionStatusCallbackTest extends AutoTestSuite { - - type S = Unit - - object Handler extends EventListener(()) { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: State): State = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.finish - } else { - state - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendLosslessPacketCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendLosslessPacketCallbackTest.scala deleted file mode 100644 index 9ab39c782..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendLosslessPacketCallbackTest.scala +++ /dev/null @@ -1,39 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxLosslessPacket } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendLosslessPacketCallbackTest extends AliceBobTest { - - override type State = Unit - override def initialState: State = () - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.addTask { (tox, av, state) => - val packet = s"_My name is $selfName".getBytes - packet(0) = 160.toByte - tox.friendSendLosslessPacket(friendNumber, ToxLosslessPacket.fromValue(packet).toOption.get) - state - } - } else { - state - } - } - - override def friendLosslessPacket(friendNumber: ToxFriendNumber, packet: ToxLosslessPacket)(state: ChatState): ChatState = { - val message = new String(packet.value, 1, packet.value.length - 1) - debug(s"received a lossless packet[id=${packet.value(0)}]: $message") - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(packet.value(0) == 160.toByte) - assert(message == s"My name is $expectedFriendName") - state.finish - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendLossyPacketCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendLossyPacketCallbackTest.scala deleted file mode 100644 index 6a4753351..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendLossyPacketCallbackTest.scala +++ /dev/null @@ -1,40 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxLossyPacket } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendLossyPacketCallbackTest extends AliceBobTest { - - override type State = Unit - override def initialState: State = () - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.addTask { (tox, av, state) => - val packet = s"_My name is $selfName".getBytes - packet(0) = 200.toByte - tox.friendSendLossyPacket(friendNumber, ToxLossyPacket.fromValue(packet).toOption.get) - state - } - } else { - state - } - } - - override def friendLossyPacket(friendNumber: ToxFriendNumber, packet: ToxLossyPacket)(state: ChatState): ChatState = { - val message = new String(packet.value, 1, packet.value.length - 1) - debug(s"received a lossy packet[id=${packet.value(0)}]: $message") - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(packet.value(0) == 200.toByte) - assert(message == s"My name is $expectedFriendName") - state.finish - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendMessageCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendMessageCallbackTest.scala deleted file mode 100644 index 0ca515fa2..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendMessageCallbackTest.scala +++ /dev/null @@ -1,45 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendMessage } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxMessageType } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendMessageCallbackTest extends AliceBobTest { - - override type State = Unit - override def initialState: State = () - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.addTask { (tox, av, state) => - tox.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, - ToxFriendMessage.fromValue(s"My name is $selfName".getBytes).toOption.get) - state - } - } else { - state - } - } - - override def friendMessage( - friendNumber: ToxFriendNumber, - newType: ToxMessageType, - timeDelta: Int, - message: ToxFriendMessage - )( - state: ChatState - ): ChatState = { - debug("received a message: " + new String(message.value)) - assert(friendNumber == AliceBobTestBase.FriendNumber) - assert(newType == ToxMessageType.NORMAL) - assert(timeDelta >= 0) - assert(new String(message.value) == s"My name is $expectedFriendName") - state.finish - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendNameCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendNameCallbackTest.scala deleted file mode 100644 index face4e339..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendNameCallbackTest.scala +++ /dev/null @@ -1,43 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxNickname } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendNameCallbackTest extends AliceBobTest { - - override type State = Int - override def initialState: State = 0 - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.addTask { (tox, av, state) => - tox.setName(ToxNickname.fromValue(selfName.getBytes).toOption.get) - state - } - } else { - state - } - } - - override def friendName(friendNumber: ToxFriendNumber, name: ToxNickname)(state: ChatState): ChatState = { - debug(s"friend changed name to: ${new String(name.value)}") - assert(friendNumber == AliceBobTestBase.FriendNumber) - - state.get match { - case 0 => - assert(name.value.isEmpty) - state.set(1) - case 1 => - assert(new String(name.value) == expectedFriendName) - state.finish - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendReadReceiptCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendReadReceiptCallbackTest.scala deleted file mode 100644 index 046ff4a25..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendReadReceiptCallbackTest.scala +++ /dev/null @@ -1,55 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.TestConstants.Iterations -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendMessage } -import im.tox.tox4j.core.enums.{ ToxConnection, ToxMessageType } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendReadReceiptCallbackTest extends AliceBobTest { - - protected override def ignoreTimeout = true - - override type State = Set[Int] - override def initialState: State = Set.empty[Int] - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.addTask { (tox, av, state) => - debug(s"Sending $Iterations messages") - assert(state.get.isEmpty) - val pendingIds = (0 until Iterations).foldLeft(state.get) { - case (receipts, i) => - val receipt = tox.friendSendMessage( - friendNumber, ToxMessageType.NORMAL, 0, - ToxFriendMessage.fromString(String.valueOf(i)).toOption.get - ) - assert(!receipts.contains(receipt)) - receipts + receipt - } - assert(pendingIds.size == Iterations) - state.set(pendingIds) - } - } else { - state - } - } - - override def friendReadReceipt(friendNumber: ToxFriendNumber, messageId: Int)(state: ChatState): ChatState = { - assert(friendNumber == AliceBobTestBase.FriendNumber) - - assert(state.get.contains(messageId)) - val pendingIds = state.get - messageId - if (pendingIds.isEmpty) { - state.finish - } else { - state.set(pendingIds) - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendRequestCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendRequestCallbackTest.scala deleted file mode 100644 index 21e310f76..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendRequestCallbackTest.scala +++ /dev/null @@ -1,47 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core._ -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendRequestMessage, ToxPublicKey } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendRequestCallbackTest extends AliceBobTest { - - override type State = Unit - override def initialState: State = () - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def setup(tox: ToxCore)(state: ChatState): ChatState = { - tox.deleteFriend(AliceBobTestBase.FriendNumber) - if (isAlice) { - tox.addFriend(expectedFriendAddress, ToxFriendRequestMessage.fromString(s"Hey this is $selfName").toOption.get) - } - state - } - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.finish - } else { - state - } - } - - override def friendRequest(publicKey: ToxPublicKey, timeDelta: Int, message: ToxFriendRequestMessage)(state: ChatState): ChatState = { - debug(s"got friend request: ${new String(message.value)}") - assert(isBob, "Alice shouldn't get a friend request") - assert(publicKey.value sameElements expectedFriendPublicKey.value) - assert(timeDelta >= 0) - assert(new String(message.value) == s"Hey this is $expectedFriendName") - state.addTask { (tox, av, state) => - tox.addFriendNorequest(publicKey) - state - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendStatusCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendStatusCallbackTest.scala deleted file mode 100644 index 13150070e..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendStatusCallbackTest.scala +++ /dev/null @@ -1,57 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxUserStatus -import im.tox.tox4j.testing.autotest.AutoTestSuite - -final class FriendStatusCallbackTest extends AutoTestSuite { - - type S = ToxUserStatus - - object Handler extends EventListener(ToxUserStatus.NONE) { - - private def go(status: ToxUserStatus)(state: State): State = { - state.addTask { (tox, av, state) => - tox.setStatus(status) - state.put(status) - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override def friendStatus(friendNumber: ToxFriendNumber, status: ToxUserStatus)(state: State): State = { - debug(state, s"friend changed status to: $status") - - val isAlice = state.friendList(friendNumber) == state.id.next - val isBob = state.friendList(friendNumber) == state.id.prev - - state.get match { - case ToxUserStatus.NONE => - if (isAlice) { - assert(status == ToxUserStatus.NONE) - go(ToxUserStatus.AWAY)(state) - } else { - if (status != ToxUserStatus.NONE) { - assert(status == ToxUserStatus.AWAY) - go(ToxUserStatus.BUSY)(state) - } else { - state - } - } - - case selfStatus => - if (isAlice && selfStatus == ToxUserStatus.AWAY) { - assert(status == ToxUserStatus.BUSY) - go(ToxUserStatus.NONE)(state) - .finish - } else if (isBob && selfStatus == ToxUserStatus.BUSY) { - assert(status == ToxUserStatus.NONE) - state.finish - } else { - state - } - } - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendStatusMessageCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendStatusMessageCallbackTest.scala deleted file mode 100644 index 8eb3e2b95..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendStatusMessageCallbackTest.scala +++ /dev/null @@ -1,43 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxStatusMessage } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class FriendStatusMessageCallbackTest extends AliceBobTest { - - override type State = Int - override def initialState: State = 0 - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connectionStatus: ToxConnection)(state: ChatState): ChatState = { - super.friendConnectionStatus(friendNumber, connectionStatus)(state) - if (connectionStatus != ToxConnection.NONE) { - state.addTask { (tox, av, state) => - tox.setStatusMessage(ToxStatusMessage.fromString(s"I like $expectedFriendName").toOption.get) - state - } - } else { - state - } - } - - override def friendStatusMessage(friendNumber: ToxFriendNumber, message: ToxStatusMessage)(state: ChatState): ChatState = { - debug(s"friend changed status message to: ${new String(message.value)}") - assert(friendNumber == AliceBobTestBase.FriendNumber) - - state.get match { - case 0 => - assert(message.value.isEmpty) - state.set(1) - case 1 => - assert(new String(message.value) == s"I like $selfName") - state.finish - } - } - } - -} - diff --git a/src/test/java/im/tox/tox4j/core/callbacks/FriendTypingCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/FriendTypingCallbackTest.scala deleted file mode 100644 index 704b0cea7..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/FriendTypingCallbackTest.scala +++ /dev/null @@ -1,62 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.testing.autotest.AutoTestSuite - -final class FriendTypingCallbackTest extends AutoTestSuite { - - type S = Map[ToxFriendNumber, Boolean] - - object Handler extends EventListener(Map.empty) { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override def friendTyping(friendNumber: ToxFriendNumber, isTyping: Boolean)(state0: State): State = { - debug(state0, s"friend ${state0.id(friendNumber)} typing state: $isTyping") - val state = state0.modify(_ + (friendNumber -> isTyping)) - state0.get.get(friendNumber) match { - case None => - assert(!isTyping) - state.addTask { (tox, av, state) => - if (state.id(friendNumber) == state.id.next) { - debug(state, s"we start typing to ${state.id(friendNumber)}") - tox.setTyping(friendNumber, typing = true) - } - state - } - - case Some(false) => - assert(isTyping) - if (state.id(friendNumber) == state.id.prev) { - // id-1 started typing to us, now we also start typing to them. - state.addTask { (tox, av, state) => - debug(state, s"we start typing back to ${state.id(friendNumber)}") - tox.setTyping(friendNumber, typing = true) - state - } - } else { - assert(state.id(friendNumber) == state.id.next) - // id+1 started typing back, so we stop typing. - state.addTask { (tox, av, state) => - debug(state, s"we stop typing to ${state.id(friendNumber)}") - tox.setTyping(friendNumber, typing = false) - state - } - } - - case Some(true) => - assert(!isTyping) - if (state.id(friendNumber) == state.id.prev) { - state.addTask { (tox, av, state) => - debug(state, s"we also stop typing to ${state.id(friendNumber)}") - tox.setTyping(friendNumber, typing = false) - state.finish - } - } else { - assert(state.id(friendNumber) == state.id.next) - state.finish - } - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/InvokeTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/InvokeTest.scala deleted file mode 100644 index 5a4ffd5d1..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/InvokeTest.scala +++ /dev/null @@ -1,62 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.core.typesafe.Equals._ -import scala.language.implicitConversions - -@SuppressWarnings(Array( - "org.wartremover.warts.Equals", - "org.wartremover.warts.ImplicitConversion" -)) -object InvokeTest { - - final case class ByteArray(private val array: Array[Byte]) { - override def equals(rhs: Any): Boolean = { - rhs match { - case rhs: ByteArray => this.array.deep == rhs.array.deep - case _ => false - } - } - - override def toString: String = { - this.array.deep.toString() - } - - override def hashCode: Int = { - this.array.deep.hashCode() - } - } - - implicit def wrapByteArray(array: Array[Byte]): ByteArray = { - if (array === null) { - null - } else { - ByteArray(array) - } - } - - final case class ShortArray(private val array: Array[Short]) { - override def equals(rhs: Any): Boolean = { - rhs match { - case rhs: ShortArray => this.array.deep == rhs.array.deep - case _ => false - } - } - - override def toString: String = { - this.array.deep.toString() - } - - override def hashCode: Int = { - this.array.deep.hashCode() - } - } - - implicit def wrapShortArray(array: Array[Short]): ShortArray = { - if (array === null) { - null - } else { - ShortArray(array) - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/NameEmptyTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/NameEmptyTest.scala deleted file mode 100644 index 2ae4a6947..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/NameEmptyTest.scala +++ /dev/null @@ -1,45 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxNickname } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class NameEmptyTest extends AliceBobTest { - - override type State = Int - override def initialState: State = 0 - - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override def friendName(friendNumber: ToxFriendNumber, name: ToxNickname)(state: ChatState): ChatState = { - debug("friend changed name to: " + new String(name.value)) - assert(friendNumber == AliceBobTestBase.FriendNumber) - - state.get match { - case 0 => - // Initial empty name - assert(name.value.isEmpty) - - state.addTask { (tox, av, state) => - tox.setName(ToxNickname.fromString("One").toOption.get) - state - }.set(1) - - case 1 => - assert(new String(name.value) == "One") - - state.addTask { (tox, av, state) => - tox.setName(ToxNickname.fromString("").toOption.get) - state - }.set(2) - - case 2 => - assert(name.value.isEmpty) - state.finish - } - } - - } - -} - diff --git a/src/test/java/im/tox/tox4j/core/callbacks/SelfConnectionStatusCallbackTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/SelfConnectionStatusCallbackTest.scala deleted file mode 100644 index e4c9a54d5..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/SelfConnectionStatusCallbackTest.scala +++ /dev/null @@ -1,18 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.autotest.AutoTestSuite - -final class SelfConnectionStatusCallbackTest extends AutoTestSuite { - - type S = ToxConnection - - object Handler extends EventListener(ToxConnection.NONE) { - - override def selfConnectionStatus(connection: ToxConnection)(state: State): State = { - state.finish - } - - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/StatusMessageEmptyTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/StatusMessageEmptyTest.scala deleted file mode 100644 index cb9e5bdc0..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/StatusMessageEmptyTest.scala +++ /dev/null @@ -1,61 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxStatusMessage } -import im.tox.tox4j.testing.autotest.{ AliceBobTest, AliceBobTestBase } - -final class StatusMessageEmptyTest extends AliceBobTest { - - override type State = Int - override def initialState: State = 0 - - protected override def newChatClient(name: String, expectedFriendName: String) = new ChatClient(name, expectedFriendName) { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override def friendStatusMessage(friendNumber: ToxFriendNumber, message: ToxStatusMessage)(state: ChatState): ChatState = { - debug(s"friend changed status message to: ${new String(message.value)}") - assert(friendNumber == AliceBobTestBase.FriendNumber) - state.get match { - case 0 => - val nextState = state.set(1) - assert(message.value.isEmpty) - if (isAlice) { - nextState.addTask { (tox, av, state) => - tox.setStatusMessage(ToxStatusMessage.fromString("One").toOption.get) - state - } - } else { - nextState - } - - case 1 => - val nextState = state.set(2) - if (isAlice) { - assert(new String(message.value) == "Two") - nextState.addTask { (tox, av, state) => - tox.setStatusMessage(ToxStatusMessage.fromString("").toOption.get) - state - } - } else { - assert(new String(message.value) == "One") - nextState.addTask { (tox, av, state) => - tox.setStatusMessage(ToxStatusMessage.fromString("Two").toOption.get) - state - } - } - - case 2 => - val nextState = state.finish - assert(message.value.isEmpty) - if (isBob) { - nextState.addTask { (tox, av, state) => - tox.setStatusMessage(ToxStatusMessage.fromString("").toOption.get) - state - } - } else { - nextState - } - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/callbacks/ToxCoreEventAdapterTest.scala b/src/test/java/im/tox/tox4j/core/callbacks/ToxCoreEventAdapterTest.scala deleted file mode 100644 index fc5330cb7..000000000 --- a/src/test/java/im/tox/tox4j/core/callbacks/ToxCoreEventAdapterTest.scala +++ /dev/null @@ -1,82 +0,0 @@ -package im.tox.tox4j.core.callbacks - -import im.tox.tox4j.core.data._ -import im.tox.tox4j.core.enums._ -import im.tox.tox4j.core.proto._ -import im.tox.tox4j.testing.GetDisjunction._ -import org.scalatest.FunSuite - -final class ToxCoreEventAdapterTest extends FunSuite { - - private val listener = new ToxCoreEventAdapter[Unit] - private val friendNumber = ToxFriendNumber.fromInt(0).get - - def test[T](f: => Unit)(implicit evidence: Manifest[T]): Unit = { - test(evidence.runtimeClass.getSimpleName)(f) - } - - test[SelfConnectionStatus] { - listener.selfConnectionStatus(ToxConnection.NONE)(()) - } - - test[FileRecvControl] { - listener.fileRecvControl(friendNumber, 0, ToxFileControl.RESUME)(()) - } - - test[FileRecv] { - listener.fileRecv(friendNumber, 0, ToxFileKind.DATA, 0, ToxFilename.fromString("").toOption.get)(()) - } - - test[FileRecvChunk] { - listener.fileRecvChunk(friendNumber, 0, 0, Array.empty)(()) - } - - test[FileChunkRequest] { - listener.fileChunkRequest(friendNumber, 0, 0, 0)(()) - } - - test[FriendConnectionStatus] { - listener.friendConnectionStatus(friendNumber, ToxConnection.NONE)(()) - } - - test[FriendMessage] { - listener.friendMessage(friendNumber, ToxMessageType.NORMAL, 0, ToxFriendMessage.fromString("hello").toOption.get)(()) - } - - test[FriendName] { - listener.friendName(friendNumber, ToxNickname.fromString("").toOption.get)(()) - } - - test[FriendRequest] { - listener.friendRequest( - ToxPublicKey.unsafeFromValue(null), - 0, - ToxFriendRequestMessage.fromString("").toOption.get - )(()) - } - - test[FriendStatus] { - listener.friendStatus(friendNumber, ToxUserStatus.NONE)(()) - } - - test[FriendStatusMessage] { - listener.friendStatusMessage(friendNumber, ToxStatusMessage.fromString("").toOption.get)(()) - } - - test[FriendTyping] { - listener.friendTyping(friendNumber, isTyping = false)(()) - } - - test[FriendLosslessPacket] { - listener.friendLosslessPacket(friendNumber, ToxLosslessPacket.fromByteArray(160, Array.empty).toOption.get)(()) - } - - test[FriendLossyPacket] { - listener.friendLossyPacket(friendNumber, ToxLossyPacket.fromByteArray(200, Array.empty).toOption.get)(()) - } - - test[FriendReadReceipt] { - listener.friendReadReceipt(friendNumber, 0)(()) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxBootstrapExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxBootstrapExceptionTest.scala deleted file mode 100644 index 981e2fdee..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxBootstrapExceptionTest.scala +++ /dev/null @@ -1,95 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.core.network.Port -import im.tox.tox4j.core.ToxCoreConstants -import im.tox.tox4j.core.data.ToxPublicKey -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxBootstrapExceptionTest extends FunSuite with ToxTestMixin { - - private val host = "192.254.75.98" - private val publicKey = ToxPublicKey.fromValue(Array.ofDim(ToxCoreConstants.PublicKeySize)).toOption.get - private val port = Port.fromInt(ToxCoreConstants.DefaultStartPort).get - - test("BootstrapBadPort1") { - interceptWithTox(ToxBootstrapException.Code.BAD_PORT)( - _.bootstrap( - host, - Port.unsafeFromInt(0), - publicKey - ) - ) - } - - test("BootstrapBadPort2") { - interceptWithTox(ToxBootstrapException.Code.BAD_PORT)( - _.bootstrap( - host, - Port.unsafeFromInt(-10), - publicKey - ) - ) - } - - test("BootstrapBadPort3") { - interceptWithTox(ToxBootstrapException.Code.BAD_PORT)( - _.bootstrap( - host, - Port.unsafeFromInt(65536), - publicKey - ) - ) - } - - test("BootstrapBadHost") { - interceptWithTox(ToxBootstrapException.Code.BAD_HOST)( - _.bootstrap( - ".", - port, - publicKey - ) - ) - } - - test("BootstrapNullHost") { - interceptWithTox(ToxBootstrapException.Code.NULL)( - _.bootstrap( - null, - port, - publicKey - ) - ) - } - - test("BootstrapNullKey") { - interceptWithTox(ToxBootstrapException.Code.NULL)( - _.bootstrap( - host, - port, - ToxPublicKey.unsafeFromValue(null) - ) - ) - } - - test("BootstrapKeyTooShort") { - interceptWithTox(ToxBootstrapException.Code.BAD_KEY)( - _.bootstrap( - host, - port, - ToxPublicKey.unsafeFromValue(Array.ofDim(ToxCoreConstants.PublicKeySize - 1)) - ) - ) - } - - test("BootstrapKeyTooLong") { - interceptWithTox(ToxBootstrapException.Code.BAD_KEY)( - _.bootstrap( - host, - port, - ToxPublicKey.unsafeFromValue(Array.ofDim(ToxCoreConstants.PublicKeySize + 1)) - ) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFileSendExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFileSendExceptionTest.scala deleted file mode 100644 index 100c1ba70..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFileSendExceptionTest.scala +++ /dev/null @@ -1,35 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.ToxCoreConstants -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFileId, ToxFilename } -import im.tox.tox4j.core.enums.ToxFileKind -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFileSendExceptionTest extends FunSuite with ToxTestMixin { - - private val friendNumber = ToxFriendNumber.fromInt(0).get - private val badFriendNumber = ToxFriendNumber.fromInt(1).get - - test("FileSendNotConnected") { - interceptWithTox(ToxFileSendException.Code.FRIEND_NOT_CONNECTED)( - _.fileSend(friendNumber, ToxFileKind.DATA, 123, ToxFileId.empty, - ToxFilename.fromString("filename").toOption.get) - ) - } - - test("FileSendNotFound") { - interceptWithTox(ToxFileSendException.Code.FRIEND_NOT_FOUND)( - _.fileSend(badFriendNumber, ToxFileKind.DATA, 123, ToxFileId.empty, - ToxFilename.fromString("filename").toOption.get) - ) - } - - test("FileSendNameTooLong") { - interceptWithTox(ToxFileSendException.Code.NAME_TOO_LONG)( - _.fileSend(friendNumber, ToxFileKind.DATA, 123, ToxFileId.empty, - ToxFilename.unsafeFromValue(Array.ofDim(ToxCoreConstants.MaxFilenameLength + 1))) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendAddExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendAddExceptionTest.scala deleted file mode 100644 index 431f783ad..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendAddExceptionTest.scala +++ /dev/null @@ -1,139 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core._ -import im.tox.tox4j.core.data.{ ToxFriendAddress, ToxFriendRequestMessage } -import im.tox.tox4j.impl.jni.ToxCoreImplFactory -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFriendAddExceptionTest extends FunSuite with ToxTestMixin { - - private val validAddress = ToxCoreImplFactory.withToxUnit(_.getAddress) - - test("InvalidAddress1") { - intercept[IllegalArgumentException] { - ToxCoreImplFactory.withToxUnit( - _.addFriend( - ToxFriendAddress.unsafeFromValue(Array.ofDim(1)), - ToxFriendRequestMessage.fromValue(Array.ofDim(1)).toOption.get - ) - ) - } - } - - test("InvalidAddress2") { - intercept[IllegalArgumentException] { - ToxCoreImplFactory.withToxUnit( - _.addFriend( - ToxFriendAddress.unsafeFromValue(Array.ofDim(ToxCoreConstants.AddressSize - 1)), - ToxFriendRequestMessage.fromValue(Array.ofDim(1)).toOption.get - ) - ) - } - } - - test("InvalidAddress3") { - intercept[IllegalArgumentException] { - ToxCoreImplFactory.withToxUnit( - _.addFriend( - ToxFriendAddress.unsafeFromValue(Array.ofDim(ToxCoreConstants.AddressSize + 1)), - ToxFriendRequestMessage.fromValue(Array.ofDim(1)).toOption.get - ) - ) - } - } - - test("Null1") { - interceptWithTox(ToxFriendAddException.Code.NULL)( - _.addFriend( - ToxFriendAddress.unsafeFromValue(null), - ToxFriendRequestMessage.fromValue(Array.ofDim(1)).toOption.get - ) - ) - } - - test("Null2") { - interceptWithTox(ToxFriendAddException.Code.NULL)( - _.addFriend(validAddress, ToxFriendRequestMessage.unsafeFromValue(null)) - ) - } - - test("Not_TooLong1") { - ToxCoreImplFactory.withToxUnit( - _.addFriend( - validAddress, - ToxFriendRequestMessage.fromValue(Array.ofDim(ToxCoreConstants.MaxFriendRequestLength - 1)).toOption.get - ) - ) - } - - test("Not_TooLong2") { - ToxCoreImplFactory.withToxUnit( - _.addFriend( - validAddress, - ToxFriendRequestMessage.fromValue(Array.ofDim(ToxCoreConstants.MaxFriendRequestLength)).toOption.get - ) - ) - } - - test("TooLong") { - interceptWithTox(ToxFriendAddException.Code.TOO_LONG)( - _.addFriend( - validAddress, - ToxFriendRequestMessage.unsafeFromValue(Array.ofDim(ToxCoreConstants.MaxFriendRequestLength + 1)) - ) - ) - } - - test("NoMessage") { - interceptWithTox(ToxFriendAddException.Code.NO_MESSAGE)( - _.addFriend( - validAddress, - ToxFriendRequestMessage.fromString("").toOption.get - ) - ) - } - - test("OwnKey") { - interceptWithTox(ToxFriendAddException.Code.OWN_KEY) { tox => - tox.addFriend( - tox.getAddress, - ToxFriendRequestMessage.fromString("hello").toOption.get - ) - } - } - - test("AlreadySent") { - interceptWithTox(ToxFriendAddException.Code.ALREADY_SENT) { tox => - tox.addFriend( - validAddress, - ToxFriendRequestMessage.fromString("hello").toOption.get - ) - tox.addFriend( - validAddress, - ToxFriendRequestMessage.fromString("hello").toOption.get - ) - } - } - - test("BadChecksum") { - interceptWithTox(ToxFriendAddException.Code.BAD_CHECKSUM)( - _.addFriend( - ToxFriendAddress.unsafeFromValue(validAddress.value.updated(0, (validAddress.value(0) + 1).toByte)), - ToxFriendRequestMessage.fromString("hello").toOption.get - ) - ) - } - - test("SetNewNospam") { - interceptWithTox(ToxFriendAddException.Code.SET_NEW_NOSPAM) { tox => - ToxCoreImplFactory.withToxUnit { friend => - friend.setNospam(12345678) - tox.addFriend(friend.getAddress, ToxFriendRequestMessage.fromString("hello").toOption.get) - friend.setNospam(87654321) - tox.addFriend(friend.getAddress, ToxFriendRequestMessage.fromString("hello").toOption.get) - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendByPublicKeyExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendByPublicKeyExceptionTest.scala deleted file mode 100644 index 933e4974c..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendByPublicKeyExceptionTest.scala +++ /dev/null @@ -1,21 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.data.ToxPublicKey -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFriendByPublicKeyExceptionTest extends FunSuite with ToxTestMixin { - - test("Null") { - interceptWithTox(ToxFriendByPublicKeyException.Code.NULL)( - _.friendByPublicKey(ToxPublicKey.unsafeFromValue(null)) - ) - } - - test("NotFound") { - interceptWithTox(ToxFriendByPublicKeyException.Code.NOT_FOUND) { tox => - tox.friendByPublicKey(tox.getPublicKey) - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendCustomPacketExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendCustomPacketExceptionTest.scala deleted file mode 100644 index 3cbf05907..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendCustomPacketExceptionTest.scala +++ /dev/null @@ -1,91 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.ToxCoreConstants -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxLosslessPacket, ToxLossyPacket } -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFriendCustomPacketExceptionTest extends FunSuite with ToxTestMixin { - - private val friendNumber = ToxFriendNumber.fromInt(0).get - private val badFriendNumber = ToxFriendNumber.fromInt(1).get - - test("SendLosslessPacketNotConnected") { - interceptWithTox(ToxFriendCustomPacketException.Code.FRIEND_NOT_CONNECTED)( - _.friendSendLosslessPacket(friendNumber, ToxLosslessPacket.fromValue(Array[Byte](160.toByte, 0, 1, 2, 3)).toOption.get) - ) - } - - test("SendLossyPacketNotConnected") { - interceptWithTox(ToxFriendCustomPacketException.Code.FRIEND_NOT_CONNECTED)( - _.friendSendLossyPacket(friendNumber, ToxLossyPacket.fromValue(200.toByte +: Array.ofDim[Byte](4)).toOption.get) - ) - } - - test("SendLosslessPacketNotFound") { - interceptWithTox(ToxFriendCustomPacketException.Code.FRIEND_NOT_FOUND)( - _.friendSendLosslessPacket(badFriendNumber, ToxLosslessPacket.fromValue(Array[Byte](160.toByte, 0, 1, 2, 3)).toOption.get) - ) - } - - test("SendLossyPacketNotFound") { - interceptWithTox(ToxFriendCustomPacketException.Code.FRIEND_NOT_FOUND)( - _.friendSendLossyPacket(badFriendNumber, ToxLossyPacket.fromValue(Array[Byte](200.toByte, 0, 1, 2, 3)).toOption.get) - ) - } - - test("SendLosslessPacketInvalid") { - interceptWithTox(ToxFriendCustomPacketException.Code.INVALID)( - _.friendSendLosslessPacket(friendNumber, ToxLosslessPacket.unsafeFromValue(Array[Byte](100.toByte))) - ) - } - - test("SendLossyPacketInvalid") { - interceptWithTox(ToxFriendCustomPacketException.Code.INVALID)( - _.friendSendLossyPacket(friendNumber, ToxLossyPacket.unsafeFromValue(Array[Byte](100.toByte))) - ) - } - - test("SendLosslessPacketEmpty") { - interceptWithTox(ToxFriendCustomPacketException.Code.EMPTY)( - _.friendSendLosslessPacket(friendNumber, ToxLosslessPacket.unsafeFromValue(Array[Byte]())) - ) - } - - test("SendLossyPacketEmpty") { - interceptWithTox(ToxFriendCustomPacketException.Code.EMPTY)( - _.friendSendLossyPacket(friendNumber, ToxLossyPacket.unsafeFromValue(Array[Byte]())) - ) - } - - test("SendLosslessPacketNull") { - interceptWithTox(ToxFriendCustomPacketException.Code.NULL)( - _.friendSendLosslessPacket(friendNumber, ToxLosslessPacket.unsafeFromValue(null)) - ) - } - - test("SendLossyPacketNull") { - interceptWithTox(ToxFriendCustomPacketException.Code.NULL)( - _.friendSendLossyPacket(friendNumber, ToxLossyPacket.unsafeFromValue(null)) - ) - } - - test("SendLosslessPacketTooLong") { - interceptWithTox(ToxFriendCustomPacketException.Code.TOO_LONG)( - _.friendSendLosslessPacket( - friendNumber, - ToxLosslessPacket.unsafeFromValue(160.toByte +: Array.ofDim[Byte](ToxCoreConstants.MaxCustomPacketSize)) - ) - ) - } - - test("SendLossyPacketTooLong") { - interceptWithTox(ToxFriendCustomPacketException.Code.TOO_LONG)( - _.friendSendLossyPacket( - friendNumber, - ToxLossyPacket.unsafeFromValue(200.toByte +: Array.ofDim[Byte](ToxCoreConstants.MaxCustomPacketSize)) - ) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendDeleteExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendDeleteExceptionTest.scala deleted file mode 100644 index 5c3642481..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendDeleteExceptionTest.scala +++ /dev/null @@ -1,25 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFriendDeleteExceptionTest extends FunSuite with ToxTestMixin { - - test("DeleteFriendTwice") { - interceptWithTox(ToxFriendDeleteException.Code.FRIEND_NOT_FOUND) { tox => - addFriends(tox, 4) - assert(tox.getFriendList sameElements Array(0, 1, 2, 3, 4)) - tox.deleteFriend(ToxFriendNumber.fromInt(2).get) - assert(tox.getFriendList sameElements Array(0, 1, 3, 4)) - tox.deleteFriend(ToxFriendNumber.fromInt(2).get) - } - } - - test("DeleteNonExistentFriend") { - interceptWithTox(ToxFriendDeleteException.Code.FRIEND_NOT_FOUND)( - _.deleteFriend(ToxFriendNumber.fromInt(1).get) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendGetPublicKeyExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendGetPublicKeyExceptionTest.scala deleted file mode 100644 index aeeeef800..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendGetPublicKeyExceptionTest.scala +++ /dev/null @@ -1,18 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFriendGetPublicKeyExceptionTest extends FunSuite with ToxTestMixin { - - private val friendNumber = ToxFriendNumber.fromInt(0).get - private val badFriendNumber = ToxFriendNumber.fromInt(1).get - - test("FriendNotFound") { - interceptWithTox(ToxFriendGetPublicKeyException.Code.FRIEND_NOT_FOUND)( - _.getFriendPublicKey(badFriendNumber) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendSendMessageExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendSendMessageExceptionTest.scala deleted file mode 100644 index dccd2ba1b..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxFriendSendMessageExceptionTest.scala +++ /dev/null @@ -1,37 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendMessage } -import im.tox.tox4j.core.enums.ToxMessageType -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxFriendSendMessageExceptionTest extends FunSuite with ToxTestMixin { - - private val friendNumber = ToxFriendNumber.fromInt(0).get - private val badFriendNumber = ToxFriendNumber.fromInt(1).get - - test("SendMessageNotFound") { - interceptWithTox(ToxFriendSendMessageException.Code.FRIEND_NOT_FOUND)( - _.friendSendMessage(badFriendNumber, ToxMessageType.NORMAL, 0, ToxFriendMessage.fromString("hello").toOption.get) - ) - } - - test("SendMessageNotConnected") { - interceptWithTox(ToxFriendSendMessageException.Code.FRIEND_NOT_CONNECTED)( - _.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, ToxFriendMessage.fromString("hello").toOption.get) - ) - } - - test("SendMessageNull") { - interceptWithTox(ToxFriendSendMessageException.Code.NULL)( - _.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, ToxFriendMessage.unsafeFromValue(null)) - ) - } - - test("SendMessageEmpty") { - interceptWithTox(ToxFriendSendMessageException.Code.EMPTY)( - _.friendSendMessage(friendNumber, ToxMessageType.NORMAL, 0, ToxFriendMessage.unsafeFromValue("".getBytes)) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxGetPortExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxGetPortExceptionTest.scala deleted file mode 100644 index 687787460..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxGetPortExceptionTest.scala +++ /dev/null @@ -1,14 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxGetPortExceptionTest extends FunSuite with ToxTestMixin { - - test("GetTcpPort_NotBound") { - interceptWithTox(ToxGetPortException.Code.NOT_BOUND)( - _.getTcpPort - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxNewExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxNewExceptionTest.scala deleted file mode 100644 index a03f1bcf0..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxNewExceptionTest.scala +++ /dev/null @@ -1,71 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.options.{ ProxyOptions, SaveDataOptions } -import im.tox.tox4j.impl.jni.ToxCoreImplFactory.{ withToxUnit, withToxes } -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxNewExceptionTest extends FunSuite with ToxTestMixin { - - test("ToxNewProxyNull") { - intercept(ToxNewException.Code.PROXY_BAD_HOST) { - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5(null, 1)) { _ => } - } - } - - test("ToxNewProxyEmpty") { - intercept(ToxNewException.Code.PROXY_BAD_HOST) { - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5("", 1)) { _ => } - } - } - - test("ToxNewProxyBadPort0") { - intercept(ToxNewException.Code.PROXY_BAD_PORT) { - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5("localhost", 0)) { _ => } - } - } - - test("ToxNewProxyBadPortNegative") { - intercept[IllegalArgumentException] { - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5("localhost", -10)) { _ => } - } - } - - test("ToxNewProxyBadPortTooLarge") { - intercept[IllegalArgumentException] { - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5("localhost", 0x10000)) { _ => } - } - } - - test("ToxNewProxyBadAddress1") { - intercept(ToxNewException.Code.PROXY_BAD_HOST) { - val host = "\u2639" // scalastyle:ignore non.ascii.character.disallowed - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5(host, 1)) { _ => } - } - } - - test("ToxNewProxyBadAddress2") { - intercept(ToxNewException.Code.PROXY_BAD_HOST) { - withToxUnit(ipv6Enabled = true, udpEnabled = true, new ProxyOptions.Socks5(".", 1)) { _ => } - } - } - - test("TooManyToxCreations") { - intercept(ToxNewException.Code.PORT_ALLOC) { - withToxes(102) { _ => } - } - } - - test("LoadEncrypted") { - intercept(ToxNewException.Code.LOAD_ENCRYPTED) { - withToxUnit(SaveDataOptions.ToxSave("toxEsave blah blah blah".getBytes)) { _ => } - } - } - - test("LoadBadFormat") { - intercept(ToxNewException.Code.LOAD_BAD_FORMAT) { - withToxUnit(SaveDataOptions.ToxSave("blah blah blah".getBytes)) { _ => } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxSetInfoExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxSetInfoExceptionTest.scala deleted file mode 100644 index 4209db59c..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxSetInfoExceptionTest.scala +++ /dev/null @@ -1,39 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.ToxCoreTestBase -import im.tox.tox4j.core.ToxCoreConstants -import im.tox.tox4j.core.data.{ ToxNickname, ToxStatusMessage } -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxSetInfoExceptionTest extends FunSuite with ToxTestMixin { - - test("SetNameTooLong") { - val array = ToxCoreTestBase.randomBytes(ToxCoreConstants.MaxNameLength + 1) - - interceptWithTox(ToxSetInfoException.Code.TOO_LONG)( - _.setName(ToxNickname.unsafeFromValue(array)) - ) - } - - test("SetStatusMessageTooLong") { - val array = ToxCoreTestBase.randomBytes(ToxCoreConstants.MaxStatusMessageLength + 1) - - interceptWithTox(ToxSetInfoException.Code.TOO_LONG)( - _.setStatusMessage(ToxStatusMessage.unsafeFromValue(array)) - ) - } - - test("SetNameNull") { - interceptWithTox(ToxSetInfoException.Code.NULL)( - _.setName(ToxNickname.unsafeFromValue(null)) - ) - } - - test("SetStatusMessageNull") { - interceptWithTox(ToxSetInfoException.Code.NULL)( - _.setStatusMessage(ToxStatusMessage.unsafeFromValue(null)) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/exceptions/ToxSetTypingExceptionTest.scala b/src/test/java/im/tox/tox4j/core/exceptions/ToxSetTypingExceptionTest.scala deleted file mode 100644 index ba1ab1f0f..000000000 --- a/src/test/java/im/tox/tox4j/core/exceptions/ToxSetTypingExceptionTest.scala +++ /dev/null @@ -1,18 +0,0 @@ -package im.tox.tox4j.core.exceptions - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.testing.ToxTestMixin -import org.scalatest.FunSuite - -final class ToxSetTypingExceptionTest extends FunSuite with ToxTestMixin { - - private val friendNumber = ToxFriendNumber.fromInt(0).get - private val badFriendNumber = ToxFriendNumber.fromInt(1).get - - test("SetTypingToNonExistent") { - interceptWithTox(ToxSetTypingException.Code.FRIEND_NOT_FOUND)( - _.setTyping(badFriendNumber, typing = true) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/core/package.scala b/src/test/java/im/tox/tox4j/core/package.scala deleted file mode 100644 index 34dad1856..000000000 --- a/src/test/java/im/tox/tox4j/core/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j - -import im.tox.documentation._ - -package object core extends Documented diff --git a/src/test/java/im/tox/tox4j/crypto/ToxCryptoTest.scala b/src/test/java/im/tox/tox4j/crypto/ToxCryptoTest.scala deleted file mode 100644 index 007812d98..000000000 --- a/src/test/java/im/tox/tox4j/crypto/ToxCryptoTest.scala +++ /dev/null @@ -1,296 +0,0 @@ -package im.tox.tox4j.crypto - -import im.tox.core.random.RandomCore -import im.tox.tox4j.crypto.ToxCryptoTest.{ EncryptedData, Salt } -import im.tox.tox4j.crypto.exceptions.{ ToxDecryptionException, ToxEncryptionException, ToxKeyDerivationException } -import im.tox.tox4j.testing.ToxTestMixin -import org.scalacheck.Arbitrary.arbitrary -import org.scalacheck.{ Arbitrary, Gen } -import org.scalatest.WordSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -import scala.language.implicitConversions -import scala.util.Random - -object ToxCryptoTest { - private final case class Salt(data: Seq[Byte]) extends AnyVal - private final case class EncryptedData(data: Seq[Byte]) extends AnyVal -} - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -abstract class ToxCryptoTest(private val toxCrypto: ToxCrypto) extends WordSpec with ScalaCheckPropertyChecks with ToxTestMixin { - - private val random = new Random - - private implicit val arbSalt: Arbitrary[Salt] = - Arbitrary(Gen.const(ToxCryptoConstants.SaltLength).map(Array.ofDim[Byte]).map { array => - random.nextBytes(array) - new Salt(array) - }) - - private implicit val arbEncryptedData: Arbitrary[EncryptedData] = - Arbitrary(Gen.zip(arbitrary[Array[Byte]], Gen.nonEmptyContainerOf[Array, Byte](arbitrary[Byte])).map { - case (passphrase, data) => - val passKey = toxCrypto.passKeyDerive(passphrase) - new EncryptedData(toxCrypto.encrypt(data, passKey)) - }) - - private implicit val arbPassKey: Arbitrary[toxCrypto.PassKey] = - Arbitrary(arbitrary[Array[Byte]].map(x => toxCrypto.passKeyDerive(x))) - - "salt" should { - "be contained correctly within the encrypted data" in { - forAll { (data: Array[Byte], passphrase: Array[Byte], salt: Salt) => - whenever(data.nonEmpty) { - val passKey = toxCrypto.passKeyDeriveWithSalt(passphrase, salt.data.toArray) - - // Check that the pass key size is reasonable. - val serialised = toxCrypto.passKeyToBytes(passKey) - assert(serialised.length == ToxCryptoConstants.KeyLength + ToxCryptoConstants.SaltLength) - - val encrypted = toxCrypto.encrypt(data, passKey) - - // Salt is contained within the encrypted data. - assert(toxCrypto.getSalt(encrypted).deep == salt.data) - } - } - } - - "be a prefix of the serialised pass key" in { - forAll { (passphrase: Array[Byte], salt: Salt) => - val passKey = toxCrypto.passKeyDeriveWithSalt(passphrase, salt.data.toArray) - - val serialised = toxCrypto.passKeyToBytes(passKey) - assert(serialised.slice(0, ToxCryptoConstants.SaltLength) == salt.data) - } - } - } - - "PassKey serialisation" should { - "produce a byte sequence of the right length" in { - forAll { (passKey: toxCrypto.PassKey) => - val serialised = toxCrypto.passKeyToBytes(passKey) - assert(serialised.length == ToxCryptoConstants.KeyLength + ToxCryptoConstants.SaltLength) - } - } - - "produce the same PassKey after deserialisation" in { - forAll { (passKey: toxCrypto.PassKey) => - val serialised = toxCrypto.passKeyToBytes(passKey) - assert(toxCrypto.passKeyFromBytes(serialised).contains(passKey)) - } - } - } - - "PassKey deserialisation" should { - "fail for byte sequences of the wrong length" in { - forAll { (serialised: Seq[Byte]) => - whenever(serialised.length != ToxCryptoConstants.KeyLength + ToxCryptoConstants.SaltLength) { - assert(toxCrypto.passKeyFromBytes(serialised).isEmpty) - } - } - } - - "produce the same PassKey after deserialisation" in { - forAll { (passKey: toxCrypto.PassKey) => - val serialised = toxCrypto.passKeyToBytes(passKey) - assert(toxCrypto.passKeyFromBytes(serialised).contains(passKey)) - } - } - } - - "encryption with a PassKey" should { - import ToxEncryptionException.Code._ - - "produce high entropy results (> 0.7)" in { - val passKey = toxCrypto.passKeyDerive(Array.ofDim(0)) - forAll { (data: Array[Byte]) => - whenever(data.nonEmpty) { - assert(RandomCore.entropy(toxCrypto.encrypt(data, passKey)) > 0.7) - } - } - } - - "produce different data each time with the same PassKey" in { - forAll { (data: Array[Byte], passKey: toxCrypto.PassKey) => - whenever(data.nonEmpty) { - val encrypted1 = toxCrypto.encrypt(data, passKey) - val encrypted2 = toxCrypto.encrypt(data, passKey) - assert(encrypted1.deep != encrypted2.deep) - } - } - } - - "produce different encrypted data with a different PassKey" in { - forAll { (data: Array[Byte], passKey1: toxCrypto.PassKey, passKey2: toxCrypto.PassKey) => - whenever(data.nonEmpty && !toxCrypto.passKeyEquals(passKey1, passKey2)) { - assert(toxCrypto.encrypt(data, passKey1).deep != toxCrypto.encrypt(data, passKey2).deep) - } - } - } - - s"fail with $NULL for zero-length data" in { - val passKey = toxCrypto.passKeyDerive(Array.ofDim(0)) - intercept(NULL) { - toxCrypto.encrypt(Array.ofDim(0), passKey) - } - } - } - - "isDataEncrypted" should { - "identify encrypted data as such" in { - forAll { (data: EncryptedData) => - assert(toxCrypto.isDataEncrypted(data.data.toArray)) - } - } - - "not identify arbitrary other data as encrypted" in { - forAll { (data: Array[Byte]) => - assert(!toxCrypto.isDataEncrypted(data)) - } - } - } - - "key derivation" should { - import ToxKeyDerivationException.Code._ - - s"fail with $INVALID_LENGTH for salt of wrong size" in { - forAll { (salt: Array[Byte]) => - whenever(salt.length != ToxCryptoConstants.SaltLength) { - intercept(INVALID_LENGTH) { - toxCrypto.passKeyDeriveWithSalt(Array.ofDim(100), salt) - } - } - } - } - - "succeed for any passphrase" in { - forAll { (passphrase: Array[Byte]) => - assert(Option(toxCrypto.passKeyDerive(passphrase)).nonEmpty) - } - } - - "produce the same key given the same salt" in { - forAll { (passphrase: Array[Byte], salt: Salt) => - val key1 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt.data.toArray) - val key2 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt.data.toArray) - assert(toxCrypto.passKeyEquals(key1, key2)) - } - } - - "produce a different key given a different salt" in { - forAll { (passphrase: Array[Byte], salt1: Salt, salt2: Salt) => - whenever(salt1 != salt2) { - val key1 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt1.data.toArray) - val key2 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt2.data.toArray) - assert(!toxCrypto.passKeyEquals(key1, key2)) - } - } - } - } - - "decryption" should { - import ToxDecryptionException.Code._ - - s"fail with $INVALID_LENGTH for zero-length data" in { - val passKey = toxCrypto.passKeyDerive(Array.ofDim(0)) - intercept(INVALID_LENGTH) { - toxCrypto.decrypt(Array.ofDim(0), passKey) - } - } - - s"fail with $INVALID_LENGTH for 0 to ${ToxCryptoConstants.EncryptionExtraLength} bytes" in { - val passKey = toxCrypto.passKeyDerive(Array.ofDim(0)) - (0 to ToxCryptoConstants.EncryptionExtraLength) foreach { length => - intercept(INVALID_LENGTH) { - toxCrypto.decrypt(Array.ofDim(length), passKey) - } - } - } - - s"fail with $BAD_FORMAT for an array of more than ${ToxCryptoConstants.EncryptionExtraLength} 0-bytes" in { - val passKey = toxCrypto.passKeyDerive(Array.ofDim(0)) - intercept(BAD_FORMAT) { - toxCrypto.decrypt(Array.ofDim(ToxCryptoConstants.EncryptionExtraLength + 1), passKey) - } - } - - "succeed with the same PassKey" in { - forAll { (data: Array[Byte], passKey: toxCrypto.PassKey) => - whenever(data.nonEmpty) { - val encrypted = toxCrypto.encrypt(data, passKey) - val decrypted = toxCrypto.decrypt(encrypted, passKey) - assert(data.deep == decrypted.deep) - } - } - } - - "succeed with the same passphrase and salt" in { - forAll { (data: Array[Byte], passphrase: Array[Byte]) => - whenever(data.nonEmpty) { - val passKey1 = toxCrypto.passKeyDerive(passphrase) - val encrypted = toxCrypto.encrypt(data, passKey1) - val salt = toxCrypto.getSalt(encrypted) - - val passKey2 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt) - val decrypted = toxCrypto.decrypt(encrypted, passKey2) - - assert(data.deep == decrypted.deep) - } - } - } - - s"fail with $FAILED for the same passphrase but different salt" in { - forAll { (data: Array[Byte], passphrase: Array[Byte], salt1: Salt, salt2: Salt) => - whenever(data.nonEmpty && salt1 != salt2) { - val passKey1 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt1.data.toArray) - val passKey2 = toxCrypto.passKeyDeriveWithSalt(passphrase, salt2.data.toArray) - val encrypted = toxCrypto.encrypt(data, passKey1) - intercept(FAILED) { - toxCrypto.decrypt(encrypted, passKey2) - } - } - } - } - - s"fail with $FAILED for a different PassKey" in { - forAll { (data: Array[Byte], passKey1: toxCrypto.PassKey, passKey2: toxCrypto.PassKey) => - whenever(data.nonEmpty && !toxCrypto.passKeyEquals(passKey1, passKey2)) { - val encrypted = toxCrypto.encrypt(data, passKey1) - intercept(FAILED) { - toxCrypto.decrypt(encrypted, passKey2) - } - } - } - } - } - - "hash computation" should { - "produce the same output when called twice on the same input" in { - forAll { (data: Array[Byte]) => - assert(toxCrypto.hash(data).deep == toxCrypto.hash(data).deep) - } - } - - "produce a different output for different inputs" in { - forAll { (data1: Array[Byte], data2: Array[Byte]) => - whenever(data1.deep != data2.deep) { - assert(toxCrypto.hash(data1).deep != toxCrypto.hash(data2).deep) - } - } - } - - "create constant-length output" in { - forAll { (data: Array[Byte]) => - assert(toxCrypto.hash(data).length == ToxCryptoConstants.HashLength) - } - } - - "produce high entropy results (> 0.5)" in { - forAll { (data: Array[Byte]) => - assert(RandomCore.entropy(toxCrypto.hash(data)) > 0.5) - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/crypto/package.scala b/src/test/java/im/tox/tox4j/crypto/package.scala deleted file mode 100644 index cf1563f36..000000000 --- a/src/test/java/im/tox/tox4j/crypto/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j - -import im.tox.documentation._ - -package object crypto extends Documented diff --git a/src/test/java/im/tox/tox4j/exceptions/ToxExceptionTest.scala b/src/test/java/im/tox/tox4j/exceptions/ToxExceptionTest.scala deleted file mode 100644 index b93038864..000000000 --- a/src/test/java/im/tox/tox4j/exceptions/ToxExceptionTest.scala +++ /dev/null @@ -1,31 +0,0 @@ -package im.tox.tox4j.exceptions - -import im.tox.tox4j.core.exceptions.ToxBootstrapException -import org.scalatest.FlatSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class ToxExceptionTest extends FlatSpec with ScalaCheckPropertyChecks { - - "getMessage" should "contain the error code name" in { - ToxBootstrapException.Code.values().foreach { code => - val exn = new ToxBootstrapException(code) - assert(exn.getMessage.contains(code.name())) - } - } - - it should "contain the exception message" in { - forAll { (message: String) => - val exn = new ToxBootstrapException(ToxBootstrapException.Code.NULL, message) - assert(exn.getMessage.contains(message)) - } - } - - "code" should "be the passed code" in { - ToxBootstrapException.Code.values().foreach { code => - val exn = new ToxBootstrapException(code) - assert(exn.code == code) - } - } - -} diff --git a/src/test/java/im/tox/tox4j/exceptions/ToxKilledExceptionTest.scala b/src/test/java/im/tox/tox4j/exceptions/ToxKilledExceptionTest.scala deleted file mode 100644 index 830531bb7..000000000 --- a/src/test/java/im/tox/tox4j/exceptions/ToxKilledExceptionTest.scala +++ /dev/null @@ -1,30 +0,0 @@ -package im.tox.tox4j.exceptions - -import im.tox.tox4j.impl.jni.ToxCoreImplFactory.withToxUnit -import org.scalatest.FunSuite - -final class ToxKilledExceptionTest extends FunSuite { - - test("UseAfterCloseInOrder") { - intercept[ToxKilledException] { - withToxUnit { tox1 => - withToxUnit { tox2 => - tox1.close() - tox1.iterationInterval - } - } - } - } - - test("UseAfterCloseReverseOrder") { - intercept[ToxKilledException] { - withToxUnit { tox1 => - withToxUnit { tox2 => - tox2.close() - tox2.iterationInterval - } - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/BadInstanceNumberTest.scala b/src/test/java/im/tox/tox4j/impl/jni/BadInstanceNumberTest.scala deleted file mode 100644 index 8f63c0795..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/BadInstanceNumberTest.scala +++ /dev/null @@ -1,75 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.exceptions.ToxKilledException -import org.scalacheck.Gen -import org.scalatest.FunSuite -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -/** - * This class tests whether the C++ code is resilient against memory corruption - * and bad people using reflection to forge invalid Tox instances. - */ -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class BadInstanceNumberTest extends FunSuite with ScalaCheckPropertyChecks { - - private def callWithInstanceNumber(instanceNumber: Int): Unit = { - val tox = new ToxCoreImpl(ToxOptions()) - - val field = tox.getClass.getDeclaredField("instanceNumber") - field.setAccessible(true) - val oldInstanceNumber = field.get(tox).asInstanceOf[Int] - field.set(tox, instanceNumber) - - val exception = - try { - tox.iterationInterval - null - } catch { - case e: Throwable => e - } - - // Set it back to the good one, so close() works. - field.set(tox, oldInstanceNumber) - tox.close() - - if (exception != null) { - throw exception - } - } - - test("negative or zero instance numbers") { - forAll(Gen.choose(Int.MinValue, 0)) { instanceNumber => - intercept[IllegalStateException] { - callWithInstanceNumber(instanceNumber) - } - } - } - - test("very large instance numbers") { - forAll(Gen.choose(0xffff, Int.MaxValue)) { instanceNumber => - intercept[IllegalStateException] { - callWithInstanceNumber(instanceNumber) - } - } - } - - test("any invalid instance numbers") { - // This could be fine if there is another Tox instance lingering around, but we assume there isn't. - // So, it's either killed (ToxKilledException) or never existed (IllegalStateException). - System.gc() // After this, there should be no lingering instances. - - forAll { (instanceNumber: Int) => - whenever(instanceNumber != 1) { - try { - callWithInstanceNumber(instanceNumber) - fail("No exception thrown. Expected IllegalStateException or ToxKilledException.") - } catch { - case _: IllegalStateException => - case _: ToxKilledException => // Both fine. - } - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/FinalizerDoubleTest.java b/src/test/java/im/tox/tox4j/impl/jni/FinalizerDoubleTest.java deleted file mode 100644 index 690856969..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/FinalizerDoubleTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package im.tox.tox4j.impl.jni; - -import im.tox.tox4j.core.ToxCoreConstants; -import im.tox.tox4j.core.options.ProxyOptions; -import im.tox.tox4j.core.options.SaveDataOptions; -import im.tox.tox4j.core.options.ToxOptions; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.scalatestplus.junit.JUnitSuite; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This test is just here to check whether calling {@link ToxCoreImpl#finalize} - * twice causes a hard crash or not. It should be handled gracefully, although - * it should never happen. This test should print an exception trace to the log. - * - *

- * Thus: This test will cause exceptions talking about a 'serious problem - * in native code'. This is expected behaviour. It is not actually a serious - * problem. - */ -public class FinalizerDoubleTest extends JUnitSuite { - - private static final Logger logger = - LoggerFactory.getLogger(FinalizerDoubleTest.class); - - private static final ToxOptions OPTIONS = new ToxOptions( // - true, // - true, // - true, // - ProxyOptions.None$.MODULE$, // - ToxCoreConstants.DefaultStartPort(), // - ToxCoreConstants.DefaultEndPort(), // - ToxCoreConstants.DefaultTcpPort(), // - SaveDataOptions.None$.MODULE$, // - true // - ); - - @Before - public void setUp() { - logger.info( - "Exceptions about 'a serious problem in native code' are expected."); - } - - /** - * This is ran after every test to clean up the broken Tox instances, so that - * a future GC doesn't cause exception traces to be printed. - */ - @After - public void tearDown() { - System.gc(); - } - - @Test - @SuppressWarnings("FinalizeCalledExplicitly") - public void testFinalizeTwice() { - ToxCoreImpl tox = new ToxCoreImpl(OPTIONS); - - tox.finalize(); - tox.finalize(); - } - - @Test(expected = IllegalStateException.class) - @SuppressWarnings("FinalizeCalledExplicitly") - public void testCloseAfterFinalize() { - ToxCoreImpl tox = new ToxCoreImpl(OPTIONS); - tox.finalize(); - tox.close(); - } - - @Test(expected = IllegalStateException.class) - @SuppressWarnings("FinalizeCalledExplicitly") - public void testAnyMethodAfterFinalize() { - ToxCoreImpl tox = new ToxCoreImpl(OPTIONS); - tox.finalize(); - tox.iterationInterval(); - } -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/FinalizerTest.scala b/src/test/java/im/tox/tox4j/impl/jni/FinalizerTest.scala deleted file mode 100644 index 042f4813b..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/FinalizerTest.scala +++ /dev/null @@ -1,27 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.core.options.ToxOptions -import org.scalatest.FlatSpec - -/** - * These tests solely exist to exercise the C++ code paths that deal with closing and finalisation. If the C++ code has - * errors in this area, these two tests can be used to single them out and debug them. - * - * Although [[System.gc()]] doesn't necessarily perform a GC, on the Oracle JVM it actually does reliably do so. - * Thus, these tests don't formally test anything, but in reality they do. - */ -final class FinalizerTest extends FlatSpec { - - "Garbage collection" should "not crash the JVM when collecting a closed ToxCoreImpl" in { - System.gc() - new ToxCoreImpl(new ToxOptions).close() - System.gc() - } - - it should "not crash the JVM when collecting an unclosed ToxCoreImpl" in { - System.gc() - new ToxCoreImpl(new ToxOptions) - System.gc() - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/MethodMap.scala b/src/test/java/im/tox/tox4j/impl/jni/MethodMap.scala deleted file mode 100644 index d1fd3cd60..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/MethodMap.scala +++ /dev/null @@ -1,47 +0,0 @@ -package im.tox.tox4j.impl.jni - -import java.lang.reflect.{ Method, Modifier } - -import im.tox.tox4j.OptimisedIdOps._ -import im.tox.tox4j.impl.jni.codegen.NameConversions - -object MethodMap { - - private val actions = Seq("get", "set", "add", "delete") - - private def removeSelf(name: Seq[String]): Seq[String] = { - name.headOption match { - case Some("self") => name.drop(1) - case _ => name - } - } - - private def moveActionToFront(name: Seq[String]): Seq[String] = { - name.indexWhere(actions.contains) match { - case -1 => - name - case actionIndex => - name(actionIndex) +: (name.slice(0, actionIndex) ++ name.slice(actionIndex + 1, name.length)) - } - } - - def apply(jniClass: Class[_]): Map[String, Method] = { - jniClass - .getDeclaredMethods.toSeq - .filter { method => - Modifier.isNative(method.getModifiers) && - method.getName.startsWith("tox") - } - .map { method => - val expectedName = (method.getName - |> NameConversions.cxxVarName - |> (_.split("_").toSeq.drop(1)) - |> removeSelf - |> moveActionToFront - |> (_.mkString("_")) - |> NameConversions.javaVarName) - (expectedName, method) - } |> { pairs => Map(pairs: _*) } - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/NamingConventionsTest.scala b/src/test/java/im/tox/tox4j/impl/jni/NamingConventionsTest.scala deleted file mode 100644 index 15ceea59b..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/NamingConventionsTest.scala +++ /dev/null @@ -1,21 +0,0 @@ -package im.tox.tox4j.impl.jni - -import org.scalatest.FunSuite - -abstract class NamingConventionsTest(jniClass: Class[_], traitClass: Class[_]) extends FunSuite { - - private val exemptions = Seq("callback", "load", "close", "create", "getFriendNumbers") - - test("Java method names should be derivable from JNI method names") { - val jniMethods = MethodMap(jniClass) - - traitClass - .getDeclaredMethods.toSeq - .map(_.getName) - .filterNot(exemptions.contains) - .foreach { name => - assert(jniMethods.contains(name)) - } - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/ToxAvImplFactory.scala b/src/test/java/im/tox/tox4j/impl/jni/ToxAvImplFactory.scala deleted file mode 100644 index 400124094..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/ToxAvImplFactory.scala +++ /dev/null @@ -1,17 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.av.{ ToxAv, ToxAvFactory } -import im.tox.tox4j.core.ToxCore - -object ToxAvImplFactory extends ToxAvFactory { - - @SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf")) - private def make(tox: ToxCore): ToxAv = { - new ToxAvImpl(tox.asInstanceOf[ToxCoreImpl]) - } - - def withToxAv[R](tox: ToxCore)(f: ToxAv => R): R = { - withToxAv(make(tox))(f) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/ToxAvNamingTest.scala b/src/test/java/im/tox/tox4j/impl/jni/ToxAvNamingTest.scala deleted file mode 100644 index 3c89c15a8..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/ToxAvNamingTest.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.av.ToxAv - -final class ToxAvNamingTest extends NamingConventionsTest(classOf[ToxAvJni], classOf[ToxAv]) diff --git a/src/test/java/im/tox/tox4j/impl/jni/ToxCoreImplFactory.scala b/src/test/java/im/tox/tox4j/impl/jni/ToxCoreImplFactory.scala deleted file mode 100644 index 7b04ccf26..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/ToxCoreImplFactory.scala +++ /dev/null @@ -1,89 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.core.exceptions.ToxNewException -import im.tox.tox4j.core.options.{ ProxyOptions, SaveDataOptions, ToxOptions } -import im.tox.tox4j.core.{ ToxCore, ToxCoreFactory, ToxList } - -import scala.collection.mutable.ArrayBuffer - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -object ToxCoreImplFactory extends ToxCoreFactory { - - private val toxes = new ArrayBuffer[ToxCore] - - private def make(options: ToxOptions = ToxOptions()): ToxCore = { - try { - new ToxCoreImpl(options) - } catch { - case e: ToxNewException if e.code == ToxNewException.Code.PORT_ALLOC => - System.gc() - new ToxCoreImpl(options) - } - } - - private def makeList(count: Int, options: ToxOptions = ToxOptions()): ToxList = { - new ToxList(() => { this(options) }, count) - } - - def destroyAll(): Unit = { - toxes.foreach(_.close()) - toxes.clear() - System.gc() - } - - def apply(options: ToxOptions): ToxCore = { - val tox = make(options) - toxes += tox - tox - } - - def withToxUnit[R](options: ToxOptions)(f: ToxCore => R): R = { - withTox(make(options))(f) - } - - def withToxUnit[R](ipv6Enabled: Boolean, udpEnabled: Boolean, proxy: ProxyOptions)(f: ToxCore => R): R = { - withToxUnit(ToxOptions(ipv6Enabled, udpEnabled, proxy = proxy))(f) - } - - def withToxUnit[R](ipv6Enabled: Boolean, udpEnabled: Boolean)(f: ToxCore => R): R = { - withToxUnit(ToxOptions(ipv6Enabled, udpEnabled))(f) - } - - def withToxUnit[R](saveData: SaveDataOptions)(f: ToxCore => R): R = { - withToxUnit(new ToxOptions(saveData = saveData))(f) - } - - def withToxUnit[R](f: ToxCore => R): R = { - withToxUnit(ipv6Enabled = true, udpEnabled = true)(f) - } - - def withTox[R](options: ToxOptions)(f: ToxCore => R): R = { - withTox(make(options))(f) - } - - def withToxS[R](options: ToxOptions)(f: ToxCore => R): R = { - withTox(make(options))(f) - } - - def withToxS[R](ipv6Enabled: Boolean, udpEnabled: Boolean)(f: ToxCore => R): R = { - withToxS(ToxOptions(ipv6Enabled, udpEnabled))(f) - } - - def withToxS[R](ipv6Enabled: Boolean, udpEnabled: Boolean, proxy: ProxyOptions)(f: ToxCore => R): R = { - withToxS(ToxOptions(ipv6Enabled, udpEnabled, proxy = proxy))(f) - } - - def withToxes[R](count: Int, options: ToxOptions)(f: ToxList => R): R = { - val toxes = makeList(count, options) - try { - f(toxes) - } finally { - toxes.close() - } - } - - def withToxes[R](count: Int)(f: ToxList => R): R = { - withToxes(count, ToxOptions())(f) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/ToxCoreNamingTest.scala b/src/test/java/im/tox/tox4j/impl/jni/ToxCoreNamingTest.scala deleted file mode 100644 index 46f96c11f..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/ToxCoreNamingTest.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.core.ToxCore - -final class ToxCoreNamingTest extends NamingConventionsTest(classOf[ToxCoreJni], classOf[ToxCore]) diff --git a/src/test/java/im/tox/tox4j/impl/jni/ToxCryptoImplTest.scala b/src/test/java/im/tox/tox4j/impl/jni/ToxCryptoImplTest.scala deleted file mode 100644 index e777762a1..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/ToxCryptoImplTest.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.crypto.ToxCryptoTest - -final class ToxCryptoImplTest extends ToxCryptoTest(ToxCryptoImpl) diff --git a/src/test/java/im/tox/tox4j/impl/jni/ToxJniLogTest.scala b/src/test/java/im/tox/tox4j/impl/jni/ToxJniLogTest.scala deleted file mode 100644 index 532eeb189..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/ToxJniLogTest.scala +++ /dev/null @@ -1,93 +0,0 @@ -package im.tox.tox4j.impl.jni - -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.impl.jni.proto.JniLog -import org.scalacheck.Gen -import org.scalatest.FunSuite -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class ToxJniLogTest extends FunSuite with ScalaCheckPropertyChecks { - - private val TestMaxSize = 100 - - private val friendNumber = ToxFriendNumber.fromInt(0).get - - test("constructing and destroying a Tox instance with logging enabled should result in a non-empty log") { - ToxJniLog() // clear - - ToxJniLog.maxSize = TestMaxSize - assert(ToxJniLog.maxSize == TestMaxSize) - assert(ToxJniLog().entries.isEmpty) - // Construct and destroy a Tox instance to cause something (tox_new) to be logged and the log - // will be non-empty. - ToxCoreImplFactory.withToxUnit { tox => } - assert(ToxJniLog().entries.nonEmpty) - } - - test("constructing and destroying a Tox instance with logging disabled should result in an empty log") { - ToxJniLog() // clear - - ToxJniLog.maxSize = 0 - assert(ToxJniLog.maxSize == 0) - assert(ToxJniLog().entries.isEmpty) - ToxCoreImplFactory.withToxUnit { tox => } - assert(ToxJniLog().entries.isEmpty) - } - - test("one log entry per native call") { - ToxJniLog() // clear - - ToxJniLog.maxSize = TestMaxSize - assert(ToxJniLog().entries.isEmpty) - - ToxCoreImplFactory.withToxUnit { tox => } - val count1 = ToxJniLog().entries.size - - ToxCoreImplFactory.withToxUnit { tox => tox.friendExists(friendNumber) } - val count2 = ToxJniLog().entries.size - - assert(count2 == count1 + 1) - } - - test("null protobufs are ignored") { - assert(ToxJniLog.fromBytes(null) == JniLog.defaultInstance) - } - - test("invalid protobufs are ignored") { - forAll { (bytes: Array[Byte]) => - assert(ToxJniLog.fromBytes(bytes) == JniLog.defaultInstance) - } - } - - test("concurrent logging works") { - ToxJniLog() // clear - ToxJniLog.maxSize = 10000 - - forAll(Gen.choose(1, 99), Gen.choose(1, 100)) { (threadCount, iterations) => - val threads = for (_ <- 1 to threadCount) yield { - new Thread { - override def run(): Unit = { - ToxCoreImplFactory.withToxUnit { tox => - for (_ <- 0 until iterations) { - tox.friendExists(friendNumber) - } - } - } - } - } - - threads.foreach(_.start()) - threads.foreach(_.join()) - - val log = ToxJniLog() - assert(log.entries.size < 10000) - assert(log.entries.size == threadCount + threadCount * iterations) - assert(ToxJniLog.toString(log).count(_ == '\n') == log.entries.size) - } - - assert(ToxJniLog().entries.isEmpty) - ToxJniLog.maxSize = 0 - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/CodeGenerator.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/CodeGenerator.scala deleted file mode 100644 index 9f5008691..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/CodeGenerator.scala +++ /dev/null @@ -1,39 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import java.io.{ File, PrintWriter } - -import com.google.common.base.CaseFormat -import gnieh.pp.PrettyRenderer -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ -import im.tox.tox4j.impl.jni.codegen.cxx.{ Ast, Print } - -object NameConversions { - - def cxxVarName(name: String): String = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name) - def cxxTypeName(name: String): String = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, name) - def javaVarName(name: String): String = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name) - def javaTypeName(name: String): String = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name) - -} - -abstract class CodeGenerator extends App { - - def writeCode(path: String, sep: String = "\n\n")(code: Ast.TranslationUnit): Unit = { - val renderer = new PrettyRenderer(130) - - val writer = new PrintWriter(new File("cpp/src", path)) - try { - writer.println(code.map(Print.printDecl).map(renderer).mkString(sep)) - } finally { - writer.close() - } - } - - def ifdef(header: String, guard: String, code: TranslationUnit*): TranslationUnit = { - Include(header) +: - Ifdef(guard) +: - code.flatten :+ - Endif - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniCallbacks.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/JniCallbacks.scala deleted file mode 100644 index 82b36525f..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniCallbacks.scala +++ /dev/null @@ -1,48 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import im.tox.core.typesafe.Equals._ -import im.tox.tox4j.av.callbacks.ToxAvEventAdapter -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter -import im.tox.tox4j.impl.jni.codegen.NameConversions.{ cxxTypeName, cxxVarName, javaVarName } -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ - -object JniCallbacks extends CodeGenerator { - - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - def getAllInterfaces(clazz: Class[_]): List[Class[_]] = { - (Option(clazz.getSuperclass).toList.flatMap(getAllInterfaces) - ++ clazz.getInterfaces - ++ clazz.getInterfaces.flatMap(getAllInterfaces)) - } - - def generateCallbacks(clazz: Class[_]): TranslationUnit = { - getAllInterfaces(clazz).filter(_.getSimpleName.endsWith("Callback")) - .sortBy(_.getSimpleName) - .flatMap { interface => - val expectedMethodName = { - val name = cxxTypeName(interface.getSimpleName) - javaVarName(name.substring(0, name.lastIndexOf('_')).toLowerCase) - } - - val method = interface.getDeclaredMethods.filter(_.getName === expectedMethodName) match { - case Array() => sys.error(s"Callback interfaces $interface does not provide a method '$expectedMethodName'") - case Array(singleMethod) => singleMethod - case methods => sys.error(s"Callback interfaces $interface contains multiple overloads for '$expectedMethodName'") - } - - Seq( - Comment(interface.getName + "#" + expectedMethodName), - MacroCall(FunCall(Identifier("CALLBACK"), Seq(Identifier(cxxVarName(method.getName))))) - ) - } - } - - writeCode("tox/generated/av.h", "\n") { - generateCallbacks(classOf[ToxAvEventAdapter[_]]) - } - - writeCode("tox/generated/core.h", "\n") { - generateCallbacks(classOf[ToxCoreEventAdapter[_]]) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniConstants.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/JniConstants.scala deleted file mode 100644 index 2bc95524c..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniConstants.scala +++ /dev/null @@ -1,45 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import im.tox.tox4j.core.ToxCoreConstants -import im.tox.tox4j.crypto.ToxCryptoConstants -import im.tox.tox4j.impl.jni.codegen.NameConversions.cxxTypeName -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ - -object JniConstants extends CodeGenerator { - - def generateNativeDecls[T](prefix: String, constants: T): Decl = { - val clazz = constants.getClass - - Fun( - returnType = Static(Type.void), - name = "check" + clazz.getSimpleName.replace("$", ""), - params = Seq(), - body = CompoundStmt( - body = clazz.getDeclaredMethods - .sortBy(method => method.getName) - .map { method => - val value = method.invoke(constants).asInstanceOf[Int] - ExprStmt(FunCall(Identifier("static_assert"), Seq( - Equals(Identifier(prefix + cxxTypeName(method.getName)), IntegerLiteral(value)), - StringLiteral("Java constant out of sync with C") - ))) - }.toSeq - ) - ) - } - - writeCode("ToxCore/generated/constants.h", "\n") { - Seq( - Comment(ToxCoreConstants.getClass.getName), - generateNativeDecls("TOX_", ToxCoreConstants) - ) - } - - writeCode("ToxCrypto/generated/constants.h", "\n") { - Seq( - Comment(ToxCryptoConstants.getClass.getName), - generateNativeDecls("TOX_PASS_", ToxCryptoConstants) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniEnums.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/JniEnums.scala deleted file mode 100644 index 2704e689e..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniEnums.scala +++ /dev/null @@ -1,113 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import com.google.common.base.CaseFormat -import im.tox.tox4j.av.enums.{ ToxavCallControl, ToxavFriendCallState } -import im.tox.tox4j.core.enums._ -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ - -object JniEnums extends CodeGenerator { - - final case class Side[E <: Enum[E]]( - ty: Type, - name: String, - origin: String, - expr: E => Expr - ) - - def enumMapping[E <: Enum[E]](values: Seq[E], cxxEnum: String, from: Side[E], to: Side[E]): Decl = { - TemplateFun( - typeParams = Nil, - typeArgs = Seq(Typename(cxxEnum)), - fun = Fun( - returnType = to.ty, - name = "Enum::" + to.name, - params = Seq( - Param(Pointer(Typename("JNIEnv")), "env"), - Param(from.ty, from.name) - ), - body = CompoundStmt( - Switch(Identifier(from.name), CompoundStmt(values.map { value => - Case(from.expr(value), Return(to.expr(value))) - })), - ExprStmt(FunCall(Identifier("tox4j_fatal"), Seq(StringLiteral(s"Invalid enumerator from ${from.origin}")))) - ) - ) - ) - } - - def enumMappings[E <: Enum[E]](values: Seq[E], cxxEnum: String): TranslationUnit = { - val from = Side[E](Typename(cxxEnum), "valueOf", "toxcore", value => Identifier(cxxEnum + "_" + value.name)) - val to = Side[E](Type.jint, "ordinal", "Java", value => IntegerLiteral(value.ordinal)) - - Seq( - enumMapping(values, cxxEnum, from, to), - enumMapping(values, cxxEnum, to, from) - ) - } - - def debugOut(expr: Expr): Stmt = { - ExprStmt( - FunCall( - Access(Identifier("value"), "set_v_string"), - Seq(expr) - ) - ) - } - - def printArg[E <: Enum[E]](values: Seq[E], cxxEnum: String): Decl = { - TemplateFun( - typeParams = Nil, - typeArgs = Seq(Typename(cxxEnum)), - fun = Fun( - returnType = Type.void, - name = "print_arg", - params = Seq( - Param(Reference(Typename("protolog::Value")), "value"), - Param(Reference(Const(Typename(cxxEnum))), "arg") - ), - body = CompoundStmt( - Switch(Identifier("arg"), CompoundStmt(values.flatMap { value => - Seq(Oneliner( - Case( - Identifier(cxxEnum + "_" + value.name), - debugOut(StringLiteral(cxxEnum + "_" + value.name)) - ), - Return() - )) - })), - debugOut(BinaryOperator( - "+", - StringLiteral(s"($cxxEnum)"), - Identifier("std::to_string (arg)") - )) - ) - ) - ) - } - - def generateEnumConversions[E <: Enum[E]](values: Array[E]): TranslationUnit = { - val javaEnum = values(0).getClass.getSimpleName - val cxxEnum = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, javaEnum) - - enumMappings(values, cxxEnum) :+ printArg(values, cxxEnum) - } - - writeCode("ToxAv/generated/enums.cpp") { - Include("../ToxAv.h") +: ( - generateEnumConversions(ToxavCallControl.values) ++ - generateEnumConversions(ToxavFriendCallState.values) - ) - } - - writeCode("ToxCore/generated/enums.cpp") { - Include("../ToxCore.h") +: ( - generateEnumConversions(ToxConnection.values) ++ - generateEnumConversions(ToxFileControl.values) ++ - generateEnumConversions(ToxMessageType.values) ++ - generateEnumConversions(ToxProxyType.values) ++ - generateEnumConversions(ToxSavedataType.values) ++ - generateEnumConversions(ToxUserStatus.values) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniErrorCodes.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/JniErrorCodes.scala deleted file mode 100644 index 8ede0e6b1..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniErrorCodes.scala +++ /dev/null @@ -1,93 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import im.tox.tox4j.av.exceptions._ -import im.tox.tox4j.core.exceptions._ -import im.tox.tox4j.crypto.exceptions._ -import im.tox.tox4j.exceptions.JavaOnly -import im.tox.tox4j.impl.jni.codegen.NameConversions.{ cxxTypeName, javaTypeName } -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ - -object JniErrorCodes extends CodeGenerator { - - def generateErrorCode[E <: Enum[E]](values: Array[E]): Decl = { - val exceptionClass = { - val name = cxxTypeName(values(0).getClass.getEnclosingClass.getSimpleName) - name.substring(name.indexOf('_') + 1, name.lastIndexOf('_')) - } - - val javaEnum = values(0).getClass.getSimpleName - val cxxEnum = cxxTypeName(javaEnum) - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - val failureCases = values filter { value => - value.getClass.getField(value.name).getAnnotation(classOf[JavaOnly]) == null - } map { value => - FunCall(Identifier("failure_case"), Seq(Identifier(exceptionClass), Identifier(value.name))) - } map ExprStmt - - MacroFun( - init = FunCall( - callee = Identifier("HANDLE"), - args = Seq( - StringLiteral(javaTypeName(exceptionClass)), - Identifier(exceptionClass) - ) - ), - body = CompoundStmt( - Switch( - cond = Identifier("error"), - body = CompoundStmt( - ExprStmt(FunCall(Identifier("success_case"), Seq(Identifier(exceptionClass)))) +: - failureCases - ) - ), - Return(FunCall(Identifier("unhandled"), Nil)) - ) - ) - } - - writeCode("ToxAv/generated/errors.cpp") { - Include("../ToxAv.h") +: - Seq( - generateErrorCode(ToxavAnswerException.Code.values), - generateErrorCode(ToxavBitRateSetException.Code.values), - generateErrorCode(ToxavCallControlException.Code.values), - generateErrorCode(ToxavCallException.Code.values), - generateErrorCode(ToxavNewException.Code.values), - generateErrorCode(ToxavSendFrameException.Code.values) - ) - } - - writeCode("ToxCore/generated/errors.cpp") { - Include("../ToxCore.h") +: - Seq( - generateErrorCode(ToxBootstrapException.Code.values), - generateErrorCode(ToxFileControlException.Code.values), - generateErrorCode(ToxFileGetException.Code.values), - generateErrorCode(ToxFileSeekException.Code.values), - generateErrorCode(ToxFileSendChunkException.Code.values), - generateErrorCode(ToxFileSendException.Code.values), - generateErrorCode(ToxFriendAddException.Code.values), - generateErrorCode(ToxFriendByPublicKeyException.Code.values), - generateErrorCode(ToxFriendCustomPacketException.Code.values), - generateErrorCode(ToxFriendDeleteException.Code.values), - generateErrorCode(ToxFriendGetPublicKeyException.Code.values), - generateErrorCode(ToxFriendSendMessageException.Code.values), - generateErrorCode(ToxGetPortException.Code.values), - generateErrorCode(ToxNewException.Code.values), - generateErrorCode(ToxSetInfoException.Code.values), - generateErrorCode(ToxSetTypingException.Code.values) - ) - } - - writeCode("ToxCrypto/generated/errors.cpp") { - Include("../ToxCrypto.h") +: - Seq( - generateErrorCode(ToxDecryptionException.Code.values), - generateErrorCode(ToxEncryptionException.Code.values), - generateErrorCode(ToxGetSaltException.Code.values), - generateErrorCode(ToxKeyDerivationException.Code.values) - ) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniMethodImpls.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/JniMethodImpls.scala deleted file mode 100644 index b799ac3e1..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniMethodImpls.scala +++ /dev/null @@ -1,78 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.impl.jni.codegen.NameConversions.cxxVarName -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ -import im.tox.tox4j.impl.jni.{ AutoGenerated, MethodMap, ToxAvJni, ToxCoreJni } - -import scala.reflect.runtime.{ universe => u } - -object JniMethodImpls extends CodeGenerator { - - private val javaTypeMap = Map( - "void" -> Type.void, - "int" -> Type.jint, - "boolean" -> Type.jboolean - ) - - private val scalaTypeMap = Map( - "Int" -> Type.jint, - classOf[ToxFriendNumber].getName -> Type.jint - ) - - def cxxType(typeSignature: u.Type): Type = { - scalaTypeMap(typeSignature.toString) - } - - def cxxParams(params: Seq[(u.Type, u.Name)]): Seq[Param] = { - params map { - case (typeSignature, name) => - Param(cxxType(typeSignature), name.toString) - } - } - - def generateNativeCode[T](jniClass: Class[_])(implicit evidence: u.TypeTag[T]): TranslationUnit = { - val mirror = u.runtimeMirror(jniClass.getClassLoader) - val traitMirror = mirror.typeOf[T] - - MethodMap(jniClass).toSeq.flatMap { - case (name, method) => - Option(method.getAnnotation(classOf[AutoGenerated])).map((name, method, _)).toIterable - }.map { - case (name, method, annotation) => - val params = traitMirror - .member(u.TermName(name)).asMethod - .paramLists.flatten - .map { sym => (sym.typeSignature, sym.name) } - - ToxFun( - returnType = javaTypeMap(method.getReturnType.getName), - name = method.getName, - params = Param(Type.jint, "instanceNumber") +: cxxParams(params), - body = CompoundStmt( - Return(FunCall( - Access(Identifier("instances"), "with_instance_noerr"), - Seq( - Identifier("env"), - Identifier("instanceNumber"), - Identifier(Option(annotation.value).filter(_.nonEmpty).getOrElse(cxxVarName(method.getName))) - ) ++ cxxParams(params).map(_.name).map(Identifier) - )) - ) - ) - } - } - - writeCode("ToxAv/generated/impls.h") { - Comment(classOf[ToxAvJni].getName) +: - generateNativeCode[ToxAv](classOf[ToxAvJni]) - } - - writeCode("ToxCore/generated/impls.h") { - Comment(classOf[ToxCoreJni].getName) +: - generateNativeCode[ToxCore](classOf[ToxCoreJni]) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniMethodRefs.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/JniMethodRefs.scala deleted file mode 100644 index 04aed3e12..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/JniMethodRefs.scala +++ /dev/null @@ -1,41 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen - -import java.lang.reflect.Modifier - -import im.tox.tox4j.impl.jni.codegen.NameConversions.cxxVarName -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ -import im.tox.tox4j.impl.jni.{ ToxAvJni, ToxCoreJni, ToxCryptoJni } - -object JniMethodRefs extends CodeGenerator { - - def generateNativeDecls(clazz: Class[_]): TranslationUnit = { - clazz.getDeclaredMethods - .filter { method => - Modifier.isNative(method.getModifiers) && - !Seq("invoke", "tox4j").exists(method.getName.startsWith) - } - .sortBy(method => method.getName) - .flatMap { method => - Seq( - MacroCall(FunCall(Identifier("JAVA_METHOD_REF"), Seq(Identifier(method.getName)))), - MacroCall(FunCall(Identifier("CXX_FUNCTION_REF"), Seq(Identifier(cxxVarName(method.getName))))) - ) - } - } - - writeCode("ToxAv/generated/natives.h", "\n") { - Comment(classOf[ToxAvJni].getName) +: - generateNativeDecls(classOf[ToxAvJni]) - } - - writeCode("ToxCore/generated/natives.h", "\n") { - Comment(classOf[ToxCoreJni].getName) +: - generateNativeDecls(classOf[ToxCoreJni]) - } - - writeCode("ToxCrypto/generated/natives.h", "\n") { - Comment(classOf[ToxCryptoJni].getName) +: - generateNativeDecls(classOf[ToxCryptoJni]) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/cxx/Ast.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/cxx/Ast.scala deleted file mode 100644 index f00818350..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/cxx/Ast.scala +++ /dev/null @@ -1,84 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen.cxx - -// scalastyle:ignore number.of.types -object Ast { - - /** - * Types. - */ - sealed trait Type - - final case class Typename(name: String) extends Type - final case class Static(inner: Type) extends Type - final case class Const(inner: Type) extends Type - final case class Pointer(inner: Type) extends Type - final case class Reference(inner: Type) extends Type - - object Type { - val void: Typename = Typename("void") - val jint: Typename = Typename("jint") - val jboolean: Typename = Typename("jboolean") - } - - /** - * Pre-processing directives. - */ - sealed trait Preproc extends Decl with Stmt - - final case class Include(header: String) extends Preproc - final case class Ifdef(name: String) extends Preproc - final case class Comment(text: String) extends Preproc - final case class MacroCall(expr: FunCall) extends Preproc - final case class MacroFun(init: FunCall, body: CompoundStmt) extends Preproc - final case class ToxFun(returnType: Type, name: String, params: Seq[Param], body: CompoundStmt) extends Preproc - case object Endif extends Preproc - - /** - * Statements. - */ - sealed trait Stmt - - final case class Switch(cond: Expr, body: CompoundStmt) extends Stmt - final case class Case(expr: Expr, body: Stmt) extends Stmt - final case class Default(body: Stmt) extends Stmt - case object Break extends Stmt - final case class Return(expr: Option[Expr] = None) extends Stmt - final case class ExprStmt(expr: Expr) extends Stmt - final case class CompoundStmt(body: Seq[Stmt]) extends Stmt - final case class Oneliner(stmts: Stmt*) extends Stmt - - object Return { - def apply(expr: Expr): Return = apply(Some(expr)) - } - - object CompoundStmt { - def apply(s1: Stmt): CompoundStmt = apply(Seq(s1)) - def apply(s1: Stmt, s2: Stmt): CompoundStmt = apply(Seq(s1, s2)) - def apply(s1: Stmt, s2: Stmt, s3: Stmt): CompoundStmt = apply(Seq(s1, s2, s3)) - } - - /** - * Expressions. - */ - sealed trait Expr - - final case class Identifier(name: String) extends Expr - final case class IntegerLiteral(value: Int) extends Expr - final case class StringLiteral(value: String) extends Expr - final case class FunCall(callee: Expr, args: Seq[Expr]) extends Expr - final case class BinaryOperator(op: String, lhs: Expr, rhs: Expr) extends Expr - - final case class Access(expr: Expr, name: String) extends Expr - final case class Equals(lhs: Expr, rhs: Expr) extends Expr - - /** - * Declarations. - */ - sealed trait Decl - - type TranslationUnit = Seq[Decl] - final case class Fun(returnType: Type, name: String, params: Seq[Param], body: CompoundStmt) extends Decl - final case class TemplateFun(typeParams: Seq[String], typeArgs: Seq[Type], fun: Fun) extends Decl - final case class Param(paramType: Type, name: String) extends Decl - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/codegen/cxx/Print.scala b/src/test/java/im/tox/tox4j/impl/jni/codegen/cxx/Print.scala deleted file mode 100644 index 80a97b5fe..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/codegen/cxx/Print.scala +++ /dev/null @@ -1,119 +0,0 @@ -package im.tox.tox4j.impl.jni.codegen.cxx - -import gnieh.pp._ -import im.tox.tox4j.impl.jni.codegen.cxx.Ast._ -import org.apache.commons.lang3.StringEscapeUtils - -@SuppressWarnings(Array("org.wartremover.warts.Recursion")) -object Print { - - private def flatten(docs: Seq[Doc], sep: Doc = empty): Doc = { - docs match { - case Nil => empty - case head +: tail => - tail.foldLeft(head)((a, b) => a :: sep :: b) - } - } - - private def printSeq[T](nodes: Seq[T], sep: Doc = empty)(printT: T => Doc): Doc = { - flatten(nodes.map(printT), sep) - } - - /** - * Types. - */ - private def printTypeInner(ty: Type): Doc = { - ty match { - case Typename(name) => text(name) :: space - case Static(inner) => "static " :: printTypeInner(inner) - case Const(inner) => printTypeInner(inner) :: "const " - case Pointer(inner) => printTypeInner(inner) :: "*" - case Reference(inner) => printTypeInner(inner) :: "&" - } - } - - def printType(ty: Type, spacing: Doc = empty): Doc = { - ty match { - case Typename(name) => text(name) :: spacing - case _ => printTypeInner(ty) - } - } - - /** - * Pre-processing directives. - */ - def printPreproc(pp: Preproc): Doc = { - pp match { - case Include(header) => "#include \"" :: header :: "\"" - case Ifdef(name) => "#ifdef " :: name - case Comment(text) => "// " :: text - case Endif => "#endif" - case MacroCall(expr) => printExpr(expr) - case MacroFun(init, body) => printExpr(init) :|: printStmt(body) - case ToxFun(returnType, name, params, body) => - "JAVA_METHOD" :+: nest(2)("(" :: printType(returnType) :: "," :+: name :: "," :|: - printSeq(params, "," :: space)(printDecl) :: ")") :|: - printStmt(body) - } - } - - /** - * Statements. - */ - @SuppressWarnings(Array("org.wartremover.warts.TraversableOps")) - def printStmt(stmt: Stmt, spacing: Doc = line): Doc = { // scalastyle:ignore cyclomatic.complexity - stmt match { - case Switch(cond, CompoundStmt(cases)) => - "switch (" :: printExpr(cond) :: nest(2)(")" :|: - "{" :|: - printSeq(cases)(printStmt(_)) :: - "}") :: - line - case Switch(cond, body) => sys.error(s"invalid switch-body: $body") - case Case(expr, body) => "case" :+: printExpr(expr) :: ":" :+: printStmt(body) - case Default(body) => "default:" :+: printStmt(body) - case Break => "break;" - case Return(expr) => "return" :: expr.fold(empty)(expr => space :: printExpr(expr)) :: ";" :: spacing - case ExprStmt(expr) => printExpr(expr) :: ";" :: spacing - case CompoundStmt(Seq(stmt0)) => nest(2)("{" :|: printStmt(stmt0, empty)) :|: "}" - case CompoundStmt(stmts) => nest(2)("{" :|: printSeq(stmts.slice(0, stmts.length - 1))(printStmt(_))) :: printStmt(stmts.last) :: "}" - case stmt: Oneliner => group(printSeq(stmt.stmts)(printStmt(_, empty))) :: spacing - case pp: Preproc => printPreproc(pp) - } - } - - /** - * Expressions. - */ - def printExpr(expr: Expr): Doc = { - expr match { - case Identifier(name) => name - case IntegerLiteral(value) => value - case StringLiteral(value) => "\"" :: StringEscapeUtils.escapeJava(value) :: "\"" - case FunCall(callee, args) => printExpr(callee) :+: "(" :: printSeq(args, "," :: space)(printExpr) :: ")" - case BinaryOperator(op, lhs, rhs) => printExpr(lhs) :+: op :+: printExpr(rhs) - case Equals(lhs, rhs) => printExpr(lhs) :+: "==" :+: printExpr(rhs) - case Access(lhs, name) => printExpr(lhs) :: "." :: name - } - } - - /** - * Declarations. - */ - def printDecl(decl: Decl): Doc = { - decl match { - case Fun(ty, name, params, body) => - printType(ty) :|: name :+: "(" :: printSeq(params, "," :: space)(printDecl) :: ")" :|: printStmt(body) - - case TemplateFun(tparams, targs, Fun(ty, name, params, body)) => - "template<" :: flatten(tparams.map("typename" :+: _)) :: ">" :|: - printType(ty) :|: - name :: "<" :: flatten(targs.map(printType(_))) :: "> (" :: printSeq(params, "," :: space)(printDecl) :: ")" :|: - printStmt(body) - - case Param(ty, name) => printType(ty, space) :: name - case pp: Preproc => printPreproc(pp) - } - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/internal/EventTest.scala b/src/test/java/im/tox/tox4j/impl/jni/internal/EventTest.scala deleted file mode 100644 index 8ff31ffcb..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/internal/EventTest.scala +++ /dev/null @@ -1,56 +0,0 @@ -package im.tox.tox4j.impl.jni.internal - -import org.scalatest.FlatSpec - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -class EventTest extends FlatSpec { - - "callback" should "be called on run()" in { - val event = new Event - var called = false - - val id = event += (() => called = true) - - event() - assert(called) - } - - it should "be called twice if run() is called twice" in { - val event = new Event - var called = 0 - - val id = event += (() => called += 1) - - event() - assert(called == 1) - event() - assert(called == 2) - } - - it should "not be called if it was deleted" in { - val event = new Event - var called = false - - val id = event += (() => called = true) - - event -= id - - event() - assert(!called) - } - - "remove" should "be idempotent" in { - val event = new Event - var called = 0 - - val id1 = event += (() => called = 1) - val id2 = event += (() => called = 2) - - event -= id1 - event -= id1 - - event() - assert(called == 2) - } - -} diff --git a/src/test/java/im/tox/tox4j/impl/jni/package.scala b/src/test/java/im/tox/tox4j/impl/jni/package.scala deleted file mode 100644 index ebeca1606..000000000 --- a/src/test/java/im/tox/tox4j/impl/jni/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j.impl - -import im.tox.documentation._ - -package object jni extends Documented diff --git a/src/test/java/im/tox/tox4j/impl/package.scala b/src/test/java/im/tox/tox4j/impl/package.scala deleted file mode 100644 index 9b496d261..000000000 --- a/src/test/java/im/tox/tox4j/impl/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package im.tox.tox4j - -import im.tox.documentation._ - -package object impl extends Documented diff --git a/src/test/java/im/tox/tox4j/package.scala b/src/test/java/im/tox/tox4j/package.scala deleted file mode 100644 index 4fdff96c6..000000000 --- a/src/test/java/im/tox/tox4j/package.scala +++ /dev/null @@ -1,31 +0,0 @@ -package im.tox - -import im.tox.documentation._ - -package object tox4j extends Documented { - doc""" - |Low level Tox network interfaces. - | - |The packages are organised by subsystem: - |- A/V in ${av.name} - |- Regular text messaging and group chats in ${core.name} - |- Cryptographic primitives in ${crypto.name} - | - |The subsystem packages define data structures and interfaces for all low level Tox library operations. Various - |implementations of these interfaces is contained within subpackages of the ${impl.name} package. The native - |interface to the C library libtoxcore is located in ${impl.jni.name}. - | - |Each subsystem package contains one or more of the following subpackages: - |- callbacks: Interfaces for the callbacks (event listeners). This package provides a combined Listener interface - | (e.g. ${ref[core.callbacks.ToxCoreEventListener[_]]} which inherits from all the single callback interfaces - | in the package. An Adapter class (e.g. ${ref[core.callbacks.ToxCoreEventAdapter[_]]}) provides empty default - | implementations for all callbacks. Run ${ref(impl.jni.codegen.JniCallbacks)} to update the JNI bridge. - |- enums: Java enums mirroring the C versions exactly. The enums' ordinal does not necessarily correspond to the C - | enum value, but the JNI bridge needs to know the ordinals, so if you reorder these, then you must update the - | bridge, by running ${ref(impl.jni.codegen.JniEnums)}. - |- exceptions: Exception classes corresponding to the C API's error codes. One exception class for each error enum in - | the C API. Each exception class contains a Code enum (e.g. ${ref[core.exceptions.ToxBootstrapException.Code]}) - | with all the errors that can occur in Java. If you change these, you need to update the JNI bridge by running - | ${ref(impl.jni.codegen.JniErrorCodes)}. - """ -} diff --git a/src/test/java/im/tox/tox4j/testing/CheckedOrdering.scala b/src/test/java/im/tox/tox4j/testing/CheckedOrdering.scala deleted file mode 100644 index 44e9ba066..000000000 --- a/src/test/java/im/tox/tox4j/testing/CheckedOrdering.scala +++ /dev/null @@ -1,23 +0,0 @@ -package im.tox.tox4j.testing - -import org.scalatest.Assertions - -/** - * Wrap an [[Ordering]] in another [[Ordering]] that checks whether !(a < b) && !(b < a) => a == b. - */ -object CheckedOrdering extends Assertions { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def apply[A](ord: Ordering[A]): Ordering[A] = { - new Ordering[A] { - override def compare(x: A, y: A): Int = { - val result = ord.compare(x, y) - if (result == 0) { - assert(x == y) - } - result - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/CheckedOrderingEq.scala b/src/test/java/im/tox/tox4j/testing/CheckedOrderingEq.scala deleted file mode 100644 index 8cdb4823d..000000000 --- a/src/test/java/im/tox/tox4j/testing/CheckedOrderingEq.scala +++ /dev/null @@ -1,23 +0,0 @@ -package im.tox.tox4j.testing - -import org.scalatest.Assertions - -/** - * Wrap an [[Ordering]] in another [[Ordering]] that checks whether !(a < b) && !(b < a) => a eq b. - */ -object CheckedOrderingEq extends Assertions { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def apply[A <: AnyRef](ord: Ordering[A]): Ordering[A] = { - new Ordering[A] { - override def compare(x: A, y: A): Int = { - val result = ord.compare(x, y) - if (result == 0) { - assert(x eq y) - } - result - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/CountingOrdering.scala b/src/test/java/im/tox/tox4j/testing/CountingOrdering.scala deleted file mode 100644 index d05abf70e..000000000 --- a/src/test/java/im/tox/tox4j/testing/CountingOrdering.scala +++ /dev/null @@ -1,15 +0,0 @@ -package im.tox.tox4j.testing - -/** - * Wrapper for an [[Ordering]] that counts the number of comparisons made. - */ -final case class CountingOrdering[A](ord: Ordering[A]) extends Ordering[A] { - - var count: Int = 0 - - override def compare(x: A, y: A): Int = { - count += 1 - ord.compare(x, y) - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/GetDisjunction.scala b/src/test/java/im/tox/tox4j/testing/GetDisjunction.scala deleted file mode 100644 index 9b2296e5a..000000000 --- a/src/test/java/im/tox/tox4j/testing/GetDisjunction.scala +++ /dev/null @@ -1,23 +0,0 @@ -package im.tox.tox4j.testing - -import im.tox.core.error.CoreError -import im.tox.core.typesafe.{ -\/, \/, \/- } -import org.scalatest.Assertions - -import scala.language.implicitConversions - -final case class GetDisjunction[T] private (disjunction: CoreError \/ T) extends Assertions { - def get: T = { - disjunction match { - case -\/(error) => fail(error.toString) - case \/-(success) => success - } - } -} - -object GetDisjunction { - - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit def toGetDisjunction[T](disjunction: CoreError \/ T): GetDisjunction[T] = GetDisjunction(disjunction) - -} diff --git a/src/test/java/im/tox/tox4j/testing/ToxExceptionChecks.scala b/src/test/java/im/tox/tox4j/testing/ToxExceptionChecks.scala deleted file mode 100644 index d73bb021a..000000000 --- a/src/test/java/im/tox/tox4j/testing/ToxExceptionChecks.scala +++ /dev/null @@ -1,19 +0,0 @@ -package im.tox.tox4j.testing - -import im.tox.tox4j.exceptions.ToxException -import org.scalatest.Assertions - -trait ToxExceptionChecks extends Assertions { - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected def intercept[E <: Enum[E]](code: E)(f: => Unit) = { - try { - f - fail(s"Expected exception with code ${code.name}") - } catch { - case e: ToxException[_] => - assert(e.code eq code) - } - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/ToxTestMixin.scala b/src/test/java/im/tox/tox4j/testing/ToxTestMixin.scala deleted file mode 100644 index 4e194b844..000000000 --- a/src/test/java/im/tox/tox4j/testing/ToxTestMixin.scala +++ /dev/null @@ -1,56 +0,0 @@ -package im.tox.tox4j.testing - -import im.tox.core.error.CoreError -import im.tox.core.typesafe.\/ -import im.tox.tox4j.DhtNodeSelector._ -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.exceptions.{ ToxBootstrapException, ToxFriendAddException, ToxNewException } -import im.tox.tox4j.impl.jni.ToxCoreImplFactory -import org.jetbrains.annotations.NotNull - -import scala.language.implicitConversions - -trait ToxTestMixin extends ToxExceptionChecks { - - protected def interceptWithTox[E <: Enum[E]](code: E)(f: ToxCore => Unit) = { - intercept(code) { - ToxCoreImplFactory.withToxUnit { tox => - addFriends(tox, 1) - f(tox) - } - } - } - - @throws[ToxNewException] - @throws[ToxFriendAddException] - protected def addFriends(@NotNull tox: ToxCore, count: Int): ToxFriendNumber = { - (0 until count).map { (i: Int) => - ToxCoreImplFactory.withToxUnit { friend => - tox.addFriendNorequest(friend.getPublicKey) - } - }.lastOption match { - case None => throw new IllegalArgumentException(s"Cannot add less than 1 friend: $count") - case Some(num) => num - } - } - - @throws[ToxBootstrapException] - def bootstrap(useIPv6: Boolean, udpEnabled: Boolean, @NotNull tox: ToxCore): ToxCore = { - if (!udpEnabled) { - tox.addTcpRelay(node.ipv4, node.tcpPort, node.dhtId) - } - tox.bootstrap( - if (useIPv6) node.ipv6 else node.ipv4, - if (udpEnabled) node.udpPort else node.tcpPort, - node.dhtId - ) - tox - } - - @SuppressWarnings(Array("org.wartremover.warts.ImplicitConversion")) - implicit final def toGetDisjunction[T](disjunction: CoreError \/ T): GetDisjunction[T] = { - GetDisjunction.toGetDisjunction(disjunction) - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/autotest/AliceBobTest.scala b/src/test/java/im/tox/tox4j/testing/autotest/AliceBobTest.scala deleted file mode 100644 index 650cbd3d0..000000000 --- a/src/test/java/im/tox/tox4j/testing/autotest/AliceBobTest.scala +++ /dev/null @@ -1,136 +0,0 @@ -package im.tox.tox4j.testing.autotest - -import im.tox.tox4j.TestConstants._ -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.options.ProxyOptions -import im.tox.tox4j.impl.jni.{ ToxAvImplFactory, ToxCoreImplFactory } -import im.tox.tox4j.{ SocksServer, ToxCoreTestBase } -import org.scalatest.concurrent.TimeLimits -import org.scalatest.exceptions.TestFailedDueToTimeoutException - -import scala.language.postfixOps - -abstract class AliceBobTest extends AliceBobTestBase with TimeLimits { - - protected def ignoreTimeout = false - - protected def enableUdp = true - protected def enableTcp = false - protected def enableIpv4 = true - protected def enableIpv6 = false - protected def enableHttp = false - protected def enableSocks = false - - private def withBootstrappedTox( - ipv6Enabled: Boolean, - udpEnabled: Boolean, - proxyOptions: ProxyOptions = ProxyOptions.None - )( - f: ToxCore => Unit - ): Unit = { - ToxCoreImplFactory.withToxS[Unit](ipv6Enabled, udpEnabled, proxyOptions) { tox => - bootstrap(ipv6Enabled, udpEnabled, tox) - f(tox) - } - } - - private def withToxAv(tox: ToxCore)(f: ToxAv => Unit): Unit = { - ToxAvImplFactory.withToxAv(tox)(f) - } - - private def runAliceBobTest_Direct(withTox: => (ToxCore => Unit) => Unit): Unit = { - failAfter(Timeout) { - runAliceBobTest( - withTox, - withToxAv - ) - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - private def runAliceBobTest_Socks(ipv6Enabled: Boolean, udpEnabled: Boolean): Unit = { - val proxy = SocksServer.withServer { proxy => - failAfter(Timeout) { - runAliceBobTest( - withBootstrappedTox(ipv6Enabled, udpEnabled, new ProxyOptions.Socks5(proxy.getAddress, proxy.getPort)), - withToxAv - ) - } - proxy - } - if (!udpEnabled) { - assert(proxy.getAccepted == 2) - } - } - - test("UDP4") { - assume(enableUdp) - assume(enableIpv4) - try { - runAliceBobTest_Direct(ToxCoreImplFactory.withToxS(ipv6Enabled = false, udpEnabled = true)) - } catch { - case e: TestFailedDueToTimeoutException if ignoreTimeout => - cancel(s"Test timed out after $Timeout", e) - } - } - - test("UDP6") { - assume(enableUdp) - assume(enableIpv6) - failAfter(Timeout) { - runAliceBobTest_Direct(ToxCoreImplFactory.withToxS(ipv6Enabled = true, udpEnabled = true)) - } - } - - test("TCP4") { - assume(enableTcp) - assume(enableIpv4) - assume(ToxCoreTestBase.checkIPv4.isEmpty) - failAfter(Timeout) { - runAliceBobTest_Direct(withBootstrappedTox(ipv6Enabled = false, udpEnabled = false)) - } - } - - test("TCP6") { - assume(enableTcp) - assume(enableIpv6) - assume(ToxCoreTestBase.checkIPv6.isEmpty) - failAfter(Timeout) { - runAliceBobTest_Direct(withBootstrappedTox(ipv6Enabled = true, udpEnabled = false)) - } - } - - test("UDP4+SOCKS5") { - assume(enableUdp) - assume(enableIpv4) - assume(enableSocks) - assume(ToxCoreTestBase.checkIPv4.isEmpty) - runAliceBobTest_Socks(ipv6Enabled = false, udpEnabled = true) - } - - test("UDP6+SOCKS5") { - assume(enableUdp) - assume(enableIpv6) - assume(enableSocks) - assume(ToxCoreTestBase.checkIPv6.isEmpty) - runAliceBobTest_Socks(ipv6Enabled = true, udpEnabled = true) - } - - test("TCP4+SOCKS5") { - assume(enableTcp) - assume(enableIpv4) - assume(enableSocks) - assume(ToxCoreTestBase.checkIPv4.isEmpty) - runAliceBobTest_Socks(ipv6Enabled = false, udpEnabled = false) - } - - test("TCP6+SOCKS5") { - assume(enableTcp) - assume(enableIpv6) - assume(enableSocks) - assume(ToxCoreTestBase.checkIPv6.isEmpty) - runAliceBobTest_Socks(ipv6Enabled = true, udpEnabled = false) - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/autotest/AliceBobTestBase.scala b/src/test/java/im/tox/tox4j/testing/autotest/AliceBobTestBase.scala deleted file mode 100644 index 2fd1b2a70..000000000 --- a/src/test/java/im/tox/tox4j/testing/autotest/AliceBobTestBase.scala +++ /dev/null @@ -1,99 +0,0 @@ -package im.tox.tox4j.testing.autotest - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.OptimisedIdOps._ -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.testing.ToxTestMixin -import im.tox.tox4j.testing.autotest.AliceBobTestBase.Chatter -import org.scalatest.FunSuite -import org.slf4j.LoggerFactory - -import scala.annotation.tailrec - -object AliceBobTestBase { - val FriendNumber: ToxFriendNumber = ToxFriendNumber.fromInt(10).get - - final case class Chatter[T]( - tox: ToxCore, - av: ToxAv, - client: ChatClientT[T], - state: ChatStateT[T] - ) -} - -abstract class AliceBobTestBase extends FunSuite with ToxTestMixin { - - protected val logger = Logger(LoggerFactory.getLogger(classOf[AliceBobTestBase])) - - protected type State - protected type ChatState = ChatStateT[State] - protected type ChatClient = ChatClientT[State] - - protected def initialState: State - - protected def newChatClient(name: String, expectedFriendName: String): ChatClient - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - private def getTopLevelMethod(stackTrace: Seq[StackTraceElement]): String = { - stackTrace - .filter(_.getClassName == classOf[AliceBobTest].getName) - .lastOption - .fold("")(_.getMethodName) - } - - @tailrec - private def mainLoop(clients: Seq[Chatter[State]]): Unit = { - val nextState = clients.map { - case Chatter(tox, av, client, state) => - Chatter[State](tox, av, client, state |> tox.iterate(client) |> (_.runTasks(tox, av))) - } - - val interval = (nextState.map(_.tox.iterationInterval) ++ nextState.map(_.av.iterationInterval)).min - Thread.sleep(interval) - - if (nextState.exists(_.state.chatting)) { - mainLoop(nextState) - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - protected def runAliceBobTest( - withTox: (ToxCore => Unit) => Unit, - withToxAv: ToxCore => (ToxAv => Unit) => Unit - ): Unit = { - val method = getTopLevelMethod(Thread.currentThread.getStackTrace) - logger.info(s"[${Thread.currentThread.getId}] --- ${getClass.getSimpleName}.$method") - - val aliceChat = newChatClient("Alice", "Bob") - val bobChat = newChatClient("Bob", "Alice") - - withTox { alice => - withTox { bob => - withToxAv(alice) { aliceAv => - withToxAv(bob) { bobAv => - assert(alice ne bob) - - addFriends(alice, AliceBobTestBase.FriendNumber.value) - addFriends(bob, AliceBobTestBase.FriendNumber.value) - - alice.addFriendNorequest(bob.getPublicKey) - bob.addFriendNorequest(alice.getPublicKey) - - aliceChat.expectedFriendAddress = bob.getAddress - bobChat.expectedFriendAddress = alice.getAddress - - val aliceState = aliceChat.setup(alice)(ChatStateT[State](initialState)) - val bobState = bobChat.setup(bob)(ChatStateT[State](initialState)) - - mainLoop(Seq( - Chatter(alice, aliceAv, aliceChat, aliceState), - Chatter(bob, bobAv, bobChat, bobState) - )) - } - } - } - } - } -} diff --git a/src/test/java/im/tox/tox4j/testing/autotest/AutoTest.scala b/src/test/java/im/tox/tox4j/testing/autotest/AutoTest.scala deleted file mode 100644 index 7cd2c461e..000000000 --- a/src/test/java/im/tox/tox4j/testing/autotest/AutoTest.scala +++ /dev/null @@ -1,170 +0,0 @@ -package im.tox.tox4j.testing.autotest - -import com.typesafe.scalalogging.Logger -import im.tox.core.typesafe.Equals._ -import im.tox.tox4j.OptimisedIdOps._ -import im.tox.tox4j.ToxEventListener -import im.tox.tox4j.av.{ ToxAv, ToxAvFactory } -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.core.{ ToxCore, ToxCoreFactory } -import im.tox.tox4j.testing.autotest.AutoTest._ -import im.tox.tox4j.testing.autotest.AutoTestSuite.timed -import org.slf4j.LoggerFactory - -import scala.annotation.tailrec - -object AutoTest { - - type Task[S] = (ToxCore, ToxAv, ClientState[S]) => ClientState[S] - - /** - * A participant in the test network. These are unique across all instances, - * so they are useful for logging and to put oneself into relation with other - * instances using the instance map [[ClientState.friendList]]. - */ - final case class ParticipantId(private val value: Int) extends AnyVal { - def prev: ParticipantId = copy(value - 1) - def next: ParticipantId = copy(value + 1) - override def toString: String = s"#$value" - } - - final case class ClientState[S]( - id: ParticipantId, - friendList: Map[ToxFriendNumber, ParticipantId], - state: S, - tasks: List[(Int, Task[S])] = Nil, - running: Boolean = true - ) { - - def finish: ClientState[S] = { - copy(running = false) - } - - private[AutoTest] def updateTasks(interval: Int, tasks: List[(Int, Task[S])]): ClientState[S] = { - copy(tasks = tasks.map { - case (delay, task) => (delay - interval, task) - }) - } - - def addTask(task: Task[S]): ClientState[S] = { - copy(tasks = (0, task) :: tasks) - } - - def addTask(delay: Int)(task: Task[S]): ClientState[S] = { - copy(tasks = (delay, task) :: tasks) - } - - def get: S = state - def put(state: S): ClientState[S] = copy(state = state) - - def modify(f: S => S): ClientState[S] = { - copy(state = f(state)) - } - - def id(friendNumber: ToxFriendNumber): ParticipantId = { - friendList(friendNumber) - } - - } - - abstract class EventListener[S] extends ToxEventListener[ClientState[S]] { - type State = ClientState[S] - def initial: S - } - - final case class Participant[S]( - tox: ToxCore, - av: ToxAv, - handler: EventListener[S], - state: ClientState[S] - ) - -} - -final case class AutoTest( - coreFactory: ToxCoreFactory, - avFactory: ToxAvFactory -) { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - private def performTasks[S]( - tox: ToxCore, - av: ToxAv, - interval: Int - )(state: ClientState[S]): ClientState[S] = { - val (delayed, runnable) = state.tasks.partition(_._1 >= 0) - logger.trace(s"Running tasks: ${runnable.size} runnable, ${delayed.size} delayed") - - runnable.foldRight(state.updateTasks(interval max 1, delayed)) { (task, state) => - assert(task._1 <= 0) - task._2(tox, av, state) - } - } - - @tailrec - private def mainLoop[S](clients: List[Participant[S]], iteration: Int = 0): List[S] = { - val interval = (clients.map(_.tox.iterationInterval) ++ clients.map(_.av.iterationInterval)).min - assert(interval >= 0) - - val (iterationTime, nextClients) = timed { - clients.map { - case Participant(tox, av, handler, state) => - Participant( - tox, av, handler, - state - |> tox.iterate(handler) - |> av.iterate(handler) - |> performTasks(tox, av, interval) - ) - } - } - - val sleepTime = (interval - iterationTime) max 0 - logger.trace(s"Iteration $iteration, interval=$interval, iterationTime=$iterationTime, sleepTime=$sleepTime") - Thread.sleep(sleepTime) - - if (nextClients.exists(_.state.running)) { - mainLoop(nextClients, iteration + 1) - } else { - nextClients.map(_.state.state) - } - } - - def run[S]( - count: Int, - options: ToxOptions, - handler: EventListener[S] - ): List[S] = { - coreFactory.withToxN[List[S]](count, options) { toxes => - avFactory.withToxAvN[List[S]](toxes) { avs => - val states = { - val avsWithIds = - for (((tox, av), id) <- avs.zipWithIndex) yield { - (tox, av, ParticipantId(id)) - } - - for ((tox, av, id) <- avsWithIds) yield { - // Everybody adds everybody else as friend. - val friendList = - for ((friendTox, friendAv, friendId) <- avsWithIds if friendId =/= id) yield { - tox.addFriendNorequest(friendTox.getPublicKey) -> friendId - } - (tox, av, id, Map(friendList: _*)) - } - } - - val participants = - for ((tox, av, id, friendList) <- states) yield { - logger.debug(s"Participant $id's friends: $friendList") - assert(!friendList.valuesIterator.contains(id)) - Participant(tox, av, handler, ClientState(id, friendList, handler.initial)) - } - - mainLoop(participants) - } - } - } - -} diff --git a/src/test/java/im/tox/tox4j/testing/autotest/AutoTestSuite.scala b/src/test/java/im/tox/tox4j/testing/autotest/AutoTestSuite.scala deleted file mode 100644 index abe5e662b..000000000 --- a/src/test/java/im/tox/tox4j/testing/autotest/AutoTestSuite.scala +++ /dev/null @@ -1,91 +0,0 @@ -package im.tox.tox4j.testing.autotest - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.TestConstants -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.core.options.ToxOptions -import im.tox.tox4j.impl.jni.{ ToxAvImplFactory, ToxCoreImplFactory } -import im.tox.tox4j.testing.autotest.AutoTest.ClientState -import org.scalatest.FunSuite -import org.scalatest.concurrent.TimeLimits -import org.slf4j.LoggerFactory -import shapeless.<:!< - -import scala.util.Random - -object AutoTestSuite { - - sealed abstract class Timed[A, R] { - - protected def wrap(time: Int, result: A): R - - def timed(block: => A): R = { - val start = System.currentTimeMillis() - val result = block - val end = System.currentTimeMillis() - wrap((end - start).toInt, result) - } - - } - - implicit def otherTimed[A](implicit notUnit: A <:!< Unit): Timed[A, (Int, A)] = new Timed[A, (Int, A)] { - protected def wrap(time: Int, result: A): (Int, A) = (time, result) - } - implicit val unitTimed: Timed[Unit, Int] = new Timed[Unit, Int] { - protected def wrap(time: Int, result: Unit): Int = time - } - - def timed[A, R](block: => A)(implicit timed: Timed[A, R]): R = timed.timed(block) - -} - -abstract class AutoTestSuite extends FunSuite with TimeLimits { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - protected def maxParticipantCount: Int = 2 - - type S - - abstract class EventListener(val initial: S) extends AutoTest.EventListener[S] { - - override def selfConnectionStatus( - connectionStatus: ToxConnection - )(state: State): State = { - debug(state, s"Our connection: $connectionStatus") - state - } - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connectionStatus: ToxConnection - )(state: State): State = { - debug(state, s"Friend ${state.id(friendNumber)}'s connection: $connectionStatus") - state - } - - } - - def Handler: EventListener // scalastyle:ignore method.name - - protected def debug(state: ClientState[S], message: String): Unit = { - logger.debug(s"[${state.id}] $message") - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - def run(ipv6Enabled: Boolean = true, udpEnabled: Boolean = true): Unit = { - failAfter(TestConstants.Timeout) { - val participantCount = - if (maxParticipantCount == 2) { - maxParticipantCount - } else { - new Random().nextInt(maxParticipantCount - 2) + 2 - } - AutoTest(ToxCoreImplFactory, ToxAvImplFactory).run(participantCount, ToxOptions(ipv6Enabled, udpEnabled), Handler) - } - } - - test("UDP")(run(ipv6Enabled = true, udpEnabled = true)) - -} diff --git a/src/test/java/im/tox/tox4j/testing/autotest/ChatClient.scala b/src/test/java/im/tox/tox4j/testing/autotest/ChatClient.scala deleted file mode 100644 index 33cf662ba..000000000 --- a/src/test/java/im/tox/tox4j/testing/autotest/ChatClient.scala +++ /dev/null @@ -1,124 +0,0 @@ -package im.tox.tox4j.testing.autotest - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.core._ -import im.tox.tox4j.core.callbacks.ToxCoreEventAdapter -import im.tox.tox4j.core.data.{ ToxFriendNumber, ToxFriendAddress, ToxPublicKey } -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.exceptions.ToxException -import im.tox.tox4j.testing.GetDisjunction._ -import org.slf4j.LoggerFactory - -import scala.annotation.tailrec - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final case class ChatStateT[T]( - state: T, - tasks: Seq[((ToxCore, ToxAv, ChatStateT[T]) => ChatStateT[T], Array[StackTraceElement])] = Nil, - chatting: Boolean = true -) { - - private def assembleStackTrace(e: ToxException[_], creationTrace: Array[StackTraceElement]): ToxException[_] = { - // The stack until the performTasks method call. - val untilPerformTasks = e.getStackTrace - .reverse - .dropWhile { callSite => - !((callSite.getClassName == classOf[ChatClientT[_]].getName) && - (callSite.getMethodName == "performTasks")) - } - .reverse - - // After that, add the task creation trace, minus the "addTask" method. - val trace = untilPerformTasks ++ creationTrace - - // Put the assembled trace into the exception and return it. - e.setStackTrace(trace) - - e - } - - private[autotest] def runTasks(tox: ToxCore, av: ToxAv): ChatStateT[T] = { - tasks.reverse.foldLeft(copy[T](tasks = Nil)) { - case (nextState, (task, stacktrace)) => - try { - task(tox, av, nextState) - } catch { - case e: ToxException[_] => - throw assembleStackTrace(e, stacktrace) - } - } - } - - def addTask(task: (ToxCore, ToxAv, ChatStateT[T]) => ChatStateT[T]): ChatStateT[T] = { - val creationTrace = Thread.currentThread.getStackTrace - copy[T](tasks = (task, creationTrace.slice(2, creationTrace.length)) +: tasks) - } - - def finish: ChatStateT[T] = { - copy[T](chatting = false) - } - - def get: T = state - - def set(value: T): ChatStateT[T] = { - copy[T](state = value) - } - - def map(f: T => T): ChatStateT[T] = { - copy[T](state = f(state)) - } - - def flatMap(f: T => ChatStateT[T]): ChatStateT[T] = { - f(state) - } - -} - -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -class ChatClientT[T](val selfName: String, val expectedFriendName: String) extends ToxCoreEventAdapter[ChatStateT[T]] { - - private val logger = Logger(LoggerFactory.getLogger(getOuterClass(getClass))) - - @tailrec - private def getOuterClass(clazz: Class[_]): Class[_] = { - Option(clazz.getEnclosingClass) match { - case None => clazz - case Some(enclosing) => getOuterClass(enclosing) - } - } - - protected def debug(message: String): Unit = { - logger.info(s"[${Thread.currentThread.getId}] $selfName: $message") - } - - var expectedFriendAddress: ToxFriendAddress = ToxFriendAddress.unsafeFromValue(null) - protected def expectedFriendPublicKey: ToxPublicKey = { - ToxPublicKey.fromValue(expectedFriendAddress.value.slice(0, ToxPublicKey.Size)).toOption.get - } - - protected def isAlice = selfName == "Alice" - protected def isBob = selfName == "Bob" - - def setup(tox: ToxCore)(state: ChatStateT[T]): ChatStateT[T] = state - - override def selfConnectionStatus(connectionStatus: ToxConnection)(state: ChatStateT[T]): ChatStateT[T] = { - if (connectionStatus != ToxConnection.NONE) { - debug(s"is now connected to the network with $connectionStatus") - } else { - debug("is now disconnected from the network") - } - state - } - - override def friendConnectionStatus(friendNumber: ToxFriendNumber, connection: ToxConnection)(state: ChatStateT[T]): ChatStateT[T] = { - assert(friendNumber == AliceBobTestBase.FriendNumber) - if (connection != ToxConnection.NONE) { - debug(s"is now connected to friend $friendNumber with $connection") - } else { - debug(s"is now disconnected from friend $friendNumber") - } - state - } - -} diff --git a/src/test/java/scalastyle-config.xml b/src/test/java/scalastyle-config.xml deleted file mode 100644 index ff9cc5be8..000000000 --- a/src/test/java/scalastyle-config.xml +++ /dev/null @@ -1,265 +0,0 @@ - - Scalastyle standard configuration - - Check the number of lines in a file - - - 800 - - - - Check the number of characters in a line - - - 160 - 4 - - - - Check that there are no tabs in a file - - - Check the first lines of each file matches the text - XXX: Disabled because we don't use file headers. - - - Checks that lines are indented by a multiple of the tab size - XXX: Disabled because we use scalariform on compile. - - - Checks that a file ends with a newline character - - - Checks that a file does not end with a newline character - XXX: Disabled because it's the opposite of the one above. - - - Checks that a regular expression cannot be matched, if found reports this - - - - - - - Check that there is no trailing whitespace at the end of lines - - - Checks that block imports are not used. - XXX: Disabled because block imports decrease verbosity. - - - Check that class names match a regular expression - - - - - - - Checks that type parameter to a class matches a regular expression - - - - - - - Check that classes and objects do not define equals without overriding equals(java.lang.Object). - - - Checks that the cyclomatic complexity of a method does exceed a value - - - 10 - - - - Checks that Java @Deprecated is not used, Scala @deprecated should be used instead - - - Disallow space after certain token(s) - - - Disallow space before certain token(s) - - - If a class/trait has no members, the braces are unnecessary - - - Ensure single space after certain token(s) - - - Ensure single space before certain token(s) - - - Check that if a class implements either equals or hashCode, it should implement the other - - - Check that field names match a regular expression - XXX: Disabled due to https://github.com/scalastyle/scalastyle/issues/149. - - - - - - - Checks that braces are used in for comprehensions. - XXX: Disabled due to https://github.com/scalastyle/scalastyle/issues/142. - - - Checks that if statements have braces - - - true - false - - - - Check that a class does not import certain classes - - - android._,sun._ - - - - Checks that imports are grouped together, not throughout the file - - - Checks that a case statement pattern match is not lower case, as this can cause confusion - - - Checks for use of magic numbers - - - -1,0,1,2,3 - - - - Checks that methods do not exceed a maximum length - - - 50 - - - - Check that method names match a regular expression - - - - - - - Checks that a string literal does not appear multiple times - - - Check that classes and objects do not define the clone() method - - - Check that classes and objects do not define the finalize() method - - - No whitespace after left bracket ‘[’ - - - No whitespace before left bracket ‘[’ - - - Some editors are unfriendly to non ascii characters. - - - Checks that the code does not have ??? operators. - - - Check that null is not used - - - Check that a class / trait / object does not have too many methods - - - 60 - - - - Checks that there are not too many types declared in a file - - - 30 - - - - Check that object names match a regular expression - - - - - - - Check that package object names match a regular expression - - - - - - - Maximum number of parameters for a method - - - 10 - - - - Use a : Unit = for procedure declarations - XXX: Disabled due to https://github.com/scalastyle/scalastyle/issues/145. - - - Check that a method has an explicit return type, it is not inferred - - - Checks that if expressions are not redundant, i.e. easily replaced by a variant of the condition - - - Check that return is not used - - - Checks that the ScalaDoc on documentable members is well-formed - - - Boolean expression can be simplified - - - Checks a space after the start of the comment. - - - Check that the plus sign is followed by a space - - - Check that the plus sign is preceded by a space - - - Check that structural types are not used. - - - Checks that a regular expression cannot be matched in a token, if found reports this - - - Avoid wildcard imports - XXX: Disabled because IDEA produces them. - - - Checks that if a long literal is used, then an uppercase L is used - - - Checks that classes and objects do not define mutable fields - - - Checks that functions do not define mutable variables - - - Checks that while is not used - - - Check that XML literals are not used - - - No-op checker - - - diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties deleted file mode 100644 index 5d753bb6c..000000000 --- a/src/test/resources/log4j.properties +++ /dev/null @@ -1,5 +0,0 @@ -log4j.rootLogger=DEBUG, stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss.SSS}] %-5p %c{1} - %m%n diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioPlaybackShow.scala b/src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioPlaybackShow.scala deleted file mode 100644 index 56b8284f5..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioPlaybackShow.scala +++ /dev/null @@ -1,34 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import im.tox.tox4j.av.data.{ AudioLength, SampleCount, SamplingRate } - -@SuppressWarnings(Array("org.wartremover.warts.While")) -object AudioPlaybackShow { - - private val audio = AudioGenerators.default - - private val samplingRate = SamplingRate.Rate24k - private val audioLength = AudioLength.Length60 - private val frameSize = SampleCount(audioLength, samplingRate) - private val playback = new AudioPlayback(samplingRate) - - def main(args: Array[String]) { - val terminalWidth = 190 - - System.out.print("\u001b[2J") - - for (t <- 0 to audio.length(samplingRate) by frameSize.value) { - val frame = audio.nextFrame16(audioLength, samplingRate, t) - System.out.print("\u001b[H") - System.out.println(AudioPlayback.showWave(frame, terminalWidth)) - System.out.println(s"t=$t") - System.out.flush() - playback.play(frame) - } - - while (!playback.done(audio.length(samplingRate))) { - Thread.sleep(100) - } - } - -} diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackShow.scala b/src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackShow.scala deleted file mode 100644 index 0152d8488..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/audio/AudioReceiveFrameCallbackShow.scala +++ /dev/null @@ -1,185 +0,0 @@ -package im.tox.tox4j.av.callbacks.audio - -import java.util -import java.util.concurrent.ArrayBlockingQueue - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.ToxavFriendCallState -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.ToxExceptionChecks -import im.tox.tox4j.testing.autotest.AutoTestSuite -import org.slf4j.LoggerFactory - -import scala.annotation.tailrec - -/** - * This test name does not end in "Test" so it won't be run by the CI build. This is - * because the test is flaky by nature: it expects every lossy audio packet to arrive. - * We keep it around, because it's fun, and because it tests something that is very - * hard to test otherwise: the received audio sounds good. This either requires a human - * or an algorithm we're too lazy to implement. - */ -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class AudioReceiveFrameCallbackShow extends AutoTestSuite with ToxExceptionChecks { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - private val terminalWidth = 190 - - override def maxParticipantCount: Int = 2 - - type S = Int - - object Handler extends EventListener(0) { - - private val bitRate = BitRate.fromInt(320).get - private val audioPerFrame = AudioLength.Length40 - private val samplingRate = SamplingRate.Rate48k - private val frameSize = SampleCount(audioPerFrame, samplingRate).value - private val framesPerIteration = 2 - - private val audio = AudioGenerators.default - private val audioLength = audio.length(samplingRate) - private val playback = new AudioPlayback(samplingRate) - private val displayWave = !sys.env.contains("TRAVIS") - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connectionStatus: ToxConnection - )(state0: State): State = { - val state = super.friendConnectionStatus(friendNumber, connectionStatus)(state0) - - if (connectionStatus == ToxConnection.NONE || state.id(friendNumber) != state.id.next) { - state - } else { - // Call id+1. - state.addTask { (tox, av, state) => - debug(state, s"Ringing ${state.id(friendNumber)}") - av.call(friendNumber, bitRate, BitRate.Disabled) - state - } - } - } - - override def call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean)(state: State): State = { - if (state.id(friendNumber) == state.id.prev) { - state.addTask { (tox, av, state) => - debug(state, s"Got a call from ${state.id(friendNumber)}; accepting") - av.answer(friendNumber, BitRate.Disabled, BitRate.Disabled) - state - } - } else { - fail(s"I shouldn't have been called by friend ${state.id(friendNumber)}") - state - } - } - - // There is no stack recursion here, it pushes thunks of itself for deferred execution. - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def sendFrame(friendNumber: ToxFriendNumber)(tox: ToxCore, av: ToxAv, state0: State): State = { - val state = state0.modify(_ + frameSize * framesPerIteration) - - for (t <- state0.get until state.get by frameSize) { - val pcm = audio.nextFrame16(audioPerFrame, samplingRate, t) - av.audioSendFrame( - friendNumber, - pcm, - SampleCount(audioPerFrame, samplingRate), - AudioChannels.Mono, - samplingRate - ) - } - - if (state.get >= audioLength) { - state.finish - } else { - state.addTask(sendFrame(friendNumber)) - } - } - - override def callState(friendNumber: ToxFriendNumber, callState: util.EnumSet[ToxavFriendCallState])(state: State): State = { - debug(state, s"Call with ${state.id(friendNumber)} is now $callState") - assert(callState == util.EnumSet.of( - ToxavFriendCallState.ACCEPTING_A, - ToxavFriendCallState.ACCEPTING_V - )) - state.addTask(sendFrame(friendNumber)) - } - - // There is no stack recursion here, it pushes thunks of itself for deferred execution. - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - def waitForPlayback(length: Int)(state: State): State = { - if (!playback.done(length)) { - state.addTask { (tox, av, state) => - waitForPlayback(length)(state) - } - } else { - state.finish - } - } - - override def audioReceiveFrame( - friendNumber: ToxFriendNumber, - pcm: Array[Short], - channels: AudioChannels, - samplingRate: SamplingRate - )(state0: State): State = { - val state = state0.modify(_ + pcm.length) - - debug(state, s"Received audio frame: ${state.get} / ${audio.length(samplingRate)}") - assert(channels == AudioChannels.Mono) - assert(samplingRate == this.samplingRate) - frameBuffer.add(Some((state0.get, pcm))) - - if (state.get >= audio.length(samplingRate)) { - frameBuffer.add(None) - waitForPlayback(audio.length(samplingRate))(state) - } else { - state - } - } - - @tailrec - private def playFrames(queue: ArrayBlockingQueue[Option[(Int, Array[Short])]]): Unit = { - queue.take() match { - case Some((t, receivedPcm)) => - val expectedPcm = audio.nextFrame16(audioPerFrame, samplingRate, t) - - assert(receivedPcm.length == expectedPcm.length) - - if (displayWave) { - if (t == 0) { - System.out.print("\u001b[2J") - } - System.out.print("\u001b[H") - System.out.println("Received:") - System.out.println(AudioPlayback.showWave(receivedPcm, terminalWidth)) - System.out.println("Expected:") - System.out.println(AudioPlayback.showWave(expectedPcm, terminalWidth)) - } - - playback.play(receivedPcm) - - playFrames(queue) - - case None => - logger.debug("Terminating audio playback thread") - } - } - - private lazy val frameBuffer = { - // Make the queue large enough to hold half the audio frames. - val queue = new ArrayBlockingQueue[Option[(Int, Array[Short])]](audioLength / frameSize / 2) - - // Start a thread to consume the frames. - new Thread { override def run() { playFrames(queue) } }.start - - queue - } - - } - -} diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/video/ConsoleVideoDisplay.scala b/src/tools/java/im/tox/tox4j/av/callbacks/video/ConsoleVideoDisplay.scala deleted file mode 100644 index 53bce9ef5..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/video/ConsoleVideoDisplay.scala +++ /dev/null @@ -1,38 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import java.io.PrintStream - -import im.tox.tox4j.av.data.{ Height, Width } - -import scala.util.{ Success, Try } - -final case class ConsoleVideoDisplay(width: Width, height: Height) extends VideoDisplay[Seq[String], PrintStream] { - - override protected def canvas: Try[PrintStream] = Success(System.out) - - override protected def displaySent(canvas: PrintStream, frameNumber: Int, senderImage: Seq[String]): Unit = { - // Don't display the sent image in text mode. - } - - override protected def displayReceived(canvas: PrintStream, frameNumber: Int, receiverImage: Seq[String]): Unit = { - canvas.print("\u001b[H\u001b[2J") - receiverImage.foreach(canvas.println) - } - - override protected def parse( - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - ): Seq[String] = { - val printable = ".-~:;/<>=()ot%!?@&O8SX$#" - - for (yPos <- 0 until height.value) yield { - new String(y.slice(yPos * yStride, yPos * yStride + width.value).map { - case b => - printable(((b & 0xff) / 255.0 * (printable.length - 1)).toInt) - }) - } - } - - override def close(): Unit = () - -} diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/video/GuiVideoDisplay.scala b/src/tools/java/im/tox/tox4j/av/callbacks/video/GuiVideoDisplay.scala deleted file mode 100644 index 8ca29d3c4..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/video/GuiVideoDisplay.scala +++ /dev/null @@ -1,141 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import java.awt.Color -import java.awt.image.{ BufferedImage, DataBufferByte } -import java.io.File -import javax.imageio.ImageIO -import javax.swing.border.EtchedBorder -import javax.swing.{ BorderFactory, ImageIcon, JDialog } - -import com.typesafe.scalalogging.Logger -import im.tox.tox4j.av.callbacks.video.GuiVideoDisplay.{ UI, newImage } -import im.tox.tox4j.av.data.{ Height, Width } -import org.slf4j.LoggerFactory - -import scala.swing._ -import scala.util.Try - -object GuiVideoDisplay { - - private val capturePath = Some(new File("capture/video")).filter(_.isDirectory) - capturePath.foreach(_.listFiles.foreach(_.delete())) - - private def newImage(width: Width, height: Height): BufferedImage = { - new BufferedImage(width.value, height.value, BufferedImage.TYPE_3BYTE_BGR) - } - - final class UI(width: Width, height: Height) { - - val senderImageView, receiverImageView: Label = new Label { - border = BorderFactory.createEtchedBorder(EtchedBorder.RAISED) - icon = new ImageIcon(newImage(width, height)) - } - - val senderLabel: Label = new Label("No frames sent yet") - val receiverLabel: Label = new Label("No frames received yet") - - val dialog: Dialog = new Dialog(new Frame) { - contents = new BoxPanel(Orientation.Vertical) { - contents += new BoxPanel(Orientation.Horizontal) { - contents += senderImageView - contents += receiverImageView - } - contents += new BoxPanel(Orientation.Horizontal) { - contents += Swing.HGlue - contents += senderLabel - contents += Swing.HGlue - contents += Swing.HGlue - contents += receiverLabel - contents += Swing.HGlue - } - } - } - - dialog.pack() - - /** - * Align a width or height number to the next multiple of 2. - * This is required so the dumped screenshots can be turned into a video by ffmpeg. - */ - private def align(dimension: Int): Int = { - dimension / 2 * 2 + 2 - } - - def screenshot(frameNumber: Int): Unit = { - capturePath.foreach { capturePath => - val image = new BufferedImage( - align(dialog.bounds.width), - align(dialog.bounds.height), - BufferedImage.TYPE_INT_RGB - ) - - dialog.self.asInstanceOf[JDialog].paint(image.getGraphics) - - ImageIO.write(image, "jpg", new File(capturePath, f"$frameNumber%03d.jpg")) - } - } - - } - -} - -final case class GuiVideoDisplay(width: Width, height: Height) extends RgbVideoDisplay[ImageIcon, UI] { - - private val logger = Logger(LoggerFactory.getLogger(getClass)) - - override protected val canvas: Try[UI] = Try(new GuiVideoDisplay.UI(width, height)) - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override protected def parse(r: Array[Byte], g: Array[Byte], b: Array[Byte]): ImageIcon = { - val image = newImage(width, height) - - val data = image.getRaster.getDataBuffer.asInstanceOf[DataBufferByte].getData - assert(data.length == r.length + g.length + b.length) - for (index <- data.indices) { - index % 3 match { - case 0 => data(index) = b(index / 3) - case 1 => data(index) = g(index / 3) - case 2 => data(index) = r(index / 3) - } - } - - new ImageIcon(image) - } - - override protected def displaySent(canvas: UI, frameNumber: Int, senderImage: ImageIcon): Unit = { - Swing.onEDTWait { - if (!canvas.dialog.visible) { - canvas.dialog.visible = true - } - canvas.senderLabel.text = s"Sent frame #$frameNumber" - canvas.senderImageView.icon = senderImage - - canvas.screenshot(frameNumber) - } - } - - @SuppressWarnings(Array("org.wartremover.warts.Equals")) - override protected def displayReceived(canvas: UI, frameNumber: Int, receiverImage: ImageIcon): Unit = { - Swing.onEDTWait { - canvas.receiverLabel.text = s"Received frame #$frameNumber" - canvas.receiverImageView.icon = receiverImage - - val FrameNumber = "Sent frame #(\\d+)".r - val sentFrameNumber = - canvas.senderLabel.text match { - case FrameNumber(number) => - if (number.toInt != frameNumber) { - canvas.receiverLabel.foreground = Color.RED - canvas.receiverLabel.text += s" (${number.toInt - frameNumber} behind)" - } - number.toInt - } - - // Overwrite the screenshot if this frame was received. - canvas.screenshot(sentFrameNumber) - } - } - - override def close(): Unit = canvas.foreach(_.dialog.close()) - -} diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/video/RgbVideoDisplay.scala b/src/tools/java/im/tox/tox4j/av/callbacks/video/RgbVideoDisplay.scala deleted file mode 100644 index 3c098ea98..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/video/RgbVideoDisplay.scala +++ /dev/null @@ -1,22 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -abstract class RgbVideoDisplay[Parsed, Canvas] extends VideoDisplay[Parsed, Canvas] { - - protected def parse(r: Array[Byte], g: Array[Byte], b: Array[Byte]): Parsed - - override protected final def parse( - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - ): Parsed = { - val width = this.width.value - val height = this.height.value - - val (r, g, b) = VideoConversions.YUVtoRGB(width, height, y, u, v, yStride, uStride, vStride) - assert(r.length == width * height) - assert(g.length == width * height) - assert(b.length == width * height) - - parse(r, g, b) - } - -} diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/video/VideoDisplay.scala b/src/tools/java/im/tox/tox4j/av/callbacks/video/VideoDisplay.scala deleted file mode 100644 index fdea1cf4a..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/video/VideoDisplay.scala +++ /dev/null @@ -1,45 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import java.io.Closeable - -import im.tox.tox4j.av.data.{ Height, Width } -import im.tox.tox4j.testing.autotest.AutoTestSuite.timed -import org.scalatest.Assertions - -import scala.util.Try - -abstract class VideoDisplay[Parsed, Canvas] extends Assertions with Closeable { - - def width: Width - def height: Height - - protected def canvas: Try[Canvas] - protected def parse( - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - ): Parsed - protected def displaySent(canvas: Canvas, frameNumber: Int, parsed: Parsed): Unit - protected def displayReceived(canvas: Canvas, frameNumber: Int, parsed: Parsed): Unit - - final def displaySent(frameNumber: Int, y: Array[Byte], u: Array[Byte], v: Array[Byte]): Unit = { - val width = this.width.value - canvas.foreach(displaySent(_, frameNumber, parse(y, u, v, width, width / 2, width / 2))) - } - - /** - * @return (parseTime, displayTime) - */ - final def displayReceived( - frameNumber: Int, - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - ): Option[(Int, Int)] = { - canvas.toOption.map { canvas => - val (parseTime, parsed) = timed(parse(y, u, v, yStride, uStride, vStride)) - val displayTime = timed(displayReceived(canvas, frameNumber, parsed)) - - (parseTime, displayTime) - } - } - -} diff --git a/src/tools/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackShow.scala b/src/tools/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackShow.scala deleted file mode 100644 index 23d4b4eeb..000000000 --- a/src/tools/java/im/tox/tox4j/av/callbacks/video/VideoReceiveFrameCallbackShow.scala +++ /dev/null @@ -1,165 +0,0 @@ -package im.tox.tox4j.av.callbacks.video - -import java.io.{ DataOutputStream, File, FileOutputStream } -import java.util - -import im.tox.tox4j.av.ToxAv -import im.tox.tox4j.av.data._ -import im.tox.tox4j.av.enums.ToxavFriendCallState -import im.tox.tox4j.core.ToxCore -import im.tox.tox4j.core.data.ToxFriendNumber -import im.tox.tox4j.core.enums.ToxConnection -import im.tox.tox4j.testing.ToxExceptionChecks -import im.tox.tox4j.testing.autotest.AutoTestSuite -import im.tox.tox4j.testing.autotest.AutoTestSuite.timed - -/** - * See [[im.tox.tox4j.av.callbacks.audio.AudioReceiveFrameCallbackShow]] for why this - * class exists. - */ -@SuppressWarnings(Array("org.wartremover.warts.Equals")) -final class VideoReceiveFrameCallbackShow extends AutoTestSuite with ToxExceptionChecks { - - private val video = VideoGenerators.default - - /** - * The time to wait for the next frame. Increase this if you need more time - * to look at the displayed images. - */ - private val frameDelay = 0 - - private val bitRate = BitRate.fromInt(1).get - - /** - * Base directory for sent frame capture. The files can be used to replay a - * session in case of bugs. - */ - private val capturePath = Some(new File("capture/videoSendFrame")).filter(_.isDirectory) - capturePath.foreach(_.listFiles.foreach(_.delete())) - - override def maxParticipantCount: Int = 2 - - type S = Int - - object Handler extends EventListener(0) { - - private lazy val displayImage = { - if (sys.env.contains("TRAVIS")) { - None - } else { - if (video.size <= 100 * 40) { - Some(ConsoleVideoDisplay(video.width, video.height)) - } else { - Some(GuiVideoDisplay(video.width, video.height)) - } - } - } - - override def friendConnectionStatus( - friendNumber: ToxFriendNumber, - connectionStatus: ToxConnection - )(state0: State): State = { - val state = super.friendConnectionStatus(friendNumber, connectionStatus)(state0) - - if (connectionStatus == ToxConnection.NONE || state.id(friendNumber) != state.id.next) { - state - } else { - // Call id+1. - state.addTask { (tox, av, state) => - debug(state, s"Ringing ${state.id(friendNumber)}") - av.call(friendNumber, BitRate.Disabled, bitRate) - state - } - } - } - - override def call(friendNumber: ToxFriendNumber, audioEnabled: Boolean, videoEnabled: Boolean)(state: State): State = { - if (state.id(friendNumber) == state.id.prev) { - state.addTask { (tox, av, state) => - debug(state, s"Got a call from ${state.id(friendNumber)}; accepting") - av.answer(friendNumber, BitRate.Disabled, BitRate.Disabled) - state - } - } else { - fail(s"I shouldn't have been called by friend ${state.id(friendNumber)}") - state - } - } - - // There is no stack recursion here, it pushes thunks of itself for deferred execution. - @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - private def sendFrame(friendNumber: ToxFriendNumber)(tox: ToxCore, av: ToxAv, state0: State): State = { - val state = state0.modify(_ + 1) - - val (generationTime, (y, u, v)) = timed { - video.yuv(state0.get) - } - assert(y.length == video.size) - assert(u.length == video.size / 4) - assert(v.length == video.size / 4) - - val displayTime = timed { - displayImage.foreach { display => - display.displaySent(state0.get, y, u, v) - } - } - val sendTime = timed { - av.videoSendFrame(friendNumber, video.width.value, video.height.value, y, u, v) - } - - capturePath.foreach { capturePath => - val out = new DataOutputStream(new FileOutputStream(new File(capturePath, f"${state0.get}%03d.dump"))) - out.writeInt(video.width.value) - out.writeInt(video.height.value) - out.write(y) - out.write(u) - out.write(v) - out.close() - } - - debug( - state, - s"Sent frame ${state0.get}: generationTime=${generationTime}ms, displayTime=${displayTime}ms, sendTime=${sendTime}ms" - ) - - if (state.get >= video.length) { - state.finish - } else { - state.addTask(frameDelay)(sendFrame(friendNumber)) - } - } - - override def callState(friendNumber: ToxFriendNumber, callState: util.EnumSet[ToxavFriendCallState])(state: State): State = { - debug(state, s"Call with ${state.id(friendNumber)} is now $callState") - state.addTask(sendFrame(friendNumber)) - } - - override def videoReceiveFrame( - friendNumber: ToxFriendNumber, - width: Width, height: Height, - y: Array[Byte], u: Array[Byte], v: Array[Byte], - yStride: Int, uStride: Int, vStride: Int - )(state0: State): State = { - val state = state0.modify(_ + 1) - - val times = - for { - displayImage <- displayImage - (parseTime, displayTime) <- displayImage.displayReceived(state0.get, y, u, v, yStride, uStride, vStride) - } yield { - s", parseTime=${parseTime}ms, displayTime=${displayTime}ms" - } - - debug(state, s"Received frame ${state0.get}: $width, $height, strides=($yStride, $uStride, $vStride)${times.getOrElse("")}") - - if (state.get >= video.length) { - displayImage.foreach(_.close()) - state.finish - } else { - state - } - } - - } - -} diff --git a/tools/cmake/FindGlog.cmake b/tools/cmake/FindGlog.cmake deleted file mode 100644 index aa16577aa..000000000 --- a/tools/cmake/FindGlog.cmake +++ /dev/null @@ -1,49 +0,0 @@ -# From https://github.com/hanjianwei/cmake-modules/blob/master/FindGlog.cmake -# -# - Try to find Glog -# -# The following variables are optionally searched for defaults -# GLOG_ROOT_DIR: Base directory where all GLOG components are found -# -# The following are set after configuration is done: -# GLOG_FOUND -# GLOG_INCLUDE_DIRS -# GLOG_LIBRARIES - -include(FindPackageHandleStandardArgs) - -set(GLOG_ROOT_DIR "" CACHE PATH "Folder contains Google glog") - -if(WIN32) - find_path(GLOG_INCLUDE_DIR glog/logging.h - PATHS ${GLOG_ROOT_DIR}/src/windows) -else() - find_path(GLOG_INCLUDE_DIR glog/logging.h - PATHS ${GLOG_ROOT_DIR}) -endif() - -if(MSVC) - find_library(GLOG_LIBRARY_RELEASE libglog_static - PATHS ${GLOG_ROOT_DIR} - PATH_SUFFIXES Release) - - find_library(GLOG_LIBRARY_DEBUG libglog_static - PATHS ${GLOG_ROOT_DIR} - PATH_SUFFIXES Debug) - - set(GLOG_LIBRARY optimized ${GLOG_LIBRARY_RELEASE} debug ${GLOG_LIBRARY_DEBUG}) -else() - find_library(GLOG_LIBRARY glog - PATHS ${GLOG_ROOT_DIR} - PATH_SUFFIXES - lib - lib64) -endif() - -find_package_handle_standard_args(GLOG DEFAULT_MSG - GLOG_INCLUDE_DIR GLOG_LIBRARY) - -if(GLOG_FOUND) - set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR}) - set(GLOG_LIBRARIES ${GLOG_LIBRARY}) -endif() diff --git a/tools/proguard.txt b/tools/proguard.txt deleted file mode 100644 index 54d5de422..000000000 --- a/tools/proguard.txt +++ /dev/null @@ -1,62 +0,0 @@ --keepattributes *Annotation*,InnerClasses - -# Main entry point. --keep public class im.tox.client.TestClient { - public static void main(java.lang.String[]); -} - -# Constructed from JNI. --keep public class * extends im.tox.tox4j.exceptions.ToxException { - public *; -} - --keep public class im.tox.** extends java.lang.Enum { - public *; -} - -# Constructed through descriptor reflection. --keep public class * implements com.google.protobuf.ProtocolMessageEnum { - public *; -} - --keep public class * implements com.google.protobuf.MessageOrBuilder { - public *; -} - -# Configured through log4j.properties. --keep public class org.apache.log4j.ConsoleAppender { - public *; -} - --keep public class org.apache.log4j.PatternLayout { - public *; -} - --verbose --optimizationpasses 2 --dontobfuscate --dontnote - -# LVTT entry for 'entry' in class file com/google/protobuf/AbstractMessage$Builder does not match any LVT entry --optimizations !code/allocation/variable - --dontwarn scala.** --dontwarn scalax.** --dontwarn scalaz.** - -# Needs javax.annotation --dontwarn com.google.common.** --dontwarn com.thoughtworks.paranamer.** --dontwarn io.netty.** --dontwarn okio.* --dontwarn org.apache.** --dontwarn org.fusesource.** --dontwarn org.jdom.xpath.* --dontwarn org.jfree.** --dontwarn org.scalacheck.** --dontwarn org.scalameter.** --dontwarn org.scalatest.** --dontwarn scodec.bits.* -# Needs javax.crypto --dontwarn scodec.codecs.Cipher* --dontwarn scoverage.**