Skip to content
Prev Previous commit
Next Next commit
Fix tests
  • Loading branch information
markushi committed Oct 9, 2024
commit 40cbfea12a0eced8214dd082c040af309ef03d20
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.sentry.IHub;
import io.sentry.SentryLevel;
import io.sentry.Session;
import io.sentry.android.core.internal.util.BreadcrumbFactory;
import io.sentry.transport.CurrentDateProvider;
import io.sentry.transport.ICurrentDateProvider;
import java.util.Timer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
public final class NetworkBreadcrumbsIntegration implements Integration, Closeable {

private final @NotNull Context context;

private final @NotNull BuildInfoProvider buildInfoProvider;

private final @NotNull ILogger logger;
private volatile boolean isClosed;
private @Nullable SentryOptions options;

@TestOnly @Nullable NetworkBreadcrumbsNetworkCallback networkCallback;
@TestOnly @Nullable volatile NetworkBreadcrumbsNetworkCallback networkCallback;

public NetworkBreadcrumbsIntegration(
final @NotNull Context context,
Expand All @@ -63,40 +63,71 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio
"NetworkBreadcrumbsIntegration enabled: %s",
androidOptions.isEnableNetworkEventBreadcrumbs());

this.options = options;

if (androidOptions.isEnableNetworkEventBreadcrumbs()) {

// The specific error is logged in the ConnectivityChecker method
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP) {
networkCallback = null;
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration requires Android 5+");
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.N) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this to N, as our AndroidConnectionStatusProvider.registerNetworkCallback itself only supports >= N

logger.log(SentryLevel.DEBUG, "NetworkCallbacks need Android N+.");
return;
}

networkCallback =
new NetworkBreadcrumbsNetworkCallback(hub, buildInfoProvider, options.getDateProvider());
final boolean registered =
AndroidConnectionStatusProvider.registerNetworkCallback(
context, logger, buildInfoProvider, networkCallback);

// The specific error is logged in the ConnectivityChecker method
if (!registered) {
networkCallback = null;
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration not installed.");
return;
try {
options
.getExecutorService()
.submit(
new Runnable() {
@Override
public void run() {
// in case integration is closed before the task is executed, simply return
final @Nullable NetworkBreadcrumbsNetworkCallback callback = networkCallback;
if (isClosed || callback == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was thinking if we should synchronize here, but since we use the same thread (executorService), I guess it should be fine, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was thinking the same, I've guarded it with synchronized now

networkCallback = null;
return;
}

final boolean registered =
AndroidConnectionStatusProvider.registerNetworkCallback(
context, logger, buildInfoProvider, callback);
if (registered) {
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration installed.");
addIntegrationToSdkVersion(getClass());
} else {
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration not installed.");
// The specific error is logged by AndroidConnectionStatusProvider
}
}
});
} catch (Throwable t) {
logger.log(SentryLevel.ERROR, "Error submitting NetworkBreadcrumbsIntegration task.", t);
}
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration installed.");
addIntegrationToSdkVersion(getClass());
}
}

@Override
public void close() throws IOException {
if (networkCallback != null) {
AndroidConnectionStatusProvider.unregisterNetworkCallback(
context, logger, buildInfoProvider, networkCallback);
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration remove.");
isClosed = true;

try {
Objects.requireNonNull(options, "Options is required")
.getExecutorService()
.submit(
() -> {
final @Nullable NetworkBreadcrumbsNetworkCallback callback = networkCallback;
if (callback != null) {
AndroidConnectionStatusProvider.unregisterNetworkCallback(
context, logger, buildInfoProvider, callback);
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration removed.");
}
networkCallback = null;
});
} catch (Throwable t) {
logger.log(SentryLevel.ERROR, "Error submitting NetworkBreadcrumbsIntegration task.", t);
}
networkCallback = null;
}

@SuppressLint("ObsoleteSdkInt")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.Session;
import io.sentry.android.core.internal.util.BreadcrumbFactory;
import io.sentry.android.core.performance.AppStartMetrics;
import io.sentry.android.core.performance.TimeSpan;
import io.sentry.android.fragment.FragmentLifecycleIntegration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,49 +137,6 @@ class LifecycleWatcherTest {
verify(fixture.hub, never()).endSession()
}

@Test
fun `When session tracking is enabled, add breadcrumb on start`() {
val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false)
watcher.onStart(fixture.ownerMock)
verify(fixture.hub).addBreadcrumb(
check<Breadcrumb> {
assertEquals("app.lifecycle", it.category)
assertEquals("session", it.type)
assertEquals(SentryLevel.INFO, it.level)
// cant assert data, its not a public API
}
)
}

@Test
fun `When session tracking is enabled, add breadcrumb on stop`() {
val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false)
watcher.onStop(fixture.ownerMock)
verify(fixture.hub, timeout(10000)).endSession()
verify(fixture.hub).addBreadcrumb(
check<Breadcrumb> {
assertEquals("app.lifecycle", it.category)
assertEquals("session", it.type)
assertEquals(SentryLevel.INFO, it.level)
// cant assert data, its not a public API
}
)
}

@Test
fun `When session tracking is disabled, do not add breadcrumb on start`() {
val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false)
watcher.onStart(fixture.ownerMock)
verify(fixture.hub, never()).addBreadcrumb(any<Breadcrumb>())
}

@Test
fun `When session tracking is disabled, do not add breadcrumb on stop`() {
val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false)
watcher.onStop(fixture.ownerMock)
verify(fixture.hub, never()).addBreadcrumb(any<Breadcrumb>())
}

@Test
fun `When app lifecycle breadcrumbs is enabled, add breadcrumb on start`() {
val watcher = fixture.getSUT(enableAutoSessionTracking = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import android.os.Build
import io.sentry.Breadcrumb
import io.sentry.DateUtils
import io.sentry.IHub
import io.sentry.ISentryExecutorService
import io.sentry.SentryDateProvider
import io.sentry.SentryLevel
import io.sentry.SentryNanotimeDate
import io.sentry.TypeCheckHint
import io.sentry.android.core.NetworkBreadcrumbsIntegration.NetworkBreadcrumbConnectionDetail
import io.sentry.android.core.NetworkBreadcrumbsIntegration.NetworkBreadcrumbsNetworkCallback
import io.sentry.test.DeferredExecutorService
import io.sentry.test.ImmediateExecutorService
import org.mockito.kotlin.KInOrder
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
Expand Down Expand Up @@ -47,14 +50,22 @@ class NetworkBreadcrumbsIntegrationTest {

init {
whenever(mockBuildInfoProvider.sdkInfoVersion).thenReturn(Build.VERSION_CODES.N)
whenever(context.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(connectivityManager)
whenever(context.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(
connectivityManager
)
}

fun getSut(enableNetworkEventBreadcrumbs: Boolean = true, buildInfo: BuildInfoProvider = mockBuildInfoProvider): NetworkBreadcrumbsIntegration {
fun getSut(
enableNetworkEventBreadcrumbs: Boolean = true,
buildInfo: BuildInfoProvider = mockBuildInfoProvider,
executor: ISentryExecutorService = ImmediateExecutorService()
): NetworkBreadcrumbsIntegration {
options = SentryAndroidOptions().apply {
executorService = executor
isEnableNetworkEventBreadcrumbs = enableNetworkEventBreadcrumbs
dateProvider = SentryDateProvider {
val nowNanos = TimeUnit.MILLISECONDS.toNanos(nowMs ?: System.currentTimeMillis())
val nowNanos =
TimeUnit.MILLISECONDS.toNanos(nowMs ?: System.currentTimeMillis())
SentryNanotimeDate(DateUtils.nanosToDate(nowNanos), nowNanos)
}
}
Expand Down Expand Up @@ -117,7 +128,10 @@ class NetworkBreadcrumbsIntegrationTest {
sut.register(fixture.hub, fixture.options)
sut.close()

verify(fixture.connectivityManager, never()).unregisterNetworkCallback(any<NetworkCallback>())
verify(
fixture.connectivityManager,
never()
).unregisterNetworkCallback(any<NetworkCallback>())
assertNull(sut.networkCallback)
}

Expand Down Expand Up @@ -482,11 +496,27 @@ class NetworkBreadcrumbsIntegrationTest {
}
}

@Test
fun `If integration is opened and closed immediately it still properly unregisters`() {
val executor = DeferredExecutorService()
val sut = fixture.getSut(executor = executor)

sut.register(fixture.hub, fixture.options)
sut.close()

executor.runAll()

assertNull(sut.networkCallback)
verify(fixture.connectivityManager, never()).registerDefaultNetworkCallback(any<NetworkCallback>())
verify(fixture.connectivityManager, never()).unregisterNetworkCallback(any<NetworkCallback>())
}

private fun KInOrder.verifyBreadcrumbInOrder(check: (detail: NetworkBreadcrumbConnectionDetail) -> Unit) {
verify(fixture.hub, times(1)).addBreadcrumb(
any<Breadcrumb>(),
check {
val connectionDetail = it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail
val connectionDetail =
it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail
check(connectionDetail)
}
)
Expand All @@ -496,7 +526,8 @@ class NetworkBreadcrumbsIntegrationTest {
verify(fixture.hub).addBreadcrumb(
any<Breadcrumb>(),
check {
val connectionDetail = it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail
val connectionDetail =
it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail
check(connectionDetail)
}
)
Expand All @@ -516,9 +547,13 @@ class NetworkBreadcrumbsIntegrationTest {
whenever(capabilities.linkUpstreamBandwidthKbps).thenReturn(upstreamBandwidthKbps)
whenever(capabilities.signalStrength).thenReturn(signalStrength)
whenever(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)).thenReturn(isVpn)
whenever(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)).thenReturn(isEthernet)
whenever(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)).thenReturn(
isEthernet
)
whenever(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)).thenReturn(isWifi)
whenever(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(isCellular)
whenever(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(
isCellular
)
return capabilities
}

Expand Down