diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceAutomaticVerification.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceAutomaticVerification.kt index de24a56f8..a54c11bae 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceAutomaticVerification.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceAutomaticVerification.kt @@ -8,7 +8,6 @@ import android.net.Uri import android.os.Handler import android.os.Looper import android.widget.Toast -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger.Companion.logger import io.embrace.android.embracesdk.samples.AutomaticVerificationChecker import io.embrace.android.embracesdk.samples.VerificationActions @@ -40,16 +39,12 @@ internal class EmbraceAutomaticVerification( private var foregroundEventTriggered = false - @VisibleForTesting internal lateinit var activityLifecycleTracker: ActivityTracker - @VisibleForTesting internal lateinit var processStateService: ProcessStateService - @VisibleForTesting var automaticVerificationChecker = AutomaticVerificationChecker() - @VisibleForTesting var verificationActions = VerificationActions(Embrace.getInstance(), automaticVerificationChecker) /** @@ -72,7 +67,6 @@ internal class EmbraceAutomaticVerification( instance.runVerifyIntegration() } - @VisibleForTesting fun setActivityListener() { if (!::activityLifecycleTracker.isInitialized) { activityLifecycleTracker = checkNotNull(Embrace.getImpl().activityLifecycleTracker) @@ -101,7 +95,6 @@ internal class EmbraceAutomaticVerification( } } - @VisibleForTesting fun startVerification() { val activity = activityLifecycleTracker.foregroundActivity if (activity != null) { @@ -144,7 +137,6 @@ internal class EmbraceAutomaticVerification( } } - @VisibleForTesting fun runEndSession() { Embrace.getInstance().endSession() logger.logInfo("$TAG End session manually") diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/AnrStacktraceSampler.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/AnrStacktraceSampler.kt index 6cd3d5a70..dbca3e1e0 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/AnrStacktraceSampler.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/AnrStacktraceSampler.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.anr -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.anr.detection.ThreadMonitoringState import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.clock.Clock @@ -24,7 +23,6 @@ internal class AnrStacktraceSampler( private val anrExecutorService: ExecutorService ) : BlockedThreadListener, MemoryCleanerListener { - @VisibleForTesting internal val anrIntervals = CopyOnWriteArrayList() private val samples = mutableListOf() private var lastUnblockedMs: Long = 0 @@ -90,7 +88,7 @@ internal class AnrStacktraceSampler( * intervals with samples has been reached & the SDK needs to discard samples. We attempt * to pick the least valuable interval in this case. */ - @VisibleForTesting + internal fun findLeastValuableIntervalWithSamples() = findIntervalsWithSamples().minByOrNull(AnrInterval::duration) @@ -101,7 +99,6 @@ internal class AnrStacktraceSampler( } } - @VisibleForTesting internal fun reachedAnrStacktraceCaptureLimit(): Boolean { val limit = configService.anrBehavior.getMaxAnrIntervalsPerSession() val count = findIntervalsWithSamples().size diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/EmbraceAnrService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/EmbraceAnrService.kt index b4661e6ad..df74d1877 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/EmbraceAnrService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/EmbraceAnrService.kt @@ -49,7 +49,6 @@ internal class EmbraceAnrService( private val sigquitDetectionService: SigquitDetectionService private val targetThreadHeartbeatScheduler: LivenessCheckScheduler - @VisibleForTesting val listeners = CopyOnWriteArrayList() init { @@ -151,7 +150,6 @@ internal class EmbraceAnrService( } } - @VisibleForTesting internal fun processAnrTick(timestamp: Long) { // Check if ANR capture is enabled if (!configService.anrBehavior.isAnrCaptureEnabled()) { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ThreadInfoCollector.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ThreadInfoCollector.kt index 400fad64a..981dd3a9f 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ThreadInfoCollector.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ThreadInfoCollector.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.anr -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.payload.ThreadInfo import java.util.regex.Pattern @@ -43,7 +42,7 @@ internal class ThreadInfoCollector( * * @return filtered threads */ - @VisibleForTesting + internal fun getAllowedThreads(configService: ConfigService): Set { val allowed: MutableSet = HashSet() val anrBehavior = configService.anrBehavior diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/AnrProcessErrorSampler.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/AnrProcessErrorSampler.kt index 528e3d6f1..f0175ebbb 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/AnrProcessErrorSampler.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/AnrProcessErrorSampler.kt @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.anr.detection import android.app.ActivityManager import android.os.Process -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.anr.BlockedThreadListener import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.clock.Clock @@ -35,14 +34,12 @@ internal class AnrProcessErrorSampler( private var intervalMs: Long = configService.anrBehavior.getAnrProcessErrorsIntervalMs() - @VisibleForTesting var scheduledFuture: ScheduledFuture<*>? = null - @VisibleForTesting var anrProcessErrors: NavigableMap = ConcurrentSkipListMap() // timestamp when the thread has been unblocked - @VisibleForTesting + var threadUnblockedMs: Long? = null override fun onThreadBlocked(thread: Thread, timestamp: Long) { @@ -95,7 +92,7 @@ internal class AnrProcessErrorSampler( * * @param threadBlockedTimestamp timestamp of when the thread has been blocked */ - @VisibleForTesting + internal fun onSearchForProcessErrors(threadBlockedTimestamp: Long) { val shouldStopScheduler = !isSchedulerAllowedToRun() if (shouldStopScheduler) { @@ -139,7 +136,7 @@ internal class AnrProcessErrorSampler( * Basically, once the thread has been unblocked, we still have [schedulerExtraTimeAllowance] * ms for the scheduler to keep running. */ - @VisibleForTesting + internal fun isSchedulerAllowedToRun(): Boolean { return when (val ms = threadUnblockedMs) { null -> true diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/BlockedThreadDetector.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/BlockedThreadDetector.kt index 1b164384f..abc12b995 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/BlockedThreadDetector.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/BlockedThreadDetector.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.anr.detection import android.os.Debug -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.anr.BlockedThreadListener import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.clock.Clock @@ -105,7 +104,7 @@ internal class BlockedThreadDetector constructor( * To avoid useless samples grouped within a few ms of each other, this function will return * false & thus avoid sampling if less than half of the interval MS has passed. */ - @VisibleForTesting + internal fun shouldAttemptAnrSample(timestamp: Long): Boolean { val lastMonitorThreadResponseMs = state.lastMonitorThreadResponseMs val delta = timestamp - lastMonitorThreadResponseMs // time since last check @@ -118,7 +117,7 @@ internal class BlockedThreadDetector constructor( * * This defaults to the main thread not having processed a message within 1s. */ - @VisibleForTesting + internal fun isAnrDurationThresholdExceeded(timestamp: Long): Boolean { enforceThread(anrMonitorThread) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/LivenessCheckScheduler.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/LivenessCheckScheduler.kt index 981ca1af5..41446cc21 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/LivenessCheckScheduler.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/LivenessCheckScheduler.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.anr.detection import android.os.Message -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.anr.detection.TargetThreadHandler.Companion.HEARTBEAT_REQUEST import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.clock.Clock @@ -107,7 +106,7 @@ internal class LivenessCheckScheduler internal constructor( * Called at regular intervals on the monitor thread. This function posts a message to the * main thread that is used to check whether it is live or not. */ - @VisibleForTesting + internal fun onMonitorThreadHeartbeat() { enforceThread(anrMonitorThread) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/TargetThreadHandler.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/TargetThreadHandler.kt index 1b310df02..c4ee5b8c7 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/TargetThreadHandler.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/detection/TargetThreadHandler.kt @@ -4,7 +4,6 @@ import android.os.Handler import android.os.Looper import android.os.Message import android.os.MessageQueue -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.enforceThread @@ -49,7 +48,6 @@ internal class TargetThreadHandler( } } - @VisibleForTesting internal fun onIdleThread(): Boolean { onMainThreadUnblocked() return true diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt index 6512903fa..21d37d21b 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.anr.ndk -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.DeviceArchitecture @@ -40,22 +39,16 @@ internal class EmbraceNativeThreadSamplerService @JvmOverloads constructor( fun finishSampling(): List? } - @VisibleForTesting internal var ignored = true - @VisibleForTesting internal var sampling = false - @VisibleForTesting internal var count = -1 - @VisibleForTesting internal var factor = -1 - @VisibleForTesting internal var intervals: MutableList = mutableListOf() - @VisibleForTesting internal val currentInterval: NativeThreadAnrInterval? get() = intervals.lastOrNull() @@ -239,7 +232,7 @@ internal class EmbraceNativeThreadSamplerService @JvmOverloads constructor( * Determines whether or not we should sample the target thread based on the thread stacktrace * and the ANR config. */ - @VisibleForTesting + internal fun containsAllowedStackframes( anrBehavior: AnrBehavior, stacktrace: Array diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt index 7e4050160..f279acbcd 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.anr.ndk import android.os.Handler import android.os.Looper -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.anr.AnrService import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.logging.InternalEmbraceLogger @@ -24,7 +23,6 @@ internal class NativeThreadSamplerInstaller( private val isMonitoring = AtomicBoolean(false) private var targetHandler: Handler? = null - @VisibleForTesting internal var currentThread: Thread? = null private fun prepareTargetHandler() { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/sigquit/SigquitDetectionService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/sigquit/SigquitDetectionService.kt index 38a040058..4a69670dd 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/sigquit/SigquitDetectionService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/sigquit/SigquitDetectionService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.anr.sigquit -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.SharedObjectLoader import io.embrace.android.embracesdk.logging.InternalEmbraceLogger @@ -56,7 +55,6 @@ internal class SigquitDetectionService( } } - @VisibleForTesting fun setupGoogleAnrHandler() { logger.logDeveloper("EmbraceAnrService", "Setting up Google ANR Handler") // TODO: split up the ANR tracking and NDK crash reporter libs diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/aei/EmbraceApplicationExitInfoService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/aei/EmbraceApplicationExitInfoService.kt index d85538d23..cfd8bb9a1 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/aei/EmbraceApplicationExitInfoService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/aei/EmbraceApplicationExitInfoService.kt @@ -4,7 +4,6 @@ import android.app.ActivityManager import android.app.ApplicationExitInfo import android.os.Build.VERSION_CODES import androidx.annotation.RequiresApi -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.capture.metadata.MetadataService import io.embrace.android.embracesdk.capture.user.UserService import io.embrace.android.embracesdk.comms.delivery.DeliveryService @@ -42,7 +41,6 @@ internal class EmbraceApplicationExitInfoService constructor( private const val SDK_AEI_SEND_LIMIT = 32 } - @VisibleForTesting @Volatile var backgroundExecution: Future<*>? = null @@ -146,7 +144,6 @@ internal class EmbraceApplicationExitInfoService constructor( return unsentAeiObjects } - @VisibleForTesting fun buildSessionAppExitInfoData( appExitInfo: ApplicationExitInfo, trace: String?, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/EmbraceBreadcrumbService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/EmbraceBreadcrumbService.kt index 2447b5c20..42fd9fecd 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/EmbraceBreadcrumbService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/EmbraceBreadcrumbService.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.capture.crumbs import android.app.Activity import android.text.TextUtils import android.util.Pair -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.ApkToolsConfig import io.embrace.android.embracesdk.internal.CacheableValue @@ -60,7 +59,6 @@ internal class EmbraceBreadcrumbService( private val viewBreadcrumbs = LinkedBlockingDeque() private val tapBreadcrumbs = LinkedBlockingDeque() - @VisibleForTesting val customBreadcrumbs = LinkedBlockingDeque() private val rnActionBreadcrumbs = LinkedBlockingDeque() val webViewBreadcrumbs = LinkedBlockingDeque() diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/PushNotificationCaptureService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/PushNotificationCaptureService.kt index 5f1081ce9..37e94638d 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/PushNotificationCaptureService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/crumbs/PushNotificationCaptureService.kt @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.capture.crumbs import android.app.Activity import android.os.Bundle -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.payload.PushNotificationBreadcrumb.NotificationType import io.embrace.android.embracesdk.session.lifecycle.ActivityLifecycleListener @@ -15,7 +14,6 @@ internal class PushNotificationCaptureService( private val logger: InternalEmbraceLogger ) : ActivityLifecycleListener { - @VisibleForTesting companion object Utils { enum class PRIORITY(val priority: Int) { @@ -39,7 +37,7 @@ internal class PushNotificationCaptureService( * this, or adding com.google.firebase:firebase-messaging as a dependency on the sdk and * add some more complex code. */ - @VisibleForTesting + fun getMessagePriority(priority: String?) = when (priority) { "high" -> PRIORITY.PRIORITY_HIGH.priority @@ -47,7 +45,6 @@ internal class PushNotificationCaptureService( else -> PRIORITY.PRIORITY_UNKNOWN.priority } - @VisibleForTesting fun extractDeveloperDefinedPayload(bundle: Bundle): Map { val keySet = bundle.keySet() ?: return emptyMap() return keySet.filter { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/metadata/EmbraceMetadataService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/metadata/EmbraceMetadataService.kt index 4b2f9f216..f1889f787 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/metadata/EmbraceMetadataService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/metadata/EmbraceMetadataService.kt @@ -9,7 +9,6 @@ import android.os.Build import android.os.Environment import android.os.StatFs import android.view.WindowManager -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.BuildConfig import io.embrace.android.embracesdk.Embrace.AppFramework import io.embrace.android.embracesdk.capture.cpu.CpuInfoDelegate @@ -227,7 +226,6 @@ internal class EmbraceMetadataService private constructor( ) } - @VisibleForTesting fun asyncRetrieveDiskUsage(isAndroid26OrAbove: Boolean) { metadataRetrieveExecutorService.submit( Callable { @@ -251,7 +249,6 @@ internal class EmbraceMetadataService private constructor( ) } - @VisibleForTesting fun getReactNativeBundleId(): String? = reactNativeBundleId.value override fun getDeviceId(): String = deviceId.value diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/strictmode/EmbraceStrictModeService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/strictmode/EmbraceStrictModeService.kt index 272a4b3fb..a80dce3aa 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/strictmode/EmbraceStrictModeService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/strictmode/EmbraceStrictModeService.kt @@ -4,7 +4,6 @@ import android.os.Build import android.os.StrictMode import android.os.strictmode.Violation import androidx.annotation.RequiresApi -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.payload.ExceptionInfo @@ -25,7 +24,6 @@ internal class EmbraceStrictModeService( addStrictModeListener(executorService, StrictMode.OnThreadViolationListener(::addViolation)) } - @VisibleForTesting internal fun addViolation(violation: Violation) { if (violations.size < configService.anrBehavior.getStrictModeViolationLimit()) { val exceptionInfo = ExceptionInfo.ofThrowable(violation) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/thermalstate/EmbraceThermalStatusService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/thermalstate/EmbraceThermalStatusService.kt index 1a1519a53..a8ac3ec90 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/thermalstate/EmbraceThermalStatusService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/thermalstate/EmbraceThermalStatusService.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.capture.thermalstate import android.os.Build import android.os.PowerManager import androidx.annotation.RequiresApi -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.payload.ThermalState @@ -33,7 +32,6 @@ internal class EmbraceThermalStatusService( } } - @VisibleForTesting fun handleThermalStateChange(status: Int?) { if (status == null) { logger.logDeveloper("ThermalStatusService", "Null thermal status, no-oping.") diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt index e9400df07..a1b8f0937 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.capture.user -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.payload.UserInfo import io.embrace.android.embracesdk.payload.UserInfo.Companion.ofStored @@ -14,7 +13,7 @@ internal class EmbraceUserService( ) : ProcessStateListener, UserService { @Volatile - @VisibleForTesting + internal var info: UserInfo = ofStored(preferencesService) override fun loadUserInfoFromDisk(): UserInfo? { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt index afd48aebc..fa765b8f4 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt @@ -216,29 +216,9 @@ internal class EmbraceApiService( @Suppress("UseCheckOrError") private fun executePost(request: ApiRequest, payload: ByteArray) { - when (val response = apiClient.executePost(request, payload)) { - is ApiResponse.Success -> {} - is ApiResponse.TooManyRequests -> { - // Temporarily, we just throw an exception. In the future, - // we will use the retryAfter to schedule a retry. - val retryAfter = response.retryAfter ?: 3 - throw IllegalStateException("Too many requests. Will retry after $retryAfter seconds.") - } - is ApiResponse.PayloadTooLarge -> { - // We don't want to retry on PayloadTooLarge error, so we just log the error. - logger.logError("Payload too large. Dropping event.") - } - is ApiResponse.Failure -> { - // We don't want to retry on 4xx or 5xx errors, so we just log the error. - logger.logError("Failed to retrieve from Embrace server. Status code: ${response.code}") - } - is ApiResponse.Incomplete -> { - // Retry on incomplete response. - throw IllegalStateException("Failed to retrieve from Embrace server.") - } - is ApiResponse.NotModified -> { - // Not expected to receive a 304 response for a POST request. - } + val response = apiClient.executePost(request, payload) + if (response !is ApiResponse.Success) { + throw IllegalStateException("Failed to retrieve from Embrace server.") } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryCacheManager.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryCacheManager.kt index 8e3edc006..880b2ecdd 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryCacheManager.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryCacheManager.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.comms.delivery -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.internal.EmbraceSerializer import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.utils.Uuid @@ -43,7 +42,6 @@ internal class EmbraceDeliveryCacheManager( */ private const val FAILED_API_CALLS_FILE_NAME = "failed_api_calls.json" - @VisibleForTesting const val MAX_SESSIONS_CACHED = 64 private const val TAG = "DeliveryCacheManager" @@ -267,7 +265,6 @@ internal class EmbraceDeliveryCacheManager( } } - @VisibleForTesting data class CachedSession( val filename: String, val sessionId: String, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryRetryManager.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryRetryManager.kt index fdb933972..4b1e8cfc7 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryRetryManager.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/delivery/EmbraceDeliveryRetryManager.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.comms.delivery -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.capture.connectivity.NetworkConnectivityListener import io.embrace.android.embracesdk.capture.connectivity.NetworkConnectivityService import io.embrace.android.embracesdk.comms.api.ApiRequest @@ -87,7 +86,7 @@ internal class EmbraceDeliveryRetryManager( /** * Returns true if there is an active pending retry task */ - @VisibleForTesting + fun isRetryTaskActive(): Boolean = lastRetryTask?.let { task -> !task.isCancelled && !task.isDone @@ -96,7 +95,7 @@ internal class EmbraceDeliveryRetryManager( /** * Returns the number of failed API calls that will be retried */ - @VisibleForTesting + fun pendingRetriesCount() = retryQueue.size /** diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/EmbraceConfigService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/EmbraceConfigService.kt index a76ca6855..d465a8a06 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/EmbraceConfigService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/EmbraceConfigService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.config -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.Embrace import io.embrace.android.embracesdk.comms.api.ApiService import io.embrace.android.embracesdk.config.behavior.AnrBehavior @@ -53,11 +52,9 @@ internal class EmbraceConfigService @JvmOverloads constructor( private val listeners: MutableSet = CopyOnWriteArraySet() private val lock = Any() - @VisibleForTesting @Volatile private var configProp = RemoteConfig() - @VisibleForTesting @Volatile var lastUpdated: Long = 0 @@ -192,7 +189,7 @@ internal class EmbraceConfigService @JvmOverloads constructor( /** * Load Config from cache if present. */ - @VisibleForTesting + fun loadConfigFromCache() { logger.logDeveloper("EmbraceConfigService", "Attempting to load config from cache") val cachedConfig = apiService.getCachedConfig() diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/behavior/NetworkBehavior.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/behavior/NetworkBehavior.kt index e1292671b..45e4dbfd8 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/behavior/NetworkBehavior.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/config/behavior/NetworkBehavior.kt @@ -4,6 +4,7 @@ import io.embrace.android.embracesdk.config.local.SdkLocalConfig import io.embrace.android.embracesdk.config.remote.NetworkCaptureRuleRemoteConfig import io.embrace.android.embracesdk.config.remote.RemoteConfig import java.util.regex.Pattern +import kotlin.math.min /** * Provides the behavior that functionality relating to network call capture should follow. @@ -65,31 +66,36 @@ internal class NetworkBehavior( local?.networking?.enableNativeMonitoring ?: ENABLE_NATIVE_MONITORING_DEFAULT /** - * List of domains to be limited for tracking. + * Map of limits being enforced for each domain suffix for the maximum number of requests that are logged given that suffix. The + * algorithm to generate the limits for each domain suffix is as follows: + * + * - Use the domain-suffix-specific settings defined in the remote config as a base. + * - For suffixes where there is both local and remote entries, use the local limit if it is smaller than the remote one + * - For suffixes with only a local entry, apply the local limit or the ceiling defined by the default limit on the remote, + * which ever is smaller. */ - fun getNetworkCallLimitsPerDomain(): Map { - return remote?.networkConfig?.domainLimits - ?: transformLocalDomainCfg() - ?: HashMap() - } - - private fun transformLocalDomainCfg(): Map? { - val mergedLimits: MutableMap = HashMap() - for (domain in local?.networking?.domains ?: return null) { - if (domain.domain != null && domain.limit != null) { - mergedLimits[domain.domain] = domain.limit + fun getNetworkCallLimitsPerDomainSuffix(): Map { + val limitCeiling = getLimitCeiling() + val domainSuffixLimits: MutableMap = remote?.networkConfig?.domainLimits?.toMutableMap() ?: mutableMapOf() + + local?.networking?.domains?.forEach { localLimit -> + if (localLimit.domain != null && localLimit.limit != null) { + domainSuffixLimits[localLimit.domain] = + domainSuffixLimits[localLimit.domain]?.let { remoteLimit -> + min(remoteLimit, localLimit.limit) + } ?: min(limitCeiling, localLimit.limit) } } - return mergedLimits + + return domainSuffixLimits } /** - * Gets the capture limit for network calls. + * Gets the default limit for network calls for all domains where the limit is not specified. */ fun getNetworkCaptureLimit(): Int { - return remote?.networkConfig?.defaultCaptureLimit - ?: local?.networking?.defaultCaptureLimit - ?: DEFAULT_NETWORK_CALL_LIMIT + val remoteDefault = getLimitCeiling() + return min(remoteDefault, local?.networking?.defaultCaptureLimit ?: remoteDefault) } /** @@ -129,4 +135,9 @@ internal class NetworkBehavior( * Gets the rules for capturing network call bodies */ fun getNetworkCaptureRules(): Set = remote?.networkCaptureRules ?: emptySet() + + /** + * Cap the default limit at whatever the default limit is that is set or implied by the remote config + */ + private fun getLimitCeiling(): Int = remote?.networkConfig?.defaultCaptureLimit ?: DEFAULT_NETWORK_CALL_LIMIT } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.java b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.java index 25b73175c..ef63afadc 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.java +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.java @@ -264,7 +264,7 @@ private void startNdk() { } } - @VisibleForTesting + void checkSignalHandlersOverwritten() { if (configService.getAutoDataCaptureBehavior().isSigHandlerDetectionEnabled()) { String culprit = delegate._checkForOverwrittenHandlers(); diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt index 963d64b3d..2186f5744 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt @@ -40,11 +40,17 @@ internal class EmbraceNetworkLoggingService( private val networkCallCache = CacheableValue> { callsStorageLastUpdate.get() } - private val domainSettings = ConcurrentHashMap() + private val domainSetting = ConcurrentHashMap() - private val callsPerDomain = hashMapOf() + private val callsPerDomainSuffix = hashMapOf() - private val ipAddressCount = AtomicInteger(0) + private val ipAddressNetworkCallCount = AtomicInteger(0) + + private val untrackedNetworkCallCount = AtomicInteger(0) + + private var defaultPerDomainSuffixCallLimit = configService.networkBehavior.getNetworkCaptureLimit() + + private var domainSuffixCallLimits = configService.networkBehavior.getNetworkCallLimitsPerDomainSuffix() override fun getNetworkCallsForSession(): NetworkSessionV2 { val calls = networkCallCache.value { @@ -57,7 +63,7 @@ internal class EmbraceNetworkLoggingService( val cachedCallsSize = calls.size val overLimit = hashMapOf() - for ((key, value) in callsPerDomain) { + for ((key, value) in callsPerDomainSuffix) { if (value.requestCount > value.captureLimit) { overLimit[key] = value } @@ -68,8 +74,6 @@ internal class EmbraceNetworkLoggingService( logger.logError(msg, IllegalStateException(msg), true) } - // clear calls per domain and session network calls lists before be used by the next session - callsPerDomain.clear() return NetworkSessionV2(calls, overLimit) } @@ -114,7 +118,6 @@ internal class EmbraceNetworkLoggingService( } processNetworkCall(callId, networkCall) - storeSettings(url) } override fun logNetworkError( @@ -156,7 +159,6 @@ internal class EmbraceNetworkLoggingService( ) } processNetworkCall(callId, networkCall) - storeSettings(url) } /** @@ -166,47 +168,37 @@ internal class EmbraceNetworkLoggingService( * @param networkCall that is going to be captured */ private fun processNetworkCall(callId: String, networkCall: NetworkCallV2) { - // Get the domain, if it can be successfully parsed + // Get the domain, if it can be successfully parsed. If not, don't log this call. val domain = networkCall.url?.let { getDomain(it) - } - - if (domain == null) { - logger.logDeveloper("EmbraceNetworkLoggingService", "Domain is not present") - return - } - - logger.logDeveloper("EmbraceNetworkLoggingService", "Domain: $domain") + } ?: return if (isIpAddress(domain)) { - logger.logDeveloper("EmbraceNetworkLoggingService", "Domain is an ip address") - val captureLimit = configService.networkBehavior.getNetworkCaptureLimit() - - if (ipAddressCount.getAndIncrement() < captureLimit) { - // only capture if the ipAddressCount has not exceeded defaultLimit - logger.logDeveloper("EmbraceNetworkLoggingService", "capturing network call") + if (ipAddressNetworkCallCount.getAndIncrement() < defaultPerDomainSuffixCallLimit) { storeNetworkCall(callId, networkCall) - } else { - logger.logDeveloper("EmbraceNetworkLoggingService", "capture limit exceeded") } return + } else if (!domainSetting.containsKey(domain)) { + createLimitForDomain(domain) } - val settings = domainSettings[domain] + val settings = domainSetting[domain] if (settings == null) { - logger.logDeveloper("EmbraceNetworkLoggingService", "no domain settings") - storeNetworkCall(callId, networkCall) + // Not sure how this is possible, but in case it is, limit logged logs where we can't figure out the settings to apply + if (untrackedNetworkCallCount.getAndIncrement() < defaultPerDomainSuffixCallLimit) { + storeNetworkCall(callId, networkCall) + } } else { val suffix = settings.suffix val limit = settings.limit - var count = callsPerDomain[suffix] + var countPerSuffix = callsPerDomainSuffix[suffix] - if (count == null) { - count = DomainCount(1, limit) + if (countPerSuffix == null) { + countPerSuffix = DomainCount(0, limit) } // Exclude if the network call exceeds the limit - if (count.requestCount < limit) { + if (countPerSuffix.requestCount < limit) { storeNetworkCall(callId, networkCall) } else { logger.logDeveloper("EmbraceNetworkLoggingService", "capture limit exceeded") @@ -214,40 +206,28 @@ internal class EmbraceNetworkLoggingService( // Track the number of calls for each domain (or configured suffix) suffix?.let { - callsPerDomain[it] = DomainCount(count.requestCount + 1, limit) + callsPerDomainSuffix[it] = DomainCount(countPerSuffix.requestCount + 1, limit) logger.logDeveloper( "EmbraceNetworkLoggingService", - "Call per domain $domain ${count.requestCount + 1}" + "Call per domain $domain ${countPerSuffix.requestCount + 1}" ) } } } - private fun storeSettings(url: String) { + private fun createLimitForDomain(domain: String) { try { - val mergedLimits = configService.networkBehavior.getNetworkCallLimitsPerDomain() - - val domain = getDomain(url) - if (domain == null) { - logger.logDeveloper("EmbraceNetworkLoggingService", "Domain not present") - return - } - if (domainSettings.containsKey(domain)) { - logger.logDeveloper("EmbraceNetworkLoggingService", "No settings for $domain") - return - } - - for ((key, value) in mergedLimits) { + for ((key, value) in domainSuffixCallLimits) { if (domain.endsWith(key)) { - domainSettings[domain] = DomainSettings(value, key) - return + domainSetting[domain] = DomainSettings(value, key) } } - val defaultLimit = configService.networkBehavior.getNetworkCaptureLimit() - domainSettings[domain] = DomainSettings(defaultLimit, domain) + if (!domainSetting.containsKey(domain)) { + domainSetting[domain] = DomainSettings(defaultPerDomainSuffixCallLimit, domain) + } } catch (ex: Exception) { - logger.logDebug("Failed to determine limits for URL: $url", ex) + logger.logDebug("Failed to determine limits for domain: $domain", ex) } } @@ -266,11 +246,13 @@ internal class EmbraceNetworkLoggingService( } override fun cleanCollections() { - domainSettings.clear() - callsPerDomain.clear() + domainSetting.clear() + callsPerDomainSuffix.clear() + ipAddressNetworkCallCount.set(0) + untrackedNetworkCallCount.set(0) clearNetworkCalls() - // reset counters - ipAddressCount.set(0) - logger.logDeveloper("EmbraceNetworkLoggingService", "Collections cleaned") + // re-fetch limits in case they changed since they last time they were fetched + defaultPerDomainSuffixCallLimit = configService.networkBehavior.getNetworkCaptureLimit() + domainSuffixCallLimits = configService.networkBehavior.getNetworkCallLimitsPerDomainSuffix() } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AnrInterval.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AnrInterval.kt index 0fcb408fe..598d08ec8 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AnrInterval.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AnrInterval.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.payload import androidx.annotation.CheckResult -import androidx.annotation.VisibleForTesting import com.google.gson.annotations.SerializedName /** @@ -20,28 +19,24 @@ internal data class AnrInterval @JvmOverloads constructor( * The last time the thread was alive. */ @SerializedName("lk") - @get:VisibleForTesting val lastKnownTime: Long? = null, /** * The time the application started responding. */ @SerializedName("en") - @get:VisibleForTesting val endTime: Long? = null, /** * The component of the application which stopped responding. */ @SerializedName("v") - @get:VisibleForTesting val type: Type = Type.UI, /** * The captured stacktraces of the anr interval. */ @SerializedName("se") - @get:VisibleForTesting val anrSampleList: AnrSampleList? = null, /** diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AppInfo.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AppInfo.kt index 5cee2eabc..ad1a83764 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AppInfo.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/AppInfo.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.payload -import androidx.annotation.VisibleForTesting import com.google.gson.annotations.SerializedName import io.embrace.android.embracesdk.internal.utils.MessageUtils @@ -107,7 +106,6 @@ internal data class AppInfo( * The version number of the platform (e.g. Unity 2021) */ @SerializedName("unv") - @get:VisibleForTesting val hostedPlatformVersion: String? = null, /** @@ -120,7 +118,6 @@ internal data class AppInfo( * The version number of the hosted SDK (e.g. Embrace Unity 1.7.0) */ @SerializedName("usv") - @get:VisibleForTesting val hostedSdkVersion: String? = null, ) { fun toJson(): String { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ExceptionError.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ExceptionError.kt index 7db063769..4bc4b32fc 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ExceptionError.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ExceptionError.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.payload -import androidx.annotation.VisibleForTesting import com.google.gson.annotations.SerializedName import io.embrace.android.embracesdk.internal.clock.Clock @@ -9,11 +8,11 @@ import io.embrace.android.embracesdk.internal.clock.Clock */ internal data class ExceptionError(@Transient private val logStrictMode: Boolean) { @SerializedName("c") - @VisibleForTesting + var occurrences = 0 @SerializedName("rep") - @VisibleForTesting + val exceptionErrors = mutableListOf() /** diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/Stacktraces.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/Stacktraces.kt index 63a433802..8b292bf71 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/Stacktraces.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/Stacktraces.kt @@ -22,19 +22,19 @@ internal class Stacktraces @JvmOverloads constructor( ) { @SerializedName("tt") - @VisibleForTesting + val jvmStacktrace: List? @SerializedName("jsk") - @VisibleForTesting + val javascriptStacktrace: String? @SerializedName("u") - @VisibleForTesting + val unityStacktrace: String? @SerializedName("f") - @VisibleForTesting + val flutterStacktrace: String? init { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ThreadInfo.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ThreadInfo.kt index 1a9253114..a33a60e7c 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ThreadInfo.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/ThreadInfo.kt @@ -1,12 +1,11 @@ package io.embrace.android.embracesdk.payload -import androidx.annotation.VisibleForTesting import com.google.gson.annotations.SerializedName /** * Represents thread information at a given point in time. */ -internal data class ThreadInfo @VisibleForTesting internal constructor( +internal data class ThreadInfo internal constructor( /** * The thread ID diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesService.kt index 84b72b0a7..9cd925f38 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesService.kt @@ -226,9 +226,11 @@ internal class EmbracePreferencesService( get() = prefs.getBooleanPreference(LAST_USER_MESSAGE_FAILED_KEY, false) set(value) = prefs.setBooleanPreference(LAST_USER_MESSAGE_FAILED_KEY, value) - override var sessionNumber: Int - get() = prefs.getIntegerPreference(LAST_SESSION_NUMBER_KEY) ?: 0 - set(value) = prefs.setIntegerPreference(LAST_SESSION_NUMBER_KEY, value) + override fun getIncrementAndGetSessionNumber(): Int { + val sessionNumber = (prefs.getIntegerPreference(LAST_SESSION_NUMBER_KEY) ?: 0) + 1 + prefs.setIntegerPreference(LAST_SESSION_NUMBER_KEY, sessionNumber) + return sessionNumber + } override var javaScriptBundleURL: String? get() = prefs.getStringPreference(JAVA_SCRIPT_BUNDLE_URL_KEY) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/PreferencesService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/PreferencesService.kt index 74c9c877b..b295b4800 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/PreferencesService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/prefs/PreferencesService.kt @@ -84,9 +84,11 @@ internal interface PreferencesService { var userMessageNeedsRetry: Boolean /** - * Last session number. Increments by one. + * Increments and returns the session number ordinal. This is an integer that increments + * at the start of every session. This allows us to check the % of sessions that didn't get + * delivered to the backend. */ - var sessionNumber: Int + fun getIncrementAndGetSessionNumber(): Int /** * Last javaScript bundle string url. diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/EmbraceCrashSamples.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/EmbraceCrashSamples.kt index 8a93a969b..9a836cc57 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/EmbraceCrashSamples.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/EmbraceCrashSamples.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.samples -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.Embrace import io.embrace.android.embracesdk.logging.InternalEmbraceLogger @@ -13,13 +12,12 @@ internal object EmbraceCrashSamples { private val logger = InternalEmbraceLogger() - @VisibleForTesting val ndkCrashSamplesNdkDelegate = EmbraceCrashSamplesNdkDelegateImpl() /** * Verifies if Embrace is initialized */ - @VisibleForTesting + fun isSdkStarted() { if (!Embrace.getInstance().isStarted) { val e = EmbraceSampleCodeException( @@ -35,7 +33,7 @@ internal object EmbraceCrashSamples { /** * verifies if ANR detection is enabled */ - @VisibleForTesting + fun checkAnrDetectionEnabled() { if (Embrace.getInstance().configService?.anrBehavior?.isAnrCaptureEnabled() == false) { val e = EmbraceSampleCodeException( @@ -88,7 +86,7 @@ internal object EmbraceCrashSamples { /** * verifies if NDK detection is enabled */ - @VisibleForTesting + fun checkNdkDetectionEnabled() { // First verifies is Embrace SDK is initialized isSdkStarted() diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/VerificationActions.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/VerificationActions.kt index e51053759..0778bd2ba 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/VerificationActions.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/samples/VerificationActions.kt @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.samples import android.os.Handler import android.os.Looper -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.BuildConfig import io.embrace.android.embracesdk.Embrace import io.embrace.android.embracesdk.EmbraceAutomaticVerification @@ -133,7 +132,6 @@ internal class VerificationActions( ) } - @VisibleForTesting private fun executeMoment() { val momentName = "Verify Integration Moment" val momentIdentifier = "Verify Integration identifier" @@ -143,7 +141,6 @@ internal class VerificationActions( }, MOMENT_DURATION_MILLIS) } - @VisibleForTesting fun executeNetworkHttpGETRequest() { val connection = URL(networkingGetUrl).openConnection() as HttpURLConnection connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded") @@ -172,7 +169,6 @@ internal class VerificationActions( } } - @VisibleForTesting private fun executeNetworkHttpPOSTRequest() { val connection = URL(networkingPostUrl).openConnection() as HttpURLConnection connection.doOutput = true @@ -185,7 +181,6 @@ internal class VerificationActions( } } - @VisibleForTesting private fun executeNetworkHttpWrongRequest() { val connection = URL(networkingWrongUrl).openConnection() as HttpURLConnection val result = connection.responseCode @@ -199,7 +194,6 @@ internal class VerificationActions( InternalStaticEmbraceLogger.logger.logInfo("${EmbraceAutomaticVerification.TAG} ANR Finished") } - @VisibleForTesting private fun throwAnException() { handler.postDelayed({ throw VerifyIntegrationException("Forced Exception to verify integration") diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceBackgroundActivityService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceBackgroundActivityService.kt index 7c74a6966..c199069a8 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceBackgroundActivityService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceBackgroundActivityService.kt @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.session import android.os.Handler import android.os.Looper -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.capture.PerformanceInfoService import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbService import io.embrace.android.embracesdk.capture.metadata.MetadataService @@ -61,12 +60,11 @@ internal class EmbraceBackgroundActivityService( /** * The active background activity session. */ - @VisibleForTesting + @Volatile var backgroundActivity: BackgroundActivity? = null private val manualBkgSessionsSent = AtomicInteger(0) - @VisibleForTesting var lastSendAttempt: Long private var isEnabled = true diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceMemoryCleanerService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceMemoryCleanerService.kt index adef8748c..534131903 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceMemoryCleanerService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceMemoryCleanerService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.session -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.logging.EmbraceInternalErrorService import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger.Companion.logDebug import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger.Companion.logDeveloper @@ -12,7 +11,7 @@ internal class EmbraceMemoryCleanerService : MemoryCleanerService { /** * List of listeners that subscribe to clean services collections. */ - @VisibleForTesting + val listeners = CopyOnWriteArrayList() override fun cleanServicesCollections( diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceSessionService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceSessionService.kt index 694ee316f..e297f8295 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceSessionService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/EmbraceSessionService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.session -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.comms.delivery.DeliveryService import io.embrace.android.embracesdk.internal.clock.Clock import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes @@ -57,7 +56,7 @@ internal class EmbraceSessionService( * The currently active session. */ @Volatile - @VisibleForTesting + internal var activeSession: Session? = null init { @@ -133,7 +132,7 @@ internal class EmbraceSessionService( /** * Caches the session, with performance information generated up to the current point. */ - @VisibleForTesting + fun onPeriodicCacheActiveSession() { try { synchronized(lock) { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/SessionHandler.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/SessionHandler.kt index 1563ad5ba..571487cf8 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/SessionHandler.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/SessionHandler.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.session -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.capture.connectivity.NetworkConnectivityService import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbService import io.embrace.android.embracesdk.capture.metadata.MetadataService @@ -48,7 +47,6 @@ internal class SessionHandler( private val sessionPeriodicCacheExecutorService: ScheduledExecutorService ) : Closeable { - @VisibleForTesting var scheduledFuture: ScheduledFuture<*>? = null /** @@ -73,7 +71,7 @@ internal class SessionHandler( coldStart, startType, startTime, - incrementAndGetSessionNumber(), + preferencesService.getIncrementAndGetSessionNumber(), userService.loadUserInfoFromDisk(), sessionProperties.get() ) @@ -356,15 +354,6 @@ internal class SessionHandler( return fullEndSessionMessage } - /** - * @return session number incremented by 1 - */ - private fun incrementAndGetSessionNumber(): Int { - val sessionNumber = preferencesService.sessionNumber + 1 - preferencesService.sessionNumber = sessionNumber - return sessionNumber - } - /** * It starts a background job that will schedule a callback to automatically end the session. */ diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/ActivityLifecycleTracker.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/ActivityLifecycleTracker.kt index a8bdf8c0a..a4fba8836 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/ActivityLifecycleTracker.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/ActivityLifecycleTracker.kt @@ -3,7 +3,6 @@ package io.embrace.android.embracesdk.session.lifecycle import android.app.Activity import android.app.Application import android.os.Bundle -import androidx.annotation.VisibleForTesting import io.embrace.android.embracesdk.annotation.StartupActivity import io.embrace.android.embracesdk.capture.orientation.OrientationService import io.embrace.android.embracesdk.logging.InternalEmbraceLogger @@ -29,7 +28,7 @@ internal class ActivityLifecycleTracker( /** * List of listeners that subscribe to activity events. */ - @VisibleForTesting + val listeners = CopyOnWriteArrayList() /** @@ -43,7 +42,7 @@ internal class ActivityLifecycleTracker( * * @param activity the activity involved in the state change. */ - @VisibleForTesting + @Synchronized fun updateStateWithActivity(activity: Activity?) { logger.logDeveloper( diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/EmbraceProcessStateService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/EmbraceProcessStateService.kt index 65e44c062..fe8216177 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/EmbraceProcessStateService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/lifecycle/EmbraceProcessStateService.kt @@ -1,6 +1,5 @@ package io.embrace.android.embracesdk.session.lifecycle -import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner @@ -23,7 +22,7 @@ internal class EmbraceProcessStateService( /** * List of listeners that subscribe to process lifecycle events. */ - @VisibleForTesting + val listeners = CopyOnWriteArrayList() /** diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/config/behavior/NetworkBehaviorTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/config/behavior/NetworkBehaviorTest.kt index 57b7fe413..11ebdaa78 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/config/behavior/NetworkBehaviorTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/config/behavior/NetworkBehaviorTest.kt @@ -41,7 +41,7 @@ internal class NetworkBehaviorTest { ) ), disabledUrlPatterns = listOf("google.com"), - defaultCaptureLimit = 220, + defaultCaptureLimit = 720, ), capturePublicKey = "test" ) @@ -71,7 +71,7 @@ internal class NetworkBehaviorTest { assertFalse(isRequestContentLengthCaptureEnabled()) assertTrue(isNativeNetworkingMonitoringEnabled()) assertEquals(1000, getNetworkCaptureLimit()) - assertEquals(emptyMap(), getNetworkCallLimitsPerDomain()) + assertEquals(emptyMap(), getNetworkCallLimitsPerDomainSuffix()) assertTrue(isUrlEnabled("google.com")) assertFalse(isCaptureBodyEncryptionEnabled()) assertNull(getCapturePublicKey()) @@ -85,19 +85,38 @@ internal class NetworkBehaviorTest { assertEquals("x-custom-trace", getTraceIdHeader()) assertTrue(isRequestContentLengthCaptureEnabled()) assertFalse(isNativeNetworkingMonitoringEnabled()) - assertEquals(mapOf("google.com" to 100), getNetworkCallLimitsPerDomain()) - assertEquals(220, getNetworkCaptureLimit()) + assertEquals(mapOf("google.com" to 100), getNetworkCallLimitsPerDomainSuffix()) + assertEquals(720, getNetworkCaptureLimit()) assertFalse(isUrlEnabled("google.com")) assertTrue(isCaptureBodyEncryptionEnabled()) assertEquals("test", getCapturePublicKey()) } } + @Test + fun testRemoteOnly() { + with(fakeNetworkBehavior(localCfg = { null }, remoteCfg = { remote })) { + assertEquals(409, getNetworkCaptureLimit()) + assertEquals(mapOf("google.com" to 50), getNetworkCallLimitsPerDomainSuffix()) + assertTrue(isUrlEnabled("google.com")) + assertFalse(isUrlEnabled("example.com")) + assertEquals( + NetworkCaptureRuleRemoteConfig( + "test", + 5000, + "GET", + "google.com", + ), + getNetworkCaptureRules().single() + ) + } + } + @Test fun testRemoteAndLocal() { with(fakeNetworkBehavior(localCfg = { local }, remoteCfg = { remote })) { assertEquals(409, getNetworkCaptureLimit()) - assertEquals(mapOf("google.com" to 50), getNetworkCallLimitsPerDomain()) + assertEquals(mapOf("google.com" to 50), getNetworkCallLimitsPerDomainSuffix()) assertTrue(isUrlEnabled("google.com")) assertFalse(isUrlEnabled("example.com")) assertEquals( diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt index 761644f91..5a0742fae 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakePreferenceService.kt @@ -19,7 +19,6 @@ internal class FakePreferenceService( @Deprecated("") override var customPersonas: Set? = null, override var lastConfigFetchDate: Long? = null, override var userMessageNeedsRetry: Boolean = false, - override var sessionNumber: Int = 0, override var reactNativeVersionNumber: String? = null, override var unityVersionNumber: String? = null, override var unityBuildIdNumber: String? = null, @@ -34,7 +33,8 @@ internal class FakePreferenceService( override var jailbroken: Boolean? = null, override var applicationExitInfoHistory: Set? = null, override var cpuName: String? = null, - override var egl: String? = null + override var egl: String? = null, + val sessionNumber: () -> Int = { 0 } ) : PreferencesService { var networkCaptureRuleOver = false @@ -47,5 +47,7 @@ internal class FakePreferenceService( override fun decreaseNetworkCaptureRuleRemainingCount(id: String, maxCount: Int) { } + override fun getIncrementAndGetSessionNumber(): Int = sessionNumber() + override fun isUsersFirstDay(): Boolean = firstDay } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingServiceTest.kt index faecdfa04..aeb39346d 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingServiceTest.kt @@ -2,79 +2,45 @@ package io.embrace.android.embracesdk.network.logging import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.config.local.DomainLocalConfig -import io.embrace.android.embracesdk.config.local.LocalConfig import io.embrace.android.embracesdk.config.local.NetworkLocalConfig import io.embrace.android.embracesdk.config.local.SdkLocalConfig -import io.embrace.android.embracesdk.fakes.FakeAndroidMetadataService +import io.embrace.android.embracesdk.config.remote.NetworkRemoteConfig +import io.embrace.android.embracesdk.config.remote.RemoteConfig +import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.fakeNetworkBehavior import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.payload.NetworkSessionV2.DomainCount -import io.embrace.android.embracesdk.session.MemoryCleanerService import io.embrace.android.embracesdk.utils.at -import io.mockk.clearAllMocks -import io.mockk.every import io.mockk.mockk -import io.mockk.unmockkAll import io.mockk.verify -import org.junit.AfterClass import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before -import org.junit.BeforeClass import org.junit.Test import java.util.UUID internal class EmbraceNetworkLoggingServiceTest { - private lateinit var service: EmbraceNetworkLoggingService - - companion object { - private lateinit var configService: ConfigService - private lateinit var localConfig: LocalConfig - private lateinit var memoryCleanerService: MemoryCleanerService - private lateinit var metadataService: FakeAndroidMetadataService - private lateinit var logger: InternalEmbraceLogger - private lateinit var networkCaptureService: EmbraceNetworkCaptureService - private lateinit var cfg: SdkLocalConfig - - @BeforeClass - @JvmStatic - fun beforeClass() { - configService = mockk(relaxed = true) { - every { networkBehavior } returns fakeNetworkBehavior( - localCfg = { cfg } - ) - } - localConfig = mockk(relaxed = true) - memoryCleanerService = mockk(relaxed = true) - logger = InternalEmbraceLogger() - metadataService = FakeAndroidMetadataService() - networkCaptureService = mockk(relaxed = true) - } - - @AfterClass - @JvmStatic - fun tearDown() { - unmockkAll() - } - } + private lateinit var networkLoggingService: EmbraceNetworkLoggingService + private lateinit var logger: InternalEmbraceLogger + private lateinit var configService: ConfigService + private lateinit var sdkLocalConfig: SdkLocalConfig + private lateinit var remoteConfig: RemoteConfig + private lateinit var mockNetworkCaptureService: EmbraceNetworkCaptureService @Before fun setUp() { - cfg = SdkLocalConfig(networking = NetworkLocalConfig()) - - clearAllMocks( - answers = false, - objectMocks = false, - constructorMocks = false, - staticMocks = false + mockNetworkCaptureService = mockk(relaxed = true) + logger = InternalEmbraceLogger() + configService = FakeConfigService( + networkBehavior = fakeNetworkBehavior( + localCfg = { sdkLocalConfig }, + remoteCfg = { remoteConfig } + ) ) - service = - EmbraceNetworkLoggingService( - configService, - logger, - networkCaptureService - ) + sdkLocalConfig = SdkLocalConfig() + remoteConfig = RemoteConfig() + createNetworkLoggingService() } @Test @@ -84,7 +50,7 @@ internal class EmbraceNetworkLoggingServiceTest { logNetworkCall("www.example3.com", 300, 400) logNetworkCall("www.example4.com", 400, 500) - val result = service.getNetworkCallsForSession() + val result = networkLoggingService.getNetworkCallsForSession() assertEquals(4, result.requests.size) val sortedRequests = result.requests.sortedBy { it.startTime } @@ -95,55 +61,273 @@ internal class EmbraceNetworkLoggingServiceTest { } @Test - fun `test getNetworkCallsForSession over limit`() { - every { configService.networkBehavior.getNetworkCaptureLimit() }.returns( - 2 + fun `test implicit default network call limits`() { + repeat(1005) { + logNetworkCall("www.overLimit1.com") + } + logNetworkCall("www.overLimit2.com") + + val result = networkLoggingService.getNetworkCallsForSession() + + assertEquals(1001, result.requests.size) + assertEquals(DomainCount(1005, 1000), result.requestCounts["overLimit1.com"]) + assertNull(result.requestCounts["overLimit2.com"]) + } + + @Test + fun `test domain specific local limits`() { + sdkLocalConfig = SdkLocalConfig( + networking = NetworkLocalConfig( + domains = listOf(DomainLocalConfig("overLimit1.com", 2)) + ) ) - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit2.com") - logNetworkCall("www.overLimit2.com") - logNetworkCall("www.overLimit3.com") + createNetworkLoggingService() - val result = service.getNetworkCallsForSession() + repeat(3) { + logNetworkCall("www.overLimit1.com") + } - // overLimit1 has 4 calls. The limit is 2. - val expectedOverLimit = DomainCount(4, 2) - assertEquals(1, result.requestCounts.size) + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(2, result.requests.size) + assertEquals(DomainCount(3, 2), result.requestCounts["overLimit1.com"]) + } + + @Test + fun `test default local limits`() { + sdkLocalConfig = SdkLocalConfig( + networking = NetworkLocalConfig( + defaultCaptureLimit = 2 + ) + ) + + createNetworkLoggingService() + + repeat(4) { + logNetworkCall("www.overLimit1.com") + } - assertEquals(expectedOverLimit, result.requestCounts["overLimit1.com"]) + logNetworkCall("www.overLimit2.com") + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(3, result.requests.size) + assertEquals(DomainCount(4, 2), result.requestCounts["overLimit1.com"]) assertNull(result.requestCounts["overLimit2.com"]) + } + + @Test + fun `test local limits with default and domain specific limits`() { + sdkLocalConfig = SdkLocalConfig( + networking = NetworkLocalConfig( + defaultCaptureLimit = 2, + domains = listOf(DomainLocalConfig("overLimit1.com", 3)) + ) + ) + + createNetworkLoggingService() + + repeat(4) { + logNetworkCall("www.overLimit1.com") + } + + repeat(3) { + logNetworkCall("www.overLimit2.com") + } + + repeat(2) { + logNetworkCall("www.overLimit3.com") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(7, result.requests.size) + assertEquals(DomainCount(4, 3), result.requestCounts["overLimit1.com"]) + assertEquals(DomainCount(3, 2), result.requestCounts["overLimit2.com"]) assertNull(result.requestCounts["overLimit3.com"]) } @Test - fun `test getNetworkCallsForSession merged limits`() { - cfg = SdkLocalConfig( + fun `test explicit remote limits as a ceiling for local limit`() { + sdkLocalConfig = SdkLocalConfig( networking = NetworkLocalConfig( - domains = listOf(DomainLocalConfig("overLimit1.com", 2)) + domains = listOf(DomainLocalConfig("limited.org", 30)), + defaultCaptureLimit = 20 ) ) - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit1.com") - logNetworkCall("www.overLimit2.com") - logNetworkCall("www.overLimit2.com") - logNetworkCall("www.overLimit3.com") + remoteConfig = RemoteConfig( + networkConfig = NetworkRemoteConfig( + domainLimits = mapOf("limited.org" to 10), + defaultCaptureLimit = 5 + ) + ) - val result = service.getNetworkCallsForSession() + createNetworkLoggingService() + + repeat(30) { + logNetworkCall("www.limited.org") + logNetworkCall("www.verylimited.com") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(15, result.requests.size) + assertEquals(DomainCount(30, 10), result.requestCounts["limited.org"]) + assertEquals(DomainCount(30, 5), result.requestCounts["verylimited.com"]) + } + + @Test + fun `test explicit remote default limit as a ceiling for local limit`() { + sdkLocalConfig = SdkLocalConfig( + networking = NetworkLocalConfig( + domains = listOf(DomainLocalConfig("limited.org", 30)), + defaultCaptureLimit = 20 + ) + ) + + remoteConfig = RemoteConfig( + networkConfig = NetworkRemoteConfig( + defaultCaptureLimit = 5 + ) + ) - // overLimit1 has 4 calls. The local limit is 2. - val expectedOverLimit = DomainCount(4, 2) + createNetworkLoggingService() + + repeat(30) { + logNetworkCall("www.limited.org") + logNetworkCall("www.verylimited.com") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(10, result.requests.size) + assertEquals(DomainCount(30, 5), result.requestCounts["limited.org"]) + assertEquals(DomainCount(30, 5), result.requestCounts["verylimited.com"]) + } + + @Test + fun `test remote domain limit as a ceiling for local limit`() { + sdkLocalConfig = SdkLocalConfig( + networking = NetworkLocalConfig( + domains = listOf(DomainLocalConfig("limited.org", 25)), + defaultCaptureLimit = 15 + ) + ) + + remoteConfig = RemoteConfig( + networkConfig = NetworkRemoteConfig( + domainLimits = mapOf("limited.org" to 10) + ) + ) + + createNetworkLoggingService() + + repeat(30) { + logNetworkCall("www.limited.org") + logNetworkCall("www.defaultlimit.com") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(25, result.requests.size) + assertEquals(DomainCount(30, 10), result.requestCounts["limited.org"]) + assertEquals(DomainCount(30, 15), result.requestCounts["defaultlimit.com"]) + } + + @Test + fun `test implicit remote limit as a ceiling for local limit`() { + sdkLocalConfig = SdkLocalConfig( + networking = NetworkLocalConfig( + domains = listOf(DomainLocalConfig("limited.org", 2000)), + defaultCaptureLimit = 1500 + ) + ) + + remoteConfig = RemoteConfig() + + repeat(2001) { + logNetworkCall("www.limited.org") + logNetworkCall("www.verylimited.com") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(2000, result.requests.size) + assertEquals(DomainCount(2001, 1000), result.requestCounts["limited.org"]) + assertEquals(DomainCount(2001, 1000), result.requestCounts["verylimited.com"]) + } + + @Test + fun `limit applies to all domains with a given suffix`() { + remoteConfig = RemoteConfig( + networkConfig = NetworkRemoteConfig( + domainLimits = mapOf("limited.org" to 15), + defaultCaptureLimit = 10 + ) + ) + + createNetworkLoggingService() + + repeat(8) { + logNetworkCall("www.limited.org") + logNetworkCall("admin.limited.org") + logNetworkCall("verylimited.org") + logNetworkCall("woopwoop.com") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(23, result.requests.size) assertEquals(1, result.requestCounts.size) + assertEquals(DomainCount(24, 15), result.requestCounts["limited.org"]) + } - assertEquals(expectedOverLimit, result.requestCounts["overLimit1.com"]) - assertNull(result.requestCounts["overLimit2.com"]) - assertNull(result.requestCounts["overLimit3.com"]) + @Test + fun `fetching session doesn't reset limit`() { + remoteConfig = RemoteConfig( + networkConfig = NetworkRemoteConfig( + defaultCaptureLimit = 5 + ) + ) + + createNetworkLoggingService() + + repeat(6) { + logNetworkCall("www.limited.org") + } + + networkLoggingService.getNetworkCallsForSession() + + repeat(6) { + logNetworkCall("www.limited.org") + } + + val result = networkLoggingService.getNetworkCallsForSession() + assertEquals(5, result.requests.size) + assertEquals(DomainCount(12, 5), result.requestCounts["limited.org"]) + } + + @Test + fun `clearing service resets the limit`() { + remoteConfig = RemoteConfig( + networkConfig = NetworkRemoteConfig( + defaultCaptureLimit = 5 + ) + ) + + createNetworkLoggingService() + + repeat(10) { + logNetworkCall("www.limited.org") + } + + val firstSession = networkLoggingService.getNetworkCallsForSession() + assertEquals(5, firstSession.requests.size) + assertEquals(DomainCount(10, 5), firstSession.requestCounts["limited.org"]) + + networkLoggingService.cleanCollections() + + repeat(6) { + logNetworkCall("www.limited.org") + } + + val secondSession = networkLoggingService.getNetworkCallsForSession() + assertEquals(5, secondSession.requests.size) + assertEquals(DomainCount(6, 5), secondSession.requestCounts["limited.org"]) } @Test @@ -153,7 +337,7 @@ internal class EmbraceNetworkLoggingServiceTest { val startTime = 10000L val endTime = 20000L - service.logNetworkError( + networkLoggingService.logNetworkError( randomId(), url, httpMethod, @@ -166,14 +350,14 @@ internal class EmbraceNetworkLoggingServiceTest { null ) - val result = service.getNetworkCallsForSession() + val result = networkLoggingService.getNetworkCallsForSession() assertEquals(url, result.requests.at(0)?.url) } @Test fun `test logNetworkCall sends the network body if necessary`() { - service.logNetworkCall( + networkLoggingService.logNetworkCall( randomId(), "www.example.com", "GET", @@ -188,7 +372,7 @@ internal class EmbraceNetworkLoggingServiceTest { ) verify(exactly = 1) { - networkCaptureService.logNetworkCapturedData( + mockNetworkCaptureService.logNetworkCapturedData( any(), any(), any(), @@ -201,7 +385,7 @@ internal class EmbraceNetworkLoggingServiceTest { @Test fun `test logNetworkCall doesn't send the network body if null`() { - service.logNetworkCall( + networkLoggingService.logNetworkCall( randomId(), "www.example.com", "GET", @@ -216,7 +400,7 @@ internal class EmbraceNetworkLoggingServiceTest { ) verify(exactly = 0) { - networkCaptureService.logNetworkCapturedData( + mockNetworkCaptureService.logNetworkCapturedData( any(), any(), any(), @@ -234,9 +418,9 @@ internal class EmbraceNetworkLoggingServiceTest { logNetworkCall("www.example.com") logNetworkCall("www.example.com") - service.cleanCollections() + networkLoggingService.cleanCollections() - val result = service.getNetworkCallsForSession() + val result = networkLoggingService.getNetworkCallsForSession() assertEquals(0, result.requests.size) assertEquals(0, result.requestCounts.size) @@ -254,7 +438,7 @@ internal class EmbraceNetworkLoggingServiceTest { logNetworkError(url = "https://embrace.io", startTime = startTime) } - assertEquals(4, service.getNetworkCallsForSession().requests.size) + assertEquals(4, networkLoggingService.getNetworkCallsForSession().requests.size) } @Test @@ -268,7 +452,7 @@ internal class EmbraceNetworkLoggingServiceTest { logNetworkError(url = "https://embrace.io", startTime = 50, callId = callId) logNetworkCall(url = expectedUrl, startTime = expectedStartTime, endTime = expectedEndTime, callId = callId) - val result = service.getNetworkCallsForSession() + val result = networkLoggingService.getNetworkCallsForSession() assertEquals(1, result.requests.size) with(result.requests[0]) { assertEquals(1, result.requests.size) @@ -278,8 +462,17 @@ internal class EmbraceNetworkLoggingServiceTest { } } + private fun createNetworkLoggingService() { + networkLoggingService = + EmbraceNetworkLoggingService( + configService, + logger, + mockNetworkCaptureService + ) + } + private fun logNetworkCall(url: String, startTime: Long = 100, endTime: Long = 200, callId: String = randomId()) { - service.logNetworkCall( + networkLoggingService.logNetworkCall( callId, url, "GET", @@ -295,7 +488,7 @@ internal class EmbraceNetworkLoggingServiceTest { } private fun logNetworkError(url: String, startTime: Long = 100, callId: String = randomId()) { - service.logNetworkError( + networkLoggingService.logNetworkError( callId, url, "GET", diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesServiceTest.kt index 6dbfd22af..4e1e8b397 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/prefs/EmbracePreferencesServiceTest.kt @@ -188,13 +188,10 @@ internal class EmbracePreferencesServiceTest { @Test fun `test session number is saved`() { - assertEquals(0, service.sessionNumber) - - service.sessionNumber = 1234 - assertEquals(1234, service.sessionNumber) - - service.sessionNumber = -1 - assertEquals(0, service.sessionNumber) + assertEquals(1, service.getIncrementAndGetSessionNumber()) + assertEquals(2, service.getIncrementAndGetSessionNumber()) + assertEquals(3, service.getIncrementAndGetSessionNumber()) + assertEquals(4, service.getIncrementAndGetSessionNumber()) } @Test diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt index ecf67e161..3ed23f783 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt @@ -20,7 +20,6 @@ import io.embrace.android.embracesdk.fakes.FakeAndroidMetadataService import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeGatingService -import io.embrace.android.embracesdk.fakes.FakePreferenceService import io.embrace.android.embracesdk.fakes.FakeUserService import io.embrace.android.embracesdk.fakes.fakeAutoDataCaptureBehavior import io.embrace.android.embracesdk.fakes.fakeDataCaptureEventBehavior @@ -36,6 +35,7 @@ import io.embrace.android.embracesdk.ndk.NdkService import io.embrace.android.embracesdk.payload.Session import io.embrace.android.embracesdk.payload.SessionMessage import io.embrace.android.embracesdk.payload.UserInfo +import io.embrace.android.embracesdk.prefs.PreferencesService import io.embrace.android.embracesdk.session.EmbraceSessionService.Companion.SESSION_CACHING_INTERVAL import io.embrace.android.embracesdk.session.properties.EmbraceSessionProperties import io.mockk.Called @@ -66,7 +66,7 @@ internal class SessionHandlerTest { companion object { private val logger: InternalEmbraceLogger = InternalEmbraceLogger() - private val preferencesService: FakePreferenceService = FakePreferenceService() + private val preferencesService: PreferencesService = mockk(relaxed = true) private val mockUserService: FakeUserService = FakeUserService() private val mockNetworkConnectivityService: NetworkConnectivityService = mockk(relaxUnitFun = true) @@ -86,7 +86,7 @@ internal class SessionHandlerTest { private val mockSessionPeriodicCacheExecutorService: ScheduledExecutorService = mockk(relaxed = true) private const val sessionUuid = "99fcae22-0db5-4b63-b49d-315eecce4889" private const val now = 123L - private const val sessionNumber = 5 + private var sessionNumber = 5 private val mockSessionProperties: EmbraceSessionProperties = mockk(relaxed = true) private val emptyMapSessionProperties: Map = emptyMap() private val mockUserInfo: UserInfo = mockk() @@ -123,7 +123,6 @@ internal class SessionHandlerTest { @Before fun before() { mockActiveSession = mockk(relaxed = true) - preferencesService.sessionNumber = sessionNumber every { mockSessionProperties.get() } returns emptyMapSessionProperties metadataService = FakeAndroidMetadataService() @@ -238,7 +237,6 @@ internal class SessionHandlerTest { with(checkNotNull(sessionMessage?.session)) { assertEquals(sessionUuid, this.sessionId) assertEquals(startTime, now) - assertEquals(sessionNumber + 1, number) assertTrue(isColdStart) assertEquals(sessionStartType, startType) assertEquals(emptyMapSessionProperties, properties) @@ -251,6 +249,7 @@ internal class SessionHandlerTest { assertEquals(metadataService.getDeviceInfo(), deviceInfo) assertEquals(metadataService.getAppInfo(), appInfo) } + verify(exactly = 1) { preferencesService.getIncrementAndGetSessionNumber() } } @Test @@ -280,7 +279,7 @@ internal class SessionHandlerTest { @Test fun `onSession started successfully with no preference service session number`() { // return absent session number - preferencesService.sessionNumber = 0 + sessionNumber = 0 sessionLocalConfig = SessionLocalConfig(maxSessionSeconds = 5, asyncEnd = false) every { mockBreadcrumbService.getLastViewBreadcrumbScreenName() } returns "screen" val sessionStartType = Session.SessionLifeEventType.STATE @@ -295,7 +294,7 @@ internal class SessionHandlerTest { mockPeriodicCachingRunnable ) - assertEquals(1, preferencesService.sessionNumber) + verify(exactly = 1) { preferencesService.getIncrementAndGetSessionNumber() } assertNotNull(sessionMessage) assertNotNull(sessionMessage!!.session) // no need to verify anything else because it's already verified in another test case