diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e2cdcedd6..e1a4e84ea 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -178,6 +178,16 @@ jobs: echo "SENTRY_RELEASE=$SENTRY_RELEASE" echo "SENTRY_DIST=$SENTRY_DIST" + - uses: actions/setup-node@v3 + if: ${{ matrix.rn-version == '0.65.3' }} + with: + node-version: 16 + + - uses: actions/setup-node@v3 + if: ${{ matrix.rn-version != '0.65.3' }} + with: + node-version: 18 + - uses: actions/setup-java@v3 with: java-version: '11' @@ -376,6 +386,16 @@ jobs: working-directory: test/e2e run: tar -xvf *.tar + - uses: actions/setup-node@v3 + if: ${{ matrix.rn-version == '0.65.3' }} + with: + node-version: 16 + + - uses: actions/setup-node@v3 + if: ${{ matrix.rn-version != '0.65.3' }} + with: + node-version: 18 + - uses: actions/setup-java@v3 with: java-version: '11' diff --git a/.github/workflows/native-tests.yml b/.github/workflows/native-tests.yml index de5ef5aaf..f560a3ef3 100644 --- a/.github/workflows/native-tests.yml +++ b/.github/workflows/native-tests.yml @@ -14,7 +14,7 @@ jobs: with: access_token: ${{ github.token }} - test: + test-ios: name: ios runs-on: macos-latest steps: @@ -32,13 +32,13 @@ jobs: run: yarn install - name: Install App Pods - working-directory: RNSentryTester + working-directory: RNSentryCocoaTester run: pod install - name: Run iOS Tests - working-directory: RNSentryTester + working-directory: RNSentryCocoaTester env: - SCHEME: RNSentryTester + SCHEME: RNSentryCocoaTester CONFIGURATION: Release DESTINATION: 'platform=iOS Simulator,OS=latest,name=iPhone 14' run: | @@ -47,3 +47,69 @@ jobs: -scheme $SCHEME -configuration $CONFIGURATION \ -destination "$DESTINATION" \ test + + test-android: + name: android + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + + - name: Gradle cache + uses: gradle/gradle-build-action@v2 + + - name: AVD cache + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-21 + + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 + with: + api-level: 21 + emulator-options: > + -accel on + -no-window + -gpu swiftshader_indirect + -noaudio + -no-boot-anim + -camera-back none + -camera-front none + -timezone US/Pacific + force-avd-creation: false + disable-animations: true + arch: x86_64 + profile: Nexus 6 + script: echo "Generated AVD snapshot for caching." + + - name: Run connected tests + uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 + with: + working-directory: RNSentryAndroidTester + api-level: 21 + emulator-options: > + -no-snapshot-save + -accel on + -no-window + -gpu swiftshader_indirect + -noaudio + -no-boot-anim + -camera-back none + -camera-front none + -timezone US/Pacific + force-avd-creation: false + disable-animations: true + arch: x86_64 + profile: Nexus 6 + script: | + ./gradlew uninstallDebug uninstallDebugAndroidTest + ./gradlew connectedCheck diff --git a/CHANGELOG.md b/CHANGELOG.md index c6991fa62..4ea215a28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Warn users about multiple versions of `promise` package which can cause unexpected behavior like undefined `Promise.allSettled` ([#3162](https://github.com/getsentry/sentry-react-native/pull/3162)) +- Event is enriched with all the Android context on the JS layer and you can filter/modify all the data in the `beforeSend` callback similarly to iOS. ([#3170](https://github.com/getsentry/sentry-react-native/pull/3170)) ### Dependencies @@ -14,6 +15,9 @@ - Bump Cocoa SDK from v8.8.0 to v8.9.3 ([#3188](https://github.com/getsentry/sentry-react-native/pull/3188), [#3206](https://github.com/getsentry/sentry-react-native/pull/3206)) - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#893) - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.3) +- Bump Android SDK from v6.25.1 to v6.27.0 ([#3170](https://github.com/getsentry/sentry-react-native/pull/3170)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6270) + - [diff](https://github.com/getsentry/sentry-java/compare/6.25.1...6.27.0) ## 5.7.1 diff --git a/RNSentryAndroidTester/.gitignore b/RNSentryAndroidTester/.gitignore new file mode 100644 index 000000000..aa724b770 --- /dev/null +++ b/RNSentryAndroidTester/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/RNSentryAndroidTester/app/.gitignore b/RNSentryAndroidTester/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/RNSentryAndroidTester/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/RNSentryAndroidTester/app/build.gradle b/RNSentryAndroidTester/app/build.gradle new file mode 100644 index 000000000..bc8ce7c86 --- /dev/null +++ b/RNSentryAndroidTester/app/build.gradle @@ -0,0 +1,49 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'io.sentry.rnsentryandroidtester' + compileSdk 33 + + defaultConfig { + applicationId "io.sentry.rnsentryandroidtester" + minSdk 21 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '11' + } + + testOptions.unitTests.all { + testLogging { + events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' + } + } +} + +dependencies { + implementation project(':RNSentry') + implementation 'com.facebook.react:react-android:0.72.0' + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} diff --git a/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/MapConverterTest.kt b/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/MapConverterTest.kt new file mode 100644 index 000000000..9ed11f387 --- /dev/null +++ b/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/MapConverterTest.kt @@ -0,0 +1,357 @@ +package io.sentry.rnsentryandroidtester + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.facebook.react.bridge.Arguments +import com.facebook.soloader.SoLoader +import io.sentry.react.MapConverter +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import android.content.Context; +import org.junit.Before +import java.math.BigDecimal +import java.math.BigInteger + +class Unknown + + +@RunWith(AndroidJUnit4::class) +class MapConverterTest { + + @Before + fun setUp() { + val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + SoLoader.init(context, false) + } + + @Test + fun convertsUnknownValueToNull() { + val actual = MapConverter.convertToWritable(Unknown()) + assertNull(actual) + } + + @Test + fun convertsFloatToDouble() { + val actual = MapConverter.convertToWritable(Float.MAX_VALUE) + assert(actual is Double) + assertEquals(Float.MAX_VALUE.toDouble(), actual) + } + + @Test + fun convertsByteToInt() { + val actual = MapConverter.convertToWritable(Byte.MAX_VALUE) + assert(actual is Int) + assertEquals(Byte.MAX_VALUE.toInt(), actual) + } + + @Test + fun convertsShortToInt() { + val actual = MapConverter.convertToWritable(Short.MAX_VALUE) + assert(actual is Int) + assertEquals(Short.MAX_VALUE.toInt(), actual) + } + + @Test + fun convertsLongToDouble() { + val actual = MapConverter.convertToWritable(Long.MAX_VALUE) + assertEquals(Long.MAX_VALUE.toDouble(), actual) + } + + @Test + fun convertsBigDecimalToDouble() { + val actual = MapConverter.convertToWritable(BigDecimal.TEN) + assertEquals(BigDecimal.TEN.toDouble(), actual) + } + + @Test + fun convertsBigIntegerToDouble() { + val actual = MapConverter.convertToWritable(BigInteger.TEN) + assertEquals(BigInteger.TEN.toDouble(), actual) + } + + @Test + fun keepsNull() { + val actual = MapConverter.convertToWritable(null) + assertNull(actual) + } + + @Test + fun keepsBoolean() { + val actual = MapConverter.convertToWritable(true) + assertEquals(true, actual) + } + + @Test + fun keepsDouble() { + val actual = MapConverter.convertToWritable(Double.MAX_VALUE) + assertEquals(Double.MAX_VALUE, actual) + } + + @Test + fun keepsInteger() { + val actual = MapConverter.convertToWritable(Integer.MAX_VALUE) + assertEquals(Integer.MAX_VALUE, actual) + } + + @Test + fun keepsString() { + val actual = MapConverter.convertToWritable("string") + assertEquals("string", actual) + } + + @Test + fun convertsMapWithUnknownValueKey() { + val actualMap = MapConverter.convertToWritable(mapOf("unknown" to Unknown())) + val expectedMap = Arguments.createMap(); + expectedMap.putNull("unknown") + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithNullKey() { + val actualMap = MapConverter.convertToWritable(mapOf("null" to null)) + val expectedMap = Arguments.createMap(); + expectedMap.putNull("null") + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithBooleanKey() { + val actualMap = MapConverter.convertToWritable(mapOf("boolean" to true)) + val expectedMap = Arguments.createMap(); + expectedMap.putBoolean("boolean", true) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithDoubleKey() { + val actualMap = MapConverter.convertToWritable(mapOf("double" to Double.MAX_VALUE)) + val expectedMap = Arguments.createMap(); + expectedMap.putDouble("double", Double.MAX_VALUE) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithIntegerKey() { + val actualMap = MapConverter.convertToWritable(mapOf("integer" to Integer.MAX_VALUE)) + val expectedMap = Arguments.createMap(); + expectedMap.putInt("integer", Integer.MAX_VALUE) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithByteKey() { + val actualMap = MapConverter.convertToWritable(mapOf("byte" to Byte.MAX_VALUE)) + val expectedMap = Arguments.createMap(); + expectedMap.putInt("byte", Byte.MAX_VALUE.toInt()) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithShortKey() { + val actualMap = MapConverter.convertToWritable(mapOf("short" to Short.MAX_VALUE)) + val expectedMap = Arguments.createMap(); + expectedMap.putInt("short", Short.MAX_VALUE.toInt()) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithFloatKey() { + val actualMap = MapConverter.convertToWritable(mapOf("float" to Float.MAX_VALUE)) + val expectedMap = Arguments.createMap(); + expectedMap.putDouble("float", Float.MAX_VALUE.toDouble()) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithLongKey() { + val actualMap = MapConverter.convertToWritable(mapOf("long" to Long.MAX_VALUE)) + val expectedMap = Arguments.createMap(); + expectedMap.putDouble("long", Long.MAX_VALUE.toDouble()) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithInBigDecimalKey() { + val actualMap = MapConverter.convertToWritable(mapOf("big_decimal" to BigDecimal.TEN)) + val expectedMap = Arguments.createMap(); + expectedMap.putDouble("big_decimal", BigDecimal.TEN.toDouble()) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithBigIntKey() { + val actualMap = MapConverter.convertToWritable(mapOf("big_int" to BigInteger.TEN)) + val expectedMap = Arguments.createMap(); + expectedMap.putDouble("big_int", BigInteger.TEN.toDouble()) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithStringKey() { + val actualMap = MapConverter.convertToWritable(mapOf("string" to "string")) + val expectedMap = Arguments.createMap(); + expectedMap.putString("string", "string") + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithListKey() { + val actualMap = MapConverter.convertToWritable(mapOf("list" to listOf())) + val expectedMap = Arguments.createMap() + val expectedArray = Arguments.createArray() + expectedMap.putArray("list", expectedArray) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsMapWithNestedMapKey() { + val actualMap = MapConverter.convertToWritable(mapOf("map" to mapOf())) + val expectedMap = Arguments.createMap() + val expectedNestedMap = Arguments.createMap() + expectedMap.putMap("map", expectedNestedMap) + assertEquals(expectedMap, actualMap) + } + + @Test + fun convertsListOfBoolean() { + val expected = Arguments.createArray() + expected.pushBoolean(true) + assertEquals(expected, MapConverter.convertToWritable(listOf(true))) + } + + @Test + fun convertsListOfDouble() { + val expected = Arguments.createArray() + expected.pushDouble(Double.MAX_VALUE) + assertEquals(expected, MapConverter.convertToWritable(listOf(Double.MAX_VALUE))) + } + + @Test + fun convertsListOfFloat() { + val expected = Arguments.createArray() + expected.pushDouble(Float.MAX_VALUE.toDouble()) + assertEquals(expected, MapConverter.convertToWritable(listOf(Float.MAX_VALUE))) + } + + @Test + fun convertsListOfInteger() { + val expected = Arguments.createArray() + expected.pushInt(Int.MAX_VALUE) + assertEquals(expected, MapConverter.convertToWritable(listOf(Int.MAX_VALUE))) + } + + @Test + fun convertsListOfShort() { + val expected = Arguments.createArray() + expected.pushInt(Short.MAX_VALUE.toInt()) + assertEquals(expected, MapConverter.convertToWritable(listOf(Short.MAX_VALUE))) + } + + @Test + fun convertsListOfByte() { + val expected = Arguments.createArray() + expected.pushInt(Byte.MAX_VALUE.toInt()) + assertEquals(expected, MapConverter.convertToWritable(listOf(Byte.MAX_VALUE))) + } + + @Test + fun convertsListOfLong() { + val expected = Arguments.createArray() + expected.pushDouble(Long.MAX_VALUE.toDouble()) + assertEquals(expected, MapConverter.convertToWritable(listOf(Long.MAX_VALUE))) + } + + @Test + fun convertsListOfBigInt() { + val expected = Arguments.createArray() + expected.pushDouble(BigInteger.TEN.toDouble()) + assertEquals(expected, MapConverter.convertToWritable(listOf(BigInteger.TEN))) + } + + @Test + fun convertsListOfBigDecimal() { + val expected = Arguments.createArray() + expected.pushDouble(BigDecimal.TEN.toDouble()) + assertEquals(expected, MapConverter.convertToWritable(listOf(BigDecimal.TEN))) + } + + @Test + fun convertsListOfString() { + val expected = Arguments.createArray() + expected.pushString("string") + assertEquals(expected, MapConverter.convertToWritable(listOf("string"))) + } + + @Test + fun convertsListOfMap() { + val expected = Arguments.createArray() + val expectedMap = Arguments.createMap() + expectedMap.putString("map", "string") + expected.pushMap(expectedMap) + assertEquals(expected, MapConverter.convertToWritable(listOf(mapOf("map" to "string")))) + } + + @Test + fun convertsNestedLists() { + val actual = MapConverter.convertToWritable(listOf(listOf())) + val expectedArray = Arguments.createArray() + val expectedNestedArray = Arguments.createArray() + expectedArray.pushArray(expectedNestedArray) + assertEquals(actual, expectedArray) + } + + @Test + fun convertsComplexMapCorrectly() { + val actual = MapConverter.convertToWritable(mapOf( + "integer" to Integer.MAX_VALUE, + "string" to "string1", + "map" to mapOf( + "integer" to Integer.MAX_VALUE, + "string" to "string2", + "map" to mapOf( + "integer" to Integer.MAX_VALUE, + "string" to "string3" + ) + ), + "list" to listOf( + Integer.MAX_VALUE, + mapOf( + "integer" to Integer.MAX_VALUE, + "string" to "string4", + ), + "string5", + ), + )) + + val expectedMap1 = Arguments.createMap() + val expectedMap2 = Arguments.createMap() + val expectedMap3 = Arguments.createMap() + val expectedMap4 = Arguments.createMap() + val expectedArray = Arguments.createArray() + + // the order of assembling the objects matters + // for the comparison at the end of the test + expectedMap4.putInt("integer", Integer.MAX_VALUE) + expectedMap4.putString("string", "string4") + + expectedArray.pushInt(Integer.MAX_VALUE) + expectedArray.pushMap(expectedMap4) + expectedArray.pushString("string5") + + expectedMap3.putInt("integer", Integer.MAX_VALUE) + expectedMap3.putString("string", "string3") + + expectedMap2.putInt("integer", Integer.MAX_VALUE) + expectedMap2.putString("string", "string2") + expectedMap2.putMap("map", expectedMap3) + + expectedMap1.putInt("integer", Integer.MAX_VALUE) + expectedMap1.putArray("list", expectedArray) + expectedMap1.putString("string", "string1") + expectedMap1.putMap("map", expectedMap2) + + assertEquals(actual, expectedMap1) + } +} diff --git a/RNSentryAndroidTester/app/src/main/AndroidManifest.xml b/RNSentryAndroidTester/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3f4a3110d --- /dev/null +++ b/RNSentryAndroidTester/app/src/main/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/RNSentryAndroidTester/build.gradle b/RNSentryAndroidTester/build.gradle new file mode 100644 index 000000000..6b0efda04 --- /dev/null +++ b/RNSentryAndroidTester/build.gradle @@ -0,0 +1,14 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false + id 'org.jetbrains.kotlin.android' version '1.8.0' apply false +} + +allprojects { + configurations.all { + resolutionStrategy.dependencySubstitution { + substitute module('com.facebook.react:react-native') using module('com.facebook.react:react-android:0.72.0') because "for compatibility reasons RNSentry has to be dependent on com.facebook.react:react-native" + } + } +} diff --git a/RNSentryAndroidTester/gradle.properties b/RNSentryAndroidTester/gradle.properties new file mode 100644 index 000000000..3c5031eb7 --- /dev/null +++ b/RNSentryAndroidTester/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/RNSentryAndroidTester/gradle/wrapper/gradle-wrapper.jar b/RNSentryAndroidTester/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..e708b1c02 Binary files /dev/null and b/RNSentryAndroidTester/gradle/wrapper/gradle-wrapper.jar differ diff --git a/RNSentryAndroidTester/gradle/wrapper/gradle-wrapper.properties b/RNSentryAndroidTester/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..22df0329c --- /dev/null +++ b/RNSentryAndroidTester/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Jul 10 13:06:14 CEST 2023 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/RNSentryAndroidTester/gradlew b/RNSentryAndroidTester/gradlew new file mode 100755 index 000000000..4f906e0c8 --- /dev/null +++ b/RNSentryAndroidTester/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/RNSentryAndroidTester/gradlew.bat b/RNSentryAndroidTester/gradlew.bat new file mode 100644 index 000000000..ac1b06f93 --- /dev/null +++ b/RNSentryAndroidTester/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/RNSentryAndroidTester/settings.gradle b/RNSentryAndroidTester/settings.gradle new file mode 100644 index 000000000..ef5cbccbd --- /dev/null +++ b/RNSentryAndroidTester/settings.gradle @@ -0,0 +1,19 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "RNSentryAndroidTester" +include ':app' +include ':RNSentry' +project(':RNSentry').projectDir = new File('../android'); diff --git a/RNSentryTester/Podfile b/RNSentryCocoaTester/Podfile similarity index 82% rename from RNSentryTester/Podfile rename to RNSentryCocoaTester/Podfile index d87e1cefa..b2bd69527 100644 --- a/RNSentryTester/Podfile +++ b/RNSentryCocoaTester/Podfile @@ -2,7 +2,7 @@ require_relative '../node_modules/react-native/scripts/react_native_pods' platform :ios, '12.4' -target 'RNSentryTesterTests' do +target 'RNSentryCocoaTesterTests' do use_react_native!() pod 'RNSentry', :path => '../RNSentry.podspec' end diff --git a/RNSentryTester/RNSentryTester.xcodeproj/project.pbxproj b/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj similarity index 76% rename from RNSentryTester/RNSentryTester.xcodeproj/project.pbxproj rename to RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj index 71c6badb9..a3cda2587 100644 --- a/RNSentryTester/RNSentryTester.xcodeproj/project.pbxproj +++ b/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj @@ -8,15 +8,15 @@ /* Begin PBXBuildFile section */ 33F58AD02977037D008F60EA /* RNSentry+initNativeSdk.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33F58ACF2977037D008F60EA /* RNSentry+initNativeSdk.mm */; }; - B5859A50A3E865EF5E61465A /* libPods-RNSentryTesterTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 650CB718ACFBD05609BF2126 /* libPods-RNSentryTesterTests.a */; }; + B5859A50A3E865EF5E61465A /* libPods-RNSentryCocoaTesterTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 1482D5685A340AB93348A43D /* Pods-RNSentryTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryTesterTests.release.xcconfig"; path = "Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests.release.xcconfig"; sourceTree = ""; }; - 3360898D29524164007C7730 /* RNSentryTesterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNSentryTesterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.release.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.release.xcconfig"; sourceTree = ""; }; + 3360898D29524164007C7730 /* RNSentryCocoaTesterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNSentryCocoaTesterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 33F58ACF2977037D008F60EA /* RNSentry+initNativeSdk.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "RNSentry+initNativeSdk.mm"; sourceTree = ""; }; - 650CB718ACFBD05609BF2126 /* libPods-RNSentryTesterTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNSentryTesterTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - E2321E7CFA55AB617247098E /* Pods-RNSentryTesterTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryTesterTests.debug.xcconfig"; path = "Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests.debug.xcconfig"; sourceTree = ""; }; + 650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNSentryCocoaTesterTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.debug.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -24,7 +24,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B5859A50A3E865EF5E61465A /* libPods-RNSentryTesterTests.a in Frameworks */, + B5859A50A3E865EF5E61465A /* libPods-RNSentryCocoaTesterTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -34,8 +34,8 @@ 00D0AE33FB669AAC85D66F8D /* Pods */ = { isa = PBXGroup; children = ( - E2321E7CFA55AB617247098E /* Pods-RNSentryTesterTests.debug.xcconfig */, - 1482D5685A340AB93348A43D /* Pods-RNSentryTesterTests.release.xcconfig */, + E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */, + 1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -43,7 +43,7 @@ 3360896929524163007C7730 = { isa = PBXGroup; children = ( - 3360899029524164007C7730 /* RNSentryTesterTests */, + 3360899029524164007C7730 /* RNSentryCocoaTesterTests */, 3360897329524163007C7730 /* Products */, 00D0AE33FB669AAC85D66F8D /* Pods */, E9CBAA4D06145A9DB2C82C1B /* Frameworks */, @@ -53,23 +53,23 @@ 3360897329524163007C7730 /* Products */ = { isa = PBXGroup; children = ( - 3360898D29524164007C7730 /* RNSentryTesterTests.xctest */, + 3360898D29524164007C7730 /* RNSentryCocoaTesterTests.xctest */, ); name = Products; sourceTree = ""; }; - 3360899029524164007C7730 /* RNSentryTesterTests */ = { + 3360899029524164007C7730 /* RNSentryCocoaTesterTests */ = { isa = PBXGroup; children = ( 33F58ACF2977037D008F60EA /* RNSentry+initNativeSdk.mm */, ); - path = RNSentryTesterTests; + path = RNSentryCocoaTesterTests; sourceTree = ""; }; E9CBAA4D06145A9DB2C82C1B /* Frameworks */ = { isa = PBXGroup; children = ( - 650CB718ACFBD05609BF2126 /* libPods-RNSentryTesterTests.a */, + 650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */, ); name = Frameworks; sourceTree = ""; @@ -77,9 +77,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 3360898C29524164007C7730 /* RNSentryTesterTests */ = { + 3360898C29524164007C7730 /* RNSentryCocoaTesterTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 336089A429524164007C7730 /* Build configuration list for PBXNativeTarget "RNSentryTesterTests" */; + buildConfigurationList = 336089A429524164007C7730 /* Build configuration list for PBXNativeTarget "RNSentryCocoaTesterTests" */; buildPhases = ( 30F19D4E16BEEFEC68733838 /* [CP] Check Pods Manifest.lock */, 3360898929524164007C7730 /* Sources */, @@ -91,9 +91,9 @@ ); dependencies = ( ); - name = RNSentryTesterTests; - productName = RNSentryTesterTests; - productReference = 3360898D29524164007C7730 /* RNSentryTesterTests.xctest */; + name = RNSentryCocoaTesterTests; + productName = RNSentryCocoaTesterTests; + productReference = 3360898D29524164007C7730 /* RNSentryCocoaTesterTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -110,7 +110,7 @@ }; }; }; - buildConfigurationList = 3360896D29524163007C7730 /* Build configuration list for PBXProject "RNSentryTester" */; + buildConfigurationList = 3360896D29524163007C7730 /* Build configuration list for PBXProject "RNSentryCocoaTester" */; compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -123,7 +123,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 3360898C29524164007C7730 /* RNSentryTesterTests */, + 3360898C29524164007C7730 /* RNSentryCocoaTesterTests */, ); }; /* End PBXProject section */ @@ -144,7 +144,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNSentryTesterTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RNSentryCocoaTesterTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -157,15 +157,15 @@ files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; E56C5E3822DFF9C2796CEB4A /* [CP] Embed Pods Frameworks */ = { @@ -174,15 +174,15 @@ files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNSentryTesterTests/Pods-RNSentryTesterTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -311,14 +311,14 @@ }; 336089A529524164007C7730 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E2321E7CFA55AB617247098E /* Pods-RNSentryTesterTests.debug.xcconfig */; + baseConfigurationReference = E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.4; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.RNSentryTesterTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.RNSentryCocoaTesterTests; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -330,14 +330,14 @@ }; 336089A629524164007C7730 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1482D5685A340AB93348A43D /* Pods-RNSentryTesterTests.release.xcconfig */; + baseConfigurationReference = 1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.4; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = io.sentry.RNSentryTesterTests; + PRODUCT_BUNDLE_IDENTIFIER = io.sentry.RNSentryCocoaTesterTests; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -350,7 +350,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 3360896D29524163007C7730 /* Build configuration list for PBXProject "RNSentryTester" */ = { + 3360896D29524163007C7730 /* Build configuration list for PBXProject "RNSentryCocoaTester" */ = { isa = XCConfigurationList; buildConfigurations = ( 3360899F29524164007C7730 /* Debug */, @@ -359,7 +359,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 336089A429524164007C7730 /* Build configuration list for PBXNativeTarget "RNSentryTesterTests" */ = { + 336089A429524164007C7730 /* Build configuration list for PBXNativeTarget "RNSentryCocoaTesterTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 336089A529524164007C7730 /* Debug */, diff --git a/RNSentryTester/RNSentryTester.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from RNSentryTester/RNSentryTester.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/RNSentryTester/RNSentryTester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from RNSentryTester/RNSentryTester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/RNSentryTester/RNSentryTester.xcodeproj/xcshareddata/xcschemes/RNSentryTester.xcscheme b/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/xcshareddata/xcschemes/RNSentryCocoaTester.xcscheme similarity index 78% rename from RNSentryTester/RNSentryTester.xcodeproj/xcshareddata/xcschemes/RNSentryTester.xcscheme rename to RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/xcshareddata/xcschemes/RNSentryCocoaTester.xcscheme index b0388fffe..b671b3f89 100644 --- a/RNSentryTester/RNSentryTester.xcodeproj/xcshareddata/xcschemes/RNSentryTester.xcscheme +++ b/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/xcshareddata/xcschemes/RNSentryCocoaTester.xcscheme @@ -15,9 +15,9 @@ + BuildableName = "RNSentryCocoaTester.app" + BlueprintName = "RNSentryCocoaTester" + ReferencedContainer = "container:RNSentryCocoaTester.xcodeproj"> @@ -34,9 +34,9 @@ + BuildableName = "RNSentryCocoaTesterTests.xctest" + BlueprintName = "RNSentryCocoaTesterTests" + ReferencedContainer = "container:RNSentryCocoaTester.xcodeproj"> @@ -56,9 +56,9 @@ + BuildableName = "RNSentryCocoaTester.app" + BlueprintName = "RNSentryCocoaTester" + ReferencedContainer = "container:RNSentryCocoaTester.xcodeproj"> @@ -73,9 +73,9 @@ + BuildableName = "RNSentryCocoaTester.app" + BlueprintName = "RNSentryCocoaTester" + ReferencedContainer = "container:RNSentryCocoaTester.xcodeproj"> diff --git a/RNSentryTester/RNSentryTester.xcworkspace/contents.xcworkspacedata b/RNSentryCocoaTester/RNSentryCocoaTester.xcworkspace/contents.xcworkspacedata similarity index 76% rename from RNSentryTester/RNSentryTester.xcworkspace/contents.xcworkspacedata rename to RNSentryCocoaTester/RNSentryCocoaTester.xcworkspace/contents.xcworkspacedata index 020faef6c..3d69933af 100644 --- a/RNSentryTester/RNSentryTester.xcworkspace/contents.xcworkspacedata +++ b/RNSentryCocoaTester/RNSentryCocoaTester.xcworkspace/contents.xcworkspacedata @@ -2,7 +2,7 @@ + location = "group:RNSentryCocoaTester.xcodeproj"> diff --git a/RNSentryTester/RNSentryTester.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/RNSentryCocoaTester/RNSentryCocoaTester.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from RNSentryTester/RNSentryTester.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to RNSentryCocoaTester/RNSentryCocoaTester.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/RNSentryTester/RNSentryTesterTests/RNSentry+initNativeSdk.mm b/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentry+initNativeSdk.mm similarity index 100% rename from RNSentryTester/RNSentryTesterTests/RNSentry+initNativeSdk.mm rename to RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentry+initNativeSdk.mm diff --git a/android/build.gradle b/android/build.gradle index e00e644f3..6954e4630 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -47,5 +47,5 @@ android { dependencies { implementation 'com.facebook.react:react-native:+' - api 'io.sentry:sentry-android:6.25.1' + api 'io.sentry:sentry-android:6.27.0' } diff --git a/android/src/main/java/io/sentry/react/MapConverter.java b/android/src/main/java/io/sentry/react/MapConverter.java new file mode 100644 index 000000000..524c3c1a4 --- /dev/null +++ b/android/src/main/java/io/sentry/react/MapConverter.java @@ -0,0 +1,134 @@ +package io.sentry.react; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; + +import org.jetbrains.annotations.Nullable; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import io.sentry.android.core.AndroidLogger; + +public class MapConverter { + public static final String NAME = "RNSentry.MapConverter"; + + private static final ILogger logger = new AndroidLogger(NAME); + + public static Object convertToWritable(@Nullable Object serialized) { + if (serialized instanceof List) { + WritableArray writable = Arguments.createArray(); + for (Object item : (List) serialized) { + addValueToWritableArray(writable, convertToWritable(item)); + } + return writable; + } else if (serialized instanceof Map) { + WritableMap writable = Arguments.createMap(); + for (Map.Entry entry : ((Map) serialized).entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + + if (key instanceof String) { + addValueToWritableMap(writable, (String) key, convertToWritable(value)); + } else { + logger.log(SentryLevel.ERROR, "Only String keys are supported in Map.", key); + } + } + return writable; + } else if (serialized instanceof Byte) { + return Integer.valueOf((Byte) serialized); + } else if (serialized instanceof Short) { + return Integer.valueOf((Short) serialized); + } else if (serialized instanceof Float) { + return Double.valueOf((Float) serialized); + } else if (serialized instanceof Long) { + return Double.valueOf((Long) serialized); + } else if (serialized instanceof BigInteger) { + return ((BigInteger) serialized).doubleValue(); + } else if (serialized instanceof BigDecimal) { + return ((BigDecimal) serialized).doubleValue(); + } else if (serialized instanceof Integer + || serialized instanceof Double + || serialized instanceof Boolean + || serialized == null + || serialized instanceof String) { + return serialized; + } else { + logger.log(SentryLevel.ERROR, "Supplied serialized value could not be converted." + serialized); + return null; + } + } + + private static void addValueToWritableArray(WritableArray writableArray, Object value) { + if (value == null) { + writableArray.pushNull(); + } else if (value instanceof Boolean) { + writableArray.pushBoolean((Boolean) value); + } else if (value instanceof Double) { + writableArray.pushDouble((Double) value); + } else if (value instanceof Float) { + writableArray.pushDouble(((Float) value).doubleValue()); + } else if (value instanceof Integer) { + writableArray.pushInt((Integer) value); + } else if (value instanceof Short) { + writableArray.pushInt(((Short) value).intValue()); + } else if (value instanceof Byte) { + writableArray.pushInt(((Byte) value).intValue()); + } else if (value instanceof Long) { + writableArray.pushDouble(((Long) value).doubleValue()); + } else if (value instanceof BigInteger) { + writableArray.pushDouble(((BigInteger) value).doubleValue()); + } else if (value instanceof BigDecimal) { + writableArray.pushDouble(((BigDecimal) value).doubleValue()); + } else if (value instanceof String) { + writableArray.pushString((String) value); + } else if (value instanceof ReadableMap) { + writableArray.pushMap((ReadableMap) value); + } else if (value instanceof ReadableArray) { + writableArray.pushArray((ReadableArray) value); + } else { + logger.log(SentryLevel.ERROR, + "Could not convert object: " + value); + } + } + + private static void addValueToWritableMap(WritableMap writableMap, String key, Object value) { + if (value == null) { + writableMap.putNull(key); + } else if (value instanceof Boolean) { + writableMap.putBoolean(key, (Boolean) value); + } else if (value instanceof Double) { + writableMap.putDouble(key, (Double) value); + } else if (value instanceof Float) { + writableMap.putDouble(key, ((Float) value).doubleValue()); + } else if (value instanceof Integer) { + writableMap.putInt(key, (Integer) value); + } else if (value instanceof Short) { + writableMap.putInt(key, ((Short) value).intValue()); + } else if (value instanceof Byte) { + writableMap.putInt(key, ((Byte) value).intValue()); + } else if (value instanceof Long) { + writableMap.putDouble(key, ((Long) value).doubleValue()); + } else if (value instanceof BigInteger) { + writableMap.putDouble(key, ((BigInteger) value).doubleValue()); + } else if (value instanceof BigDecimal) { + writableMap.putDouble(key, ((BigDecimal) value).doubleValue()); + } else if (value instanceof String) { + writableMap.putString(key, (String) value); + } else if (value instanceof ReadableArray) { + writableMap.putArray(key, (ReadableArray) value); + } else if (value instanceof ReadableMap) { + writableMap.putMap(key, (ReadableMap) value); + } else { + logger.log(SentryLevel.ERROR, + "Could not convert object" + value); + } + } +} diff --git a/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java b/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java index 0d8713a41..5092969d0 100644 --- a/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +++ b/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java @@ -9,7 +9,6 @@ import android.content.res.AssetManager; import android.util.SparseIntArray; -import androidx.annotation.Nullable; import androidx.core.app.FrameMetricsAggregator; import com.facebook.react.bridge.Arguments; @@ -24,16 +23,16 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.io.BufferedInputStream; -import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.InputStream; import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -43,10 +42,12 @@ import io.sentry.ILogger; import io.sentry.ISerializer; import io.sentry.Integration; +import io.sentry.Scope; import io.sentry.Sentry; import io.sentry.SentryDate; import io.sentry.SentryEvent; import io.sentry.SentryLevel; +import io.sentry.SentryOptions; import io.sentry.UncaughtExceptionHandlerIntegration; import io.sentry.android.core.AndroidLogger; import io.sentry.android.core.AnrIntegration; @@ -54,8 +55,10 @@ import io.sentry.android.core.BuildConfig; import io.sentry.android.core.BuildInfoProvider; import io.sentry.android.core.CurrentActivityHolder; +import io.sentry.android.core.InternalSentrySdk; import io.sentry.android.core.NdkIntegration; import io.sentry.android.core.SentryAndroid; +import io.sentry.android.core.SentryAndroidOptions; import io.sentry.android.core.ViewHierarchyEventProcessor; import io.sentry.protocol.SdkVersion; import io.sentry.protocol.SentryException; @@ -335,19 +338,10 @@ public void captureEnvelope(ReadableArray rawBytes, ReadableMap options, Promise } try { - final String outboxPath = HubAdapter.getInstance().getOptions().getOutboxPath(); - - if (outboxPath == null) { - logger.log(SentryLevel.ERROR, - "Error retrieving outboxPath. Envelope will not be sent. Is the Android SDK initialized?"); - } else { - File installation = new File(outboxPath, UUID.randomUUID().toString()); - try (FileOutputStream out = new FileOutputStream(installation)) { - out.write(bytes); - } - } - } catch (Throwable ignored) { - logger.log(SentryLevel.ERROR, "Error while writing envelope to outbox."); + InternalSentrySdk.captureEnvelope(bytes); + } catch (Throwable e) { + logger.log(SentryLevel.ERROR, "Error while capturing envelope"); + promise.resolve(false); } promise.resolve(true); } @@ -616,6 +610,40 @@ public void disableNativeFramesTracking() { } } + public void fetchNativeDeviceContexts(Promise promise) { + final @NotNull SentryOptions options = HubAdapter.getInstance().getOptions(); + if (!(options instanceof SentryAndroidOptions)) { + promise.resolve(null); + return; + } + + final @Nullable Context context = this.getReactApplicationContext().getApplicationContext(); + if (context == null) { + promise.resolve(null); + return; + } + + final @Nullable Scope currentScope = InternalSentrySdk.getCurrentScope(); + final @NotNull Map serialized = InternalSentrySdk.serializeScope( + context, + (SentryAndroidOptions) options, + currentScope); + final @Nullable Object deviceContext = MapConverter.convertToWritable(serialized); + promise.resolve(deviceContext); + } + + public void fetchNativeSdkInfo(Promise promise) { + final @Nullable SdkVersion sdkVersion = HubAdapter.getInstance().getOptions().getSdkVersion(); + if (sdkVersion == null) { + promise.resolve(null); + } else { + final WritableMap sdkInfo = new WritableNativeMap(); + sdkInfo.putString("name", sdkVersion.getName()); + sdkInfo.putString("version", sdkVersion.getVersion()); + promise.resolve(sdkInfo); + } + } + private void setEventOriginTag(SentryEvent event) { SdkVersion sdk = event.getSdk(); if (sdk != null) { diff --git a/android/src/newarch/java/io/sentry/react/RNSentryModule.java b/android/src/newarch/java/io/sentry/react/RNSentryModule.java index 40eb787b1..e167de797 100644 --- a/android/src/newarch/java/io/sentry/react/RNSentryModule.java +++ b/android/src/newarch/java/io/sentry/react/RNSentryModule.java @@ -114,11 +114,11 @@ public void disableNativeFramesTracking() { @Override public void fetchNativeDeviceContexts(Promise promise) { - // Not used on android + this.impl.fetchNativeDeviceContexts(promise); } @Override public void fetchNativeSdkInfo(Promise promise) { - // Not used on android + this.impl.fetchNativeSdkInfo(promise); } } diff --git a/android/src/oldarch/java/io/sentry/react/RNSentryModule.java b/android/src/oldarch/java/io/sentry/react/RNSentryModule.java index c1f09c5c4..f6aa57802 100644 --- a/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +++ b/android/src/oldarch/java/io/sentry/react/RNSentryModule.java @@ -114,11 +114,11 @@ public void disableNativeFramesTracking() { @ReactMethod public void fetchNativeDeviceContexts(Promise promise) { - // Not used on android + this.impl.fetchNativeDeviceContexts(promise); } @ReactMethod public void fetchNativeSdkInfo(Promise promise) { - // Not used on android + this.impl.fetchNativeSdkInfo(promise); } } diff --git a/ios/RNSentry.mm b/ios/RNSentry.mm index 3204a1824..c2bd9cf69 100644 --- a/ios/RNSentry.mm +++ b/ios/RNSentry.mm @@ -188,44 +188,44 @@ - (void)setEventEnvironmentTag:(SentryEvent *)event rejecter:(RCTPromiseRejectBlock)reject) { NSLog(@"Bridge call to: deviceContexts"); - __block NSMutableDictionary *contexts; + __block NSMutableDictionary *serializedScope; // Temp work around until sorted out this API in sentry-cocoa. // TODO: If the callback isnt' executed the promise wouldn't be resolved. [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { - NSDictionary *serializedScope = [scope serialize]; - contexts = [serializedScope mutableCopy]; + serializedScope = [[scope serialize] mutableCopy]; - NSDictionary *user = [contexts valueForKey:@"user"]; + NSDictionary *user = [serializedScope valueForKey:@"user"]; if (user == nil) { - [contexts + [serializedScope setValue:@{ @"id": PrivateSentrySDKOnly.installationID } forKey:@"user"]; } if (PrivateSentrySDKOnly.options.debug) { - NSData *data = [NSJSONSerialization dataWithJSONObject:contexts options:0 error:nil]; + NSData *data = [NSJSONSerialization dataWithJSONObject:serializedScope options:0 error:nil]; NSString *debugContext = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"Contexts: %@", debugContext); } }]; NSDictionary *extraContext = [PrivateSentrySDKOnly getExtraContext]; - NSMutableDictionary *> *context = [contexts[@"context"] mutableCopy]; + NSMutableDictionary *> *contexts = [serializedScope[@"context"] mutableCopy]; if (extraContext && [extraContext[@"device"] isKindOfClass:[NSDictionary class]]) { - NSMutableDictionary *> *deviceContext = [contexts[@"context"][@"device"] mutableCopy]; + NSMutableDictionary *> *deviceContext = [contexts[@"device"] mutableCopy]; [deviceContext addEntriesFromDictionary:extraContext[@"device"]]; - [context setValue:deviceContext forKey:@"device"]; + [contexts setValue:deviceContext forKey:@"device"]; } if (extraContext && [extraContext[@"app"] isKindOfClass:[NSDictionary class]]) { - NSMutableDictionary *> *appContext = [contexts[@"context"][@"app"] mutableCopy]; + NSMutableDictionary *> *appContext = [contexts[@"app"] mutableCopy]; [appContext addEntriesFromDictionary:extraContext[@"app"]]; - [context setValue:appContext forKey:@"app"]; + [contexts setValue:appContext forKey:@"app"]; } - [contexts setValue:context forKey:@"context"]; - resolve(contexts); + [serializedScope setValue:contexts forKey:@"contexts"]; + [serializedScope removeObjectForKey:@"context"]; + resolve(serializedScope); } RCT_EXPORT_METHOD(fetchNativeAppStart:(RCTPromiseResolveBlock)resolve diff --git a/src/js/NativeRNSentry.ts b/src/js/NativeRNSentry.ts index 4c2f8bb7a..d9e8cec9f 100644 --- a/src/js/NativeRNSentry.ts +++ b/src/js/NativeRNSentry.ts @@ -19,8 +19,8 @@ export interface Spec extends TurboModule { closeNativeSdk(): Promise; disableNativeFramesTracking(): void; fetchNativeRelease(): Promise; - fetchNativeSdkInfo(): Promise; - fetchNativeDeviceContexts(): Promise; + fetchNativeSdkInfo(): Promise; + fetchNativeDeviceContexts(): Promise; fetchNativeAppStart(): Promise; fetchNativeFrames(): Promise; initNativeSdk(options: UnsafeObject): Promise; @@ -52,14 +52,15 @@ export type NativeReleaseResponse = { }; /** - * This type describes serialized scope from sentry-cocoa. (This is not used for Android) + * This type describes serialized scope from sentry-cocoa and sentry-android * https://github.com/getsentry/sentry-cocoa/blob/master/Sources/Sentry/SentryScope.m + * https://github.com/getsentry/sentry-java/blob/a461f7e125b65240004e6162b341f383ce2e1394/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java#L32 */ export type NativeDeviceContextsResponse = { [key: string]: unknown; tags?: Record; extra?: Record; - context?: Record>; + contexts?: Record>; user?: { userId?: string; email?: string; diff --git a/src/js/integrations/devicecontext.ts b/src/js/integrations/devicecontext.ts index 6a7e57503..b75955479 100644 --- a/src/js/integrations/devicecontext.ts +++ b/src/js/integrations/devicecontext.ts @@ -45,16 +45,16 @@ export class DeviceContext implements Integration { event.user = nativeUser; } - let nativeContext = native.context; + let nativeContexts = native.contexts; if (AppState.currentState !== 'unknown') { - nativeContext = nativeContext || {}; - nativeContext.app = { - ...nativeContext.app, + nativeContexts = nativeContexts || {}; + nativeContexts.app = { + ...nativeContexts.app, in_foreground: AppState.currentState === 'active', }; } - if (nativeContext) { - event.contexts = { ...nativeContext, ...event.contexts }; + if (nativeContexts) { + event.contexts = { ...nativeContexts, ...event.contexts }; } const nativeTags = native.tags; diff --git a/src/js/integrations/sdkinfo.ts b/src/js/integrations/sdkinfo.ts index 7a55dd6c1..c2fc143c3 100644 --- a/src/js/integrations/sdkinfo.ts +++ b/src/js/integrations/sdkinfo.ts @@ -36,9 +36,8 @@ export class SdkInfo implements Integration { */ public setupOnce(addGlobalEventProcessor: (e: EventProcessor) => void): void { addGlobalEventProcessor(async event => { - // The native SDK info package here is only used on iOS as `beforeSend` is not called on `captureEnvelope`. // this._nativeSdkInfo should be defined a following time so this call won't always be awaited. - if (NATIVE.platform === 'ios' && this._nativeSdkPackage === null) { + if (this._nativeSdkPackage === null) { try { this._nativeSdkPackage = await NATIVE.fetchNativeSdkInfo(); } catch (e) { diff --git a/src/js/wrapper.ts b/src/js/wrapper.ts index a8176ed57..b0bc3bf56 100644 --- a/src/js/wrapper.ts +++ b/src/js/wrapper.ts @@ -49,7 +49,6 @@ interface SentryNativeWrapper { _processLevel(level: SeverityLevel): SeverityLevel; _serializeObject(data: { [key: string]: unknown }): { [key: string]: string }; _isModuleLoaded(module: Spec | undefined): module is Spec; - _getBreadcrumbs(event: Event): Breadcrumb[] | undefined; isNativeAvailable(): boolean; @@ -60,7 +59,7 @@ interface SentryNativeWrapper { captureScreenshot(): Promise; fetchNativeRelease(): PromiseLike; - fetchNativeDeviceContexts(): PromiseLike; + fetchNativeDeviceContexts(): PromiseLike; fetchNativeAppStart(): PromiseLike; fetchNativeFrames(): PromiseLike; fetchNativeSdkInfo(): PromiseLike; @@ -224,7 +223,6 @@ export const NATIVE: SentryNativeWrapper = { /** * Fetches the Sdk info for the native sdk. - * NOTE: Only available on iOS. */ async fetchNativeSdkInfo(): Promise { if (!this.enableNative) { @@ -234,17 +232,13 @@ export const NATIVE: SentryNativeWrapper = { throw this._NativeClientError; } - if (this.platform !== 'ios') { - return null; - } - return RNSentry.fetchNativeSdkInfo(); }, /** * Fetches the device contexts. Not used on Android. */ - async fetchNativeDeviceContexts(): Promise { + async fetchNativeDeviceContexts(): Promise { if (!this.enableNative) { throw this._DisabledNativeError; } @@ -252,11 +246,6 @@ export const NATIVE: SentryNativeWrapper = { throw this._NativeClientError; } - if (this.platform !== 'ios') { - // Only ios uses deviceContexts, return an empty object. - return {}; - } - return RNSentry.fetchNativeDeviceContexts(); }, @@ -514,7 +503,6 @@ export const NATIVE: SentryNativeWrapper = { // @ts-ignore Android still uses the old message object, without this the serialization of events will break. event.message = { message: event.message }; } - event.breadcrumbs = this._getBreadcrumbs(event); } return [itemHeader, event]; @@ -582,27 +570,6 @@ export const NATIVE: SentryNativeWrapper = { _NativeClientError: new SentryError("Native Client is not available, can't start on native."), - /** - * Get breadcrumbs (removes breadcrumbs from handled exceptions on Android) - * - * We do this to avoid duplicate breadcrumbs on Android as sentry-android applies the breadcrumbs - * from the native scope onto every envelope sent through it. This scope will contain the breadcrumbs - * sent through the scope sync feature. This causes duplicate breadcrumbs. - * We then remove the breadcrumbs in all cases but if it is handled == false, - * this is a signal that the app would crash and android would lose the breadcrumbs by the time the app is restarted to read - * the envelope. - */ - _getBreadcrumbs(event: Event): Breadcrumb[] | undefined { - let breadcrumbs: Breadcrumb[] | undefined = event.breadcrumbs; - - const hardCrashed = isHardCrash(event); - if (NATIVE.platform === 'android' && event.breadcrumbs && !hardCrashed) { - breadcrumbs = []; - } - - return breadcrumbs; - }, - enableNative: true, nativeIsReady: false, platform: Platform.OS, diff --git a/test/integrations/devicecontext.test.ts b/test/integrations/devicecontext.test.ts index a217e0d63..09f514f7a 100644 --- a/test/integrations/devicecontext.test.ts +++ b/test/integrations/devicecontext.test.ts @@ -45,7 +45,7 @@ describe('Device Context Integration', () => { it('merge event and native contexts', async () => { const { processedEvent } = await executeIntegrationWith({ - nativeContexts: { context: { duplicate: { context: 'native-value' }, native: { context: 'value' } } }, + nativeContexts: { contexts: { duplicate: { context: 'native-value' }, native: { context: 'value' } } }, mockEvent: { contexts: { duplicate: { context: 'event-value' }, event: { context: 'value' } } }, }); expect(processedEvent).toStrictEqual({ @@ -142,7 +142,7 @@ describe('Device Context Integration', () => { it('adds in_foreground false to native app contexts', async () => { mockCurrentAppState = 'background'; const { processedEvent } = await executeIntegrationWith({ - nativeContexts: { context: { app: { native: 'value' } } }, + nativeContexts: { contexts: { app: { native: 'value' } } }, }); expect(processedEvent).toStrictEqual({ contexts: { @@ -157,7 +157,7 @@ describe('Device Context Integration', () => { it('adds in_foreground to native app contexts', async () => { mockCurrentAppState = 'active'; const { processedEvent } = await executeIntegrationWith({ - nativeContexts: { context: { app: { native: 'value' } } }, + nativeContexts: { contexts: { app: { native: 'value' } } }, }); expect(processedEvent).toStrictEqual({ contexts: { @@ -172,7 +172,7 @@ describe('Device Context Integration', () => { it('do not add in_foreground if unknown', async () => { mockCurrentAppState = 'unknown'; const { processedEvent } = await executeIntegrationWith({ - nativeContexts: { context: { app: { native: 'value' } } }, + nativeContexts: { contexts: { app: { native: 'value' } } }, }); expect(processedEvent).toStrictEqual({ contexts: { diff --git a/test/integrations/sdkinfo.test.ts b/test/integrations/sdkinfo.test.ts index 940aa767c..f82902e6b 100644 --- a/test/integrations/sdkinfo.test.ts +++ b/test/integrations/sdkinfo.test.ts @@ -6,11 +6,16 @@ import { NATIVE } from '../../src/js/wrapper'; let mockedFetchNativeSdkInfo: jest.Mock, []>; -const mockPackage = { +const mockCocoaPackage = { name: 'sentry-cocoa', version: '0.0.1', }; +const mockAndroidPackage = { + name: 'sentry-android', + version: '0.0.1', +}; + jest.mock('../../src/js/wrapper', () => { const actual = jest.requireActual('../../src/js/wrapper'); @@ -29,24 +34,24 @@ afterEach(() => { describe('Sdk Info', () => { it('Adds native package and javascript platform to event on iOS', async () => { - mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(mockPackage); + mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(mockCocoaPackage); const mockEvent: Event = {}; const processedEvent = await executeIntegrationFor(mockEvent); - expect(processedEvent?.sdk?.packages).toEqual(expect.arrayContaining([mockPackage])); + expect(processedEvent?.sdk?.packages).toEqual(expect.arrayContaining([mockCocoaPackage])); expect(processedEvent?.platform === 'javascript'); expect(mockedFetchNativeSdkInfo).toBeCalledTimes(1); }); - it('Adds javascript platform but not native package on Android', async () => { + it('Adds native package and javascript platform to event on Android', async () => { NATIVE.platform = 'android'; - mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(mockPackage); + mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(mockAndroidPackage); const mockEvent: Event = {}; const processedEvent = await executeIntegrationFor(mockEvent); - expect(processedEvent?.sdk?.packages).toEqual(expect.not.arrayContaining([mockPackage])); + expect(processedEvent?.sdk?.packages).toEqual(expect.not.arrayContaining([mockCocoaPackage])); expect(processedEvent?.platform === 'javascript'); - expect(mockedFetchNativeSdkInfo).not.toBeCalled(); + expect(mockedFetchNativeSdkInfo).toBeCalledTimes(1); }); it('Does not add any default non native packages', async () => { diff --git a/test/wrapper.test.ts b/test/wrapper.test.ts index 243f3dbe0..11bb81d3a 100644 --- a/test/wrapper.test.ts +++ b/test/wrapper.test.ts @@ -346,7 +346,7 @@ describe('Tests Native Wrapper', () => { { store: false }, ); }); - test('Clears breadcrumbs on Android if mechanism.handled is true', async () => { + test('Keeps breadcrumbs on Android if mechanism.handled is true', async () => { NATIVE.platform = 'android'; const event: Event = { @@ -377,13 +377,13 @@ describe('Tests Native Wrapper', () => { expect(RNSentry.captureEnvelope).toBeCalledWith( utf8ToBytes( '{"event_id":"event0","sent_at":"123"}\n' + - '{"type":"event","content_type":"application/json","length":104}\n' + - '{"event_id":"event0","exception":{"values":[{"mechanism":{"handled":true,"type":""}}]},"breadcrumbs":[]}\n', + '{"type":"event","content_type":"application/json","length":124}\n' + + '{"event_id":"event0","exception":{"values":[{"mechanism":{"handled":true,"type":""}}]},"breadcrumbs":[{"message":"crumb!"}]}\n', ), { store: false }, ); }); - test('Clears breadcrumbs on Android if there is no exception', async () => { + test('Keeps breadcrumbs on Android if there is no exception', async () => { NATIVE.platform = 'android'; const event: Event = { @@ -404,13 +404,13 @@ describe('Tests Native Wrapper', () => { expect(RNSentry.captureEnvelope).toBeCalledWith( utf8ToBytes( '{"event_id":"event0","sent_at":"123"}\n' + - '{"type":"event","content_type":"application/json","length":38}\n' + - '{"event_id":"event0","breadcrumbs":[]}\n', + '{"type":"event","content_type":"application/json","length":58}\n' + + '{"event_id":"event0","breadcrumbs":[{"message":"crumb!"}]}\n', ), { store: false }, ); }); - test('Does not clear breadcrumbs on Android if mechanism.handled is false', async () => { + test('Keeps breadcrumbs on Android if mechanism.handled is false', async () => { NATIVE.platform = 'android'; const event: Event = { @@ -471,12 +471,16 @@ describe('Tests Native Wrapper', () => { expect(RNSentry.fetchNativeDeviceContexts).toBeCalled(); }); - test('returns empty object on android', async () => { + test('returns context object from native module on android', async () => { NATIVE.platform = 'android'; - await expect(NATIVE.fetchNativeDeviceContexts()).resolves.toMatchObject({}); + await expect(NATIVE.fetchNativeDeviceContexts()).resolves.toMatchObject({ + someContext: { + someValue: 0, + }, + }); - expect(RNSentry.fetchNativeDeviceContexts).not.toBeCalled(); + expect(RNSentry.fetchNativeDeviceContexts).toBeCalled(); }); });