diff --git a/CHANGELOG.md b/CHANGELOG.md index a0307d0e0e6..570cd9cc00a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes +- Avoid StrictMode warnings ([#4724](https://github.com/getsentry/sentry-java/pull/4724)) - Use logger from options for JVM profiler ([#4771](https://github.com/getsentry/sentry-java/pull/4771)) - Session Replay: Avoid deadlock when pausing replay if no connection ([#4788](https://github.com/getsentry/sentry-java/pull/4788)) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 70c76c72824..02006d0924b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -30,6 +30,7 @@ import io.sentry.android.core.internal.modules.AssetsModulesLoader; import io.sentry.android.core.internal.util.AndroidConnectionStatusProvider; import io.sentry.android.core.internal.util.AndroidCurrentDateProvider; +import io.sentry.android.core.internal.util.AndroidRuntimeManager; import io.sentry.android.core.internal.util.AndroidThreadChecker; import io.sentry.android.core.internal.util.SentryFrameMetricsCollector; import io.sentry.android.core.performance.AppStartMetrics; @@ -108,7 +109,7 @@ static void loadDefaultAndMetadataOptions( final @NotNull BuildInfoProvider buildInfoProvider) { Objects.requireNonNull(context, "The context is required."); - context = ContextUtils.getApplicationContext(context); + @NotNull final Context finalContext = ContextUtils.getApplicationContext(context); Objects.requireNonNull(options, "The options object is required."); Objects.requireNonNull(logger, "The ILogger object is required."); @@ -120,17 +121,22 @@ static void loadDefaultAndMetadataOptions( options.setDefaultScopeType(ScopeType.CURRENT); options.setOpenTelemetryMode(SentryOpenTelemetryMode.OFF); options.setDateProvider(new SentryAndroidDateProvider()); + options.setRuntimeManager(new AndroidRuntimeManager()); // set a lower flush timeout on Android to avoid ANRs options.setFlushTimeoutMillis(DEFAULT_FLUSH_TIMEOUT_MS); options.setFrameMetricsCollector( - new SentryFrameMetricsCollector(context, logger, buildInfoProvider)); + new SentryFrameMetricsCollector(finalContext, logger, buildInfoProvider)); - ManifestMetadataReader.applyMetadata(context, options, buildInfoProvider); - options.setCacheDirPath(getCacheDir(context).getAbsolutePath()); + ManifestMetadataReader.applyMetadata(finalContext, options, buildInfoProvider); - readDefaultOptionValues(options, context, buildInfoProvider); + options.setCacheDirPath( + options + .getRuntimeManager() + .runWithRelaxedPolicy(() -> getCacheDir(finalContext).getAbsolutePath())); + + readDefaultOptionValues(options, finalContext, buildInfoProvider); AppState.getInstance().registerLifecycleObserver(options); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java index 3c162aab1ad..12ec55f8e1b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java @@ -20,10 +20,12 @@ import io.sentry.SentryOptions; import io.sentry.TracesSampler; import io.sentry.TracesSamplingDecision; +import io.sentry.android.core.internal.util.AndroidRuntimeManager; import io.sentry.android.core.internal.util.SentryFrameMetricsCollector; import io.sentry.android.core.performance.AppStartMetrics; import io.sentry.android.core.performance.TimeSpan; import io.sentry.util.AutoClosableReentrantLock; +import io.sentry.util.runtime.IRuntimeManager; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -108,7 +110,9 @@ private void launchAppStartProfiler(final @NotNull AppStartMetrics appStartMetri return; } - final @NotNull File cacheDir = AndroidOptionsInitializer.getCacheDir(context); + final @NotNull IRuntimeManager runtimeManager = new AndroidRuntimeManager(); + final @NotNull File cacheDir = + runtimeManager.runWithRelaxedPolicy(() -> AndroidOptionsInitializer.getCacheDir(context)); final @NotNull File configFile = new File(cacheDir, APP_START_PROFILING_CONFIG_FILE_NAME); // No config exists: app start profiling is not enabled diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java b/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java index 2829abc50c9..af57727a8d2 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java @@ -19,6 +19,7 @@ import io.sentry.util.HintUtils; import io.sentry.util.Objects; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import org.jetbrains.annotations.ApiStatus; @@ -157,18 +158,18 @@ public static boolean hasStartupCrashMarker(final @NotNull SentryOptions options final File lastAnrMarker = new File(cacheDirPath, LAST_ANR_REPORT); try { - if (lastAnrMarker.exists() && lastAnrMarker.canRead()) { - final String content = FileUtils.readText(lastAnrMarker); - // we wrapped into try-catch already - //noinspection ConstantConditions - return content.equals("null") ? null : Long.parseLong(content.trim()); - } else { + final String content = FileUtils.readText(lastAnrMarker); + // we wrapped into try-catch already + //noinspection ConstantConditions + return content.equals("null") ? null : Long.parseLong(content.trim()); + } catch (Throwable e) { + if (e instanceof FileNotFoundException) { options .getLogger() .log(DEBUG, "Last ANR marker does not exist. %s.", lastAnrMarker.getAbsolutePath()); + } else { + options.getLogger().log(ERROR, "Error reading last ANR marker", e); } - } catch (Throwable e) { - options.getLogger().log(ERROR, "Error reading last ANR marker", e); } return null; } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidRuntimeManager.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidRuntimeManager.java new file mode 100644 index 00000000000..b24ac70b5b0 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidRuntimeManager.java @@ -0,0 +1,23 @@ +package io.sentry.android.core.internal.util; + +import android.os.StrictMode; +import io.sentry.util.runtime.IRuntimeManager; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public final class AndroidRuntimeManager implements IRuntimeManager { + @Override + public T runWithRelaxedPolicy(final @NotNull IRuntimeManagerCallback toRun) { + final @NotNull StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + final @NotNull StrictMode.VmPolicy oldVmPolicy = StrictMode.getVmPolicy(); + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX); + StrictMode.setVmPolicy(StrictMode.VmPolicy.LAX); + try { + return toRun.run(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + StrictMode.setVmPolicy(oldVmPolicy); + } + } +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CpuInfoUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CpuInfoUtils.java index 019db99fc7d..c289a9c06ac 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CpuInfoUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CpuInfoUtils.java @@ -51,8 +51,6 @@ private CpuInfoUtils() {} if (!cpuDir.getName().matches("cpu[0-9]+")) continue; File cpuMaxFreqFile = new File(cpuDir, CPUINFO_MAX_FREQ_PATH); - if (!cpuMaxFreqFile.exists() || !cpuMaxFreqFile.canRead()) continue; - long khz; try { String content = FileUtils.readText(cpuMaxFreqFile); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index d49a905772d..dc6926c1b17 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -24,6 +24,7 @@ import io.sentry.android.core.internal.debugmeta.AssetsDebugMetaLoader import io.sentry.android.core.internal.gestures.AndroidViewGestureTargetLocator import io.sentry.android.core.internal.modules.AssetsModulesLoader import io.sentry.android.core.internal.util.AndroidConnectionStatusProvider +import io.sentry.android.core.internal.util.AndroidRuntimeManager import io.sentry.android.core.internal.util.AndroidThreadChecker import io.sentry.android.core.performance.AppStartMetrics import io.sentry.android.fragment.FragmentLifecycleIntegration @@ -917,4 +918,10 @@ class AndroidOptionsInitializerTest { fixture.sentryOptions.compositePerformanceCollector is DefaultCompositePerformanceCollector } } + + @Test + fun `AndroidRuntimeManager is set in the options`() { + fixture.initSut() + assertIs(fixture.sentryOptions.runtimeManager) + } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidRuntimeManagerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidRuntimeManagerTest.kt new file mode 100644 index 00000000000..427d1287423 --- /dev/null +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidRuntimeManagerTest.kt @@ -0,0 +1,87 @@ +package io.sentry.android.core.internal.util + +import android.os.StrictMode +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class AndroidRuntimeManagerTest { + + val sut = AndroidRuntimeManager() + + @AfterTest + fun `clean up`() { + // Revert StrictMode policies to avoid issues with other tests + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX) + StrictMode.setVmPolicy(StrictMode.VmPolicy.LAX) + } + + @Test + fun `runWithRelaxedPolicy changes policy when running and restores it afterwards`() { + var called = false + val threadPolicy = StrictMode.ThreadPolicy.Builder().detectAll().penaltyDeath().build() + val vmPolicy = StrictMode.VmPolicy.Builder().detectAll().penaltyDeath().build() + assertNotEquals(StrictMode.ThreadPolicy.LAX, threadPolicy) + assertNotEquals(StrictMode.VmPolicy.LAX, vmPolicy) + + // Set and assert the StrictMode policies + StrictMode.setThreadPolicy(threadPolicy) + StrictMode.setVmPolicy(vmPolicy) + assertEquals(threadPolicy.toString(), StrictMode.getThreadPolicy().toString()) + assertEquals(vmPolicy.toString(), StrictMode.getVmPolicy().toString()) + + // Run the function and assert LAX policies + called = + sut.runWithRelaxedPolicy { + assertEquals( + StrictMode.ThreadPolicy.LAX.toString(), + StrictMode.getThreadPolicy().toString(), + ) + assertEquals(StrictMode.VmPolicy.LAX.toString(), StrictMode.getVmPolicy().toString()) + true + } + + // Policies should be reverted back + assertEquals(threadPolicy.toString(), StrictMode.getThreadPolicy().toString()) + assertEquals(vmPolicy.toString(), StrictMode.getVmPolicy().toString()) + + // Ensure the code ran + assertTrue(called) + } + + @Test + fun `runWithRelaxedPolicy changes policy and restores it afterwards even if the code throws`() { + var called = false + val threadPolicy = StrictMode.ThreadPolicy.Builder().detectAll().penaltyDeath().build() + val vmPolicy = StrictMode.VmPolicy.Builder().detectAll().penaltyDeath().build() + + // Set and assert the StrictMode policies + StrictMode.setThreadPolicy(threadPolicy) + StrictMode.setVmPolicy(vmPolicy) + + // Run the function and assert LAX policies + try { + sut.runWithRelaxedPolicy { + assertEquals( + StrictMode.ThreadPolicy.LAX.toString(), + StrictMode.getThreadPolicy().toString(), + ) + assertEquals(StrictMode.VmPolicy.LAX.toString(), StrictMode.getVmPolicy().toString()) + called = true + throw Exception("Test exception") + } + } catch (_: Exception) {} + + // Policies should be reverted back + assertEquals(threadPolicy.toString(), StrictMode.getThreadPolicy().toString()) + assertEquals(vmPolicy.toString(), StrictMode.getVmPolicy().toString()) + + // Ensure the code ran + assertTrue(called) + } +} diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt index d3a60d2c198..8a6dc50c1fa 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt @@ -1,5 +1,6 @@ package io.sentry.uitest.android +import android.os.StrictMode import androidx.lifecycle.Lifecycle import androidx.test.core.app.launchActivity import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -252,6 +253,15 @@ class SdkInitTests : BaseUiTest() { assertDefaultIntegrations() } + @Test + fun initNotThrowStrictMode() { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().detectAll().penaltyDeath().build()) + StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder().detectAll().penaltyDeath().build()) + val sampleScenario = launchActivity() + initSentry() + sampleScenario.moveToState(Lifecycle.State.DESTROYED) + } + private fun assertDefaultIntegrations() { val integrations = mutableListOf( diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 23b75e9aa05..3964619e0b9 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3394,6 +3394,7 @@ public class io/sentry/SentryOptions { public fun getReadTimeoutMillis ()I public fun getRelease ()Ljava/lang/String; public fun getReplayController ()Lio/sentry/ReplayController; + public fun getRuntimeManager ()Lio/sentry/util/runtime/IRuntimeManager; public fun getSampleRate ()Ljava/lang/Double; public fun getScopeObservers ()Ljava/util/List; public fun getSdkVersion ()Lio/sentry/protocol/SdkVersion; @@ -3539,6 +3540,7 @@ public class io/sentry/SentryOptions { public fun setReadTimeoutMillis (I)V public fun setRelease (Ljava/lang/String;)V public fun setReplayController (Lio/sentry/ReplayController;)V + public fun setRuntimeManager (Lio/sentry/util/runtime/IRuntimeManager;)V public fun setSampleRate (Ljava/lang/Double;)V public fun setSdkVersion (Lio/sentry/protocol/SdkVersion;)V public fun setSendClientReports (Z)V @@ -7293,6 +7295,19 @@ public final class io/sentry/util/UrlUtils$UrlDetails { public fun getUrlOrFallback ()Ljava/lang/String; } +public abstract interface class io/sentry/util/runtime/IRuntimeManager { + public abstract fun runWithRelaxedPolicy (Lio/sentry/util/runtime/IRuntimeManager$IRuntimeManagerCallback;)Ljava/lang/Object; +} + +public abstract interface class io/sentry/util/runtime/IRuntimeManager$IRuntimeManagerCallback { + public abstract fun run ()Ljava/lang/Object; +} + +public final class io/sentry/util/runtime/NeutralRuntimeManager : io/sentry/util/runtime/IRuntimeManager { + public fun ()V + public fun runWithRelaxedPolicy (Lio/sentry/util/runtime/IRuntimeManager$IRuntimeManagerCallback;)Ljava/lang/Object; +} + public abstract interface class io/sentry/util/thread/IThreadChecker { public abstract fun currentThreadSystemId ()J public abstract fun getCurrentThreadName ()Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/DirectoryProcessor.java b/sentry/src/main/java/io/sentry/DirectoryProcessor.java index a6bb258f30d..1bc277b18cd 100644 --- a/sentry/src/main/java/io/sentry/DirectoryProcessor.java +++ b/sentry/src/main/java/io/sentry/DirectoryProcessor.java @@ -40,34 +40,22 @@ public void processDirectory(final @NotNull File directory) { try { logger.log(SentryLevel.DEBUG, "Processing dir. %s", directory.getAbsolutePath()); - if (!directory.exists()) { + final File[] filteredListFiles = directory.listFiles((d, name) -> isRelevantFileName(name)); + if (filteredListFiles == null) { logger.log( - SentryLevel.WARNING, - "Directory '%s' doesn't exist. No cached events to send.", + SentryLevel.ERROR, + "Cache dir %s is null or is not a directory.", directory.getAbsolutePath()); return; } - if (!directory.isDirectory()) { - logger.log( - SentryLevel.ERROR, "Cache dir %s is not a directory.", directory.getAbsolutePath()); - return; - } - - final File[] listFiles = directory.listFiles(); - if (listFiles == null) { - logger.log(SentryLevel.ERROR, "Cache dir %s is null.", directory.getAbsolutePath()); - return; - } - - final File[] filteredListFiles = directory.listFiles((d, name) -> isRelevantFileName(name)); logger.log( SentryLevel.DEBUG, "Processing %d items from cache dir %s", - filteredListFiles != null ? filteredListFiles.length : 0, + filteredListFiles.length, directory.getAbsolutePath()); - for (File file : listFiles) { + for (File file : filteredListFiles) { // it ignores .sentry-native database folder and new ones that might come up if (!file.isFile()) { logger.log(SentryLevel.DEBUG, "File %s is not a File.", file.getAbsolutePath()); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 4aee71715d7..a28ee936e62 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -580,7 +580,8 @@ private static boolean preInitConfigurations(final @NotNull SentryOptions option return true; } - @SuppressWarnings("FutureReturnValueIgnored") + // older AGP versions do not support method references + @SuppressWarnings({"FutureReturnValueIgnored", "Convert2MethodRef"}) private static void initConfigurations(final @NotNull SentryOptions options) { final @NotNull ILogger logger = options.getLogger(); logger.log(SentryLevel.INFO, "Initializing SDK with DSN: '%s'", options.getDsn()); @@ -592,7 +593,7 @@ private static void initConfigurations(final @NotNull SentryOptions options) { final String outboxPath = options.getOutboxPath(); if (outboxPath != null) { final File outboxDir = new File(outboxPath); - outboxDir.mkdirs(); + options.getRuntimeManager().runWithRelaxedPolicy(() -> outboxDir.mkdirs()); } else { logger.log(SentryLevel.INFO, "No outbox dir path is defined in options."); } @@ -600,7 +601,7 @@ private static void initConfigurations(final @NotNull SentryOptions options) { final String cacheDirPath = options.getCacheDirPath(); if (cacheDirPath != null) { final File cacheDir = new File(cacheDirPath); - cacheDir.mkdirs(); + options.getRuntimeManager().runWithRelaxedPolicy(() -> cacheDir.mkdirs()); final IEnvelopeCache envelopeCache = options.getEnvelopeDiskCache(); // only overwrite the cache impl if it's not already set if (envelopeCache instanceof NoOpEnvelopeCache) { @@ -613,7 +614,7 @@ private static void initConfigurations(final @NotNull SentryOptions options) { && profilingTracesDirPath != null) { final File profilingTracesDir = new File(profilingTracesDirPath); - profilingTracesDir.mkdirs(); + options.getRuntimeManager().runWithRelaxedPolicy(() -> profilingTracesDir.mkdirs()); try { options diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index f868ecacad2..3a81b268b25 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -27,6 +27,8 @@ import io.sentry.util.Platform; import io.sentry.util.SampleRateUtils; import io.sentry.util.StringUtils; +import io.sentry.util.runtime.IRuntimeManager; +import io.sentry.util.runtime.NeutralRuntimeManager; import io.sentry.util.thread.IThreadChecker; import io.sentry.util.thread.NoOpThreadChecker; import java.io.File; @@ -597,6 +599,9 @@ public class SentryOptions { private @NotNull ISocketTagger socketTagger = NoOpSocketTagger.getInstance(); + /** Runtime manager to manage runtime policies, like StrictMode on Android. */ + private @NotNull IRuntimeManager runtimeManager = new NeutralRuntimeManager(); + private @Nullable String profilingTracesDirPath; /** @@ -3004,6 +3009,26 @@ public void setSocketTagger(final @Nullable ISocketTagger socketTagger) { this.socketTagger = socketTagger != null ? socketTagger : NoOpSocketTagger.getInstance(); } + /** + * Returns the IRuntimeManager + * + * @return the runtime manager + */ + @ApiStatus.Internal + public @NotNull IRuntimeManager getRuntimeManager() { + return runtimeManager; + } + + /** + * Sets the IRuntimeManager + * + * @param runtimeManager the runtime manager + */ + @ApiStatus.Internal + public void setRuntimeManager(final @NotNull IRuntimeManager runtimeManager) { + this.runtimeManager = runtimeManager; + } + /** * Load the lazy fields. Useful to load in the background, so that results are already cached. DO * NOT CALL THIS METHOD ON THE MAIN THREAD. diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index d2a32d44cd0..893bef4ab90 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -336,18 +336,17 @@ public void discard(final @NotNull SentryEnvelope envelope) { Objects.requireNonNull(envelope, "Envelope is required."); final File envelopeFile = getEnvelopeFile(envelope); - if (envelopeFile.exists()) { + if (envelopeFile.delete()) { options .getLogger() .log(DEBUG, "Discarding envelope from cache: %s", envelopeFile.getAbsolutePath()); - - if (!envelopeFile.delete()) { - options - .getLogger() - .log(ERROR, "Failed to delete envelope: %s", envelopeFile.getAbsolutePath()); - } } else { - options.getLogger().log(DEBUG, "Envelope was not cached: %s", envelopeFile.getAbsolutePath()); + options + .getLogger() + .log( + DEBUG, + "Envelope was not cached or could not be deleted: %s", + envelopeFile.getAbsolutePath()); } } diff --git a/sentry/src/main/java/io/sentry/util/runtime/IRuntimeManager.java b/sentry/src/main/java/io/sentry/util/runtime/IRuntimeManager.java new file mode 100644 index 00000000000..f6e27cce89b --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/runtime/IRuntimeManager.java @@ -0,0 +1,13 @@ +package io.sentry.util.runtime; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public interface IRuntimeManager { + T runWithRelaxedPolicy(final @NotNull IRuntimeManagerCallback toRun); + + interface IRuntimeManagerCallback { + T run(); + } +} diff --git a/sentry/src/main/java/io/sentry/util/runtime/NeutralRuntimeManager.java b/sentry/src/main/java/io/sentry/util/runtime/NeutralRuntimeManager.java new file mode 100644 index 00000000000..a6112ff60b3 --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/runtime/NeutralRuntimeManager.java @@ -0,0 +1,12 @@ +package io.sentry.util.runtime; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public final class NeutralRuntimeManager implements IRuntimeManager { + @Override + public T runWithRelaxedPolicy(final @NotNull IRuntimeManagerCallback toRun) { + return toRun.run(); + } +} diff --git a/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt b/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt index 4ddc5561af8..dce7c3d5863 100644 --- a/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt +++ b/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt @@ -61,8 +61,8 @@ class EnvelopeSenderTest { val sut = fixture.getSut() sut.processDirectory(File("i don't exist")) verify(fixture.logger)!!.log( - eq(SentryLevel.WARNING), - eq("Directory '%s' doesn't exist. No cached events to send."), + eq(SentryLevel.ERROR), + eq("Cache dir %s is null or is not a directory."), any(), ) verifyNoMoreInteractions(fixture.scopes) @@ -79,14 +79,14 @@ class EnvelopeSenderTest { sut.processDirectory(testFile) verify(fixture.logger)!!.log( eq(SentryLevel.ERROR), - eq("Cache dir %s is not a directory."), + eq("Cache dir %s is null or is not a directory."), any(), ) verifyNoMoreInteractions(fixture.scopes) } @Test - fun `when directory has non event files, processDirectory logs that`() { + fun `when directory has non event files, processDirectory skips them`() { val sut = fixture.getSut() val testFile = File( @@ -96,7 +96,8 @@ class EnvelopeSenderTest { testFile.deleteOnExit() verify(fixture.logger)!!.log( eq(SentryLevel.DEBUG), - eq("File '%s' doesn't match extension expected."), + eq("Processing %d items from cache dir %s"), + eq(0), any(), ) verify(fixture.scopes, never())!!.captureEnvelope(any(), anyOrNull()) diff --git a/sentry/src/test/java/io/sentry/util/runtime/NeutralRuntimeManagerTest.kt b/sentry/src/test/java/io/sentry/util/runtime/NeutralRuntimeManagerTest.kt new file mode 100644 index 00000000000..a645915d7ab --- /dev/null +++ b/sentry/src/test/java/io/sentry/util/runtime/NeutralRuntimeManagerTest.kt @@ -0,0 +1,19 @@ +package io.sentry.util.runtime + +import kotlin.test.Test +import kotlin.test.assertTrue + +class NeutralRuntimeManagerTest { + + val sut = NeutralRuntimeManager() + + @Test + fun `runWithRelaxedPolicy runs the code`() { + var called = false + + called = sut.runWithRelaxedPolicy { true } + + // Ensure the code ran + assertTrue(called) + } +}