diff --git a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt index 3495d3a71f0..1bdeb7d369c 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt @@ -3,17 +3,21 @@ package io.sentry.android.sqlite import android.database.CrossProcessCursor import android.database.SQLException import io.sentry.IScopes +import io.sentry.ISpan +import io.sentry.Instrumenter import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage +import io.sentry.SentryStackTraceFactory +import io.sentry.SpanDataConvention import io.sentry.SpanStatus -import io.sentry.sqlite.SQLiteSpanInstrumentation + +private const val TRACE_ORIGIN = "auto.db.sqlite" internal class SQLiteSpanManager( private val scopes: IScopes = ScopesAdapter.getInstance(), - databaseName: String? = null, + private val databaseName: String? = null, ) { - - private val spans = SQLiteSpanInstrumentation.fromDatabaseName(databaseName, scopes) + private val stackTraceFactory = SentryStackTraceFactory(scopes.options) init { SentryIntegrationPackageStorage.getInstance().addIntegration("SQLite") @@ -29,8 +33,8 @@ internal class SQLiteSpanManager( @Suppress("TooGenericExceptionCaught", "UNCHECKED_CAST") @Throws(SQLException::class) fun performSql(sql: String, operation: () -> T): T { - val startTimestamp = spans.startTimestamp() - + val startTimestamp = scopes.getOptions().dateProvider.now() + var span: ISpan? = null return try { val result = operation() /* @@ -41,11 +45,34 @@ internal class SQLiteSpanManager( if (result is CrossProcessCursor) { return SentryCrossProcessCursor(result, this, sql) as T } - spans.recordSpan(sql, startTimestamp, SpanStatus.OK) + span = scopes.span?.startChild("db.sql.query", sql, startTimestamp, Instrumenter.SENTRY) + span?.spanContext?.origin = TRACE_ORIGIN + span?.status = SpanStatus.OK result } catch (e: Throwable) { - spans.recordSpan(sql, startTimestamp, SpanStatus.INTERNAL_ERROR, e) + span = scopes.span?.startChild("db.sql.query", sql, startTimestamp, Instrumenter.SENTRY) + span?.spanContext?.origin = TRACE_ORIGIN + span?.status = SpanStatus.INTERNAL_ERROR + span?.throwable = e throw e + } finally { + span?.apply { + val isMainThread: Boolean = scopes.options.threadChecker.isMainThread + setData(SpanDataConvention.BLOCKED_MAIN_THREAD_KEY, isMainThread) + if (isMainThread) { + setData(SpanDataConvention.CALL_STACK_KEY, stackTraceFactory.inAppCallStack) + } + // if db name is null, then it's an in-memory database as per + // https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteOpenHelper.kt;l=38-42 + if (databaseName != null) { + setData(SpanDataConvention.DB_SYSTEM_KEY, "sqlite") + setData(SpanDataConvention.DB_NAME_KEY, databaseName) + } else { + setData(SpanDataConvention.DB_SYSTEM_KEY, "in-memory") + } + + finish() + } } } } diff --git a/sentry-android-sqlite/src/main/java/io/sentry/sqlite/DbMetadata.kt b/sentry-android-sqlite/src/main/java/io/sentry/sqlite/DbMetadata.kt index aa3c186b6d9..598dc524ed1 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/sqlite/DbMetadata.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/sqlite/DbMetadata.kt @@ -36,14 +36,3 @@ internal fun dbMetadataFromFileName(fileName: String): DbMetadata { val basename = if (index >= 0) trimmed.substring(index + 1) else trimmed return DbMetadata(name = basename.ifEmpty { null }, system = DB_SYSTEM_SQLITE) } - -/** - * Returns metadata based on - * [SupportSQLiteOpenHelper.databaseName][androidx.sqlite.db.SupportSQLiteOpenHelper.databaseName]. - */ -internal fun dbMetadataFromDatabaseName(databaseName: String?): DbMetadata = - if (databaseName == null) { - DbMetadata(name = null, system = DB_SYSTEM_IN_MEMORY) - } else { - DbMetadata(name = databaseName, system = DB_SYSTEM_SQLITE) - } diff --git a/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SQLiteSpanInstrumentation.kt b/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SQLiteSpanInstrumentation.kt index 4c925198bd5..5099f38f691 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SQLiteSpanInstrumentation.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SQLiteSpanInstrumentation.kt @@ -1,17 +1,26 @@ package io.sentry.sqlite import io.sentry.IScopes +import io.sentry.ISpan import io.sentry.Instrumenter import io.sentry.ScopesAdapter import io.sentry.SentryDate import io.sentry.SentryLongDate +import io.sentry.SentryNanotimeDate import io.sentry.SentryStackTraceFactory import io.sentry.SpanDataConvention import io.sentry.SpanStatus +import java.util.Date private const val SQLITE_TRACE_ORIGIN = "auto.db.sqlite" -/** Shared span instrumentation for SQLite. */ +/** + * Sentinel for extracting a [SentryNanotimeDate]'s underlying [System.nanoTime] value via + * [SentryDate.diff]. + */ +private val EMPTY_NANO_TIME = SentryNanotimeDate(Date(0), 0L) + +/** Span instrumentation for [SentrySQLiteDriver]. */ internal class SQLiteSpanInstrumentation( private val scopes: IScopes, private val dbMetadata: DbMetadata, @@ -20,44 +29,32 @@ internal class SQLiteSpanInstrumentation( private val stackTraceFactory = SentryStackTraceFactory(scopes.options) /** - * Returns a start timestamp for a `db.sql.query` span. + * Returns a timestamp in nanoseconds for use with [recordSpan]. Timestamp is ns-precise if the + * active parent span uses a [SentryNanotimeDate] (the ordinary case); otherwise it's ms-precise. * - * Exposed so callers can capture a wall-clock start before accumulating database time. - * Internalizing the start time in [recordSpan] would shift spans to end-of-work on the trace - * timeline, which is less desirable. + * Note: Internalizing the start time in [recordSpan] would shift spans to end-of-work on the + * trace timeline, which is less desirable; callers capture the start before doing database work + * and pass it back to [recordSpan]. */ - fun startTimestamp(): SentryDate = scopes.options.dateProvider.now() - - /** Records a `db.sql.query` span from [startTimestamp] to the moment of invocation. */ - fun recordSpan( - sql: String, - startTimestamp: SentryDate, - status: SpanStatus, - throwable: Throwable? = null, - ) { - recordSpan(sql, startTimestamp, endTimestamp = null, status, throwable) - } + fun startTimestamp(): Long = + // Try to retain nanosecond precision + avoid SentryDate allocation... + scopes.span?.computeNanoStartTimestampForChild() + // ...otherwise fall back to millisecond precision + allocate. + ?: scopes.options.dateProvider.now().nanoTimestamp() - /** Records a `db.sql.query` span from [startTimestamp] to [startTimestamp] + [durationNanos]. */ + /** Records a `db.sql.query` span. */ fun recordSpan( sql: String, - startTimestamp: SentryDate, + startTimestampNanos: Long, durationNanos: Long, status: SpanStatus, throwable: Throwable? = null, ) { - val endTimestamp = SentryLongDate(startTimestamp.nanoTimestamp() + durationNanos) - recordSpan(sql, startTimestamp, endTimestamp, status, throwable) - } + val parent = scopes.span ?: return + val startTimestamp = SentryLongDate(startTimestampNanos) + val endTimestamp = SentryLongDate(startTimestampNanos + durationNanos) - private fun recordSpan( - sql: String, - startTimestamp: SentryDate, - endTimestamp: SentryDate?, - status: SpanStatus, - throwable: Throwable?, - ) { - scopes.span?.startChild("db.sql.query", sql, startTimestamp, Instrumenter.SENTRY)?.apply { + parent.startChild("db.sql.query", sql, startTimestamp, Instrumenter.SENTRY).apply { spanContext.origin = SQLITE_TRACE_ORIGIN throwable?.let { this.throwable = it } @@ -85,15 +82,43 @@ internal class SQLiteSpanInstrumentation( scopes: IScopes = ScopesAdapter.getInstance(), ): SQLiteSpanInstrumentation = SQLiteSpanInstrumentation(scopes, dbMetadataFromFileName(fileName)) + } +} - /** - * Returns [SQLiteSpanInstrumentation] based on - * [SupportSQLiteOpenHelper.databaseName][androidx.sqlite.db.SupportSQLiteOpenHelper.databaseName]. - */ - fun fromDatabaseName( - databaseName: String?, - scopes: IScopes = ScopesAdapter.getInstance(), - ): SQLiteSpanInstrumentation = - SQLiteSpanInstrumentation(scopes, dbMetadataFromDatabaseName(databaseName)) +/** + * Computes a start timestamp with nanosecond precision for the child of the receiver span. Returns + * null if nanosecond precision isn't possible. + * + * Lets us improve the display of spans in the Sentry UI. If timestamps are only ms-precise, the + * Sentry UI will left-align and arbitrarily reorder spans that share the same wall clock ms: + * ``` + * (Relative start times out of order) + * ↓ + * Parent span ├█████████████┤ + * END TRANSACTION ├███┤ 0.33 ms + * BEGIN IMMEDIATE TRANSACTION ├████┤ 0.02 ms + * INSERT INTO `my_db` … ├██┤ 0.30 ms + * ↑ + * (All spans share the same ms baseline + * even though their execution was staggered) + * ``` + * + * Nanosecond precision ensures proper ordering and lets the spans stagger: + * ``` + * Parent span ├█████████████┤ + * BEGIN IMMEDIATE TRANSACTION ├████┤ 0.02 ms + * INSERT INTO `my_db` … ├██┤ 0.30 ms + * END TRANSACTION ├███┤ 0.33 ms + * ``` + */ +internal fun ISpan.computeNanoStartTimestampForChild(): Long? { + if (startDate !is SentryNanotimeDate) { + return null } + + val parentWallClockNanos = startDate.nanoTimestamp() + val parentMonotonicNanos = startDate.diff(EMPTY_NANO_TIME) + val elapsedSinceParentStart = System.nanoTime() - parentMonotonicNanos + // Return the child's absolute start time. + return parentWallClockNanos + elapsedSinceParentStart } diff --git a/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SentrySQLiteStatement.kt b/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SentrySQLiteStatement.kt index 41df37444b5..a739a396bcb 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SentrySQLiteStatement.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/sqlite/SentrySQLiteStatement.kt @@ -1,7 +1,6 @@ package io.sentry.sqlite import androidx.sqlite.SQLiteStatement -import io.sentry.SentryDate import io.sentry.SpanStatus /** @@ -22,7 +21,7 @@ internal class SentrySQLiteStatement( private val nanoTimeProvider: () -> Long = { System.nanoTime() }, ) : SQLiteStatement by delegate { - private var firstStepTimestamp: SentryDate? = null + private var firstStepTimestampNanos: Long? = null private var accumulatedDbNanos: Long = 0L private var stepsComplete = false private var closed = false @@ -35,8 +34,8 @@ internal class SentrySQLiteStatement( val beforeNanos = nanoTimeProvider() return try { - if (firstStepTimestamp == null) { - firstStepTimestamp = spans.startTimestamp() + if (firstStepTimestampNanos == null) { + firstStepTimestampNanos = spans.startTimestamp() } stepsComplete = !delegate.step() @@ -71,10 +70,10 @@ internal class SentrySQLiteStatement( } private fun recordSpan(status: SpanStatus, throwable: Throwable? = null) { - val start = firstStepTimestamp ?: return + val startNanos = firstStepTimestampNanos ?: return val duration = accumulatedDbNanos - firstStepTimestamp = null + firstStepTimestampNanos = null accumulatedDbNanos = 0L - spans.recordSpan(sql, start, duration, status, throwable) + spans.recordSpan(sql, startNanos, duration, status, throwable) } } diff --git a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/ComputeNanoStartTimestampForChildTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/ComputeNanoStartTimestampForChildTest.kt new file mode 100644 index 00000000000..92a98b6e56d --- /dev/null +++ b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/ComputeNanoStartTimestampForChildTest.kt @@ -0,0 +1,100 @@ +package io.sentry.sqlite + +import io.sentry.DateUtils +import io.sentry.ISpan +import io.sentry.SentryLongDate +import io.sentry.SentryNanotimeDate +import java.util.Date +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +class ComputeNanoStartTimestampForChildTest { + + @Test + fun `returns parent wall clock plus elapsed monotonic time since parent started`() { + val wallClockMillis = 1_000_000L + val elapsedNanos = 500_000L + val parentMonotonicNanos = System.nanoTime() - elapsedNanos + val span = spanWithNanotimeStart(wallClockMillis, parentMonotonicNanos) + + val timestamp = span.computeNanoStartTimestampForChild()!! + + val elapsedSinceParentStart = timestamp - DateUtils.millisToNanos(wallClockMillis) + assertTrue(elapsedSinceParentStart >= elapsedNanos) + assertTrue(elapsedSinceParentStart < elapsedNanos + TEST_SLACK_NANOS) + } + + @Test + fun `same millisecond wall clocks with different monotonic offsets produce distinct ordered timestamps`() { + val wallClockMillis = 1_000_000L + val wallClockNanos = DateUtils.millisToNanos(wallClockMillis) + val earlierParentMonotonicNanos = System.nanoTime() - 200_000L + val laterParentMonotonicNanos = System.nanoTime() - 800_000L + val earlierSpan = spanWithNanotimeStart(wallClockMillis, earlierParentMonotonicNanos) + val laterSpan = spanWithNanotimeStart(wallClockMillis, laterParentMonotonicNanos) + + assertEquals( + earlierSpan.startDate.nanoTimestamp(), + laterSpan.startDate.nanoTimestamp(), + "Raw parent timestamps share the same ms-quantized value", + ) + + val earlier = earlierSpan.computeNanoStartTimestampForChild()!! + val later = laterSpan.computeNanoStartTimestampForChild()!! + + assertTrue(earlier > wallClockNanos) + assertTrue(later > wallClockNanos) + assertTrue(earlier < later) + assertTrue(later - earlier >= 500_000L) + } + + @Test + fun `returns parent wall clock when no monotonic time has elapsed since parent started`() { + val wallClockMillis = 1_000_000L + val parentMonotonicNanos = System.nanoTime() + val span = spanWithNanotimeStart(wallClockMillis, parentMonotonicNanos) + + val elapsedSinceParentStart = + span.computeNanoStartTimestampForChild()!! - DateUtils.millisToNanos(wallClockMillis) + assertTrue(elapsedSinceParentStart >= 0L) + assertTrue(elapsedSinceParentStart < TEST_SLACK_NANOS) + } + + @Test + fun `works when parent wall clock differs from millisecond baseline`() { + val wallClockMillis = 1_000_001L + val elapsedNanos = 1_500_000L + val parentMonotonicNanos = System.nanoTime() - elapsedNanos + val span = spanWithNanotimeStart(wallClockMillis, parentMonotonicNanos) + + val elapsedSinceParentStart = + span.computeNanoStartTimestampForChild()!! - DateUtils.millisToNanos(wallClockMillis) + assertTrue(elapsedSinceParentStart >= elapsedNanos) + assertTrue(elapsedSinceParentStart < elapsedNanos + TEST_SLACK_NANOS) + } + + @Test + fun `returns null when start date is not SentryNanotimeDate`() { + val span = mock() + whenever(span.startDate).thenReturn(SentryLongDate(DateUtils.millisToNanos(1_000_000L))) + + assertNull(span.computeNanoStartTimestampForChild()) + } + + private fun spanWithNanotimeStart(wallClockMillis: Long, parentMonotonicNanos: Long): ISpan { + val startDate = SentryNanotimeDate(Date(wallClockMillis), parentMonotonicNanos) + val span = mock() + whenever(span.startDate).thenReturn(startDate) + return span + } + + companion object { + + // Upper bound for monotonic drift while the test body runs. + private const val TEST_SLACK_NANOS = 50_000_000L + } +} diff --git a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/DbMetadataTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/DbMetadataTest.kt index 227b9d9558c..09d80793ed2 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/DbMetadataTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/DbMetadataTest.kt @@ -13,14 +13,6 @@ class DbMetadataTest { ) } - @Test - fun `dbMetadataFromDatabaseName returns in-memory system with no db name when databaseName is null`() { - assertEquals( - DbMetadata(name = null, system = DB_SYSTEM_IN_MEMORY), - dbMetadataFromDatabaseName(null), - ) - } - @Test fun `dbMetadataFromFileName returns sqlite system and db name for unix path`() { assertEquals( diff --git a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SQLiteSpanInstrumentationTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SQLiteSpanInstrumentationTest.kt index ead123a190b..a38be242ec5 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SQLiteSpanInstrumentationTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SQLiteSpanInstrumentationTest.kt @@ -1,15 +1,21 @@ package io.sentry.sqlite import io.sentry.IScopes +import io.sentry.ISpan +import io.sentry.SentryDateProvider +import io.sentry.SentryLongDate +import io.sentry.SentryNanotimeDate import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention import io.sentry.SpanStatus import io.sentry.TransactionContext import io.sentry.util.thread.IThreadChecker +import java.util.Date import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertIs import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue @@ -40,6 +46,63 @@ class SQLiteSpanInstrumentationTest { private val fixture = Fixture() + @Test + fun `startTimestamp is ns-precise and skips date provider when parent uses SentryNanotimeDate`() { + // Only the parent date is queued. If startTimestamp() were to call dateProvider.now(), + // the queue would underflow and the test would fail loudly — this is what verifies the + // optimization is in effect. + val parentDate = SentryNanotimeDate(Date(1_000_000L), 100_000_000L) + val sut = setUpWithNanotimeDates(parentDate) + + val start = sut.startTimestamp() + + val durationNanos = 42_000_000L + sut.recordSpan("SELECT 1", start, durationNanos, SpanStatus.OK) + + val span = fixture.sentryTracer.children.first() + + // startTimestamp returns an already-ns-precise value, anchored to the parent's wall clock and + // offset by elapsed System.nanoTime(). The exact ns-math is unit-tested in + // ChildStartTimestampOrNullTest; here we verify the integration shape. + assertIs(span.startDate) + assertEquals(start, span.startDate.nanoTimestamp()) + assertEquals(start + durationNanos, span.finishDate!!.nanoTimestamp()) + } + + @Test + fun `startTimestamp falls back to date provider when parent does not use SentryNanotimeDate`() { + val providerDate = SentryNanotimeDate(Date(2_000_000L), 200_000_000L) + val parentSpan = mock() + whenever(parentSpan.startDate).thenReturn(SentryLongDate(1_000_000_000_000_000L)) + val options = + SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + dateProvider = SentryDateProvider { providerDate } + } + whenever(fixture.scopes.options).thenReturn(options) + whenever(fixture.scopes.span).thenReturn(parentSpan) + + val sut = SQLiteSpanInstrumentation.fromFileName(":memory:", fixture.scopes) + + assertEquals(providerDate.nanoTimestamp(), sut.startTimestamp()) + } + + @Test + fun `startTimestamp falls back to date provider when no transaction is active`() { + val providerDate = SentryNanotimeDate(Date(2_000_000L), 200_000_000L) + val options = + SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + dateProvider = SentryDateProvider { providerDate } + } + whenever(fixture.scopes.options).thenReturn(options) + whenever(fixture.scopes.span).thenReturn(null) + + val sut = SQLiteSpanInstrumentation.fromFileName(":memory:", fixture.scopes) + + assertEquals(providerDate.nanoTimestamp(), sut.startTimestamp()) + } + @Test fun `recordSpan records a span if a transaction is active`() { val sut = fixture.getSut(isTransactionActive = true) @@ -79,7 +142,6 @@ class SQLiteSpanInstrumentationTest { sut.recordSpan("SELECT 1", start, durationNanos, SpanStatus.OK) val span = fixture.sentryTracer.children.first() - assertEquals(start, span.startDate) assertEquals(span.startDate.nanoTimestamp() + durationNanos, span.finishDate!!.nanoTimestamp()) } @@ -146,48 +208,16 @@ class SQLiteSpanInstrumentationTest { assertNull(span.getData(SpanDataConvention.CALL_STACK_KEY)) } - @Test - fun `recordSpan without a duration finishes the span at the time of invocation`() { - val sut = fixture.getSut() - val start = sut.startTimestamp() - - sut.recordSpan("SELECT 1", start, SpanStatus.OK) - - val span = fixture.sentryTracer.children.first() - assertTrue(span.isFinished) - assertEquals(SpanStatus.OK, span.status) - // Unlike the duration overload, no synthetic end timestamp is supplied; the span finishes at - // "now", i.e. at or after its start. - assertTrue(span.finishDate!!.nanoTimestamp() >= start.nanoTimestamp()) - } - - @Test - fun `fromFileName sets db name from fileName`() { - val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(fixture.scopes.options).thenReturn(options) - fixture.sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) - whenever(fixture.scopes.span).thenReturn(fixture.sentryTracer) - - val sut = SQLiteSpanInstrumentation.fromFileName("tracks.db", fixture.scopes) - sut.recordSpan("SELECT 1", sut.startTimestamp(), SpanStatus.OK) - - val span = fixture.sentryTracer.children.first() - assertEquals("sqlite", span.data[SpanDataConvention.DB_SYSTEM_KEY]) - assertEquals("tracks.db", span.data[SpanDataConvention.DB_NAME_KEY]) - } - - @Test - fun `fromDatabaseName sets db name from databaseName`() { - val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } + private fun setUpWithNanotimeDates(vararg dates: SentryNanotimeDate): SQLiteSpanInstrumentation { + val dateQueue = ArrayDeque(dates.toList()) + val options = + SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + dateProvider = SentryDateProvider { dateQueue.removeFirst() } + } whenever(fixture.scopes.options).thenReturn(options) fixture.sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) whenever(fixture.scopes.span).thenReturn(fixture.sentryTracer) - - val sut = SQLiteSpanInstrumentation.fromDatabaseName("tracks.db", fixture.scopes) - sut.recordSpan("SELECT 1", sut.startTimestamp(), SpanStatus.OK) - - val span = fixture.sentryTracer.children.first() - assertEquals("sqlite", span.data[SpanDataConvention.DB_SYSTEM_KEY]) - assertEquals("tracks.db", span.data[SpanDataConvention.DB_NAME_KEY]) + return SQLiteSpanInstrumentation.fromFileName(":memory:", fixture.scopes) } } diff --git a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SentrySQLiteStatementTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SentrySQLiteStatementTest.kt index 6691910e358..ce2c3f00cd5 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SentrySQLiteStatementTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/sqlite/SentrySQLiteStatementTest.kt @@ -1,7 +1,6 @@ package io.sentry.sqlite import androidx.sqlite.SQLiteStatement -import io.sentry.SentryLongDate import io.sentry.SpanStatus import java.util.concurrent.atomic.AtomicLong import kotlin.test.Test @@ -21,11 +20,11 @@ class SentrySQLiteStatementTest { private class Fixture { val mockStatement = mock() val mockSpans = mock() - val startDate = SentryLongDate(1_000_000_000_000L) + val startTimestampNanos = 1_000_000_000_000L val fakeClock = AtomicLong(0L) fun getSut(sql: String): SentrySQLiteStatement { - whenever(mockSpans.startTimestamp()).thenReturn(startDate) + whenever(mockSpans.startTimestamp()).thenReturn(startTimestampNanos) return SentrySQLiteStatement(mockStatement, mockSpans, sql, fakeClock::getAndIncrement) } } @@ -43,7 +42,7 @@ class SentrySQLiteStatementTest { verify(fixture.mockSpans) .recordSpan( eq("SELECT * FROM users"), - eq(fixture.startDate), + eq(fixture.startTimestampNanos), any(), eq(SpanStatus.OK), anyOrNull(), @@ -61,7 +60,7 @@ class SentrySQLiteStatementTest { verify(fixture.mockSpans) .recordSpan( eq("BAD SQL"), - eq(fixture.startDate), + eq(fixture.startTimestampNanos), any(), eq(SpanStatus.INTERNAL_ERROR), eq(exception),