From 55b00a78a8e4c96c00cfab29357417eab0bddae3 Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 09:05:26 -0800 Subject: [PATCH 1/8] no message --- .../replay/capture/CaptureSource.kt | 9 ++ .../replay/capture/TiledSignature.kt | 104 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt index 1923f0f4d..35f51a240 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt @@ -59,6 +59,9 @@ class CaptureSource( style = Paint.Style.FILL } + private val tiledSignatureManager = TiledSignatureManager() + @Volatile + private var tiledSignature: TiledSignature? = null /** * Requests a [CaptureEvent] be taken now. */ @@ -136,6 +139,12 @@ class CaptureSource( } } + val newSignature = tiledSignatureManager.compute(baseResult.bitmap, 64) + if (newSignature != null && newSignature == tiledSignature) { + return@withContext null + } + tiledSignature = newSignature + createCaptureEvent(baseResult.bitmap, rect, timestamp, session) } } diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt new file mode 100644 index 000000000..98c71ec79 --- /dev/null +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt @@ -0,0 +1,104 @@ +package com.launchdarkly.observability.replay.capture + +import android.graphics.Bitmap + +data class TiledSignature( + val tileHashes: LongArray +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TiledSignature + + if (!tileHashes.contentEquals(other.tileHashes)) { + return false + } + + return true + } + + override fun hashCode(): Int { + return tileHashes.contentHashCode() + } +} + +class TiledSignatureManager { + var pixelBuffer: IntArray = IntArray(0) + var tileHashesBuffer: LongArray = LongArray(0) + + fun compute( + bitmap: Bitmap, + tileSize: Int + ): TiledSignature? { + val width = bitmap.width + val height = bitmap.height + if (width <= 0 || height <= 0) { + return null + } + + val pixelsNeeded = width * height + if (pixelBuffer.size < pixelsNeeded) { + pixelBuffer = IntArray(pixelsNeeded) + } + val pixels = pixelBuffer + bitmap.getPixels(pixels, 0, width, 0, 0, width, height) + + val tilesX = (width + tileSize - 1) / tileSize + val tilesY = (width + tileSize - 1) / tileSize + val tileCount = tilesX * tilesY + + if (tileHashesBuffer.size < tileCount) { + tileHashesBuffer = LongArray(tileCount) + } + + val tileHashes = tileHashesBuffer.copyOf() + + var tileIndex = 0 + for(ty in 0 until tilesY) { + val startY = ty * tileSize + val endY = minOf(startY + tileSize, height) + + for(tx in 0 until tilesX) { + val startX = tx * tileSize + val endX = minOf(startX + tileSize, width) + tileHashes[tileIndex] = hashTile( + pixels = pixels, + width = width, + startX = startX, + startY = startY, + endX = endX, + endY = endY + ) + tileIndex++ + } + } + + return TiledSignature(tileHashes) + } + + private fun hashTile( + pixels: IntArray, + width: Int, + startX: Int, + startY: Int, + endX: Int, + endY: Int + ): Long { + var hash: Long = 5163949831757626579 + val prime: Long = 1238197591667094937 // from https://bigprimes.org + for(y in startY until endY) { + val rowOffset = y * width + for(x in startX until endX) { + val argb = pixels[rowOffset + x] + hash = (hash xor (argb and 0xFF).toLong()) * prime + hash = (hash xor ((argb ushr 8) and 0xFF).toLong()) * prime + hash = (hash xor ((argb ushr 16) and 0xFF).toLong()) * prime + hash = (hash xor ((argb ushr 24) and 0xFF).toLong()) * prime + } + } + return hash + } +} + + From 8ddba2461ec2c5a5edf0683784d6fd1c65e68873 Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 09:23:30 -0800 Subject: [PATCH 2/8] fix size bug --- .../observability/replay/capture/TiledSignature.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt index 98c71ec79..ee3cd4fa1 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt @@ -24,13 +24,14 @@ data class TiledSignature( } class TiledSignatureManager { - var pixelBuffer: IntArray = IntArray(0) - var tileHashesBuffer: LongArray = LongArray(0) + private var pixelBuffer: IntArray = IntArray(0) + private var tileHashesBuffer: LongArray = LongArray(0) fun compute( bitmap: Bitmap, tileSize: Int ): TiledSignature? { + if (tileSize <= 0) return null val width = bitmap.width val height = bitmap.height if (width <= 0 || height <= 0) { @@ -45,14 +46,14 @@ class TiledSignatureManager { bitmap.getPixels(pixels, 0, width, 0, 0, width, height) val tilesX = (width + tileSize - 1) / tileSize - val tilesY = (width + tileSize - 1) / tileSize + val tilesY = (height + tileSize - 1) / tileSize val tileCount = tilesX * tilesY if (tileHashesBuffer.size < tileCount) { tileHashesBuffer = LongArray(tileCount) } - val tileHashes = tileHashesBuffer.copyOf() + val tileHashes = tileHashesBuffer var tileIndex = 0 for(ty in 0 until tilesY) { @@ -74,7 +75,8 @@ class TiledSignatureManager { } } - return TiledSignature(tileHashes) + //TODO: optimize memory allocations here to have 2 arrays instead of 1 + return TiledSignature(tileHashes.copyOf()) } private fun hashTile( From 008a41480f0b2b7f061f3e9a85d961ac56cca8a1 Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 09:34:41 -0800 Subject: [PATCH 3/8] Recycle bitmap early --- .../launchdarkly/observability/replay/capture/CaptureSource.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt index 35f51a240..71379f549 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt @@ -141,6 +141,7 @@ class CaptureSource( val newSignature = tiledSignatureManager.compute(baseResult.bitmap, 64) if (newSignature != null && newSignature == tiledSignature) { + baseResult.bitmap.recycle() return@withContext null } tiledSignature = newSignature From 4d5536bfbc23c0095f5cfdc4194e45abbb8e240e Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 10:11:16 -0800 Subject: [PATCH 4/8] Fix feedback --- .../observability/replay/capture/TiledSignature.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt index ee3cd4fa1..3d7edba7a 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt @@ -25,7 +25,6 @@ data class TiledSignature( class TiledSignatureManager { private var pixelBuffer: IntArray = IntArray(0) - private var tileHashesBuffer: LongArray = LongArray(0) fun compute( bitmap: Bitmap, @@ -48,12 +47,7 @@ class TiledSignatureManager { val tilesX = (width + tileSize - 1) / tileSize val tilesY = (height + tileSize - 1) / tileSize val tileCount = tilesX * tilesY - - if (tileHashesBuffer.size < tileCount) { - tileHashesBuffer = LongArray(tileCount) - } - - val tileHashes = tileHashesBuffer + val tileHashes = LongArray(tileCount) var tileIndex = 0 for(ty in 0 until tilesY) { @@ -76,7 +70,7 @@ class TiledSignatureManager { } //TODO: optimize memory allocations here to have 2 arrays instead of 1 - return TiledSignature(tileHashes.copyOf()) + return TiledSignature(tileHashes) } private fun hashTile( From ce45b4200de4e62bcc2d9994b70e05a06e42f2cb Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 12:11:33 -0800 Subject: [PATCH 5/8] address feedback --- .../replay/capture/TiledSignature.kt | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt index 3d7edba7a..c81755362 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt @@ -5,27 +5,31 @@ import android.graphics.Bitmap data class TiledSignature( val tileHashes: LongArray ) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TiledSignature - - if (!tileHashes.contentEquals(other.tileHashes)) { - return false - } - - return true - } - - override fun hashCode(): Int { - return tileHashes.contentHashCode() - } + override fun equals(other: Any?): Boolean = + other is TiledSignature && tileHashes.contentEquals(other.tileHashes) + + override fun hashCode(): Int = tileHashes.contentHashCode() } +/** + * Computes tiled signatures for bitmaps. + * + * This class is intentionally not thread-safe in order to reuse a single internal + * pixel buffer allocation and minimize memory churn and GC pressure. Do not invoke + * methods on the same instance from multiple threads concurrently. If cross-thread + * use is required, create one instance per thread or guard access with external + * synchronization. + */ class TiledSignatureManager { private var pixelBuffer: IntArray = IntArray(0) +/** + * Computes a tiled signature for the given bitmap. Not thread-safe. + * + * @param bitmap The bitmap to compute a signature for. + * @param tileSize The size of the tiles to use for the signature. + * @return The tiled signature. + */ fun compute( bitmap: Bitmap, tileSize: Int @@ -81,8 +85,8 @@ class TiledSignatureManager { endX: Int, endY: Int ): Long { - var hash: Long = 5163949831757626579 - val prime: Long = 1238197591667094937 // from https://bigprimes.org + var hash = 5163949831757626579L + val prime = 1238197591667094937L // from https://bigprimes.org for(y in startY until endY) { val rowOffset = y * width for(x in startX until endX) { @@ -96,5 +100,3 @@ class TiledSignatureManager { return hash } } - - From 50aee557522a51556be323f5ce17fb22286e208c Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 12:40:50 -0800 Subject: [PATCH 6/8] Volatile --- .../launchdarkly/observability/replay/capture/TiledSignature.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt index c81755362..48b9f85d2 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TiledSignature.kt @@ -21,6 +21,7 @@ data class TiledSignature( * synchronization. */ class TiledSignatureManager { + @Volatile private var pixelBuffer: IntArray = IntArray(0) /** From 04c252ebce47fcd8ef5313bb7c9eec2976655442 Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Mon, 1 Dec 2025 12:45:35 -0800 Subject: [PATCH 7/8] Unit tests --- .../replay/capture/CaptureSource.kt | 1 + .../capture/TiledSignatureManagerTest.kt | 122 ++++++++++++++++++ .../observability/testutil/BitmapMocks.kt | 83 ++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt create mode 100644 sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/testutil/BitmapMocks.kt diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt index 71379f549..b6f3e9c7c 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/CaptureSource.kt @@ -142,6 +142,7 @@ class CaptureSource( val newSignature = tiledSignatureManager.compute(baseResult.bitmap, 64) if (newSignature != null && newSignature == tiledSignature) { baseResult.bitmap.recycle() + // the similar bitmap not send return@withContext null } tiledSignature = newSignature diff --git a/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt b/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt new file mode 100644 index 000000000..faa7a7a0f --- /dev/null +++ b/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt @@ -0,0 +1,122 @@ +package com.launchdarkly.observability.replay.capture + +import com.launchdarkly.observability.testutil.mockBitmap +import com.launchdarkly.observability.testutil.withOverlayRect +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test + +class TiledSignatureManagerTest { + + private val RED = 0xFFFF0000.toInt() + private val BLUE = 0xFF0000FF.toInt() + private val WHITE = 0xFFFFFFFF.toInt() + private fun solidPixels(width: Int, height: Int, color: Int): IntArray { + val pixels = IntArray(width * height) + java.util.Arrays.fill(pixels, color) + return pixels + } + + @Test + fun compute_returnsNull_whenTileSizeNonPositive() { + val manager = TiledSignatureManager() + val bitmap = mockBitmap(2, 2, RED) + + assertNull(manager.compute(bitmap, 0)) + assertNull(manager.compute(bitmap, -8)) + } + + @Test + fun compute_returnsSignature_whenValidInputs() { + val manager = TiledSignatureManager() + val bitmap = mockBitmap(4, 4, BLUE) + + val signature = manager.compute(bitmap, 2) + assertNotNull(signature) + // 4x4 with tileSize 2 => 2x2 = 4 tiles + assertEquals(4, signature!!.tileHashes.size) + } + + @Test + fun signatures_equal_forIdenticalContent() { + val manager = TiledSignatureManager() + val a = mockBitmap(8, 8, BLUE) + val b = mockBitmap(8, 8, BLUE) + + val sigA = manager.compute(a, 4) + val sigB = manager.compute(b, 4) + + assertNotNull(sigA) + assertNotNull(sigB) + assertEquals(sigA, sigB) + } + + @Test + fun signatures_differ_forDifferentContent() { + val manager = TiledSignatureManager() + val a = mockBitmap(8, 8, RED) + val b = mockBitmap(8, 8, WHITE) + + val sigA = manager.compute(a, 4) + val sigB = manager.compute(b, 4) + + assertNotNull(sigA) + assertNotNull(sigB) + assertNotEquals(sigA, sigB) + } + + @Test + fun tileCount_matchesExpectedCeilDivision() { + val manager = TiledSignatureManager() + val bmp = mockBitmap(10, 10, RED) + + // tileSize 4 => ceil(10/4)=3 in each dimension => 9 tiles + val sig4 = manager.compute(bmp, 4) + assertNotNull(sig4) + assertEquals(9, sig4!!.tileHashes.size) + + // tileSize 6 => ceil(10/6)=2 in each dimension => 4 tiles + val sig6 = manager.compute(bmp, 6) + assertNotNull(sig6) + assertEquals(4, sig6!!.tileHashes.size) + } + + @Test + fun smallOverlay_changesOnlyAffectedTiles_hashes() { + val manager = TiledSignatureManager() + val width = 12 + val height = 12 + val basePixels = solidPixels(width, height, WHITE) + val overlayPixels = withOverlayRect( + basePixels = basePixels, + imageWidth = width, + imageHeight = height, + color = RED, + left = 8, // touches only the last column of tiles for tileSize=4 + top = 8, // touches only the last row of tiles for tileSize=4 + right = 12, + bottom = 12 + ) + val base = mockBitmap(width, height, basePixels) + val withOverlay = mockBitmap(width, height, overlayPixels) + + val tileSize = 4 + val sigBase = manager.compute(base, tileSize)!! + val sigOverlay = manager.compute(withOverlay, tileSize)!! + + // 12x12 with tile size 4 => 3x3 tiles + assertEquals(9, sigBase.tileHashes.size) + assertEquals(9, sigOverlay.tileHashes.size) + + var diffCount = 0 + for (i in sigBase.tileHashes.indices) { + if (sigBase.tileHashes[i] != sigOverlay.tileHashes[i]) { + diffCount++ + } + } + org.junit.jupiter.api.Assertions.assertNotEquals(0, diffCount) + } +} + diff --git a/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/testutil/BitmapMocks.kt b/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/testutil/BitmapMocks.kt new file mode 100644 index 000000000..1eed604ba --- /dev/null +++ b/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/testutil/BitmapMocks.kt @@ -0,0 +1,83 @@ +package com.launchdarkly.observability.testutil + +import android.graphics.Bitmap +import io.mockk.every +import io.mockk.mockk + +/** + * Creates a MockK-based fake Android Bitmap that returns the provided width/height + * and serves pixels from the given [pixels] array via getPixels. + * + * The [pixels] array must be of size width * height, in row-major order (left-to-right, top-to-bottom). + */ +fun mockBitmap(imageWidth: Int, imageHeight: Int, pixels: IntArray): Bitmap { + require(imageWidth > 0 && imageHeight > 0) { "imageWidth and imageHeight must be positive." } + require(pixels.size == imageWidth * imageHeight) { + "pixels size ${pixels.size} must equal imageWidth*imageHeight=${imageWidth * imageHeight}" + } + + val bmp = mockk() + + every { bmp.width } returns imageWidth + every { bmp.height } returns imageHeight + every { bmp.getPixels(any(), any(), any(), any(), any(), any(), any()) } answers { + val dest = invocation.args[0] as IntArray + val offset = invocation.args[1] as Int + val stride = invocation.args[2] as Int + val x = invocation.args[3] as Int + val y = invocation.args[4] as Int + val w = invocation.args[5] as Int + val h = invocation.args[6] as Int + + for (row in 0 until h) { + val srcRowStart = (y + row) * imageWidth + x + val dstRowStart = offset + row * stride + for (col in 0 until w) { + dest[dstRowStart + col] = pixels[srcRowStart + col] + } + } + Unit + } + + return bmp +} + +/** + * Returns a copy of [basePixels] with a filled rectangle overlay applied. + * + * The rectangle is defined by [left], [top], [right], [bottom] in pixel coordinates and is + * clamped to the image bounds defined by [imageWidth] and [imageHeight]. + */ +fun withOverlayRect( + basePixels: IntArray, + imageWidth: Int, + imageHeight: Int, + color: Int, + left: Int, + top: Int, + right: Int, + bottom: Int +): IntArray { + val out = basePixels.clone() + val clampedLeft = left.coerceIn(0, imageWidth) + val clampedTop = top.coerceIn(0, imageHeight) + val clampedRight = right.coerceIn(0, imageWidth) + val clampedBottom = bottom.coerceIn(0, imageHeight) + for (y in clampedTop until clampedBottom) { + val rowStart = y * imageWidth + for (x in clampedLeft until clampedRight) { + out[rowStart + x] = color + } + } + return out +} + +/** + * Convenience overload to create a solid-color mock Bitmap. + */ +fun mockBitmap(imageWidth: Int, imageHeight: Int, color: Int): Bitmap { + val pixels = IntArray(imageWidth * imageHeight) { color } + return mockBitmap(imageWidth, imageHeight, pixels) +} + + From 9797c02418f887157ad1cd28fb6a5022f49afad7 Mon Sep 17 00:00:00 2001 From: Andrey Belonogov Date: Tue, 2 Dec 2025 09:10:11 -0800 Subject: [PATCH 8/8] Fix style of unit test --- .../replay/capture/TiledSignatureManagerTest.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt b/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt index faa7a7a0f..6e2e9d01f 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/test/kotlin/com/launchdarkly/observability/replay/capture/TiledSignatureManagerTest.kt @@ -2,6 +2,7 @@ package com.launchdarkly.observability.replay.capture import com.launchdarkly.observability.testutil.mockBitmap import com.launchdarkly.observability.testutil.withOverlayRect +import java.util.Arrays import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotEquals import org.junit.jupiter.api.Assertions.assertNotNull @@ -15,12 +16,12 @@ class TiledSignatureManagerTest { private val WHITE = 0xFFFFFFFF.toInt() private fun solidPixels(width: Int, height: Int, color: Int): IntArray { val pixels = IntArray(width * height) - java.util.Arrays.fill(pixels, color) + Arrays.fill(pixels, color) return pixels } @Test - fun compute_returnsNull_whenTileSizeNonPositive() { + fun `compute returns null when tile size is non positive`() { val manager = TiledSignatureManager() val bitmap = mockBitmap(2, 2, RED) @@ -29,7 +30,7 @@ class TiledSignatureManagerTest { } @Test - fun compute_returnsSignature_whenValidInputs() { + fun `compute returns signature when inputs are valid`() { val manager = TiledSignatureManager() val bitmap = mockBitmap(4, 4, BLUE) @@ -40,7 +41,7 @@ class TiledSignatureManagerTest { } @Test - fun signatures_equal_forIdenticalContent() { + fun `signatures are equal for identical content`() { val manager = TiledSignatureManager() val a = mockBitmap(8, 8, BLUE) val b = mockBitmap(8, 8, BLUE) @@ -54,7 +55,7 @@ class TiledSignatureManagerTest { } @Test - fun signatures_differ_forDifferentContent() { + fun `signatures differ for different content`() { val manager = TiledSignatureManager() val a = mockBitmap(8, 8, RED) val b = mockBitmap(8, 8, WHITE) @@ -68,7 +69,7 @@ class TiledSignatureManagerTest { } @Test - fun tileCount_matchesExpectedCeilDivision() { + fun `tile count matches expected ceil division`() { val manager = TiledSignatureManager() val bmp = mockBitmap(10, 10, RED) @@ -84,7 +85,7 @@ class TiledSignatureManagerTest { } @Test - fun smallOverlay_changesOnlyAffectedTiles_hashes() { + fun `small overlay changes only affected tiles hashes`() { val manager = TiledSignatureManager() val width = 12 val height = 12 @@ -116,7 +117,7 @@ class TiledSignatureManagerTest { diffCount++ } } - org.junit.jupiter.api.Assertions.assertNotEquals(0, diffCount) + assertNotEquals(0, diffCount) } }