diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index d9909bdf8..becbf0df5 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -71,7 +71,8 @@ static void init( readDefaultOptionValues(options, context); - options.addEventProcessor(new DefaultAndroidEventProcessor(context, options)); + options.addEventProcessor( + new DefaultAndroidEventProcessor(context, options, new BuildInfoProvider())); options.setSerializer(new AndroidSerializer(options.getLogger(), envelopeReader)); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransportGate.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransportGate.java index b53457a12..f0469c978 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransportGate.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransportGate.java @@ -12,20 +12,27 @@ final class AndroidTransportGate implements ITransportGate { private final Context context; private final @NotNull ILogger logger; - AndroidTransportGate(@NotNull Context context, @NotNull ILogger logger) { + AndroidTransportGate(final @NotNull Context context, final @NotNull ILogger logger) { this.context = context; this.logger = logger; } @Override public boolean isConnected() { - return isConnected(ConnectivityChecker.isConnected(context, logger)); + return isConnected(ConnectivityChecker.getConnectionStatus(context, logger)); } @TestOnly - boolean isConnected(Boolean connected) { + boolean isConnected(final @NotNull ConnectivityChecker.Status status) { // let's assume its connected if there's no permission or something as we can't really know // whether is online or not. - return connected != null ? connected : true; + switch (status) { + case CONNECTED: + case UNKNOWN: + case NO_PERMISSION: + return true; + default: + return false; + } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/BuildInfoProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/BuildInfoProvider.java new file mode 100644 index 000000000..0c43c224e --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/BuildInfoProvider.java @@ -0,0 +1,19 @@ +package io.sentry.android.core; + +import android.os.Build; +import org.jetbrains.annotations.ApiStatus; + +/** The Android Impl. of IBuildInfoProvider which returns the Build class info. */ +@ApiStatus.Internal +public final class BuildInfoProvider implements IBuildInfoProvider { + + /** + * Returns the Build.VERSION.SDK_INT + * + * @return the Build.VERSION.SDK_INT + */ + @Override + public int getSdkInfoVersion() { + return Build.VERSION.SDK_INT; + } +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java index 02e793a07..5fae9b23d 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java @@ -75,10 +75,16 @@ final class DefaultAndroidEventProcessor implements EventProcessor { @TestOnly final Future> contextData; + private final @NotNull IBuildInfoProvider buildInfoProvider; + public DefaultAndroidEventProcessor( - final @NotNull Context context, final @NotNull SentryOptions options) { + final @NotNull Context context, + final @NotNull SentryOptions options, + final @NotNull IBuildInfoProvider buildInfoProvider) { this.context = Objects.requireNonNull(context, "The application context is required."); this.options = Objects.requireNonNull(options, "The SentryOptions is required."); + this.buildInfoProvider = + Objects.requireNonNull(buildInfoProvider, "The BuildInfoProvider is required."); ExecutorService executorService = Executors.newSingleThreadExecutor(); // dont ref. to method reference, theres a bug on it @@ -287,7 +293,18 @@ private void setArchitectures(final @NotNull Device device) { device.setCharging(isCharging(batteryIntent)); device.setBatteryTemperature(getBatteryTemperature(batteryIntent)); } - device.setOnline(ConnectivityChecker.isConnected(context, options.getLogger())); + Boolean connected; + switch (ConnectivityChecker.getConnectionStatus(context, options.getLogger())) { + case NOT_CONNECTED: + connected = false; + break; + case CONNECTED: + connected = true; + break; + default: + connected = null; + } + device.setOnline(connected); device.setOrientation(getOrientation()); try { @@ -344,7 +361,8 @@ private void setArchitectures(final @NotNull Device device) { } if (device.getConnectionType() == null) { // wifi, ethernet or cellular, null if none - device.setConnectionType(ConnectivityChecker.getConnectionType(context, options.getLogger())); + device.setConnectionType( + ConnectivityChecker.getConnectionType(context, options.getLogger(), buildInfoProvider)); } return device; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/IBuildInfoProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/IBuildInfoProvider.java new file mode 100644 index 000000000..54fc8a3a8 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/IBuildInfoProvider.java @@ -0,0 +1,12 @@ +package io.sentry.android.core; + +/** To make SDK info classes testable */ +public interface IBuildInfoProvider { + + /** + * Returns the SDK version of the given SDK + * + * @return the SDK Version + */ + int getSdkInfoVersion(); +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/util/ConnectivityChecker.java b/sentry-android-core/src/main/java/io/sentry/android/core/util/ConnectivityChecker.java index 9f8cf2c19..02b23437c 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/util/ConnectivityChecker.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/util/ConnectivityChecker.java @@ -7,6 +7,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.os.Build; +import io.sentry.android.core.IBuildInfoProvider; import io.sentry.core.ILogger; import io.sentry.core.SentryLevel; import org.jetbrains.annotations.ApiStatus; @@ -16,19 +17,27 @@ @ApiStatus.Internal public final class ConnectivityChecker { + public enum Status { + CONNECTED, + NOT_CONNECTED, + NO_PERMISSION, + UNKNOWN + } + private ConnectivityChecker() {} /** - * Check whether the application has internet access at a point in time. + * Return the Connection status * - * @return true if the application has internet access + * @return the ConnectionStatus */ - public static @Nullable Boolean isConnected(@NotNull Context context, @NotNull ILogger logger) { - ConnectivityManager connectivityManager = getConnectivityManager(context, logger); + public static @NotNull ConnectivityChecker.Status getConnectionStatus( + final @NotNull Context context, final @NotNull ILogger logger) { + final ConnectivityManager connectivityManager = getConnectivityManager(context, logger); if (connectivityManager == null) { - return null; + return Status.UNKNOWN; } - return isConnected(context, connectivityManager, logger); + return getConnectionStatus(context, connectivityManager, logger); // getActiveNetworkInfo might return null if VPN doesn't specify its // underlying network @@ -36,30 +45,30 @@ private ConnectivityChecker() {} // connectivityManager.registerDefaultNetworkCallback(...) } + /** + * Return the Connection status + * + * @param context the Context + * @param connectivityManager the ConnectivityManager + * @param logger the Logger + * @return true if connected or no permission to check, false otherwise + */ @SuppressWarnings({"deprecation", "MissingPermission"}) - private static Boolean isConnected( - Context context, ConnectivityManager connectivityManager, ILogger logger) { - android.net.NetworkInfo activeNetwork = - getActiveNetworkInfo(context, connectivityManager, logger); - - if (activeNetwork == null) { - logger.log(SentryLevel.INFO, "NetworkInfo is null and cannot check network status"); - return null; - } - return activeNetwork.isConnected(); - } - - @SuppressWarnings({"deprecation", "MissingPermission"}) - private static @Nullable android.net.NetworkInfo getActiveNetworkInfo( - @NotNull Context context, - @NotNull ConnectivityManager connectivityManager, - @NotNull ILogger logger) { + private static @NotNull ConnectivityChecker.Status getConnectionStatus( + final @NotNull Context context, + final @NotNull ConnectivityManager connectivityManager, + final @NotNull ILogger logger) { if (!Permissions.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)) { logger.log(SentryLevel.INFO, "No permission (ACCESS_NETWORK_STATE) to check network status."); - return null; + return Status.NO_PERMISSION; } + final android.net.NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); - return connectivityManager.getActiveNetworkInfo(); + if (activeNetworkInfo == null) { + logger.log(SentryLevel.INFO, "NetworkInfo is null, there's no active network."); + return Status.NOT_CONNECTED; + } + return activeNetworkInfo.isConnected() ? Status.CONNECTED : Status.NOT_CONNECTED; } /** @@ -67,47 +76,104 @@ private static Boolean isConnected( * * @param context the App. context * @param logger the logger from options + * @param buildInfoProvider the BuildInfoProvider provider * @return the connection type wifi, ethernet, cellular or null */ - @SuppressLint({"ObsoleteSdkInt", "MissingPermission"}) - public static String getConnectionType(@NotNull Context context, @NotNull ILogger logger) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ConnectivityManager connectivityManager = getConnectivityManager(context, logger); - if (connectivityManager == null) { - return null; - } - if (!Permissions.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)) { - logger.log( - SentryLevel.INFO, "No permission (ACCESS_NETWORK_STATE) to check network status."); - return null; - } - Network activeNetwork = connectivityManager.getActiveNetwork(); + @SuppressLint({"ObsoleteSdkInt", "MissingPermission", "NewApi"}) + public static @Nullable String getConnectionType( + final @NotNull Context context, + final @NotNull ILogger logger, + final @NotNull IBuildInfoProvider buildInfoProvider) { + final ConnectivityManager connectivityManager = getConnectivityManager(context, logger); + if (connectivityManager == null) { + return null; + } + if (!Permissions.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)) { + logger.log(SentryLevel.INFO, "No permission (ACCESS_NETWORK_STATE) to check network status."); + return null; + } + + boolean ethernet = false; + boolean wifi = false; + boolean cellular = false; + + if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.M) { + final Network activeNetwork = connectivityManager.getActiveNetwork(); if (activeNetwork == null) { logger.log(SentryLevel.INFO, "Network is null and cannot check network status"); return null; } - NetworkCapabilities networkCapabilities = + final NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork); if (networkCapabilities == null) { logger.log(SentryLevel.INFO, "NetworkCapabilities is null and cannot check network type"); return null; } - if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { - return "wifi"; - } if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { - return "ethernet"; + ethernet = true; + } + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + wifi = true; } if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - return "cellular"; + cellular = true; + } + } else { + // ideally using connectivityManager.getAllNetworks(), but its >= Android L only + + // for some reason linting didn't allow a single @SuppressWarnings("deprecation") on method + // signature + @SuppressWarnings("deprecation") + final android.net.NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + + if (activeNetworkInfo == null) { + logger.log(SentryLevel.INFO, "NetworkInfo is null, there's no active network."); + return null; + } + + @SuppressWarnings("deprecation") + final int type = activeNetworkInfo.getType(); + + @SuppressWarnings("deprecation") + final int TYPE_ETHERNET = ConnectivityManager.TYPE_ETHERNET; + + @SuppressWarnings("deprecation") + final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI; + + @SuppressWarnings("deprecation") + final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE; + + switch (type) { + case TYPE_ETHERNET: + ethernet = true; + break; + case TYPE_WIFI: + wifi = true; + break; + case TYPE_MOBILE: + cellular = true; + break; } } + + // TODO: change the protocol to be a list of transports as a device may have the capability of + // multiple + if (ethernet) { + return "ethernet"; + } + if (wifi) { + return "wifi"; + } + if (cellular) { + return "cellular"; + } + return null; } private static @Nullable ConnectivityManager getConnectivityManager( - @NotNull Context context, @NotNull ILogger logger) { - ConnectivityManager connectivityManager = + final @NotNull Context context, final @NotNull ILogger logger) { + final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivityManager == null) { logger.log(SentryLevel.INFO, "ConnectivityManager is null and cannot check network status"); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransportGateTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransportGateTest.kt index 1c791e31f..7f2132f1e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransportGateTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransportGateTest.kt @@ -1,45 +1,43 @@ package io.sentry.android.core -import android.content.Context -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 import com.nhaarman.mockitokotlin2.mock -import kotlin.test.BeforeTest +import io.sentry.android.core.util.ConnectivityChecker import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertTrue -import org.junit.runner.RunWith -@RunWith(AndroidJUnit4::class) class AndroidTransportGateTest { - private lateinit var context: Context - private lateinit var transportGate: AndroidTransportGate - - @BeforeTest - fun `set up`() { - context = ApplicationProvider.getApplicationContext() - transportGate = AndroidTransportGate(context, mock()) + private class Fixture { + fun getSut(): AndroidTransportGate { + return AndroidTransportGate(mock(), mock()) + } } + private val fixture = Fixture() @Test - fun `isSendingAllowed is not null`() { - assertNotNull(transportGate.isConnected) + fun `isConnected doesn't throw`() { + assertNotNull(fixture.getSut().isConnected) } @Test - fun `isConnected returns true if connection was not found or no permission`() { - assertTrue(transportGate.isConnected(null)) + fun `isConnected returns true if connection was not found`() { + assertTrue(fixture.getSut().isConnected(ConnectivityChecker.Status.UNKNOWN)) } @Test fun `isConnected returns true if connection is connected`() { - assertTrue(transportGate.isConnected(true)) + assertTrue(fixture.getSut().isConnected(ConnectivityChecker.Status.CONNECTED)) } @Test fun `isConnected returns false if connection is not connected`() { - assertFalse(transportGate.isConnected(false)) + assertFalse(fixture.getSut().isConnected(ConnectivityChecker.Status.NOT_CONNECTED)) + } + + @Test + fun `isConnected returns false if no permission`() { + assertTrue(fixture.getSut().isConnected(ConnectivityChecker.Status.NO_PERMISSION)) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ConnectivityCheckerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ConnectivityCheckerTest.kt index 447706af8..cfa723e21 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ConnectivityCheckerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ConnectivityCheckerTest.kt @@ -1,31 +1,164 @@ package io.sentry.android.core import android.content.Context -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 +import android.content.pm.PackageManager.PERMISSION_DENIED +import android.net.ConnectivityManager +import android.net.ConnectivityManager.TYPE_ETHERNET +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +import android.net.NetworkCapabilities.TRANSPORT_ETHERNET +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.net.NetworkInfo +import android.os.Build +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever import io.sentry.android.core.util.ConnectivityChecker import kotlin.test.BeforeTest import kotlin.test.Test -import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertNull -@RunWith(AndroidJUnit4::class) class ConnectivityCheckerTest { - private lateinit var context: Context + private lateinit var contextMock: Context + private lateinit var connectivityManager: ConnectivityManager + private lateinit var networkInfo: NetworkInfo + private lateinit var buildInfo: IBuildInfoProvider + private lateinit var network: Network + private lateinit var networkCapabilities: NetworkCapabilities @BeforeTest - fun `set up`() { - context = ApplicationProvider.getApplicationContext() + fun beforeTest() { + contextMock = mock() + connectivityManager = mock() + whenever(contextMock.getSystemService(any())).thenReturn(connectivityManager) + + networkInfo = mock() + whenever(connectivityManager.activeNetworkInfo).thenReturn(networkInfo) + + buildInfo = mock() + whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) + + network = mock() + whenever(connectivityManager.activeNetwork).thenReturn(network) + + networkCapabilities = mock() + whenever(connectivityManager.getNetworkCapabilities(any())).thenReturn(networkCapabilities) + } + + @Test + fun `When network is active and connected with permission, return CONNECTED for isConnected`() { + whenever(networkInfo.isConnected).thenReturn(true) + assertEquals(ConnectivityChecker.Status.CONNECTED, ConnectivityChecker.getConnectionStatus(contextMock, mock())) + } + + @Test + fun `When network is active but not connected with permission, return NOT_CONNECTED for isConnected`() { + whenever(networkInfo.isConnected).thenReturn(false) + + assertEquals(ConnectivityChecker.Status.NOT_CONNECTED, + ConnectivityChecker.getConnectionStatus(contextMock, mock())) + } + + @Test + fun `When there's no permission, return NO_PERMISSION for isConnected`() { + whenever(contextMock.checkPermission(any(), any(), any())).thenReturn(PERMISSION_DENIED) + + assertEquals(ConnectivityChecker.Status.NO_PERMISSION, + ConnectivityChecker.getConnectionStatus(contextMock, mock())) } @Test - fun `isConnected won't throw exception`() { - ConnectivityChecker.isConnected(context, mock()) + fun `When network is not active, return NOT_CONNECTED for isConnected`() { + assertEquals(ConnectivityChecker.Status.NOT_CONNECTED, + ConnectivityChecker.getConnectionStatus(contextMock, mock())) + } + + @Test + fun `When ConnectivityManager is not available, return UNKNOWN for isConnected`() { + assertEquals(ConnectivityChecker.Status.UNKNOWN, + ConnectivityChecker.getConnectionStatus(mock(), mock())) + } + + @Test + fun `When ConnectivityManager is not available, return null for getConnectionType`() { + assertNull(ConnectivityChecker.getConnectionType(mock(), mock(), buildInfo)) + } + + @Test + fun `When sdkInfoVersion is not min Marshmallow, return null for getConnectionType`() { + val buildInfo = mock() + whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + + assertNull(ConnectivityChecker.getConnectionType(mock(), mock(), buildInfo)) + } + + @Test + fun `When there's no permission, return null for getConnectionType`() { + whenever(contextMock.checkPermission(any(), any(), any())).thenReturn(PERMISSION_DENIED) + + assertNull(ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) } @Test - fun `getConnectionType won't throw exception`() { - ConnectivityChecker.getConnectionType(context, mock()) + fun `When network is not active, return null for getConnectionType`() { + whenever(contextMock.getSystemService(any())).thenReturn(connectivityManager) + + assertNull(ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network capabilities are not available, return null for getConnectionType`() { + assertNull(ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network capabilities has TRANSPORT_WIFI, return wifi`() { + whenever(networkCapabilities.hasTransport(eq(TRANSPORT_WIFI))).thenReturn(true) + + assertEquals("wifi", ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network is TYPE_WIFI, return wifi`() { + whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + whenever(networkInfo.type).thenReturn(TYPE_WIFI) + + assertEquals("wifi", ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network capabilities has TRANSPORT_ETHERNET, return ethernet`() { + whenever(networkCapabilities.hasTransport(eq(TRANSPORT_ETHERNET))).thenReturn(true) + + assertEquals("ethernet", ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network is TYPE_ETHERNET, return ethernet`() { + whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + whenever(networkInfo.type).thenReturn(TYPE_ETHERNET) + + assertEquals("ethernet", ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network capabilities has TRANSPORT_CELLULAR, return cellular`() { + whenever(networkCapabilities.hasTransport(eq(TRANSPORT_CELLULAR))).thenReturn(true) + + assertEquals("cellular", ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) + } + + @Test + fun `When network is TYPE_MOBILE, return cellular`() { + whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + whenever(networkInfo.type).thenReturn(TYPE_MOBILE) + + assertEquals("cellular", ConnectivityChecker.getConnectionType(contextMock, mock(), buildInfo)) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index a1c62478b..713aa4a2e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -34,6 +34,7 @@ class DefaultAndroidEventProcessorTest { private lateinit var context: Context private class Fixture { + val buildInfo = mock() val options = SentryOptions().apply { isDebug = true setLogger(mock()) @@ -49,7 +50,7 @@ class DefaultAndroidEventProcessorTest { @Test fun `when instance is created, application context reference is stored`() { - val sut = DefaultAndroidEventProcessor(context, fixture.options) + val sut = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) assertEquals(sut.context, context) } @@ -57,22 +58,30 @@ class DefaultAndroidEventProcessorTest { @Test fun `when null context is provided, invalid argument is thrown`() { val clazz = Class.forName("io.sentry.android.core.DefaultAndroidEventProcessor") - val ctor = clazz.getConstructor(Context::class.java, SentryOptions::class.java) - val params = arrayOf(null, mock()) + val ctor = clazz.getConstructor(Context::class.java, SentryOptions::class.java, IBuildInfoProvider::class.java) + val params = arrayOf(null, mock(), null) assertFailsWith { ctor.newInstance(params) } } @Test fun `when null options is provided, invalid argument is thrown`() { val clazz = Class.forName("io.sentry.android.core.DefaultAndroidEventProcessor") - val ctor = clazz.getConstructor(Context::class.java, SentryOptions::class.java) - val params = arrayOf(mock(), null) + val ctor = clazz.getConstructor(Context::class.java, SentryOptions::class.java, IBuildInfoProvider::class.java) + val params = arrayOf(mock(), null, null) + assertFailsWith { ctor.newInstance(params) } + } + + @Test + fun `when null buildInfo is provided, invalid argument is thrown`() { + val clazz = Class.forName("io.sentry.android.core.DefaultAndroidEventProcessor") + val ctor = clazz.getConstructor(Context::class.java, SentryOptions::class.java, IBuildInfoProvider::class.java) + val params = arrayOf(null, null, mock()) assertFailsWith { ctor.newInstance(params) } } @Test fun `When hint is not Cached, data should be applied`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) var event = SentryEvent().apply { } // refactor and mock data later on @@ -86,7 +95,7 @@ class DefaultAndroidEventProcessorTest { @Test fun `Current should be true if it comes from main thread`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) val sentryThread = SentryThread().apply { id = Looper.getMainLooper().thread.id } @@ -100,7 +109,7 @@ class DefaultAndroidEventProcessorTest { @Test fun `Current should be false if it its not the main thread`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) val sentryThread = SentryThread().apply { id = 10L } @@ -114,7 +123,7 @@ class DefaultAndroidEventProcessorTest { @Test fun `When hint is Cached, data should not be applied`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) var event = SentryEvent().apply { } // refactor and mock data later on @@ -129,7 +138,7 @@ class DefaultAndroidEventProcessorTest { @Test fun `Executor service should be called on ctor`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) val contextData = processor.contextData.get() assertNotNull(contextData) assertEquals("test", (contextData[PROGUARD_UUID] as Array<*>)[0]) @@ -141,14 +150,14 @@ class DefaultAndroidEventProcessorTest { @Test fun `Processor won't throw exception`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) processor.process(SentryEvent(), null) verify((fixture.options.logger as DiagnosticLogger).logger, never())!!.log(eq(SentryLevel.ERROR), any(), any()) } @Test fun `Processor won't throw exception when theres a hint`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options) + val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo) processor.process(SentryEvent(), CachedEvent()) verify((fixture.options.logger as DiagnosticLogger).logger, never())!!.log(eq(SentryLevel.ERROR), any(), any()) }