Skip to content

Commit

Permalink
chore(internal): Add snapshot view hierarchy as byte array (#2508)
Browse files Browse the repository at this point in the history
  • Loading branch information
krystofwoldrich authored Feb 2, 2023
1 parent 658c557 commit 14c083a
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 0 deletions.
1 change: 1 addition & 0 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentr
public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent;
public static fun snapshotViewHierarchy (Landroid/app/Activity;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchy;
public static fun snapshotViewHierarchy (Landroid/view/View;)Lio/sentry/protocol/ViewHierarchy;
public static fun snapshotViewHierarchyAsData (Landroid/app/Activity;Lio/sentry/ISerializer;Lio/sentry/ILogger;)[B
}

public final class io/sentry/android/core/cache/AndroidEnvelopeCache : io/sentry/cache/EnvelopeCache {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import io.sentry.EventProcessor;
import io.sentry.Hint;
import io.sentry.ILogger;
import io.sentry.ISerializer;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.android.core.internal.gestures.ViewUtils;
import io.sentry.protocol.ViewHierarchy;
import io.sentry.protocol.ViewHierarchyNode;
import io.sentry.util.JsonSerializationUtils;
import io.sentry.util.Objects;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -52,6 +54,30 @@ public ViewHierarchyEventProcessor(final @NotNull SentryAndroidOptions options)
return event;
}

@Nullable
public static byte[] snapshotViewHierarchyAsData(
@Nullable Activity activity, @NotNull ISerializer serializer, @NotNull ILogger logger) {
@Nullable ViewHierarchy viewHierarchy = snapshotViewHierarchy(activity, logger);

if (viewHierarchy == null) {
logger.log(SentryLevel.ERROR, "Could not get ViewHierarchy.");
return null;
}

final @Nullable byte[] bytes =
JsonSerializationUtils.bytesFrom(serializer, logger, viewHierarchy);
if (bytes == null) {
logger.log(SentryLevel.ERROR, "Could not serialize ViewHierarchy.");
return null;
}
if (bytes.length < 1) {
logger.log(SentryLevel.ERROR, "Got empty bytes array after serializing ViewHierarchy.");
return null;
}

return bytes;
}

@Nullable
public static ViewHierarchy snapshotViewHierarchy(
@Nullable Activity activity, @NotNull ILogger logger) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,40 @@ import android.view.ViewGroup
import android.view.Window
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.sentry.Hint
import io.sentry.JsonSerializable
import io.sentry.JsonSerializer
import io.sentry.SentryEvent
import io.sentry.protocol.SentryException
import org.junit.runner.RunWith
import org.mockito.invocation.InvocationOnMock
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.io.Writer
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull

@RunWith(AndroidJUnit4::class)
class ViewHierarchyEventProcessorTest {
private class Fixture {
val logger = mock<AndroidLogger>()
val serializer: JsonSerializer = mock {
on(it.serialize(any<JsonSerializable>(), any())).then { invocationOnMock: InvocationOnMock ->
val writer: Writer = invocationOnMock.getArgument(1)
writer.write("mock-data")
writer.flush()
}
}
val emptySerializer: JsonSerializer = mock {
on(it.serialize(any<JsonSerializable>(), any())).then { invocationOnMock: InvocationOnMock ->
val writer: Writer = invocationOnMock.getArgument(1)
writer.flush()
}
}
val activity = mock<Activity>()
val window = mock<Window>()
val view = mock<View>()
Expand Down Expand Up @@ -61,6 +81,29 @@ class ViewHierarchyEventProcessorTest {
fixture = Fixture()
}

@Test
fun `should return a view hierarchy as byte array`() {
val viewHierarchy = ViewHierarchyEventProcessor.snapshotViewHierarchyAsData(
fixture.activity,
fixture.serializer,
fixture.logger
)

assertNotNull(viewHierarchy)
assertFalse(viewHierarchy.isEmpty())
}

@Test
fun `should return null as bytes are empty array`() {
val viewHierarchy = ViewHierarchyEventProcessor.snapshotViewHierarchyAsData(
fixture.activity,
fixture.emptySerializer,
fixture.logger
)

assertNull(viewHierarchy)
}

@Test
fun `when an event errored, the view hierarchy should not attached if the feature is disabled`() {
val (event, hint) = fixture.process(
Expand Down

0 comments on commit 14c083a

Please sign in to comment.