From 1d778b55ed111f46086603455d1dffc3910da6e7 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 26 Mar 2021 13:28:09 +0100 Subject: [PATCH] Add option to ignore exceptions by type. (#1352) Fixes #1306 --- CHANGELOG.md | 2 + .../spring/boot/SentryAutoConfiguration.java | 5 + .../boot/SentryAutoConfigurationTest.kt | 5 +- sentry/api/sentry.api | 4 +- sentry/src/main/java/io/sentry/Sentry.java | 2 +- .../src/main/java/io/sentry/SentryClient.java | 14 +- .../main/java/io/sentry/SentryOptions.java | 66 ++++- .../test/java/io/sentry/SentryClientTest.kt | 10 + .../test/java/io/sentry/SentryOptionsTest.kt | 230 ++++++------------ 9 files changed, 171 insertions(+), 167 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f69c63976d..e160a01caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +* Feat: Add option to ignore exceptions by type (#1352) + # 4.4.0-alpha.1 * Bump: sentry-native to 0.4.8 diff --git a/sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java b/sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java index 4509216f91..3374909e26 100644 --- a/sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java +++ b/sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java @@ -109,6 +109,11 @@ static class HubConfiguration { if (options.getTracesSampleRate() == null) { options.setTracesSampleRate(0.0); } + // Spring Boot sets ignored exceptions in runtime using reflection - where the generic + // information is lost + // its technically possible to set non-throwable class to `ignoredExceptionsForType` set + // here we make sure that only classes that extend throwable are set on this field + options.getIgnoredExceptionsForType().removeIf(it -> !Throwable.class.isAssignableFrom(it)); Sentry.init(options); return HubAdapter.getInstance(); } diff --git a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index 32fe685f92..ea53e58e08 100644 --- a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -27,6 +27,7 @@ import io.sentry.test.checkEvent import io.sentry.transport.ITransport import io.sentry.transport.ITransportGate import io.sentry.transport.apache.ApacheHttpClientTransportFactory +import java.lang.RuntimeException import javax.servlet.Filter import kotlin.test.Test import kotlin.test.assertEquals @@ -142,7 +143,8 @@ class SentryAutoConfigurationTest { "sentry.enable-tracing=true", "sentry.traces-sample-rate=0.3", "sentry.tags.tag1=tag1-value", - "sentry.tags.tag2=tag2-value" + "sentry.tags.tag2=tag2-value", + "sentry.ignored-exceptions-for-type=java.lang.RuntimeException,java.lang.IllegalStateException,io.sentry.Sentry" ).run { val options = it.getBean(SentryProperties::class.java) assertThat(options.readTimeoutMillis).isEqualTo(10) @@ -169,6 +171,7 @@ class SentryAutoConfigurationTest { assertThat(options.isEnableTracing).isTrue() assertThat(options.tracesSampleRate).isEqualTo(0.3) assertThat(options.tags).containsEntry("tag1", "tag1-value").containsEntry("tag2", "tag2-value") + assertThat(options.ignoredExceptionsForType).containsOnly(RuntimeException::class.java, IllegalStateException::class.java) } } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index ba7cfa7f6e..dd2e2403f4 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -719,11 +719,12 @@ public final class io/sentry/SentryLevel : java/lang/Enum { public class io/sentry/SentryOptions { public fun ()V public fun addEventProcessor (Lio/sentry/EventProcessor;)V + public fun addIgnoredExceptionForType (Ljava/lang/Class;)V public fun addInAppExclude (Ljava/lang/String;)V public fun addInAppInclude (Ljava/lang/String;)V public fun addIntegration (Lio/sentry/Integration;)V public fun addScopeObserver (Lio/sentry/IScopeObserver;)V - public static fun from (Lio/sentry/config/PropertiesProvider;)Lio/sentry/SentryOptions; + public static fun from (Lio/sentry/config/PropertiesProvider;Lio/sentry/ILogger;)Lio/sentry/SentryOptions; public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback; public fun getBeforeSend ()Lio/sentry/SentryOptions$BeforeSendCallback; public fun getCacheDirPath ()Ljava/lang/String; @@ -740,6 +741,7 @@ public class io/sentry/SentryOptions { public fun getEventProcessors ()Ljava/util/List; public fun getFlushTimeoutMillis ()J public fun getHostnameVerifier ()Ljavax/net/ssl/HostnameVerifier; + public fun getIgnoredExceptionsForType ()Ljava/util/Set; public fun getInAppExcludes ()Ljava/util/List; public fun getInAppIncludes ()Ljava/util/List; public fun getIntegrations ()Ljava/util/List; diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 7986fea8df..49e010da95 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -184,7 +184,7 @@ private static synchronized void init( private static boolean initConfigurations(final @NotNull SentryOptions options) { if (options.isEnableExternalConfiguration()) { - options.merge(SentryOptions.from(PropertiesProviderFactory.create())); + options.merge(SentryOptions.from(PropertiesProviderFactory.create(), options.getLogger())); } final String dsn = options.getDsn(); diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 3943a3c9f1..4d2934c18e 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -94,7 +94,19 @@ public boolean isEnabled() { } } + SentryId sentryId = SentryId.EMPTY_ID; + if (event != null) { + if (event.getOriginThrowable() != null + && options.containsIgnoredExceptionForType(event.getOriginThrowable())) { + options + .getLogger() + .log( + SentryLevel.DEBUG, + "Event was dropped as the exception %s is ignored", + event.getOriginThrowable().getClass()); + return sentryId; + } event = executeBeforeSend(event, hint); if (event == null) { @@ -102,8 +114,6 @@ public boolean isEnabled() { } } - SentryId sentryId = SentryId.EMPTY_ID; - if (event != null) { sentryId = event.getEventId(); } diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index efb76e227d..8c3330b79d 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -10,10 +10,13 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSocketFactory; import org.jetbrains.annotations.ApiStatus; @@ -36,6 +39,10 @@ public class SentryOptions { */ private final @NotNull List eventProcessors = new CopyOnWriteArrayList<>(); + /** Exceptions that once captured will not be sent to Sentry as {@link SentryEvent}. */ + private final @NotNull Set> ignoredExceptionsForType = + new CopyOnWriteArraySet<>(); + /** * Code that provides middlewares, bindings or hooks into certain frameworks or environments, * along with code that inserts those bindings and activates them. @@ -267,7 +274,9 @@ public class SentryOptions { * @param propertiesProvider the properties provider * @return the sentry options */ - public static @NotNull SentryOptions from(final @NotNull PropertiesProvider propertiesProvider) { + @SuppressWarnings("unchecked") + public static @NotNull SentryOptions from( + final @NotNull PropertiesProvider propertiesProvider, final @NotNull ILogger logger) { final SentryOptions options = new SentryOptions(); options.setDsn(propertiesProvider.getProperty("dsn")); options.setEnvironment(propertiesProvider.getProperty("environment")); @@ -299,6 +308,27 @@ public class SentryOptions { for (final String inAppExclude : propertiesProvider.getList("in-app-excludes")) { options.addInAppExclude(inAppExclude); } + for (final String ignoredExceptionType : + propertiesProvider.getList("ignored-exceptions-for-type")) { + try { + Class clazz = Class.forName(ignoredExceptionType); + if (Throwable.class.isAssignableFrom(clazz)) { + options.addIgnoredExceptionForType((Class) clazz); + } else { + logger.log( + SentryLevel.WARNING, + "Skipping setting %s as ignored-exception-for-type. Reason: %s does not extend Throwable", + ignoredExceptionType, + ignoredExceptionType); + } + } catch (ClassNotFoundException e) { + logger.log( + SentryLevel.WARNING, + "Skipping setting %s as ignored-exception-for-type. Reason: %s class is not found", + ignoredExceptionType, + ignoredExceptionType); + } + } return options; } @@ -1278,6 +1308,36 @@ public boolean isTracingEnabled() { return getTracesSampleRate() != null || getTracesSampler() != null; } + /** + * Returns the list of exception classes that once captured will not be sent to Sentry as {@link + * SentryEvent}. + * + * @return the list of exception classes that once captured will not be sent to Sentry as {@link + * SentryEvent}. + */ + public @NotNull Set> getIgnoredExceptionsForType() { + return ignoredExceptionsForType; + } + + /** + * Adds exception type to the list of ignored exceptions. + * + * @param exceptionType - the exception type + */ + public void addIgnoredExceptionForType(final @NotNull Class exceptionType) { + this.ignoredExceptionsForType.add(exceptionType); + } + + /** + * Checks if the type of exception given by parameter is ignored. + * + * @param throwable the throwable + * @return if the type of exception is ignored + */ + boolean containsIgnoredExceptionForType(final @NotNull Throwable throwable) { + return this.ignoredExceptionsForType.contains(throwable.getClass()); + } + /** The BeforeSend callback */ public interface BeforeSendCallback { @@ -1410,6 +1470,10 @@ void merge(final @NotNull SentryOptions options) { for (final String inAppExclude : inAppExcludes) { addInAppExclude(inAppExclude); } + for (final Class exceptionType : + new HashSet<>(options.getIgnoredExceptionsForType())) { + addIgnoredExceptionForType(exceptionType); + } } private @NotNull SdkVersion createSdkVersion() { diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 95a4523eba..543c69ed23 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -10,6 +10,7 @@ import com.nhaarman.mockitokotlin2.mockingDetails import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import com.nhaarman.mockitokotlin2.verifyZeroInteractions import com.nhaarman.mockitokotlin2.whenever import io.sentry.exception.InvalidDsnException import io.sentry.exception.SentryEnvelopeException @@ -29,6 +30,7 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStreamReader +import java.lang.IllegalStateException import java.lang.RuntimeException import java.nio.charset.Charset import java.util.Arrays @@ -903,6 +905,14 @@ class SentryClientTest { assertEquals("abc", transaction.platform) } + @Test + fun `when exception type is ignored, capturing event does not send it`() { + fixture.sentryOptions.addIgnoredExceptionForType(IllegalStateException::class.java) + val sut = fixture.getSut() + sut.captureException(IllegalStateException()) + verifyZeroInteractions(fixture.transport) + } + private fun createScope(): Scope { return Scope(SentryOptions()).apply { addBreadcrumb(Breadcrumb().apply { diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index e094e24f67..c1545711e3 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -1,8 +1,12 @@ package io.sentry +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify import io.sentry.config.PropertiesProviderFactory import java.io.File +import java.lang.RuntimeException import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -241,177 +245,79 @@ class SentryOptionsTest { @Test fun `creates options with proxy using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("proxy.host=proxy.example.com\n") - file.appendText("proxy.port=9090\n") - file.appendText("proxy.user=some-user\n") - file.appendText("proxy.pass=some-pass") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertNotNull(options.proxy) - assertEquals("proxy.example.com", options.proxy!!.host) - assertEquals("9090", options.proxy!!.port) - assertEquals("some-user", options.proxy!!.user) - assertEquals("some-pass", options.proxy!!.pass) - } finally { - temporaryFolder.delete() + withPropertiesFile(listOf("proxy.host=proxy.example.com", "proxy.port=9090", "proxy.user=some-user", "proxy.pass=some-pass")) { + assertNotNull(it.proxy) { proxy -> + assertEquals("proxy.example.com", proxy.host) + assertEquals("9090", proxy.port) + assertEquals("some-user", proxy.user) + assertEquals("some-pass", proxy.pass) + } } } @Test fun `when proxy port is not set default proxy port is used`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("proxy.host=proxy.example.com\n") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertNotNull(options.proxy) - assertEquals("proxy.example.com", options.proxy!!.host) - assertEquals("80", options.proxy!!.port) - } finally { - temporaryFolder.delete() + withPropertiesFile("proxy.host=proxy.example.com") { + assertNotNull(it.proxy) + assertEquals("proxy.example.com", it.proxy!!.host) + assertEquals("80", it.proxy!!.port) } } @Test fun `creates options with tags using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("tags.tag1=value1\n") - file.appendText("tags.tag2=value2") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertEquals(mapOf("tag1" to "value1", "tag2" to "value2"), options.tags) - } finally { - temporaryFolder.delete() + withPropertiesFile(listOf("tags.tag1=value1", "tags.tag2=value2")) { + assertEquals(mapOf("tag1" to "value1", "tag2" to "value2"), it.tags) } } @Test fun `creates options with uncaught handler set to true enabled using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("uncaught.handler.enabled=true") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertNotNull(options.enableUncaughtExceptionHandler) - assertTrue(options.enableUncaughtExceptionHandler!!) - } finally { - temporaryFolder.delete() + withPropertiesFile("uncaught.handler.enabled=true") { options -> + assertNotNull(options.enableUncaughtExceptionHandler) { + assertTrue(it) + } } } @Test fun `creates options with uncaught handler set to false enabled using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("uncaught.handler.enabled=false") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertNotNull(options.enableUncaughtExceptionHandler) - assertFalse(options.enableUncaughtExceptionHandler!!) - } finally { - temporaryFolder.delete() + withPropertiesFile("uncaught.handler.enabled=false") { options -> + assertNotNull(options.enableUncaughtExceptionHandler) { + assertFalse(it) + } } } @Test fun `creates options with uncaught handler set to null enabled using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertNull(options.enableUncaughtExceptionHandler) - } finally { - temporaryFolder.delete() + withPropertiesFile { + assertNull(it.enableUncaughtExceptionHandler) } } @Test fun `creates options with debug set to true enabled using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("debug=true") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertTrue(options.isDebug) - } finally { - temporaryFolder.delete() + withPropertiesFile("debug=true") { + assertTrue(it.isDebug) } } @Test fun `creates options with debug set to false enabled using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("debug=false") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertFalse(options.isDebug) - } finally { - temporaryFolder.delete() + withPropertiesFile("debug=false") { + assertFalse(it.isDebug) } } @Test fun `creates options with debug set to null enabled using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) + withPropertiesFile() { val mergeResult = SentryOptions().apply { setDebug(true) } - mergeResult.merge(options) + mergeResult.merge(it) assertTrue(mergeResult.isDebug) - } finally { - temporaryFolder.delete() } } @@ -422,62 +328,64 @@ class SentryOptionsTest { @Test fun `creates options with inAppInclude and inAppExclude using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("in-app-includes=org.springframework,com.myapp\n") - file.appendText("in-app-excludes=org.jboss,com.microsoft") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertEquals(listOf("org.springframework", "com.myapp"), options.inAppIncludes) - assertEquals(listOf("org.jboss", "com.microsoft"), options.inAppExcludes) - } finally { - temporaryFolder.delete() + withPropertiesFile(listOf("in-app-includes=org.springframework,com.myapp", "in-app-excludes=org.jboss,com.microsoft")) { + assertEquals(listOf("org.springframework", "com.myapp"), it.inAppIncludes) + assertEquals(listOf("org.jboss", "com.microsoft"), it.inAppExcludes) } } @Test fun `creates options with tracesSampleRate using external properties`() { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - file.appendText("traces-sample-rate=0.2") - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertEquals(0.2, options.tracesSampleRate) - } finally { - temporaryFolder.delete() + withPropertiesFile("traces-sample-rate=0.2") { + assertEquals(0.2, it.tracesSampleRate) } } @Test fun `creates options with enableDeduplication using external properties`() { + withPropertiesFile("enable-deduplication=true") { + assertTrue(it.isEnableDeduplication) + } + } + + @Test + fun `creates options with ignored exception types using external properties`() { + val logger = mock() + // Setting few types of classes: + // - RuntimeException and IllegalStateException - valid exception classes + // - NonExistingClass - class that does not exist - should not trigger a failure to create options + // - io.sentry.Sentry - class that does not extend Throwable - should not trigger a failure + withPropertiesFile("ignored-exceptions-for-type=java.lang.RuntimeException,java.lang.IllegalStateException,com.xx.NonExistingClass,io.sentry.Sentry", logger) { options -> + assertTrue(options.ignoredExceptionsForType.contains(RuntimeException::class.java)) + assertTrue(options.ignoredExceptionsForType.contains(IllegalStateException::class.java)) + verify(logger).log(eq(SentryLevel.WARNING), any(), eq("com.xx.NonExistingClass"), eq("com.xx.NonExistingClass")) + verify(logger).log(eq(SentryLevel.WARNING), any(), eq("io.sentry.Sentry"), eq("io.sentry.Sentry")) + } + } + + @Test + fun `when options are initialized, maxAttachmentSize is 20`() { + assertEquals(20 * 1024 * 1024, SentryOptions().maxAttachmentSize) + } + + private fun withPropertiesFile(textLines: List = emptyList(), logger: ILogger = mock(), fn: (SentryOptions) -> Unit) { // create a sentry.properties file in temporary folder val temporaryFolder = TemporaryFolder() temporaryFolder.create() val file = temporaryFolder.newFile("sentry.properties") - file.appendText("enable-deduplication=true") + textLines.forEach { file.appendText("$it\n") } // set location of the sentry.properties file System.setProperty("sentry.properties.file", file.absolutePath) try { - val options = SentryOptions.from(PropertiesProviderFactory.create()) - assertTrue(options.isEnableDeduplication) + val options = SentryOptions.from(PropertiesProviderFactory.create(), logger) + fn.invoke(options) } finally { temporaryFolder.delete() } } - @Test - fun `when options are initialized, maxAttachmentSize is 20`() { - assertEquals(20 * 1024 * 1024, SentryOptions().maxAttachmentSize) + private fun withPropertiesFile(text: String, logger: ILogger = mock(), fn: (SentryOptions) -> Unit) { + withPropertiesFile(listOf(text), logger, fn) } }