From a8814d16f8595c57bf615028f2d3c19d4ed67355 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 23 Aug 2024 16:12:00 -0700 Subject: [PATCH] Move all "testing-fns" APIs into libsignal-bridge-testing This means they'll be shipped to clients, but still filtered out of the device builds on Android (via having two separate libraries on disk) and iOS (by not being included in non-simulator builds). The biggest benefit of this is dropping the :android:makeTestJniLibraries step for running libsignal's Android tests. --- .github/workflows/build_and_test.yml | 2 +- .github/workflows/slow_tests.yml | 6 +- Cargo.lock | 8 + java/android/build.gradle | 21 +-- java/build_jni.sh | 14 +- .../libsignal/media/InputStreamTest.java | 5 +- .../libsignal/net/CdsiLookupResponseTest.java | 6 +- .../signal/libsignal/net/ChatServiceTest.java | 24 ++- java/gradle/verification-metadata.xml | 4 +- .../org/signal/libsignal/internal/Native.java | 47 ----- .../libsignal/internal/NativeTesting.java | 47 +++++ .../libsignal/internal/BridgingTest.java | 50 ++--- .../signal/libsignal/internal/FutureTest.java | 6 +- node/build_node_bridge.py | 2 +- rust/bridge/ffi/Cargo.toml | 1 - rust/bridge/ffi/cbindgen-testing.toml | 30 ++- rust/bridge/ffi/cbindgen.toml | 7 +- rust/bridge/ffi/src/lib.rs | 2 +- rust/bridge/jni/Cargo.toml | 3 - rust/bridge/jni/bin/gen_java_decl.py | 2 +- rust/bridge/jni/cbindgen.toml | 6 +- rust/bridge/node/Cargo.toml | 3 - rust/bridge/node/bin/gen_ts_decl.py | 2 +- rust/bridge/shared/Cargo.toml | 1 - rust/bridge/shared/src/lib.rs | 5 - rust/bridge/shared/src/net/chat.rs | 28 --- rust/bridge/shared/testing/Cargo.toml | 8 + .../testing/mod.rs => testing/src/convert.rs} | 6 +- rust/bridge/shared/testing/src/lib.rs | 3 + .../{src/testing => testing/src}/net.rs | 31 ++- .../{src/testing => testing/src}/types.rs | 0 swift/Sources/SignalFfi/signal_ffi.h | 156 ---------------- swift/Sources/SignalFfi/signal_ffi_testing.h | 176 ++++++++++++++++++ swift/build_ffi.sh | 2 +- 34 files changed, 368 insertions(+), 346 deletions(-) rename rust/bridge/shared/{src/testing/mod.rs => testing/src/convert.rs} (98%) rename rust/bridge/shared/{src/testing => testing/src}/net.rs (89%) rename rust/bridge/shared/{src/testing => testing/src}/types.rs (100%) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index d95cbea1..6e55122b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -292,7 +292,7 @@ jobs: - name: Verify that the JNI bindings are up to date run: rust/bridge/jni/bin/gen_java_decl.py --verify - - run: ./gradlew build assembleDebugAndroidTest android:lintDebug -PandroidArchs=arm,arm64 -PandroidTestingArchs=x86_64 | tee ./gradle-output.txt + - run: ./gradlew build assembleDebugAndroidTest android:lintDebug -PandroidArchs=arm,arm64,x86_64 | tee ./gradle-output.txt working-directory: java shell: bash # Explicitly setting the shell turns on pipefail in GitHub Actions diff --git a/.github/workflows/slow_tests.yml b/.github/workflows/slow_tests.yml index 570a3a24..a27c4d15 100644 --- a/.github/workflows/slow_tests.yml +++ b/.github/workflows/slow_tests.yml @@ -43,7 +43,7 @@ jobs: uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: jniLibs - path: java/android/src/androidTest/jniLibs/* + path: java/android/src/main/jniLibs/* retention-days: 2 - name: Upload full JARs uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 @@ -144,7 +144,7 @@ jobs: uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 with: name: jniLibs - path: java/android/src/androidTest/jniLibs/ + path: java/android/src/main/jniLibs/ # From reactivecircus/android-emulator-runner - name: AVD cache @@ -173,7 +173,7 @@ jobs: api-level: 21 force-avd-creation: false emulator-options: -no-snapshot-save -no-window -noaudio -no-boot-anim - script: ./gradlew android:connectedCheck -x makeJniLibrariesDesktop -x android:makeJniLibraries -x android:makeTestJniLibraries + script: ./gradlew android:connectedCheck -x makeJniLibrariesDesktop -x android:makeJniLibraries working-directory: java aarch64: diff --git a/Cargo.lock b/Cargo.lock index 9c02a5c9..c6edac8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2019,16 +2019,24 @@ dependencies = [ name = "libsignal-bridge-testing" version = "0.1.0" dependencies = [ + "attest", "futures-util", + "http 1.1.0", "jni 0.21.1", "libsignal-bridge-macros", "libsignal-bridge-types", "libsignal-message-backup", + "libsignal-net", + "libsignal-protocol", "linkme", "neon", + "nonzero_ext", "paste", + "prost", "scopeguard", + "strum", "tokio", + "uuid", ] [[package]] diff --git a/java/android/build.gradle b/java/android/build.gradle index db65687e..3c40fe66 100644 --- a/java/android/build.gradle +++ b/java/android/build.gradle @@ -50,10 +50,6 @@ android { } packagingOptions { - jniLibs { - pickFirst 'lib/*/libsignal_jni.so' - pickFirst 'lib/*/libsignal_jni_testing.so' - } // Defer stripping to the Android app project. doNotStrip '**/*.so' } @@ -123,7 +119,6 @@ dependencies { preBuild { dependsOn 'collectAssets' dependsOn 'makeJniLibraries' - dependsOn 'makeTestJniLibraries' } String[] archsFromProperty(String prop) { @@ -136,21 +131,9 @@ task makeJniLibraries(type:Exec) { def archs = archsFromProperty('androidArchs') ?: ['android'] def debugLevelLogsFlag = project.hasProperty('debugLevelLogs') ? ['--debug-level-logs'] : [] + def debugFlag = project.hasProperty('debugRust') ? ['--debug'] : [] // Explicitly specify 'bash' for Windows compatibility. - commandLine 'bash', '../build_jni.sh', *debugLevelLogsFlag, *archs - environment 'ANDROID_NDK_HOME', android.ndkDirectory -} - -task makeTestJniLibraries(type:Exec) { - group 'Rust' - description 'Build JNI libraries for Android for testing' - - def archs = archsFromProperty('androidTestingArchs') ?: archsFromProperty('androidArchs') ?: ['android'] - // While it might make sense for tests to always have debug level logs on, - // we would lose most of the shared incremental build between test and non-test that way. - def debugLevelLogsFlag = project.hasProperty('debugLevelLogs') ? ['--debug-level-logs'] : [] - // Explicitly specify 'bash' for Windows compatibility. - commandLine 'bash', '../build_jni.sh', '--testing', *debugLevelLogsFlag, *archs + commandLine 'bash', '../build_jni.sh', *debugLevelLogsFlag, *debugFlag, *archs environment 'ANDROID_NDK_HOME', android.ndkDirectory } diff --git a/java/build_jni.sh b/java/build_jni.sh index 15274d3a..42a39e1d 100755 --- a/java/build_jni.sh +++ b/java/build_jni.sh @@ -19,20 +19,15 @@ SERVER_LIB_DIR=java/server/src/main/resources export CARGO_PROFILE_RELEASE_DEBUG=1 # enable line tables export RUSTFLAGS="--cfg aes_armv8 --cfg polyval_armv8 ${RUSTFLAGS:-}" # Enable ARMv8 cryptography acceleration when available -BUILD_FOR_TEST= DEBUG_LEVEL_LOGS= while [ "${1:-}" != "" ]; do case "${1:-}" in - --testing ) - BUILD_FOR_TEST=1 - shift - ;; --debug-level-logs ) DEBUG_LEVEL_LOGS=1 shift ;; -* ) - echo "Unrecognized flag $1; expected --testing or --debug-level-logs" >&2 + echo "Unrecognized flag $1; expected --debug-level-logs" >&2 exit 2 ;; *) @@ -75,6 +70,7 @@ build_desktop_for_arch () { export CPATH="/usr/${cpuarch}-linux-gnu/include" fi fi + echo_then_run cargo build -p libsignal-jni -p libsignal-jni-testing --release ${FEATURES:+--features "${FEATURES[*]}"} --target "$1" copy_built_library "target/${1}/release" signal_jni "$lib_dir" "signal_jni_${suffix}" copy_built_library "target/${1}/release" signal_jni_testing "$lib_dir" "signal_jni_testing_${suffix}" @@ -95,7 +91,6 @@ while [ "${1:-}" != "" ]; do # Using LTO works around this at the cost of a slightly slower build. # https://github.com/rust-lang/rfcs/issues/2771 export CARGO_PROFILE_RELEASE_LTO=thin - FEATURES+=("testing-fns") host_triple=$(rustc -vV | sed -n 's|host: ||p') if [[ "$1" == "server-all" ]]; then build_desktop_for_arch x86_64-unknown-linux-gnu "$host_triple" $lib_dir @@ -162,11 +157,6 @@ export TARGET_AR="${ANDROID_TOOLCHAIN_DIR}/llvm-ar" # Comment out the following to allow the 32-bit backend on 32-bit targets. export RUSTFLAGS="--cfg curve25519_dalek_bits=\"64\" ${RUSTFLAGS:-}" -if [ $BUILD_FOR_TEST ]; then - FEATURES+=("testing-fns") - ANDROID_LIB_DIR="${ANDROID_LIB_DIR}/../../androidTest/jniLibs" -fi - target_for_abi() { case "$1" in arm64-v8a) diff --git a/java/client/src/test/java/org/signal/libsignal/media/InputStreamTest.java b/java/client/src/test/java/org/signal/libsignal/media/InputStreamTest.java index 27553ba3..af01da39 100644 --- a/java/client/src/test/java/org/signal/libsignal/media/InputStreamTest.java +++ b/java/client/src/test/java/org/signal/libsignal/media/InputStreamTest.java @@ -9,7 +9,7 @@ import static org.junit.Assert.assertArrayEquals; import java.io.ByteArrayInputStream; import org.junit.Test; -import org.signal.libsignal.internal.Native; +import org.signal.libsignal.internal.NativeTesting; public class InputStreamTest { @@ -17,6 +17,7 @@ public class InputStreamTest { public void testReadIntoEmptyBuffer() { byte[] data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(); assertArrayEquals( - Native.TESTING_InputStreamReadIntoZeroLengthSlice(new ByteArrayInputStream(data)), data); + NativeTesting.TESTING_InputStreamReadIntoZeroLengthSlice(new ByteArrayInputStream(data)), + data); } } diff --git a/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java b/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java index d969c760..427890ff 100644 --- a/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java +++ b/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java @@ -15,8 +15,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.junit.Test; import org.signal.libsignal.attest.AttestationDataException; -import org.signal.libsignal.internal.Native; import org.signal.libsignal.internal.NativeHandleGuard; +import org.signal.libsignal.internal.NativeTesting; import org.signal.libsignal.protocol.ServiceId; public class CdsiLookupResponseTest { @@ -45,7 +45,7 @@ public class CdsiLookupResponseTest { Future response; try (NativeHandleGuard guard = new NativeHandleGuard(context)) { - response = Native.TESTING_CdsiLookupResponseConvert(guard.nativeHandle()); + response = NativeTesting.TESTING_CdsiLookupResponseConvert(guard.nativeHandle()); } CdsiLookupResponse actual = (CdsiLookupResponse) response.get(); @@ -90,7 +90,7 @@ public class CdsiLookupResponseTest { assertThrows( "for " + errorDescription, expectedErrorType, - () -> Native.TESTING_CdsiLookupErrorConvert(errorDescription)); + () -> NativeTesting.TESTING_CdsiLookupErrorConvert(errorDescription)); assertEquals(e.getMessage(), expectedMessage); return e; } diff --git a/java/client/src/test/java/org/signal/libsignal/net/ChatServiceTest.java b/java/client/src/test/java/org/signal/libsignal/net/ChatServiceTest.java index 2b0de348..e9cd6543 100644 --- a/java/client/src/test/java/org/signal/libsignal/net/ChatServiceTest.java +++ b/java/client/src/test/java/org/signal/libsignal/net/ChatServiceTest.java @@ -12,7 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.Map; import org.junit.Assume; import org.junit.Test; -import org.signal.libsignal.internal.Native; +import org.signal.libsignal.internal.NativeTesting; import org.signal.libsignal.util.TestEnvironment; public class ChatServiceTest { @@ -34,14 +34,14 @@ public class ChatServiceTest { public void testConvertResponse() throws Exception { // empty body final ChatService.Response response1 = - (ChatService.Response) Native.TESTING_ChatServiceResponseConvert(false); + (ChatService.Response) NativeTesting.TESTING_ChatServiceResponseConvert(false); assertEquals(EXPECTED_STATUS, response1.status()); assertEquals(EXPECTED_MESSAGE, response1.message()); assertArrayEquals(new byte[0], response1.body()); assertEquals(EXPECTED_HEADERS, response1.headers()); final ChatService.Response response2 = - (ChatService.Response) Native.TESTING_ChatServiceResponseConvert(true); + (ChatService.Response) NativeTesting.TESTING_ChatServiceResponseConvert(true); assertEquals(EXPECTED_STATUS, response2.status()); assertEquals(EXPECTED_MESSAGE, response2.message()); assertArrayEquals(EXPECTED_CONTENT, response2.body()); @@ -51,7 +51,7 @@ public class ChatServiceTest { @Test public void testConvertDebugInfo() throws Exception { final ChatService.DebugInfo debugInfo = - (ChatService.DebugInfo) Native.TESTING_ChatServiceDebugInfoConvert(); + (ChatService.DebugInfo) NativeTesting.TESTING_ChatServiceDebugInfoConvert(); assertEquals(IpType.IPv4, debugInfo.ipType()); assertEquals(200, debugInfo.durationMs()); assertEquals("connection_info", debugInfo.connectionInfo()); @@ -60,7 +60,8 @@ public class ChatServiceTest { @Test public void testConvertResponseAndDebugInfo() throws Exception { final ChatService.ResponseAndDebugInfo responseAndDebugInfo = - (ChatService.ResponseAndDebugInfo) Native.TESTING_ChatServiceResponseAndDebugInfoConvert(); + (ChatService.ResponseAndDebugInfo) + NativeTesting.TESTING_ChatServiceResponseAndDebugInfoConvert(); final ChatService.Response response = responseAndDebugInfo.response(); assertEquals(EXPECTED_STATUS, response.status()); @@ -95,7 +96,7 @@ public class ChatServiceTest { return assertThrows( "for " + errorDescription, expectedErrorType, - () -> Native.TESTING_ChatServiceErrorConvert(errorDescription)); + () -> NativeTesting.TESTING_ChatServiceErrorConvert(errorDescription)); } @Test @@ -106,14 +107,17 @@ public class ChatServiceTest { new ChatService.Request( expectedMethod, expectedPathAndQuery, EXPECTED_HEADERS, EXPECTED_CONTENT, 5000); final ChatService.InternalRequest internal = ChatService.buildInternalRequest(request); - assertEquals(expectedMethod, internal.guardedMap(Native::TESTING_ChatRequestGetMethod)); - assertEquals(expectedPathAndQuery, internal.guardedMap(Native::TESTING_ChatRequestGetPath)); - assertArrayEquals(EXPECTED_CONTENT, internal.guardedMap(Native::TESTING_ChatRequestGetBody)); + assertEquals(expectedMethod, internal.guardedMap(NativeTesting::TESTING_ChatRequestGetMethod)); + assertEquals( + expectedPathAndQuery, internal.guardedMap(NativeTesting::TESTING_ChatRequestGetPath)); + assertArrayEquals( + EXPECTED_CONTENT, internal.guardedMap(NativeTesting::TESTING_ChatRequestGetBody)); EXPECTED_HEADERS.forEach( (name, value) -> assertEquals( value, - internal.guardedMap(h -> Native.TESTING_ChatRequestGetHeaderValue(h, name)))); + internal.guardedMap( + h -> NativeTesting.TESTING_ChatRequestGetHeaderValue(h, name)))); } @Test diff --git a/java/gradle/verification-metadata.xml b/java/gradle/verification-metadata.xml index efb7604e..8700da6d 100644 --- a/java/gradle/verification-metadata.xml +++ b/java/gradle/verification-metadata.xml @@ -7,8 +7,8 @@ Run the following command to update this file after adding or updating a depende (remove the backslashes before running) Depending on which dependency you've updated, you may have to run an actual build action instead of -just `help`. You can add `-x android:makeJniLibraries -x android:makeTestJniLibraries` to skip -building the Android native libraries. +just `help`. You can add `-x android:makeJniLibraries` to skip building the Android native +libraries. For more information, see: https://docs.gradle.org/current/userguide/dependency_verification.html diff --git a/java/shared/java/org/signal/libsignal/internal/Native.java b/java/shared/java/org/signal/libsignal/internal/Native.java index 931e1488..55a75e80 100644 --- a/java/shared/java/org/signal/libsignal/internal/Native.java +++ b/java/shared/java/org/signal/libsignal/internal/Native.java @@ -397,8 +397,6 @@ public final class Native { public static native byte[] NumericFingerprintGenerator_GetScannableEncoding(long obj) throws Exception; public static native long NumericFingerprintGenerator_New(int iterations, int version, byte[] localIdentifier, byte[] localKey, byte[] remoteIdentifier, byte[] remoteKey) throws Exception; - public static native void OtherTestingHandleType_Destroy(long handle); - public static native byte[] PinHash_AccessKey(long ph); public static native void PinHash_Destroy(long handle); public static native byte[] PinHash_EncryptionKey(long ph); @@ -648,51 +646,6 @@ public final class Native { public static native CompletableFuture Svr3Restore(long asyncRuntime, long connectionManager, String password, byte[] shareSet, String username, String enclavePassword); - public static native void TESTING_CdsiLookupErrorConvert(String errorDescription) throws Exception; - public static native CompletableFuture TESTING_CdsiLookupResponseConvert(long asyncRuntime); - public static native byte[] TESTING_ChatRequestGetBody(long request); - public static native String TESTING_ChatRequestGetHeaderValue(long request, String headerName); - public static native String TESTING_ChatRequestGetMethod(long request); - public static native String TESTING_ChatRequestGetPath(long request); - public static native Object TESTING_ChatServiceDebugInfoConvert() throws Exception; - public static native void TESTING_ChatServiceErrorConvert(String errorDescription) throws Exception; - public static native Object TESTING_ChatServiceResponseAndDebugInfoConvert() throws Exception; - public static native Object TESTING_ChatServiceResponseConvert(boolean bodyPresent) throws Exception; - public static native void TESTING_ChatService_InjectConnectionInterrupted(long chat); - public static native void TESTING_ChatService_InjectRawServerRequest(long chat, byte[] bytes); - public static native void TESTING_ErrorOnBorrowAsync(Object input); - public static native CompletableFuture TESTING_ErrorOnBorrowIo(long asyncRuntime, Object input); - public static native void TESTING_ErrorOnBorrowSync(Object input); - public static native Object TESTING_ErrorOnReturnAsync(Object needsCleanup); - public static native CompletableFuture TESTING_ErrorOnReturnIo(long asyncRuntime, Object needsCleanup); - public static native Object TESTING_ErrorOnReturnSync(Object needsCleanup); - public static native CompletableFuture TESTING_FutureFailure(long asyncRuntime, int input); - public static native CompletableFuture TESTING_FutureProducesOtherPointerType(long asyncRuntime, String input); - public static native CompletableFuture TESTING_FutureProducesPointerType(long asyncRuntime, int input); - public static native CompletableFuture TESTING_FutureSuccess(long asyncRuntime, int input); - public static native CompletableFuture TESTING_FutureThrowsCustomErrorType(long asyncRuntime); - public static native byte[] TESTING_InputStreamReadIntoZeroLengthSlice(InputStream capsAlphabetInput); - public static native void TESTING_NonSuspendingBackgroundThreadRuntime_Destroy(long handle); - public static native CompletableFuture TESTING_OnlyCompletesByCancellation(long asyncRuntime); - public static native String TESTING_OtherTestingHandleType_getValue(long handle); - public static native void TESTING_PanicInBodyAsync(Object input); - public static native CompletableFuture TESTING_PanicInBodyIo(long asyncRuntime, Object input); - public static native void TESTING_PanicInBodySync(Object input); - public static native void TESTING_PanicOnBorrowAsync(Object input); - public static native CompletableFuture TESTING_PanicOnBorrowIo(long asyncRuntime, Object input); - public static native void TESTING_PanicOnBorrowSync(Object input); - public static native void TESTING_PanicOnLoadAsync(Object needsCleanup, Object input); - public static native CompletableFuture TESTING_PanicOnLoadIo(long asyncRuntime, Object needsCleanup, Object input); - public static native void TESTING_PanicOnLoadSync(Object needsCleanup, Object input); - public static native Object TESTING_PanicOnReturnAsync(Object needsCleanup); - public static native CompletableFuture TESTING_PanicOnReturnIo(long asyncRuntime, Object needsCleanup); - public static native Object TESTING_PanicOnReturnSync(Object needsCleanup); - public static native byte[][] TESTING_ProcessBytestringArray(ByteBuffer[] input); - public static native Object[] TESTING_ReturnStringArray(); - public static native int TESTING_TestingHandleType_getValue(long handle); - - public static native void TestingHandleType_Destroy(long handle); - public static native void TokioAsyncContext_Destroy(long handle); public static native void TokioAsyncContext_cancel(long context, long rawCancellationId); public static native long TokioAsyncContext_new(); diff --git a/java/shared/java/org/signal/libsignal/internal/NativeTesting.java b/java/shared/java/org/signal/libsignal/internal/NativeTesting.java index 6663f012..7cb87c1d 100644 --- a/java/shared/java/org/signal/libsignal/internal/NativeTesting.java +++ b/java/shared/java/org/signal/libsignal/internal/NativeTesting.java @@ -49,5 +49,52 @@ public final class NativeTesting { public static native Object[] ComparableBackup_GetUnknownFields(long backup); public static native long ComparableBackup_ReadUnencrypted(InputStream stream, long len, int purpose) throws Exception; + public static native void OtherTestingHandleType_Destroy(long handle); + + public static native void TESTING_CdsiLookupErrorConvert(String errorDescription) throws Exception; + public static native CompletableFuture TESTING_CdsiLookupResponseConvert(long asyncRuntime); + public static native byte[] TESTING_ChatRequestGetBody(long request); + public static native String TESTING_ChatRequestGetHeaderValue(long request, String headerName); + public static native String TESTING_ChatRequestGetMethod(long request); + public static native String TESTING_ChatRequestGetPath(long request); + public static native Object TESTING_ChatServiceDebugInfoConvert() throws Exception; + public static native void TESTING_ChatServiceErrorConvert(String errorDescription) throws Exception; + public static native Object TESTING_ChatServiceResponseAndDebugInfoConvert() throws Exception; + public static native Object TESTING_ChatServiceResponseConvert(boolean bodyPresent) throws Exception; + public static native void TESTING_ChatService_InjectConnectionInterrupted(long chat); + public static native void TESTING_ChatService_InjectRawServerRequest(long chat, byte[] bytes); + public static native void TESTING_ErrorOnBorrowAsync(Object input); + public static native CompletableFuture TESTING_ErrorOnBorrowIo(long asyncRuntime, Object input); + public static native void TESTING_ErrorOnBorrowSync(Object input); + public static native Object TESTING_ErrorOnReturnAsync(Object needsCleanup); + public static native CompletableFuture TESTING_ErrorOnReturnIo(long asyncRuntime, Object needsCleanup); + public static native Object TESTING_ErrorOnReturnSync(Object needsCleanup); + public static native CompletableFuture TESTING_FutureFailure(long asyncRuntime, int input); + public static native CompletableFuture TESTING_FutureProducesOtherPointerType(long asyncRuntime, String input); + public static native CompletableFuture TESTING_FutureProducesPointerType(long asyncRuntime, int input); + public static native CompletableFuture TESTING_FutureSuccess(long asyncRuntime, int input); + public static native CompletableFuture TESTING_FutureThrowsCustomErrorType(long asyncRuntime); + public static native byte[] TESTING_InputStreamReadIntoZeroLengthSlice(InputStream capsAlphabetInput); + public static native void TESTING_NonSuspendingBackgroundThreadRuntime_Destroy(long handle); + public static native CompletableFuture TESTING_OnlyCompletesByCancellation(long asyncRuntime); + public static native String TESTING_OtherTestingHandleType_getValue(long handle); + public static native void TESTING_PanicInBodyAsync(Object input); + public static native CompletableFuture TESTING_PanicInBodyIo(long asyncRuntime, Object input); + public static native void TESTING_PanicInBodySync(Object input); + public static native void TESTING_PanicOnBorrowAsync(Object input); + public static native CompletableFuture TESTING_PanicOnBorrowIo(long asyncRuntime, Object input); + public static native void TESTING_PanicOnBorrowSync(Object input); + public static native void TESTING_PanicOnLoadAsync(Object needsCleanup, Object input); + public static native CompletableFuture TESTING_PanicOnLoadIo(long asyncRuntime, Object needsCleanup, Object input); + public static native void TESTING_PanicOnLoadSync(Object needsCleanup, Object input); + public static native Object TESTING_PanicOnReturnAsync(Object needsCleanup); + public static native CompletableFuture TESTING_PanicOnReturnIo(long asyncRuntime, Object needsCleanup); + public static native Object TESTING_PanicOnReturnSync(Object needsCleanup); + public static native byte[][] TESTING_ProcessBytestringArray(ByteBuffer[] input); + public static native Object[] TESTING_ReturnStringArray(); + public static native int TESTING_TestingHandleType_getValue(long handle); + + public static native void TestingHandleType_Destroy(long handle); + public static native int test_only_fn_returns_123(); } diff --git a/java/shared/test/java/org/signal/libsignal/internal/BridgingTest.java b/java/shared/test/java/org/signal/libsignal/internal/BridgingTest.java index e20a0a02..83d1953f 100644 --- a/java/shared/test/java/org/signal/libsignal/internal/BridgingTest.java +++ b/java/shared/test/java/org/signal/libsignal/internal/BridgingTest.java @@ -14,62 +14,69 @@ import org.junit.Test; public class BridgingTest { @Test public void testErrorOnBorrow() throws Exception { - assertThrows(IllegalArgumentException.class, () -> Native.TESTING_ErrorOnBorrowSync(null)); - assertThrows(IllegalArgumentException.class, () -> Native.TESTING_ErrorOnBorrowAsync(null)); assertThrows( - IllegalArgumentException.class, () -> Native.TESTING_ErrorOnBorrowIo(-1, null).get()); + IllegalArgumentException.class, () -> NativeTesting.TESTING_ErrorOnBorrowSync(null)); + assertThrows( + IllegalArgumentException.class, () -> NativeTesting.TESTING_ErrorOnBorrowAsync(null)); + assertThrows( + IllegalArgumentException.class, + () -> NativeTesting.TESTING_ErrorOnBorrowIo(-1, null).get()); } @Test public void testPanicOnBorrow() throws Exception { - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnBorrowSync(null)); - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnBorrowAsync(null)); - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnBorrowIo(-1, null).get()); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnBorrowSync(null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnBorrowAsync(null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnBorrowIo(-1, null).get()); } @Test public void testPanicOnLoad() throws Exception { - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnLoadSync(null, null)); - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnLoadAsync(null, null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnLoadSync(null, null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnLoadAsync(null, null)); ExecutionException e = assertThrows( - ExecutionException.class, () -> Native.TESTING_PanicOnLoadIo(-1, null, null).get()); + ExecutionException.class, + () -> NativeTesting.TESTING_PanicOnLoadIo(-1, null, null).get()); assertTrue(e.getCause().toString(), e.getCause() instanceof AssertionError); } @Test public void testPanicInBody() throws Exception { - assertThrows(AssertionError.class, () -> Native.TESTING_PanicInBodySync(null)); - assertThrows(AssertionError.class, () -> Native.TESTING_PanicInBodyAsync(null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicInBodySync(null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicInBodyAsync(null)); ExecutionException e = - assertThrows(ExecutionException.class, () -> Native.TESTING_PanicInBodyIo(-1, null).get()); + assertThrows( + ExecutionException.class, () -> NativeTesting.TESTING_PanicInBodyIo(-1, null).get()); assertTrue(e.getCause().toString(), e.getCause() instanceof AssertionError); } @Test public void testErrorOnReturn() throws Exception { - assertThrows(IllegalArgumentException.class, () -> Native.TESTING_ErrorOnReturnSync(null)); - assertThrows(IllegalArgumentException.class, () -> Native.TESTING_ErrorOnReturnAsync(null)); + assertThrows( + IllegalArgumentException.class, () -> NativeTesting.TESTING_ErrorOnReturnSync(null)); + assertThrows( + IllegalArgumentException.class, () -> NativeTesting.TESTING_ErrorOnReturnAsync(null)); ExecutionException e = assertThrows( - ExecutionException.class, () -> Native.TESTING_ErrorOnReturnIo(-1, null).get()); + ExecutionException.class, () -> NativeTesting.TESTING_ErrorOnReturnIo(-1, null).get()); assertTrue(e.getCause().toString(), e.getCause() instanceof IllegalArgumentException); } @Test public void testPanicOnReturn() throws Exception { - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnReturnSync(null)); - assertThrows(AssertionError.class, () -> Native.TESTING_PanicOnReturnAsync(null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnReturnSync(null)); + assertThrows(AssertionError.class, () -> NativeTesting.TESTING_PanicOnReturnAsync(null)); ExecutionException e = assertThrows( - ExecutionException.class, () -> Native.TESTING_PanicOnReturnIo(-1, null).get()); + ExecutionException.class, () -> NativeTesting.TESTING_PanicOnReturnIo(-1, null).get()); assertTrue(e.getCause().toString(), e.getCause() instanceof AssertionError); } @Test public void testReturnStringArray() { assertArrayEquals( - Native.TESTING_ReturnStringArray(), new String[] {"easy", "as", "ABC", "123"}); + NativeTesting.TESTING_ReturnStringArray(), new String[] {"easy", "as", "ABC", "123"}); } @Test @@ -80,12 +87,13 @@ public class BridgingTest { ByteBuffer second = ByteBuffer.allocateDirect(3); second.put(new byte[] {4, 5, 6}); byte[][] result = - Native.TESTING_ProcessBytestringArray(new ByteBuffer[] {first, empty, second}); + NativeTesting.TESTING_ProcessBytestringArray(new ByteBuffer[] {first, empty, second}); assertArrayEquals(result, new byte[][] {{1, 2, 3, 1, 2, 3}, {}, {4, 5, 6, 4, 5, 6}}); } @Test public void testProcessEmptyBytestringArray() { - assertArrayEquals(Native.TESTING_ProcessBytestringArray(new ByteBuffer[] {}), new byte[][] {}); + assertArrayEquals( + NativeTesting.TESTING_ProcessBytestringArray(new ByteBuffer[] {}), new byte[][] {}); } } diff --git a/java/shared/test/java/org/signal/libsignal/internal/FutureTest.java b/java/shared/test/java/org/signal/libsignal/internal/FutureTest.java index 7e5b057c..6408b5c5 100644 --- a/java/shared/test/java/org/signal/libsignal/internal/FutureTest.java +++ b/java/shared/test/java/org/signal/libsignal/internal/FutureTest.java @@ -14,20 +14,20 @@ import org.junit.Test; public class FutureTest { @Test public void testSuccessFromRust() throws Exception { - Future future = Native.TESTING_FutureSuccess(1, 21); + Future future = NativeTesting.TESTING_FutureSuccess(1, 21); assertEquals(42, (int) future.get()); } @Test public void testFailureFromRust() throws Exception { - Future future = Native.TESTING_FutureFailure(1, 21); + Future future = NativeTesting.TESTING_FutureFailure(1, 21); ExecutionException e = assertThrows(ExecutionException.class, () -> future.get()); assertTrue(e.getCause() instanceof IllegalArgumentException); } @Test public void testFutureThrowsUnloadedException() throws Exception { - Future future = Native.TESTING_FutureThrowsCustomErrorType(1); + Future future = NativeTesting.TESTING_FutureThrowsCustomErrorType(1); ExecutionException e = assertThrows(ExecutionException.class, () -> future.get()); assertTrue(e.getCause() instanceof org.signal.libsignal.internal.TestingException); } diff --git a/node/build_node_bridge.py b/node/build_node_bridge.py index e75c2d04..25f8422c 100755 --- a/node/build_node_bridge.py +++ b/node/build_node_bridge.py @@ -93,7 +93,7 @@ def main(args: Optional[List[str]] = None) -> int: out_dir = options.out_dir.strip('"') or os.path.join('build', configuration_name) - features = ['testing-fns'] + features = [] if 'npm_config_libsignal_debug_level_logs' not in os.environ: features.append('log/release_max_level_info') diff --git a/rust/bridge/ffi/Cargo.toml b/rust/bridge/ffi/Cargo.toml index e38d0205..7cdf3f1f 100644 --- a/rust/bridge/ffi/Cargo.toml +++ b/rust/bridge/ffi/Cargo.toml @@ -18,7 +18,6 @@ crate-type = ["staticlib"] # Testing the Swift side of this requires compiling with SIGNAL_MEDIA_SUPPORTED enabled for both Swift and C: # swift test -Xswiftc -DSIGNAL_MEDIA_SUPPORTED -Xcc -DSIGNAL_MEDIA_SUPPORTED signal-media = ["libsignal-bridge/signal-media"] -testing-fns = ["libsignal-bridge/testing-fns", "dep:libsignal-bridge-testing"] [dependencies] libsignal-bridge = { path = "../shared", features = ["ffi"] } diff --git a/rust/bridge/ffi/cbindgen-testing.toml b/rust/bridge/ffi/cbindgen-testing.toml index fecfedaa..60bfec69 100644 --- a/rust/bridge/ffi/cbindgen-testing.toml +++ b/rust/bridge/ffi/cbindgen-testing.toml @@ -24,7 +24,24 @@ usize_is_size_t = true prefix_with_name = true [export] -exclude = ["TAG_SIZE", "NONCE_SIZE"] +exclude = [ + # Already in signal_ffi.h + "BorrowedSliceOfc_uchar", + "BorrowedSliceOfBorrowedSliceOfc_uchar", + "BytestringArray", + "CPromisebool", + "CPromiseFfiCdsiLookupResponse", + "FfiCdsiLookupResponse", + "FfiCdsiLookupResponseEntry", + "FfiChatResponse", + "FfiChatServiceDebugInfo", + "FfiInputStreamStruct", + "FfiResponseAndDebugInfo", + "OwnedBufferOfCStringPtr", + "OwnedBufferOfc_uchar", + "OwnedBufferOfFfiCdsiLookupResponseEntry", + "OwnedBufferOfusize", +] item_types = [ "enums", "functions", @@ -40,6 +57,14 @@ renaming_overrides_prefixing = true [export.rename] "FfiInputStreamStruct" = "SignalInputStream" +"BorrowedSliceOfc_uchar" = "SignalBorrowedBuffer" +"BorrowedSliceOfBorrowedSliceOfc_uchar" = "SignalBorrowedSliceOfBuffers" +"BorrowedMutableSliceOfc_uchar" = "SignalBorrowedMutableBuffer" +"OwnedBufferOfc_uchar" = "SignalOwnedBuffer" +"OwnedBufferOfFfiLookupResponseEntry" = "SignalOwnedLookupResponseEntryList" + +"CPromisec_void" = "SignalCPromiseRawPointer" + # Avoid double-prefixing these "SignalFfiError" = "SignalFfiError" "SignalErrorCode" = "SignalErrorCode" @@ -53,8 +78,7 @@ args = "horizontal" [parse] parse_deps = true -include = [] -extra_bindings = ["libsignal-bridge-testing", "libsignal-bridge-types"] +include = ["libsignal-bridge-types"] [parse.expand] crates = ["libsignal-bridge-testing"] diff --git a/rust/bridge/ffi/cbindgen.toml b/rust/bridge/ffi/cbindgen.toml index 2a8ea389..7114bbf3 100644 --- a/rust/bridge/ffi/cbindgen.toml +++ b/rust/bridge/ffi/cbindgen.toml @@ -62,7 +62,6 @@ renaming_overrides_prefixing = true "OwnedBufferOfc_uchar" = "SignalOwnedBuffer" "OwnedBufferOfFfiLookupResponseEntry" = "SignalOwnedLookupResponseEntryList" "FfiOptionalServiceIdFixedWidthBinaryBytes" = "SignalOptionalServiceIdFixedWidthBinaryBytes" -"CPromisec_void" = "SignalCPromiseRawPointer" "RawCancellationId" = "SignalCancellationId" @@ -94,8 +93,4 @@ extra_bindings = ["libsignal-bridge", "libsignal-bridge-types", "zkgroup"] [parse.expand] crates = ["libsignal-ffi", "libsignal-bridge"] -features = [ - "libsignal-bridge/ffi", - "libsignal-bridge/signal-media", - "libsignal-bridge/testing-fns", -] +features = ["libsignal-bridge/ffi", "libsignal-bridge/signal-media"] diff --git a/rust/bridge/ffi/src/lib.rs b/rust/bridge/ffi/src/lib.rs index 3047d0cf..0a660b6c 100644 --- a/rust/bridge/ffi/src/lib.rs +++ b/rust/bridge/ffi/src/lib.rs @@ -8,7 +8,7 @@ use futures_util::FutureExt; use libsignal_bridge::ffi::*; -#[cfg(feature = "testing-fns")] +#[cfg(feature = "libsignal-bridge-testing")] #[allow(unused_imports)] use libsignal_bridge_testing::*; use libsignal_protocol::*; diff --git a/rust/bridge/jni/Cargo.toml b/rust/bridge/jni/Cargo.toml index 17dd6dff..763788ab 100644 --- a/rust/bridge/jni/Cargo.toml +++ b/rust/bridge/jni/Cargo.toml @@ -24,9 +24,6 @@ jni = { workspace = true } log = { workspace = true } log-panics = { workspace = true, features = ["with-backtrace"] } -[features] -testing-fns = ["libsignal-bridge/testing-fns"] - [target.aarch64-linux-android.dependencies] cpufeatures = "0.2.2" # Make sure 64-bit Android gets optimized crypto diff --git a/rust/bridge/jni/bin/gen_java_decl.py b/rust/bridge/jni/bin/gen_java_decl.py index b1717556..624f0760 100755 --- a/rust/bridge/jni/bin/gen_java_decl.py +++ b/rust/bridge/jni/bin/gen_java_decl.py @@ -41,7 +41,7 @@ IGNORE_THIS_WARNING = re.compile( r"WARN: Missing `\[defines\]` entry for `feature = \".*\"` in cbindgen config\.|" r"WARN: Missing `\[defines\]` entry for `target_os = \"android\"` in cbindgen config\.|" r"WARN: Missing `\[defines\]` entry for `ios_device_as_detected_in_build_rs` in cbindgen config\.|" - r"WARN: Skip libsignal-bridge::.+ - \(not `(?:pub|no_mangle)`\)\.|" + r"WARN: Skip libsignal-bridge(-testing)?::.+ - \(not `(pub|no_mangle)`\)\.|" r"WARN: Couldn't find path for Array\(Path\(GenericPath \{ .+ \}\), Name\(\"LEN\"\)\), skipping associated constants|" r"WARN: Cannot find a mangling for generic path GenericPath { path: Path { name: \"JavaCompletableFuture\" }.+|" r"WARN: Cannot find a mangling for generic path GenericPath { path: Path { name: \"Throwing\" }.+" diff --git a/rust/bridge/jni/cbindgen.toml b/rust/bridge/jni/cbindgen.toml index fc06806f..0a6ff3bd 100644 --- a/rust/bridge/jni/cbindgen.toml +++ b/rust/bridge/jni/cbindgen.toml @@ -25,8 +25,4 @@ extra_bindings = ["libsignal-bridge"] [parse.expand] crates = ["libsignal-jni", "libsignal-bridge"] -features = [ - "libsignal-bridge/jni", - "libsignal-bridge/signal-media", - "libsignal-bridge/testing-fns", -] +features = ["libsignal-bridge/jni", "libsignal-bridge/signal-media"] diff --git a/rust/bridge/node/Cargo.toml b/rust/bridge/node/Cargo.toml index 301ee712..a5394e0b 100644 --- a/rust/bridge/node/Cargo.toml +++ b/rust/bridge/node/Cargo.toml @@ -31,6 +31,3 @@ neon = { version = "1.0.0", default-features = false, features = ["napi-6"] } # cmake 0.1.49 breaks no-toolchain Windows cross-compilation using Visual Studio # https://github.com/rust-lang/cmake-rs/pull/158#issuecomment-1544695163 cmake = ">= 0.1, < 0.1.49" - -[features] -testing-fns = ["libsignal-bridge/testing-fns"] diff --git a/rust/bridge/node/bin/gen_ts_decl.py b/rust/bridge/node/bin/gen_ts_decl.py index 48371381..738528b9 100755 --- a/rust/bridge/node/bin/gen_ts_decl.py +++ b/rust/bridge/node/bin/gen_ts_decl.py @@ -277,7 +277,7 @@ def main() -> None: convert_to_typescript( rust_crates=[ Crate(path=os.path.join(our_abs_dir, '..')), - Crate(path=os.path.join(our_abs_dir, '..', '..', 'shared'), features=('node', 'signal-media', 'testing-fns')), + Crate(path=os.path.join(our_abs_dir, '..', '..', 'shared'), features=('node', 'signal-media')), Crate(path=os.path.join(our_abs_dir, '..', '..', 'shared', 'types'), features=('node', 'signal-media')), Crate(path=os.path.join(our_abs_dir, '..', '..', 'shared', 'testing'), features=('node', 'signal-media')), ], diff --git a/rust/bridge/shared/Cargo.toml b/rust/bridge/shared/Cargo.toml index 2b7aa748..af3886b9 100644 --- a/rust/bridge/shared/Cargo.toml +++ b/rust/bridge/shared/Cargo.toml @@ -62,4 +62,3 @@ ffi = ["libsignal-bridge-types/ffi"] jni = ["dep:jni", "libsignal-bridge-types/jni"] node = ["neon", "linkme", "libsignal-bridge-types/node"] signal-media = ["dep:signal-media", "libsignal-bridge-types/signal-media"] -testing-fns = [] diff --git a/rust/bridge/shared/src/lib.rs b/rust/bridge/shared/src/lib.rs index 76fa151e..930c50af 100644 --- a/rust/bridge/shared/src/lib.rs +++ b/rust/bridge/shared/src/lib.rs @@ -62,8 +62,3 @@ pub mod usernames; #[cfg(feature = "signal-media")] pub mod media; - -// These APIs are only useful for tests. To save on code size, we omit them by default. -// To run tests, build with `--features testing-fns`. -#[cfg(feature = "testing-fns")] -pub mod testing; diff --git a/rust/bridge/shared/src/net/chat.rs b/rust/bridge/shared/src/net/chat.rs index 606b074f..fbb049c2 100644 --- a/rust/bridge/shared/src/net/chat.rs +++ b/rust/bridge/shared/src/net/chat.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: AGPL-3.0-only // -use std::future; use std::time::Duration; use http::uri::InvalidUri; @@ -16,7 +15,6 @@ use libsignal_net::auth::Auth; use libsignal_net::chat::{ self, ChatServiceError, DebugInfo as ChatServiceDebugInfo, Request, Response as ChatResponse, }; -use libsignal_net::infra::ws::WebSocketServiceError; use crate::support::*; use crate::*; @@ -204,32 +202,6 @@ fn ChatService_SetListenerUnauth( chat.set_listener_unauth(listener, runtime) } -#[cfg(feature = "testing-fns")] -#[bridge_fn] -fn TESTING_ChatService_InjectRawServerRequest(chat: &Chat, bytes: &[u8]) { - let request_proto = ::decode(bytes) - .expect("invalid protobuf cannot use this endpoint to test"); - chat.synthetic_request_tx - .blocking_send(chat::ws::ServerEvent::fake(request_proto)) - .expect("not closed"); -} - -#[cfg(feature = "testing-fns")] -#[bridge_fn] -fn TESTING_ChatService_InjectConnectionInterrupted(chat: &Chat) { - chat.synthetic_request_tx - .blocking_send(chat::ws::ServerEvent::Stopped(ChatServiceError::WebSocket( - WebSocketServiceError::ChannelClosed, - ))) - .expect("not closed"); -} - -#[cfg(feature = "testing-fns")] -#[bridge_fn(jni = false, ffi = false)] -fn TESTING_ServerMessageAck_Create() -> ServerMessageAck { - ServerMessageAck::new(Box::new(|_| Box::pin(future::ready(Ok(()))))) -} - bridge_handle_fns!(ServerMessageAck, clone = false); #[bridge_io(TokioAsyncContext, node = false)] diff --git a/rust/bridge/shared/testing/Cargo.toml b/rust/bridge/shared/testing/Cargo.toml index b12773ae..9b6cab0d 100644 --- a/rust/bridge/shared/testing/Cargo.toml +++ b/rust/bridge/shared/testing/Cargo.toml @@ -11,14 +11,22 @@ edition = "2021" license = "AGPL-3.0-only" [dependencies] +attest = { path = "../../../attest" } libsignal-bridge-macros = { path = "../macros" } libsignal-bridge-types = { path = "../types" } libsignal-message-backup = { path = "../../../message-backup", features = ["json"] } +libsignal-net = { path = "../../../net" } +libsignal-protocol = { path = "../../../protocol" } futures-util = { workspace = true } +http = { workspace = true } +nonzero_ext = { workspace = true } paste = { workspace = true } +prost = { workspace = true } scopeguard = { workspace = true } +strum = { workspace = true, features = ["derive"] } tokio = { workspace = true } +uuid = { workspace = true } jni = { workspace = true, optional = true } linkme = { workspace = true, optional = true } diff --git a/rust/bridge/shared/src/testing/mod.rs b/rust/bridge/shared/testing/src/convert.rs similarity index 98% rename from rust/bridge/shared/src/testing/mod.rs rename to rust/bridge/shared/testing/src/convert.rs index 82a58bac..cd83e536 100644 --- a/rust/bridge/shared/src/testing/mod.rs +++ b/rust/bridge/shared/testing/src/convert.rs @@ -1,5 +1,5 @@ // -// Copyright 2023 Signal Messenger, LLC. +// Copyright 2022 Signal Messenger, LLC. // SPDX-License-Identifier: AGPL-3.0-only // @@ -12,9 +12,7 @@ use libsignal_protocol::SignalProtocolError; use std::future::Future; -mod net; -mod types; -use types::*; +use crate::types::*; pub struct NonSuspendingBackgroundThreadRuntime; bridge_as_handle!( diff --git a/rust/bridge/shared/testing/src/lib.rs b/rust/bridge/shared/testing/src/lib.rs index 6ffc45b0..ef9479ef 100644 --- a/rust/bridge/shared/testing/src/lib.rs +++ b/rust/bridge/shared/testing/src/lib.rs @@ -18,4 +18,7 @@ pub fn test_only_fn_returns_123() -> u32 { 123 } +pub mod convert; pub mod message_backup; +pub mod net; +pub mod types; diff --git a/rust/bridge/shared/src/testing/net.rs b/rust/bridge/shared/testing/src/net.rs similarity index 89% rename from rust/bridge/shared/src/testing/net.rs rename to rust/bridge/shared/testing/src/net.rs index bfebf14e..400e403b 100644 --- a/rust/bridge/shared/src/testing/net.rs +++ b/rust/bridge/shared/testing/src/net.rs @@ -8,18 +8,20 @@ use std::time::Duration; use http::{HeaderMap, HeaderName, HeaderValue, StatusCode}; use libsignal_bridge_macros::*; -use libsignal_bridge_types::net::chat::{HttpRequest, ResponseAndDebugInfo}; +use libsignal_bridge_types::net::chat::{ + Chat, HttpRequest, ResponseAndDebugInfo, ServerMessageAck, +}; use libsignal_bridge_types::net::TokioAsyncContext; use libsignal_net::cdsi::{LookupError, LookupResponse, LookupResponseEntry, E164}; use libsignal_net::chat::{ - ChatServiceError, DebugInfo as ChatServiceDebugInfo, Response as ChatResponse, + self, ChatServiceError, DebugInfo as ChatServiceDebugInfo, Response as ChatResponse, }; +use libsignal_net::infra::ws::WebSocketServiceError; use libsignal_net::infra::IpType; use libsignal_protocol::{Aci, Pni}; use nonzero_ext::nonzero; use uuid::Uuid; -use crate::support::*; use crate::*; #[bridge_io(TokioAsyncContext)] @@ -257,3 +259,26 @@ fn TESTING_ChatRequestGetBody(request: &HttpRequest) -> Vec { .map(|b| b.into_vec()) .unwrap_or_default() } + +#[bridge_fn] +fn TESTING_ChatService_InjectRawServerRequest(chat: &Chat, bytes: &[u8]) { + let request_proto = ::decode(bytes) + .expect("invalid protobuf cannot use this endpoint to test"); + chat.synthetic_request_tx + .blocking_send(chat::ws::ServerEvent::fake(request_proto)) + .expect("not closed"); +} + +#[bridge_fn] +fn TESTING_ChatService_InjectConnectionInterrupted(chat: &Chat) { + chat.synthetic_request_tx + .blocking_send(chat::ws::ServerEvent::Stopped(ChatServiceError::WebSocket( + WebSocketServiceError::ChannelClosed, + ))) + .expect("not closed"); +} + +#[bridge_fn(jni = false, ffi = false)] +fn TESTING_ServerMessageAck_Create() -> ServerMessageAck { + ServerMessageAck::new(Box::new(|_| Box::pin(std::future::ready(Ok(()))))) +} diff --git a/rust/bridge/shared/src/testing/types.rs b/rust/bridge/shared/testing/src/types.rs similarity index 100% rename from rust/bridge/shared/src/testing/types.rs rename to rust/bridge/shared/testing/src/types.rs diff --git a/swift/Sources/SignalFfi/signal_ffi.h b/swift/Sources/SignalFfi/signal_ffi.h index 1eda6c8e..6395c06b 100644 --- a/swift/Sources/SignalFfi/signal_ffi.h +++ b/swift/Sources/SignalFfi/signal_ffi.h @@ -238,10 +238,6 @@ typedef struct SignalMessageBackupKey SignalMessageBackupKey; typedef struct SignalMessageBackupValidationOutcome SignalMessageBackupValidationOutcome; -typedef struct SignalNonSuspendingBackgroundThreadRuntime SignalNonSuspendingBackgroundThreadRuntime; - -typedef struct SignalOtherTestingHandleType SignalOtherTestingHandleType; - typedef struct SignalPinHash SignalPinHash; typedef struct SignalPlaintextContent SignalPlaintextContent; @@ -302,8 +298,6 @@ typedef struct SignalMessage SignalMessage; typedef struct SignalSignedPreKeyRecord SignalSignedPreKeyRecord; -typedef struct SignalTestingHandleType SignalTestingHandleType; - typedef struct SignalTokioAsyncContext SignalTokioAsyncContext; typedef struct SignalUnidentifiedSenderMessageContent SignalUnidentifiedSenderMessageContent; @@ -671,66 +665,6 @@ typedef struct { typedef SignalInputStream SignalSyncInputStream; -/** - * A C callback used to report the results of Rust futures. - * - * cbindgen will produce independent C types like `SignalCPromisei32` and - * `SignalCPromiseProtocolAddress`. - * - * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be - * completed once. - */ -typedef struct { - void (*complete)(SignalFfiError *error, const int32_t *result, const void *context); - const void *context; - SignalCancellationId cancellation_id; -} SignalCPromisei32; - -/** - * A C callback used to report the results of Rust futures. - * - * cbindgen will produce independent C types like `SignalCPromisei32` and - * `SignalCPromiseProtocolAddress`. - * - * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be - * completed once. - */ -typedef struct { - void (*complete)(SignalFfiError *error, SignalTestingHandleType *const *result, const void *context); - const void *context; - SignalCancellationId cancellation_id; -} SignalCPromiseTestingHandleType; - -/** - * A C callback used to report the results of Rust futures. - * - * cbindgen will produce independent C types like `SignalCPromisei32` and - * `SignalCPromiseProtocolAddress`. - * - * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be - * completed once. - */ -typedef struct { - void (*complete)(SignalFfiError *error, SignalOtherTestingHandleType *const *result, const void *context); - const void *context; - SignalCancellationId cancellation_id; -} SignalCPromiseOtherTestingHandleType; - -/** - * A C callback used to report the results of Rust futures. - * - * cbindgen will produce independent C types like `SignalCPromisei32` and - * `SignalCPromiseProtocolAddress`. - * - * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be - * completed once. - */ -typedef struct { - void (*complete)(SignalFfiError *error, const void *const *result, const void *context); - const void *context; - SignalCancellationId cancellation_id; -} SignalCPromiseRawPointer; - typedef uint8_t SignalRandomnessBytes[SignalRANDOMNESS_LEN]; void signal_print_ptr(const void *p); @@ -1583,10 +1517,6 @@ SignalFfiError *signal_chat_service_set_listener_auth(const SignalTokioAsyncCont SignalFfiError *signal_chat_service_set_listener_unauth(const SignalTokioAsyncContext *runtime, const SignalChat *chat, const SignalFfiMakeChatListenerStruct *make_listener); -SignalFfiError *signal_testing_chat_service_inject_raw_server_request(const SignalChat *chat, SignalBorrowedBuffer bytes); - -SignalFfiError *signal_testing_chat_service_inject_connection_interrupted(const SignalChat *chat); - SignalFfiError *signal_server_message_ack_destroy(SignalServerMessageAck *p); SignalFfiError *signal_server_message_ack_send(SignalCPromisebool *promise, const SignalTokioAsyncContext *async_runtime, const SignalServerMessageAck *ack); @@ -1691,90 +1621,4 @@ SignalFfiError *signal_sanitized_metadata_get_data_offset(uint64_t *out, const S SignalFfiError *signal_sanitized_metadata_get_data_len(uint64_t *out, const SignalSanitizedMetadata *sanitized); #endif -SignalFfiError *signal_testing_NonSuspendingBackgroundThreadRuntime_destroy(SignalNonSuspendingBackgroundThreadRuntime *p); - -SignalFfiError *signal_testing_future_success(SignalCPromisei32 *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, uint8_t input); - -SignalFfiError *signal_testing_future_failure(SignalCPromisei32 *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, uint8_t _input); - -SignalFfiError *signal_testing_handle_type_destroy(SignalTestingHandleType *p); - -SignalFfiError *signal_testing_handle_type_clone(SignalTestingHandleType **new_obj, const SignalTestingHandleType *obj); - -SignalFfiError *signal_testing_testing_handle_type_get_value(uint8_t *out, const SignalTestingHandleType *handle); - -SignalFfiError *signal_testing_future_produces_pointer_type(SignalCPromiseTestingHandleType *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, uint8_t input); - -SignalFfiError *signal_other_testing_handle_type_destroy(SignalOtherTestingHandleType *p); - -SignalFfiError *signal_other_testing_handle_type_clone(SignalOtherTestingHandleType **new_obj, const SignalOtherTestingHandleType *obj); - -SignalFfiError *signal_testing_other_testing_handle_type_get_value(const char **out, const SignalOtherTestingHandleType *handle); - -SignalFfiError *signal_testing_future_produces_other_pointer_type(SignalCPromiseOtherTestingHandleType *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const char *input); - -SignalFfiError *signal_testing_panic_on_borrow_sync(const void *_input); - -SignalFfiError *signal_testing_panic_on_borrow_async(const void *_input); - -SignalFfiError *signal_testing_panic_on_borrow_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_input); - -SignalFfiError *signal_testing_error_on_borrow_sync(const void *_input); - -SignalFfiError *signal_testing_error_on_borrow_async(const void *_input); - -SignalFfiError *signal_testing_error_on_borrow_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_input); - -SignalFfiError *signal_testing_panic_on_load_sync(const void *_needs_cleanup, const void *_input); - -SignalFfiError *signal_testing_panic_on_load_async(const void *_needs_cleanup, const void *_input); - -SignalFfiError *signal_testing_panic_on_load_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_needs_cleanup, const void *_input); - -SignalFfiError *signal_testing_panic_in_body_sync(const void *_input); - -SignalFfiError *signal_testing_panic_in_body_async(const void *_input); - -SignalFfiError *signal_testing_panic_in_body_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_input); - -SignalFfiError *signal_testing_panic_on_return_sync(const void **out, const void *_needs_cleanup); - -SignalFfiError *signal_testing_panic_on_return_async(const void **out, const void *_needs_cleanup); - -SignalFfiError *signal_testing_panic_on_return_io(SignalCPromiseRawPointer *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_needs_cleanup); - -SignalFfiError *signal_testing_error_on_return_sync(const void **out, const void *_needs_cleanup); - -SignalFfiError *signal_testing_error_on_return_async(const void **out, const void *_needs_cleanup); - -SignalFfiError *signal_testing_error_on_return_io(SignalCPromiseRawPointer *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_needs_cleanup); - -SignalFfiError *signal_testing_return_string_array(SignalStringArray *out); - -SignalFfiError *signal_testing_process_bytestring_array(SignalBytestringArray *out, SignalBorrowedSliceOfBuffers input); - -SignalFfiError *signal_testing_input_stream_read_into_zero_length_slice(SignalOwnedBuffer *out, const SignalInputStream *caps_alphabet_input); - -SignalFfiError *signal_testing_cdsi_lookup_response_convert(SignalCPromiseFfiCdsiLookupResponse *promise, const SignalTokioAsyncContext *async_runtime); - -SignalFfiError *signal_testing_only_completes_by_cancellation(SignalCPromisebool *promise, const SignalTokioAsyncContext *async_runtime); - -SignalFfiError *signal_testing_cdsi_lookup_error_convert(const char *error_description); - -SignalFfiError *signal_testing_chat_service_error_convert(const char *error_description); - -SignalFfiError *signal_testing_chat_service_response_convert(SignalFfiChatResponse *out, bool body_present); - -SignalFfiError *signal_testing_chat_service_debug_info_convert(SignalFfiChatServiceDebugInfo *out); - -SignalFfiError *signal_testing_chat_service_response_and_debug_info_convert(SignalFfiResponseAndDebugInfo *out); - -SignalFfiError *signal_testing_chat_request_get_method(const char **out, const SignalHttpRequest *request); - -SignalFfiError *signal_testing_chat_request_get_path(const char **out, const SignalHttpRequest *request); - -SignalFfiError *signal_testing_chat_request_get_header_value(const char **out, const SignalHttpRequest *request, const char *header_name); - -SignalFfiError *signal_testing_chat_request_get_body(SignalOwnedBuffer *out, const SignalHttpRequest *request); - #endif /* SIGNAL_FFI_H_ */ diff --git a/swift/Sources/SignalFfi/signal_ffi_testing.h b/swift/Sources/SignalFfi/signal_ffi_testing.h index 6460b799..62cf7981 100644 --- a/swift/Sources/SignalFfi/signal_ffi_testing.h +++ b/swift/Sources/SignalFfi/signal_ffi_testing.h @@ -16,10 +16,160 @@ SPDX-License-Identifier: AGPL-3.0-only #include #include "signal_ffi.h" +typedef struct SignalChat SignalChat; + typedef struct SignalComparableBackup SignalComparableBackup; +typedef struct SignalHttpRequest SignalHttpRequest; + +typedef struct SignalNonSuspendingBackgroundThreadRuntime SignalNonSuspendingBackgroundThreadRuntime; + +typedef struct SignalOtherTestingHandleType SignalOtherTestingHandleType; + +/** + * The top-level error type (opaquely) returned to C clients when something goes wrong. + * + * Ideally this would use [ThinBox][], and then we wouldn't need an extra level of indirection when + * returning it to C, but unfortunately that isn't stable yet. + * + * [ThinBox]: https://doc.rust-lang.org/std/boxed/struct.ThinBox.html + */ +typedef struct SignalFfiError SignalFfiError; + +typedef struct SignalTestingHandleType SignalTestingHandleType; + +typedef struct SignalTokioAsyncContext SignalTokioAsyncContext; + +typedef uint64_t SignalRawCancellationId; + +/** + * A C callback used to report the results of Rust futures. + * + * cbindgen will produce independent C types like `SignalCPromisei32` and + * `SignalCPromiseProtocolAddress`. + * + * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be + * completed once. + */ +typedef struct { + void (*complete)(SignalFfiError *error, const int32_t *result, const void *context); + const void *context; + SignalRawCancellationId cancellation_id; +} SignalCPromisei32; + +/** + * A C callback used to report the results of Rust futures. + * + * cbindgen will produce independent C types like `SignalCPromisei32` and + * `SignalCPromiseProtocolAddress`. + * + * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be + * completed once. + */ +typedef struct { + void (*complete)(SignalFfiError *error, SignalTestingHandleType *const *result, const void *context); + const void *context; + SignalRawCancellationId cancellation_id; +} SignalCPromiseTestingHandleType; + +/** + * A C callback used to report the results of Rust futures. + * + * cbindgen will produce independent C types like `SignalCPromisei32` and + * `SignalCPromiseProtocolAddress`. + * + * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be + * completed once. + */ +typedef struct { + void (*complete)(SignalFfiError *error, SignalOtherTestingHandleType *const *result, const void *context); + const void *context; + SignalRawCancellationId cancellation_id; +} SignalCPromiseOtherTestingHandleType; + +/** + * A C callback used to report the results of Rust futures. + * + * cbindgen will produce independent C types like `SignalCPromisei32` and + * `SignalCPromiseProtocolAddress`. + * + * This derives Copy because it behaves like a C type; nevertheless, a promise should still only be + * completed once. + */ +typedef struct { + void (*complete)(SignalFfiError *error, const void *const *result, const void *context); + const void *context; + SignalRawCancellationId cancellation_id; +} SignalCPromiseRawPointer; + +typedef SignalBytestringArray SignalStringArray; + SignalFfiError *signal_test_only_fn_returns_123(uint32_t *out); +SignalFfiError *signal_testing_NonSuspendingBackgroundThreadRuntime_destroy(SignalNonSuspendingBackgroundThreadRuntime *p); + +SignalFfiError *signal_testing_future_success(SignalCPromisei32 *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, uint8_t input); + +SignalFfiError *signal_testing_future_failure(SignalCPromisei32 *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, uint8_t _input); + +SignalFfiError *signal_testing_handle_type_destroy(SignalTestingHandleType *p); + +SignalFfiError *signal_testing_handle_type_clone(SignalTestingHandleType **new_obj, const SignalTestingHandleType *obj); + +SignalFfiError *signal_testing_testing_handle_type_get_value(uint8_t *out, const SignalTestingHandleType *handle); + +SignalFfiError *signal_testing_future_produces_pointer_type(SignalCPromiseTestingHandleType *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, uint8_t input); + +SignalFfiError *signal_other_testing_handle_type_destroy(SignalOtherTestingHandleType *p); + +SignalFfiError *signal_other_testing_handle_type_clone(SignalOtherTestingHandleType **new_obj, const SignalOtherTestingHandleType *obj); + +SignalFfiError *signal_testing_other_testing_handle_type_get_value(const char **out, const SignalOtherTestingHandleType *handle); + +SignalFfiError *signal_testing_future_produces_other_pointer_type(SignalCPromiseOtherTestingHandleType *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const char *input); + +SignalFfiError *signal_testing_panic_on_borrow_sync(const void *_input); + +SignalFfiError *signal_testing_panic_on_borrow_async(const void *_input); + +SignalFfiError *signal_testing_panic_on_borrow_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_input); + +SignalFfiError *signal_testing_error_on_borrow_sync(const void *_input); + +SignalFfiError *signal_testing_error_on_borrow_async(const void *_input); + +SignalFfiError *signal_testing_error_on_borrow_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_input); + +SignalFfiError *signal_testing_panic_on_load_sync(const void *_needs_cleanup, const void *_input); + +SignalFfiError *signal_testing_panic_on_load_async(const void *_needs_cleanup, const void *_input); + +SignalFfiError *signal_testing_panic_on_load_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_needs_cleanup, const void *_input); + +SignalFfiError *signal_testing_panic_in_body_sync(const void *_input); + +SignalFfiError *signal_testing_panic_in_body_async(const void *_input); + +SignalFfiError *signal_testing_panic_in_body_io(SignalCPromisebool *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_input); + +SignalFfiError *signal_testing_panic_on_return_sync(const void **out, const void *_needs_cleanup); + +SignalFfiError *signal_testing_panic_on_return_async(const void **out, const void *_needs_cleanup); + +SignalFfiError *signal_testing_panic_on_return_io(SignalCPromiseRawPointer *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_needs_cleanup); + +SignalFfiError *signal_testing_error_on_return_sync(const void **out, const void *_needs_cleanup); + +SignalFfiError *signal_testing_error_on_return_async(const void **out, const void *_needs_cleanup); + +SignalFfiError *signal_testing_error_on_return_io(SignalCPromiseRawPointer *promise, const SignalNonSuspendingBackgroundThreadRuntime *async_runtime, const void *_needs_cleanup); + +SignalFfiError *signal_testing_return_string_array(SignalStringArray *out); + +SignalFfiError *signal_testing_process_bytestring_array(SignalBytestringArray *out, SignalBorrowedSliceOfBuffers input); + +SignalFfiError *signal_testing_input_stream_read_into_zero_length_slice(SignalOwnedBuffer *out, const SignalInputStream *caps_alphabet_input); + SignalFfiError *signal_comparable_backup_destroy(SignalComparableBackup *p); SignalFfiError *signal_comparable_backup_read_unencrypted(SignalComparableBackup **out, const SignalInputStream *stream, uint64_t len, uint8_t purpose); @@ -28,4 +178,30 @@ SignalFfiError *signal_comparable_backup_get_comparable_string(const char **out, SignalFfiError *signal_comparable_backup_get_unknown_fields(SignalStringArray *out, const SignalComparableBackup *backup); +SignalFfiError *signal_testing_cdsi_lookup_response_convert(SignalCPromiseFfiCdsiLookupResponse *promise, const SignalTokioAsyncContext *async_runtime); + +SignalFfiError *signal_testing_only_completes_by_cancellation(SignalCPromisebool *promise, const SignalTokioAsyncContext *async_runtime); + +SignalFfiError *signal_testing_cdsi_lookup_error_convert(const char *error_description); + +SignalFfiError *signal_testing_chat_service_error_convert(const char *error_description); + +SignalFfiError *signal_testing_chat_service_response_convert(SignalFfiChatResponse *out, bool body_present); + +SignalFfiError *signal_testing_chat_service_debug_info_convert(SignalFfiChatServiceDebugInfo *out); + +SignalFfiError *signal_testing_chat_service_response_and_debug_info_convert(SignalFfiResponseAndDebugInfo *out); + +SignalFfiError *signal_testing_chat_request_get_method(const char **out, const SignalHttpRequest *request); + +SignalFfiError *signal_testing_chat_request_get_path(const char **out, const SignalHttpRequest *request); + +SignalFfiError *signal_testing_chat_request_get_header_value(const char **out, const SignalHttpRequest *request, const char *header_name); + +SignalFfiError *signal_testing_chat_request_get_body(SignalOwnedBuffer *out, const SignalHttpRequest *request); + +SignalFfiError *signal_testing_chat_service_inject_raw_server_request(const SignalChat *chat, SignalBorrowedBuffer bytes); + +SignalFfiError *signal_testing_chat_service_inject_connection_interrupted(const SignalChat *chat); + #endif /* SIGNAL_FFI_TESTING_H_ */ diff --git a/swift/build_ffi.sh b/swift/build_ffi.sh index e14924c0..ea297d78 100755 --- a/swift/build_ffi.sh +++ b/swift/build_ffi.sh @@ -32,7 +32,7 @@ export CFLAGS_x86_64_apple_ios_macabi="--target=x86_64-apple-ios-macabi ${CFLAGS FEATURES=() if [[ "${CARGO_BUILD_TARGET:-}" != "aarch64-apple-ios" ]]; then - FEATURES+=("testing-fns") + FEATURES+=("libsignal-bridge-testing") fi usage() {